@marlinjai/mail-contract 0.6.1 → 0.8.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 +16533 -2594
- package/dist/index.d.ts +16533 -2594
- package/dist/index.js +1063 -763
- package/dist/index.mjs +1036 -763
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -248,6 +248,13 @@ __export(index_exports, {
|
|
|
248
248
|
MAX_STEPS: () => MAX_STEPS,
|
|
249
249
|
MAX_STEP_VISITS: () => MAX_STEP_VISITS,
|
|
250
250
|
MAX_SUBJECT_LENGTH: () => MAX_SUBJECT_LENGTH,
|
|
251
|
+
MAX_TEMPLATE_INPUTS: () => MAX_TEMPLATE_INPUTS,
|
|
252
|
+
MAX_TEMPLATE_INPUTS_APP_LENGTH: () => MAX_TEMPLATE_INPUTS_APP_LENGTH,
|
|
253
|
+
MAX_TEMPLATE_INPUTS_SEEN: () => MAX_TEMPLATE_INPUTS_SEEN,
|
|
254
|
+
MAX_TEMPLATE_INPUT_EXAMPLE_LENGTH: () => MAX_TEMPLATE_INPUT_EXAMPLE_LENGTH,
|
|
255
|
+
MAX_TEMPLATE_INPUT_LABEL_LENGTH: () => MAX_TEMPLATE_INPUT_LABEL_LENGTH,
|
|
256
|
+
MAX_TEMPLATE_INPUT_NOTE_LENGTH: () => MAX_TEMPLATE_INPUT_NOTE_LENGTH,
|
|
257
|
+
MAX_TEMPLATE_INPUT_VALUES: () => MAX_TEMPLATE_INPUT_VALUES,
|
|
251
258
|
MAX_TEMPLATE_TRANSLATIONS: () => MAX_TEMPLATE_TRANSLATIONS,
|
|
252
259
|
MAX_TRIGGERS: () => MAX_TRIGGERS,
|
|
253
260
|
MEMBER_ROLES: () => MEMBER_ROLES,
|
|
@@ -342,6 +349,7 @@ __export(index_exports, {
|
|
|
342
349
|
RecipientStatus: () => RecipientStatus,
|
|
343
350
|
RemoteImage: () => RemoteImage,
|
|
344
351
|
ResendSigningSecret: () => ResendSigningSecret,
|
|
352
|
+
SERVICE_FILLED_MERGE_FIELDS: () => SERVICE_FILLED_MERGE_FIELDS,
|
|
345
353
|
SIGNUP_FORM_FIELDS: () => SIGNUP_FORM_FIELDS,
|
|
346
354
|
SKIP_REASONS: () => SKIP_REASONS,
|
|
347
355
|
SMTP_SECURITY: () => SMTP_SECURITY,
|
|
@@ -371,6 +379,8 @@ __export(index_exports, {
|
|
|
371
379
|
SuppressionCreate: () => SuppressionCreate,
|
|
372
380
|
SuppressionListQuery: () => SuppressionListQuery,
|
|
373
381
|
SuppressionReason: () => SuppressionReason,
|
|
382
|
+
TEMPLATE_INPUT_KINDS: () => TEMPLATE_INPUT_KINDS,
|
|
383
|
+
TEMPLATE_INPUT_PROBLEM_REASONS: () => TEMPLATE_INPUT_PROBLEM_REASONS,
|
|
374
384
|
TEMPLATE_LANGUAGE_STATUSES: () => TEMPLATE_LANGUAGE_STATUSES,
|
|
375
385
|
TERMINAL_MAILING_STATUSES: () => TERMINAL_MAILING_STATUSES,
|
|
376
386
|
Tag: () => Tag,
|
|
@@ -387,6 +397,16 @@ __export(index_exports, {
|
|
|
387
397
|
TemplateImportPreview: () => TemplateImportPreview,
|
|
388
398
|
TemplateImportPreviewRequest: () => TemplateImportPreviewRequest,
|
|
389
399
|
TemplateImportResult: () => TemplateImportResult,
|
|
400
|
+
TemplateInput: () => TemplateInput,
|
|
401
|
+
TemplateInputMap: () => TemplateInputMap,
|
|
402
|
+
TemplateInputName: () => TemplateInputName,
|
|
403
|
+
TemplateInputSeen: () => TemplateInputSeen,
|
|
404
|
+
TemplateInputSource: () => TemplateInputSource,
|
|
405
|
+
TemplateInputValue: () => TemplateInputValue,
|
|
406
|
+
TemplateInputs: () => TemplateInputs,
|
|
407
|
+
TemplateInputsDeclaration: () => TemplateInputsDeclaration,
|
|
408
|
+
TemplateInputsDeclared: () => TemplateInputsDeclared,
|
|
409
|
+
TemplateInputsSeen: () => TemplateInputsSeen,
|
|
390
410
|
TemplateLanguageStatus: () => TemplateLanguageStatus,
|
|
391
411
|
TemplateListQuery: () => TemplateListQuery,
|
|
392
412
|
TemplatePreheader: () => TemplatePreheader,
|
|
@@ -540,6 +560,10 @@ __export(index_exports, {
|
|
|
540
560
|
renameInsertionChecked: () => renameInsertionChecked,
|
|
541
561
|
renameInsertionChoice: () => renameInsertionChoice,
|
|
542
562
|
renameInsertionChoiceChecked: () => renameInsertionChoiceChecked,
|
|
563
|
+
renameInsertionEverywhere: () => renameInsertionEverywhere,
|
|
564
|
+
renameInsertionPlacements: () => renameInsertionPlacements,
|
|
565
|
+
renameTokenInDocument: () => renameTokenInDocument,
|
|
566
|
+
renameTokenInText: () => renameTokenInText,
|
|
543
567
|
resolveInsertions: () => resolveInsertions,
|
|
544
568
|
restorePatch: () => restorePatch,
|
|
545
569
|
routes: () => routes,
|
|
@@ -555,6 +579,8 @@ __export(index_exports, {
|
|
|
555
579
|
stepTargets: () => stepTargets,
|
|
556
580
|
storedTemplateLine: () => storedTemplateLine,
|
|
557
581
|
stronglyConnected: () => stronglyConnected,
|
|
582
|
+
templateInputDrift: () => templateInputDrift,
|
|
583
|
+
templateInputProblems: () => templateInputProblems,
|
|
558
584
|
templateRoutes: () => templateRoutes,
|
|
559
585
|
testGroupSize: () => testGroupSize,
|
|
560
586
|
tightLoops: () => tightLoops,
|
|
@@ -562,6 +588,7 @@ __export(index_exports, {
|
|
|
562
588
|
translationInsertionProblems: () => translationInsertionProblems,
|
|
563
589
|
translationTextFromInput: () => translationTextFromInput,
|
|
564
590
|
triggerKey: () => triggerKey,
|
|
591
|
+
unknownRefusedInsertions: () => unknownRefusedInsertions,
|
|
565
592
|
unplacedInsertions: () => unplacedInsertions,
|
|
566
593
|
validateAutomation: () => validateAutomation,
|
|
567
594
|
verifyWebhook: () => verifyWebhook
|
|
@@ -715,13 +742,13 @@ var API_VERSION_PREFIX = "/v1";
|
|
|
715
742
|
var HEALTH_PATH = "/healthz";
|
|
716
743
|
|
|
717
744
|
// src/workspace.ts
|
|
718
|
-
var
|
|
745
|
+
var import_zod10 = require("zod");
|
|
719
746
|
|
|
720
747
|
// src/automations.ts
|
|
721
|
-
var
|
|
748
|
+
var import_zod9 = require("zod");
|
|
722
749
|
|
|
723
750
|
// src/templates.ts
|
|
724
|
-
var
|
|
751
|
+
var import_zod5 = require("zod");
|
|
725
752
|
|
|
726
753
|
// src/insertions.ts
|
|
727
754
|
var import_zod3 = require("zod");
|
|
@@ -1012,22 +1039,133 @@ function sampleMergeValue(values) {
|
|
|
1012
1039
|
};
|
|
1013
1040
|
}
|
|
1014
1041
|
|
|
1042
|
+
// src/template-inputs.ts
|
|
1043
|
+
var import_zod4 = require("zod");
|
|
1044
|
+
var MAX_TEMPLATE_INPUTS = 50;
|
|
1045
|
+
var MAX_TEMPLATE_INPUT_VALUES = 30;
|
|
1046
|
+
var MAX_TEMPLATE_INPUT_LABEL_LENGTH = 80;
|
|
1047
|
+
var MAX_TEMPLATE_INPUT_NOTE_LENGTH = 300;
|
|
1048
|
+
var MAX_TEMPLATE_INPUT_EXAMPLE_LENGTH = 500;
|
|
1049
|
+
var MAX_TEMPLATE_INPUTS_APP_LENGTH = 80;
|
|
1050
|
+
var MAX_TEMPLATE_INPUTS_SEEN = 200;
|
|
1051
|
+
var SERVICE_FILLED_MERGE_FIELDS = ["email", "unsubscribe_url"];
|
|
1052
|
+
var line = (max) => import_zod4.z.string().min(1).max(max).regex(/^[^\x00-\x1f\x7f]*$/, "one line, without control characters").refine((s) => s.trim() !== "", "not blank");
|
|
1053
|
+
var label = line(MAX_TEMPLATE_INPUT_LABEL_LENGTH);
|
|
1054
|
+
var note = line(MAX_TEMPLATE_INPUT_NOTE_LENGTH);
|
|
1055
|
+
var TemplateInputName = InsertionName;
|
|
1056
|
+
var TemplateInputSource = import_zod4.z.discriminatedUnion("kind", [
|
|
1057
|
+
import_zod4.z.object({ kind: import_zod4.z.literal("form_field"), form: label, field: label, note: note.optional() }),
|
|
1058
|
+
import_zod4.z.object({ kind: import_zod4.z.literal("derived"), note })
|
|
1059
|
+
]);
|
|
1060
|
+
var TemplateInputValue = import_zod4.z.object({
|
|
1061
|
+
/** The key the app sends, in an insertion choice's syntax, so a row "When it is venue" matches it exactly. */
|
|
1062
|
+
value: InsertionChoiceKey,
|
|
1063
|
+
label
|
|
1064
|
+
});
|
|
1065
|
+
var HttpUrl = import_zod4.z.string().max(MAX_TEMPLATE_INPUT_EXAMPLE_LENGTH).url().refine((u) => {
|
|
1066
|
+
try {
|
|
1067
|
+
const { protocol } = new URL(u);
|
|
1068
|
+
return protocol === "https:" || protocol === "http:";
|
|
1069
|
+
} catch {
|
|
1070
|
+
return false;
|
|
1071
|
+
}
|
|
1072
|
+
}, "an http or https address");
|
|
1073
|
+
var common = {
|
|
1074
|
+
/** What an author sees: "Contact reason", "First name". */
|
|
1075
|
+
label,
|
|
1076
|
+
source: TemplateInputSource,
|
|
1077
|
+
/** The app may send it empty or not at all (a name nobody typed). Absent means it is always sent. */
|
|
1078
|
+
optional: import_zod4.z.boolean().optional()
|
|
1079
|
+
};
|
|
1080
|
+
var TemplateInput = import_zod4.z.discriminatedUnion("kind", [
|
|
1081
|
+
import_zod4.z.object({ kind: import_zod4.z.literal("text"), ...common, example: import_zod4.z.string().max(MAX_TEMPLATE_INPUT_EXAMPLE_LENGTH).optional() }),
|
|
1082
|
+
import_zod4.z.object({ kind: import_zod4.z.literal("url"), ...common, example: HttpUrl.optional() }),
|
|
1083
|
+
import_zod4.z.object({ kind: import_zod4.z.literal("boolean"), ...common, example: import_zod4.z.boolean().optional() }),
|
|
1084
|
+
import_zod4.z.object({
|
|
1085
|
+
kind: import_zod4.z.literal("one_of"),
|
|
1086
|
+
...common,
|
|
1087
|
+
values: import_zod4.z.array(TemplateInputValue).min(1).max(MAX_TEMPLATE_INPUT_VALUES),
|
|
1088
|
+
example: InsertionChoiceKey.optional()
|
|
1089
|
+
})
|
|
1090
|
+
]);
|
|
1091
|
+
var TEMPLATE_INPUT_KINDS = ["text", "url", "boolean", "one_of"];
|
|
1092
|
+
var TemplateInputMap = import_zod4.z.record(TemplateInputName, TemplateInput).refine((m) => Object.keys(m).length <= MAX_TEMPLATE_INPUTS, `at most ${MAX_TEMPLATE_INPUTS} inputs`);
|
|
1093
|
+
var TemplateInputsDeclaration = import_zod4.z.object({
|
|
1094
|
+
app: line(MAX_TEMPLATE_INPUTS_APP_LENGTH),
|
|
1095
|
+
app_version: line(MAX_TEMPLATE_INPUTS_APP_LENGTH).optional(),
|
|
1096
|
+
inputs: TemplateInputMap
|
|
1097
|
+
});
|
|
1098
|
+
var TemplateInputs = import_zod4.z.object({
|
|
1099
|
+
app: import_zod4.z.string(),
|
|
1100
|
+
app_version: import_zod4.z.string().nullable(),
|
|
1101
|
+
inputs: TemplateInputMap,
|
|
1102
|
+
declared_by: import_zod4.z.object({ api_key_id: Id, api_key_name: import_zod4.z.string() }),
|
|
1103
|
+
declared_at: Timestamp
|
|
1104
|
+
});
|
|
1105
|
+
var TemplateInputSeen = import_zod4.z.object({
|
|
1106
|
+
count: import_zod4.z.number().int().min(1),
|
|
1107
|
+
first_seen_at: Timestamp,
|
|
1108
|
+
last_seen_at: Timestamp
|
|
1109
|
+
});
|
|
1110
|
+
var TemplateInputsSeen = import_zod4.z.record(TemplateInputName, TemplateInputSeen);
|
|
1111
|
+
var TemplateInputsDeclared = import_zod4.z.object({
|
|
1112
|
+
template_id: Id,
|
|
1113
|
+
inputs: TemplateInputs,
|
|
1114
|
+
/** False when the stored declaration already said exactly this (a redeploy of the same commit). */
|
|
1115
|
+
changed: import_zod4.z.boolean()
|
|
1116
|
+
});
|
|
1117
|
+
var TEMPLATE_INPUT_PROBLEM_REASONS = [
|
|
1118
|
+
/** `email` or `unsubscribe_url`: the service fills it, a client's value is never read. */
|
|
1119
|
+
"input_reserved",
|
|
1120
|
+
/** A `one_of` input lists the same value twice. */
|
|
1121
|
+
"input_values",
|
|
1122
|
+
/** A `one_of` input's example is not one of its values. */
|
|
1123
|
+
"input_example"
|
|
1124
|
+
];
|
|
1125
|
+
function templateInputProblems(inputs) {
|
|
1126
|
+
const problems = [];
|
|
1127
|
+
for (const [name, input] of Object.entries(inputs)) {
|
|
1128
|
+
if (SERVICE_FILLED_MERGE_FIELDS.includes(name)) {
|
|
1129
|
+
problems.push({ reason: "input_reserved", path: name, message: `"${name}" is filled by the service, never by the app; leave it out of the declaration` });
|
|
1130
|
+
}
|
|
1131
|
+
if (input.kind !== "one_of") continue;
|
|
1132
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1133
|
+
input.values.forEach((v, i) => {
|
|
1134
|
+
if (seen.has(v.value)) {
|
|
1135
|
+
problems.push({ reason: "input_values", path: `${name}.values.${i}`, message: `"${v.value}" is listed twice` });
|
|
1136
|
+
}
|
|
1137
|
+
seen.add(v.value);
|
|
1138
|
+
});
|
|
1139
|
+
if (input.example !== void 0 && !seen.has(input.example)) {
|
|
1140
|
+
problems.push({ reason: "input_example", path: `${name}.example`, message: `the example "${input.example}" is not one of the values` });
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
return problems;
|
|
1144
|
+
}
|
|
1145
|
+
function templateInputDrift(inputs, seen) {
|
|
1146
|
+
const declared = inputs ? Object.keys(inputs.inputs) : [];
|
|
1147
|
+
return {
|
|
1148
|
+
declared_not_seen: declared.filter((name) => ownEntry(seen, name) === void 0).sort(),
|
|
1149
|
+
seen_not_declared: Object.keys(seen).filter((name) => ownEntry(inputs?.inputs, name) === void 0).sort()
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1015
1153
|
// src/templates.ts
|
|
1016
1154
|
var DOCUMENT_SCHEMA_VERSIONS = ["1.0", "1.1"];
|
|
1017
|
-
var DocumentSchemaVersion =
|
|
1018
|
-
var TemplateDocument =
|
|
1155
|
+
var DocumentSchemaVersion = import_zod5.z.enum(DOCUMENT_SCHEMA_VERSIONS);
|
|
1156
|
+
var TemplateDocument = import_zod5.z.object({
|
|
1019
1157
|
version: DocumentSchemaVersion,
|
|
1020
|
-
metadata:
|
|
1021
|
-
sections:
|
|
1158
|
+
metadata: import_zod5.z.record(import_zod5.z.unknown()),
|
|
1159
|
+
sections: import_zod5.z.array(import_zod5.z.record(import_zod5.z.unknown()))
|
|
1022
1160
|
}).passthrough();
|
|
1023
1161
|
var MAX_DOCUMENT_BYTES = 1e6;
|
|
1024
1162
|
var MAX_SUBJECT_LENGTH = 998;
|
|
1025
1163
|
var MAX_PREHEADER_LENGTH = 500;
|
|
1026
|
-
var TemplateSubject =
|
|
1027
|
-
var TemplatePreheader =
|
|
1164
|
+
var TemplateSubject = import_zod5.z.string().max(MAX_SUBJECT_LENGTH).regex(/^[^\x00-\x1f\x7f]*$/, "one line, without control characters");
|
|
1165
|
+
var TemplatePreheader = import_zod5.z.string().max(MAX_PREHEADER_LENGTH).regex(/^[^\x00-\x1f\x7f]*$/, "one line, without control characters");
|
|
1028
1166
|
var TEMPLATE_LANGUAGE_STATUSES = ["draft", "ready"];
|
|
1029
|
-
var TemplateLanguageStatus =
|
|
1030
|
-
var TemplateTranslation =
|
|
1167
|
+
var TemplateLanguageStatus = import_zod5.z.enum(TEMPLATE_LANGUAGE_STATUSES);
|
|
1168
|
+
var TemplateTranslation = import_zod5.z.object({
|
|
1031
1169
|
subject: TemplateSubject.nullable(),
|
|
1032
1170
|
preheader: TemplatePreheader.nullable(),
|
|
1033
1171
|
document: TemplateDocument,
|
|
@@ -1045,11 +1183,11 @@ var TemplateTranslation = import_zod4.z.object({
|
|
|
1045
1183
|
insertions: TranslationInsertions.optional()
|
|
1046
1184
|
});
|
|
1047
1185
|
var MAX_TEMPLATE_TRANSLATIONS = 19;
|
|
1048
|
-
var TemplateTranslations =
|
|
1049
|
-
var Template =
|
|
1186
|
+
var TemplateTranslations = import_zod5.z.record(LocaleTag, TemplateTranslation).refine((t) => Object.keys(t).length <= MAX_TEMPLATE_TRANSLATIONS, `at most ${MAX_TEMPLATE_TRANSLATIONS} language versions`);
|
|
1187
|
+
var Template = import_zod5.z.object({
|
|
1050
1188
|
id: Id,
|
|
1051
|
-
name:
|
|
1052
|
-
description:
|
|
1189
|
+
name: import_zod5.z.string().min(1).max(200),
|
|
1190
|
+
description: import_zod5.z.string().max(2e3).nullable(),
|
|
1053
1191
|
/**
|
|
1054
1192
|
* The main language: what `subject`, `preheader` and `document` are written
|
|
1055
1193
|
* in, and what every recipient without a ready version of their own gets.
|
|
@@ -1073,21 +1211,33 @@ var Template = import_zod4.z.object({
|
|
|
1073
1211
|
* texts in `translations[tag].insertions`.
|
|
1074
1212
|
*/
|
|
1075
1213
|
insertions: Insertions,
|
|
1214
|
+
/**
|
|
1215
|
+
* What the sending app declared this template receives
|
|
1216
|
+
* (`templates.declareInputs`); null until an app declares. Not part of a
|
|
1217
|
+
* version: declaring never changes `version` or `updated_at`.
|
|
1218
|
+
*/
|
|
1219
|
+
inputs: TemplateInputs.nullable(),
|
|
1220
|
+
/**
|
|
1221
|
+
* The merge names real sends of this template carried, with a count and
|
|
1222
|
+
* when first and last seen. Names only, never values. `{}` before the
|
|
1223
|
+
* first send.
|
|
1224
|
+
*/
|
|
1225
|
+
inputs_seen: TemplateInputsSeen,
|
|
1076
1226
|
/** Increments on every saved change, of any language. */
|
|
1077
|
-
version:
|
|
1078
|
-
thumbnail_url:
|
|
1227
|
+
version: import_zod5.z.number().int().min(1),
|
|
1228
|
+
thumbnail_url: import_zod5.z.string().url().nullable(),
|
|
1079
1229
|
archived_at: Timestamp.nullable(),
|
|
1080
1230
|
created_at: Timestamp,
|
|
1081
1231
|
updated_at: Timestamp
|
|
1082
1232
|
});
|
|
1083
|
-
var TemplateSummary = Template.omit({ document: true, translations: true, insertions: true }).extend({
|
|
1084
|
-
languages:
|
|
1233
|
+
var TemplateSummary = Template.omit({ document: true, translations: true, insertions: true, inputs: true, inputs_seen: true }).extend({
|
|
1234
|
+
languages: import_zod5.z.record(LocaleTag, TemplateLanguageStatus),
|
|
1085
1235
|
/** The names of the template's insertions, sorted alphabetically. */
|
|
1086
|
-
insertion_names:
|
|
1236
|
+
insertion_names: import_zod5.z.array(import_zod5.z.string())
|
|
1087
1237
|
});
|
|
1088
|
-
var TemplateCreate =
|
|
1089
|
-
name:
|
|
1090
|
-
description:
|
|
1238
|
+
var TemplateCreate = import_zod5.z.object({
|
|
1239
|
+
name: import_zod5.z.string().min(1).max(200),
|
|
1240
|
+
description: import_zod5.z.string().max(2e3).optional(),
|
|
1091
1241
|
locale: LocaleTag.optional(),
|
|
1092
1242
|
subject: TemplateSubject.nullable().optional(),
|
|
1093
1243
|
preheader: TemplatePreheader.nullable().optional(),
|
|
@@ -1095,15 +1245,15 @@ var TemplateCreate = import_zod4.z.object({
|
|
|
1095
1245
|
translations: TemplateTranslations.optional(),
|
|
1096
1246
|
insertions: Insertions.optional()
|
|
1097
1247
|
});
|
|
1098
|
-
var TemplateUpdate =
|
|
1099
|
-
base_version:
|
|
1100
|
-
name:
|
|
1101
|
-
description:
|
|
1248
|
+
var TemplateUpdate = import_zod5.z.object({
|
|
1249
|
+
base_version: import_zod5.z.number().int().min(1),
|
|
1250
|
+
name: import_zod5.z.string().min(1).max(200).optional(),
|
|
1251
|
+
description: import_zod5.z.string().max(2e3).nullable().optional(),
|
|
1102
1252
|
locale: LocaleTag.optional(),
|
|
1103
1253
|
subject: TemplateSubject.nullable().optional(),
|
|
1104
1254
|
preheader: TemplatePreheader.nullable().optional(),
|
|
1105
1255
|
document: TemplateDocument.optional(),
|
|
1106
|
-
translations:
|
|
1256
|
+
translations: import_zod5.z.record(LocaleTag, TemplateTranslation.nullable()).optional(),
|
|
1107
1257
|
/**
|
|
1108
1258
|
* The main language's insertions, whole: what is left out is removed, from
|
|
1109
1259
|
* every language in the same save. A rename is a removal and an addition,
|
|
@@ -1111,87 +1261,87 @@ var TemplateUpdate = import_zod4.z.object({
|
|
|
1111
1261
|
* save (it sends those languages in `translations`).
|
|
1112
1262
|
*/
|
|
1113
1263
|
insertions: Insertions.optional(),
|
|
1114
|
-
archived:
|
|
1264
|
+
archived: import_zod5.z.boolean().optional()
|
|
1115
1265
|
}).refine(
|
|
1116
1266
|
(v) => v.name !== void 0 || v.description !== void 0 || v.locale !== void 0 || v.subject !== void 0 || v.preheader !== void 0 || v.document !== void 0 || v.translations !== void 0 || v.insertions !== void 0 || v.archived !== void 0,
|
|
1117
1267
|
"at least one field besides base_version"
|
|
1118
1268
|
);
|
|
1119
|
-
var SavedSectionBody =
|
|
1269
|
+
var SavedSectionBody = import_zod5.z.record(import_zod5.z.unknown());
|
|
1120
1270
|
var MAX_SAVED_SECTIONS = 50;
|
|
1121
|
-
var SavedSection =
|
|
1271
|
+
var SavedSection = import_zod5.z.object({
|
|
1122
1272
|
id: Id,
|
|
1123
|
-
name:
|
|
1124
|
-
description:
|
|
1273
|
+
name: import_zod5.z.string().min(1).max(200),
|
|
1274
|
+
description: import_zod5.z.string().max(2e3).nullable(),
|
|
1125
1275
|
section: SavedSectionBody,
|
|
1126
|
-
created_by:
|
|
1276
|
+
created_by: import_zod5.z.string().nullable(),
|
|
1127
1277
|
created_at: Timestamp,
|
|
1128
1278
|
updated_at: Timestamp
|
|
1129
1279
|
});
|
|
1130
|
-
var SavedSectionCreate =
|
|
1131
|
-
name:
|
|
1132
|
-
description:
|
|
1280
|
+
var SavedSectionCreate = import_zod5.z.object({
|
|
1281
|
+
name: import_zod5.z.string().min(1).max(200),
|
|
1282
|
+
description: import_zod5.z.string().max(2e3).optional(),
|
|
1133
1283
|
section: SavedSectionBody
|
|
1134
1284
|
});
|
|
1135
|
-
var SavedSectionUpdate =
|
|
1136
|
-
name:
|
|
1137
|
-
description:
|
|
1285
|
+
var SavedSectionUpdate = import_zod5.z.object({
|
|
1286
|
+
name: import_zod5.z.string().min(1).max(200).optional(),
|
|
1287
|
+
description: import_zod5.z.string().max(2e3).nullable().optional(),
|
|
1138
1288
|
section: SavedSectionBody.optional()
|
|
1139
1289
|
}).refine((v) => v.name !== void 0 || v.description !== void 0 || v.section !== void 0, "at least one field");
|
|
1140
1290
|
var TemplateListQuery = PageQuery.extend({
|
|
1141
|
-
archived:
|
|
1291
|
+
archived: import_zod5.z.enum(["true", "false"]).optional()
|
|
1142
1292
|
});
|
|
1143
|
-
var TemplateVersion =
|
|
1293
|
+
var TemplateVersion = import_zod5.z.object({
|
|
1144
1294
|
template_id: Id,
|
|
1145
|
-
version:
|
|
1295
|
+
version: import_zod5.z.number().int().min(1),
|
|
1146
1296
|
locale: LocaleTag,
|
|
1147
1297
|
subject: TemplateSubject.nullable(),
|
|
1148
1298
|
preheader: TemplatePreheader.nullable(),
|
|
1149
1299
|
document: TemplateDocument,
|
|
1150
1300
|
translations: TemplateTranslations,
|
|
1151
1301
|
insertions: Insertions,
|
|
1152
|
-
created_by:
|
|
1302
|
+
created_by: import_zod5.z.string().nullable(),
|
|
1153
1303
|
created_at: Timestamp
|
|
1154
1304
|
});
|
|
1155
|
-
var TemplateVersionParams =
|
|
1305
|
+
var TemplateVersionParams = import_zod5.z.object({
|
|
1156
1306
|
id: Id,
|
|
1157
|
-
version:
|
|
1307
|
+
version: import_zod5.z.coerce.number().int().min(1)
|
|
1158
1308
|
});
|
|
1159
|
-
var CompileMessage =
|
|
1160
|
-
message:
|
|
1309
|
+
var CompileMessage = import_zod5.z.object({
|
|
1310
|
+
message: import_zod5.z.string().min(1),
|
|
1161
1311
|
/** Where in the document or the MJML the message points, when known. */
|
|
1162
|
-
path:
|
|
1163
|
-
line:
|
|
1312
|
+
path: import_zod5.z.string().optional(),
|
|
1313
|
+
line: import_zod5.z.number().int().min(1).optional()
|
|
1164
1314
|
});
|
|
1165
|
-
var CompileResult =
|
|
1166
|
-
mjml:
|
|
1167
|
-
html:
|
|
1168
|
-
warnings:
|
|
1169
|
-
errors:
|
|
1315
|
+
var CompileResult = import_zod5.z.object({
|
|
1316
|
+
mjml: import_zod5.z.string(),
|
|
1317
|
+
html: import_zod5.z.string(),
|
|
1318
|
+
warnings: import_zod5.z.array(CompileMessage),
|
|
1319
|
+
errors: import_zod5.z.array(CompileMessage)
|
|
1170
1320
|
});
|
|
1171
|
-
var TemplateCompileRequest =
|
|
1172
|
-
version:
|
|
1321
|
+
var TemplateCompileRequest = import_zod5.z.object({
|
|
1322
|
+
version: import_zod5.z.number().int().min(1).optional(),
|
|
1173
1323
|
locale: LocaleTag.optional()
|
|
1174
1324
|
});
|
|
1175
|
-
var CompileRequest =
|
|
1325
|
+
var CompileRequest = import_zod5.z.object({
|
|
1176
1326
|
document: TemplateDocument
|
|
1177
1327
|
});
|
|
1178
1328
|
var ASSET_CONTENT_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
1179
|
-
var AssetContentType =
|
|
1329
|
+
var AssetContentType = import_zod5.z.enum(ASSET_CONTENT_TYPES);
|
|
1180
1330
|
var MAX_ASSET_BYTES = 10 * 1024 * 1024;
|
|
1181
|
-
var Asset =
|
|
1331
|
+
var Asset = import_zod5.z.object({
|
|
1182
1332
|
id: Id,
|
|
1183
|
-
url:
|
|
1333
|
+
url: import_zod5.z.string().url(),
|
|
1184
1334
|
content_type: AssetContentType,
|
|
1185
|
-
size_bytes:
|
|
1186
|
-
width:
|
|
1187
|
-
height:
|
|
1188
|
-
filename:
|
|
1335
|
+
size_bytes: import_zod5.z.number().int().min(1).max(MAX_ASSET_BYTES),
|
|
1336
|
+
width: import_zod5.z.number().int().min(1).nullable(),
|
|
1337
|
+
height: import_zod5.z.number().int().min(1).nullable(),
|
|
1338
|
+
filename: import_zod5.z.string().min(1).max(255),
|
|
1189
1339
|
created_at: Timestamp
|
|
1190
1340
|
});
|
|
1191
1341
|
var ASSET_UPLOAD_FIELD = "file";
|
|
1192
1342
|
var MAX_IMPORT_URL_LENGTH = 2048;
|
|
1193
|
-
var AssetImport =
|
|
1194
|
-
url:
|
|
1343
|
+
var AssetImport = import_zod5.z.object({
|
|
1344
|
+
url: import_zod5.z.string().max(MAX_IMPORT_URL_LENGTH).url().refine((u) => {
|
|
1195
1345
|
try {
|
|
1196
1346
|
const url = new URL(u);
|
|
1197
1347
|
if (url.protocol === "https:") return true;
|
|
@@ -1201,7 +1351,7 @@ var AssetImport = import_zod4.z.object({
|
|
|
1201
1351
|
}
|
|
1202
1352
|
}, "must be https (http only for localhost)"),
|
|
1203
1353
|
/** The stored file name; derived from the address when omitted. */
|
|
1204
|
-
filename:
|
|
1354
|
+
filename: import_zod5.z.string().min(1).max(255).optional()
|
|
1205
1355
|
});
|
|
1206
1356
|
var MAX_MJML_IMPORT_BYTES = 512 * 1024;
|
|
1207
1357
|
var MAX_MJML_DEPTH = 32;
|
|
@@ -1216,56 +1366,56 @@ var MJML_IMPORT_REFUSALS = [
|
|
|
1216
1366
|
"too_complex",
|
|
1217
1367
|
"invalid_document"
|
|
1218
1368
|
];
|
|
1219
|
-
var ImportWarning =
|
|
1220
|
-
severity:
|
|
1369
|
+
var ImportWarning = import_zod5.z.object({
|
|
1370
|
+
severity: import_zod5.z.enum(["info", "warning"]),
|
|
1221
1371
|
/** Stable kind, e.g. `kept_as_html`, `unknown_component`, `remote_image_not_imported`. */
|
|
1222
|
-
code:
|
|
1372
|
+
code: import_zod5.z.string().min(1).max(64),
|
|
1223
1373
|
/** Where in the source, e.g. `mj-body > mj-section[2] > mj-column[1] > mj-social[1]`. */
|
|
1224
|
-
path:
|
|
1225
|
-
line:
|
|
1226
|
-
message:
|
|
1227
|
-
fragment:
|
|
1374
|
+
path: import_zod5.z.string(),
|
|
1375
|
+
line: import_zod5.z.number().int().min(1).optional(),
|
|
1376
|
+
message: import_zod5.z.string().min(1),
|
|
1377
|
+
fragment: import_zod5.z.string().optional()
|
|
1228
1378
|
});
|
|
1229
|
-
var RemoteImage =
|
|
1230
|
-
url:
|
|
1379
|
+
var RemoteImage = import_zod5.z.object({
|
|
1380
|
+
url: import_zod5.z.string().min(1),
|
|
1231
1381
|
/** The element and attribute it is loaded from, e.g. `<img src>`. */
|
|
1232
|
-
where:
|
|
1382
|
+
where: import_zod5.z.string()
|
|
1233
1383
|
});
|
|
1234
|
-
var ImportedAsset =
|
|
1235
|
-
source_url:
|
|
1384
|
+
var ImportedAsset = import_zod5.z.object({
|
|
1385
|
+
source_url: import_zod5.z.string().min(1),
|
|
1236
1386
|
asset_id: Id,
|
|
1237
|
-
url:
|
|
1387
|
+
url: import_zod5.z.string().url()
|
|
1238
1388
|
});
|
|
1239
|
-
var mjmlSource =
|
|
1240
|
-
var TemplateImportPreviewRequest =
|
|
1241
|
-
var TemplateImportPreview =
|
|
1389
|
+
var mjmlSource = import_zod5.z.string().min(1, "paste or upload the MJML");
|
|
1390
|
+
var TemplateImportPreviewRequest = import_zod5.z.object({ mjml: mjmlSource });
|
|
1391
|
+
var TemplateImportPreview = import_zod5.z.object({
|
|
1242
1392
|
document: TemplateDocument,
|
|
1243
|
-
warnings:
|
|
1393
|
+
warnings: import_zod5.z.array(ImportWarning),
|
|
1244
1394
|
/** The document compiled under the workspace's asset policy, exactly as a send would. */
|
|
1245
1395
|
compiled: CompileResult,
|
|
1246
1396
|
/** Images loaded from outside the service; under `service_only` each is also a compile error. */
|
|
1247
|
-
remote_images:
|
|
1248
|
-
asset_policy:
|
|
1397
|
+
remote_images: import_zod5.z.array(RemoteImage),
|
|
1398
|
+
asset_policy: import_zod5.z.enum(["any", "service_only"])
|
|
1249
1399
|
});
|
|
1250
|
-
var TemplateImport =
|
|
1251
|
-
name:
|
|
1252
|
-
description:
|
|
1400
|
+
var TemplateImport = import_zod5.z.object({
|
|
1401
|
+
name: import_zod5.z.string().min(1).max(200),
|
|
1402
|
+
description: import_zod5.z.string().max(2e3).optional(),
|
|
1253
1403
|
mjml: mjmlSource,
|
|
1254
|
-
import_remote_assets:
|
|
1404
|
+
import_remote_assets: import_zod5.z.boolean().optional()
|
|
1255
1405
|
});
|
|
1256
|
-
var TemplateImportResult =
|
|
1406
|
+
var TemplateImportResult = import_zod5.z.object({
|
|
1257
1407
|
template: Template,
|
|
1258
|
-
warnings:
|
|
1259
|
-
imported_assets:
|
|
1408
|
+
warnings: import_zod5.z.array(ImportWarning),
|
|
1409
|
+
imported_assets: import_zod5.z.array(ImportedAsset)
|
|
1260
1410
|
});
|
|
1261
1411
|
var EXPORT_FORMATS = ["mjml", "html"];
|
|
1262
|
-
var ExportFormat =
|
|
1263
|
-
var TemplateExportQuery =
|
|
1412
|
+
var ExportFormat = import_zod5.z.enum(EXPORT_FORMATS);
|
|
1413
|
+
var TemplateExportQuery = import_zod5.z.object({
|
|
1264
1414
|
format: ExportFormat,
|
|
1265
|
-
version:
|
|
1415
|
+
version: import_zod5.z.coerce.number().int().min(1).optional(),
|
|
1266
1416
|
locale: LocaleTag.optional()
|
|
1267
1417
|
});
|
|
1268
|
-
var MailingExportQuery =
|
|
1418
|
+
var MailingExportQuery = import_zod5.z.object({ format: ExportFormat });
|
|
1269
1419
|
var EXPORT_CONTENT_TYPES = {
|
|
1270
1420
|
mjml: "text/plain; charset=utf-8",
|
|
1271
1421
|
html: "text/html; charset=utf-8"
|
|
@@ -1284,7 +1434,7 @@ function formatExportWarningsHeader(messages) {
|
|
|
1284
1434
|
function parseExportWarningsHeader(value) {
|
|
1285
1435
|
if (!value) return [];
|
|
1286
1436
|
try {
|
|
1287
|
-
const parsed =
|
|
1437
|
+
const parsed = import_zod5.z.array(CompileMessage).safeParse(JSON.parse(decodeURIComponent(value)));
|
|
1288
1438
|
return parsed.success ? parsed.data : [];
|
|
1289
1439
|
} catch {
|
|
1290
1440
|
return [];
|
|
@@ -1296,32 +1446,32 @@ function exportFilename(name, format) {
|
|
|
1296
1446
|
}
|
|
1297
1447
|
|
|
1298
1448
|
// src/platform.ts
|
|
1299
|
-
var
|
|
1449
|
+
var import_zod8 = require("zod");
|
|
1300
1450
|
|
|
1301
1451
|
// src/mailings.ts
|
|
1302
|
-
var
|
|
1452
|
+
var import_zod7 = require("zod");
|
|
1303
1453
|
|
|
1304
1454
|
// src/ab-test.ts
|
|
1305
|
-
var
|
|
1306
|
-
var AbVariant =
|
|
1307
|
-
key:
|
|
1308
|
-
subject:
|
|
1455
|
+
var import_zod6 = require("zod");
|
|
1456
|
+
var AbVariant = import_zod6.z.object({
|
|
1457
|
+
key: import_zod6.z.string().regex(/^[a-z]$/, "a single lowercase letter"),
|
|
1458
|
+
subject: import_zod6.z.string().min(1).max(998).optional(),
|
|
1309
1459
|
/** Replaces the mailing's document for this variant. */
|
|
1310
|
-
document:
|
|
1460
|
+
document: import_zod6.z.record(import_zod6.z.unknown()).optional(),
|
|
1311
1461
|
/**
|
|
1312
1462
|
* Keeps the document this variant key already has in the mailing's current
|
|
1313
1463
|
* test, so a test can be changed without sending every document again. The
|
|
1314
1464
|
* service refuses it (`validation_failed`) when that variant has none.
|
|
1315
1465
|
*/
|
|
1316
|
-
keep_document:
|
|
1466
|
+
keep_document: import_zod6.z.literal(true).optional()
|
|
1317
1467
|
});
|
|
1318
1468
|
var AB_WINNER_METRICS = ["opens", "clicks", "manual"];
|
|
1319
|
-
var AbTestConfig =
|
|
1320
|
-
variants:
|
|
1469
|
+
var AbTestConfig = import_zod6.z.object({
|
|
1470
|
+
variants: import_zod6.z.array(AbVariant).min(2).max(5),
|
|
1321
1471
|
/** Share of recipients in the test group, split evenly across variants; the rest get the winner. */
|
|
1322
|
-
test_fraction:
|
|
1323
|
-
winner_metric:
|
|
1324
|
-
decide_after_minutes:
|
|
1472
|
+
test_fraction: import_zod6.z.number().gt(0).lte(1),
|
|
1473
|
+
winner_metric: import_zod6.z.enum(AB_WINNER_METRICS),
|
|
1474
|
+
decide_after_minutes: import_zod6.z.number().int().min(15).max(10080).optional()
|
|
1325
1475
|
}).refine((c) => new Set(c.variants.map((v) => v.key)).size === c.variants.length, "variant keys must be unique").refine((c) => c.variants.every((v) => v.subject !== void 0 || v.document !== void 0 || v.keep_document === true), {
|
|
1326
1476
|
message: "every variant changes the subject, the document or both",
|
|
1327
1477
|
path: ["variants"]
|
|
@@ -1333,19 +1483,19 @@ var AbTestConfig = import_zod5.z.object({
|
|
|
1333
1483
|
path: ["decide_after_minutes"]
|
|
1334
1484
|
});
|
|
1335
1485
|
var AB_TEST_STATUSES = ["pending", "testing", "awaiting_pick", "decided"];
|
|
1336
|
-
var AbTestState =
|
|
1337
|
-
variants:
|
|
1338
|
-
test_fraction:
|
|
1339
|
-
winner_metric:
|
|
1340
|
-
decide_after_minutes:
|
|
1341
|
-
status:
|
|
1486
|
+
var AbTestState = import_zod6.z.object({
|
|
1487
|
+
variants: import_zod6.z.array(import_zod6.z.object({ key: import_zod6.z.string(), subject: import_zod6.z.string().nullable(), has_document: import_zod6.z.boolean() })),
|
|
1488
|
+
test_fraction: import_zod6.z.number(),
|
|
1489
|
+
winner_metric: import_zod6.z.enum(AB_WINNER_METRICS),
|
|
1490
|
+
decide_after_minutes: import_zod6.z.number().int().nullable(),
|
|
1491
|
+
status: import_zod6.z.enum(AB_TEST_STATUSES),
|
|
1342
1492
|
/** When the metric decides; null for manual or before the start. */
|
|
1343
1493
|
decide_at: Timestamp.nullable(),
|
|
1344
|
-
winner:
|
|
1345
|
-
decided_by:
|
|
1494
|
+
winner: import_zod6.z.string().nullable(),
|
|
1495
|
+
decided_by: import_zod6.z.enum(["metric", "manual"]).nullable(),
|
|
1346
1496
|
decided_at: Timestamp.nullable()
|
|
1347
1497
|
});
|
|
1348
|
-
var AbWinnerRequest =
|
|
1498
|
+
var AbWinnerRequest = import_zod6.z.object({ variant: import_zod6.z.string().regex(/^[a-z]$/) });
|
|
1349
1499
|
function testGroupSize(total, fraction, variants) {
|
|
1350
1500
|
return Math.min(total, Math.max(variants, Math.ceil(total * fraction)));
|
|
1351
1501
|
}
|
|
@@ -1360,9 +1510,9 @@ var MAILING_STATUSES = [
|
|
|
1360
1510
|
"partially_failed",
|
|
1361
1511
|
"cancelled"
|
|
1362
1512
|
];
|
|
1363
|
-
var MailingStatus =
|
|
1513
|
+
var MailingStatus = import_zod7.z.enum(MAILING_STATUSES);
|
|
1364
1514
|
var MAILING_ACTIONS = ["send", "schedule", "unschedule", "pause", "resume", "cancel", "retry-failed"];
|
|
1365
|
-
var MailingAction =
|
|
1515
|
+
var MailingAction = import_zod7.z.enum(MAILING_ACTIONS);
|
|
1366
1516
|
var MAILING_TRANSITIONS = {
|
|
1367
1517
|
draft: ["send", "schedule", "cancel"],
|
|
1368
1518
|
scheduled: ["send", "schedule", "unschedule", "cancel"],
|
|
@@ -1377,23 +1527,23 @@ function canTransition(status, action) {
|
|
|
1377
1527
|
}
|
|
1378
1528
|
var EDITABLE_MAILING_STATUSES = ["draft", "scheduled"];
|
|
1379
1529
|
var TERMINAL_MAILING_STATUSES = ["sent", "cancelled"];
|
|
1380
|
-
var MailingMetadata =
|
|
1381
|
-
var MailingCounts =
|
|
1382
|
-
total:
|
|
1383
|
-
queued:
|
|
1384
|
-
sending:
|
|
1385
|
-
sent:
|
|
1386
|
-
failed:
|
|
1387
|
-
skipped:
|
|
1530
|
+
var MailingMetadata = import_zod7.z.record(import_zod7.z.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");
|
|
1531
|
+
var MailingCounts = import_zod7.z.object({
|
|
1532
|
+
total: import_zod7.z.number().int().min(0),
|
|
1533
|
+
queued: import_zod7.z.number().int().min(0),
|
|
1534
|
+
sending: import_zod7.z.number().int().min(0),
|
|
1535
|
+
sent: import_zod7.z.number().int().min(0),
|
|
1536
|
+
failed: import_zod7.z.number().int().min(0),
|
|
1537
|
+
skipped: import_zod7.z.number().int().min(0)
|
|
1388
1538
|
});
|
|
1389
1539
|
var MAILING_KINDS = ["broadcast", "automation", "notification"];
|
|
1390
|
-
var MailingKind =
|
|
1391
|
-
var Mailing =
|
|
1540
|
+
var MailingKind = import_zod7.z.enum(MAILING_KINDS);
|
|
1541
|
+
var Mailing = import_zod7.z.object({
|
|
1392
1542
|
id: Id,
|
|
1393
1543
|
kind: MailingKind,
|
|
1394
|
-
name:
|
|
1395
|
-
subject:
|
|
1396
|
-
preheader:
|
|
1544
|
+
name: import_zod7.z.string().max(200).nullable(),
|
|
1545
|
+
subject: import_zod7.z.string().min(1).max(998),
|
|
1546
|
+
preheader: import_zod7.z.string().max(500).nullable(),
|
|
1397
1547
|
/**
|
|
1398
1548
|
* The template the document was taken from, if any. The snapshot is what is
|
|
1399
1549
|
* sent in the main language; the template's other language versions are
|
|
@@ -1438,15 +1588,15 @@ var Mailing = import_zod6.z.object({
|
|
|
1438
1588
|
* recipients refused as dead addresses in one run (see the provider's
|
|
1439
1589
|
* `rejections.anomaly`). Resuming clears it and starts a new run.
|
|
1440
1590
|
*/
|
|
1441
|
-
pause_reason:
|
|
1591
|
+
pause_reason: import_zod7.z.string().nullable().default(null),
|
|
1442
1592
|
created_at: Timestamp,
|
|
1443
1593
|
updated_at: Timestamp
|
|
1444
1594
|
});
|
|
1445
1595
|
var MailingSummary = Mailing.omit({ document: true });
|
|
1446
1596
|
var MailingContent = {
|
|
1447
|
-
name:
|
|
1448
|
-
subject:
|
|
1449
|
-
preheader:
|
|
1597
|
+
name: import_zod7.z.string().max(200).optional(),
|
|
1598
|
+
subject: import_zod7.z.string().min(1).max(998),
|
|
1599
|
+
preheader: import_zod7.z.string().max(500).optional(),
|
|
1450
1600
|
/** See `Mailing.send_locale`. */
|
|
1451
1601
|
send_locale: LocaleTag.nullable().optional(),
|
|
1452
1602
|
/**
|
|
@@ -1459,7 +1609,7 @@ var MailingContent = {
|
|
|
1459
1609
|
provider_id: Id,
|
|
1460
1610
|
metadata: MailingMetadata.optional()
|
|
1461
1611
|
};
|
|
1462
|
-
var MailingCreate =
|
|
1612
|
+
var MailingCreate = import_zod7.z.object({
|
|
1463
1613
|
...MailingContent,
|
|
1464
1614
|
subject: MailingContent.subject.optional(),
|
|
1465
1615
|
document: TemplateDocument.optional(),
|
|
@@ -1471,10 +1621,10 @@ var MailingCreate = import_zod6.z.object({
|
|
|
1471
1621
|
message: "a mailing made from a document needs a subject",
|
|
1472
1622
|
path: ["subject"]
|
|
1473
1623
|
});
|
|
1474
|
-
var MailingUpdate =
|
|
1475
|
-
name:
|
|
1624
|
+
var MailingUpdate = import_zod7.z.object({
|
|
1625
|
+
name: import_zod7.z.string().max(200).nullable().optional(),
|
|
1476
1626
|
subject: MailingContent.subject.optional(),
|
|
1477
|
-
preheader:
|
|
1627
|
+
preheader: import_zod7.z.string().max(500).nullable().optional(),
|
|
1478
1628
|
/** Null turns a draft into a letter, which it can only be with at most one recipient. */
|
|
1479
1629
|
topic: Slug.nullable().optional(),
|
|
1480
1630
|
send_locale: LocaleTag.nullable().optional(),
|
|
@@ -1488,30 +1638,30 @@ var MailingListQuery = PageQuery.extend({
|
|
|
1488
1638
|
kind: MailingKind.optional(),
|
|
1489
1639
|
automation_id: Id.optional()
|
|
1490
1640
|
});
|
|
1491
|
-
var MailingSendRequest =
|
|
1492
|
-
var MailingRetryFailedRequest =
|
|
1641
|
+
var MailingSendRequest = import_zod7.z.object({}).strict();
|
|
1642
|
+
var MailingRetryFailedRequest = import_zod7.z.object({
|
|
1493
1643
|
/**
|
|
1494
1644
|
* Also requeue recipients whose outcome is unknown after a crash. They may
|
|
1495
1645
|
* already have received the message, so this is a human's decision, never a
|
|
1496
1646
|
* default.
|
|
1497
1647
|
*/
|
|
1498
|
-
include_outcome_unknown:
|
|
1648
|
+
include_outcome_unknown: import_zod7.z.boolean().optional()
|
|
1499
1649
|
}).strict();
|
|
1500
|
-
var MailingActionRequest =
|
|
1501
|
-
var MailingTestRequest =
|
|
1650
|
+
var MailingActionRequest = import_zod7.z.object({}).strict();
|
|
1651
|
+
var MailingTestRequest = import_zod7.z.object({
|
|
1502
1652
|
to: Email,
|
|
1503
1653
|
merge: Properties.optional(),
|
|
1504
1654
|
locale: LocaleTag.optional()
|
|
1505
1655
|
});
|
|
1506
|
-
var MailingTestResult =
|
|
1656
|
+
var MailingTestResult = import_zod7.z.object({
|
|
1507
1657
|
message_id: Id,
|
|
1508
|
-
provider_message_id:
|
|
1658
|
+
provider_message_id: import_zod7.z.string().nullable()
|
|
1509
1659
|
});
|
|
1510
1660
|
var RECIPIENT_STATUSES = ["queued", "sending", "sent", "failed", "skipped"];
|
|
1511
|
-
var RecipientStatus =
|
|
1661
|
+
var RecipientStatus = import_zod7.z.enum(RECIPIENT_STATUSES);
|
|
1512
1662
|
var SKIP_REASONS = ["suppressed", "not_subscribed", "contact_erased", "cancelled", "outcome_unknown"];
|
|
1513
|
-
var SkipReason =
|
|
1514
|
-
var Recipient =
|
|
1663
|
+
var SkipReason = import_zod7.z.enum(SKIP_REASONS);
|
|
1664
|
+
var Recipient = import_zod7.z.object({
|
|
1515
1665
|
id: Id,
|
|
1516
1666
|
mailing_id: Id,
|
|
1517
1667
|
contact_id: Id.nullable(),
|
|
@@ -1519,55 +1669,55 @@ var Recipient = import_zod6.z.object({
|
|
|
1519
1669
|
merge: Properties,
|
|
1520
1670
|
status: RecipientStatus,
|
|
1521
1671
|
skip_reason: SkipReason.nullable(),
|
|
1522
|
-
attempts:
|
|
1672
|
+
attempts: import_zod7.z.number().int().min(0),
|
|
1523
1673
|
message_id: Id.nullable(),
|
|
1524
|
-
last_error:
|
|
1674
|
+
last_error: import_zod7.z.string().nullable(),
|
|
1525
1675
|
created_at: Timestamp,
|
|
1526
1676
|
updated_at: Timestamp
|
|
1527
1677
|
});
|
|
1528
1678
|
var MAX_RECIPIENTS_PER_BATCH = 1e3;
|
|
1529
1679
|
var MAX_LETTER_RECIPIENTS = 1;
|
|
1530
1680
|
var LETTER_RECIPIENT_REFUSAL = "letter_has_one_recipient";
|
|
1531
|
-
var RecipientInput =
|
|
1681
|
+
var RecipientInput = import_zod7.z.object({
|
|
1532
1682
|
contact_id: Id.optional(),
|
|
1533
|
-
external_id:
|
|
1683
|
+
external_id: import_zod7.z.string().min(1).max(255).optional(),
|
|
1534
1684
|
email: Email.optional(),
|
|
1535
1685
|
merge: Properties.optional()
|
|
1536
1686
|
}).refine((v) => v.contact_id !== void 0 || v.external_id !== void 0 || v.email !== void 0, {
|
|
1537
1687
|
message: "contact_id, external_id or email is required",
|
|
1538
1688
|
path: ["email"]
|
|
1539
1689
|
});
|
|
1540
|
-
var RecipientBatch =
|
|
1541
|
-
recipients:
|
|
1690
|
+
var RecipientBatch = import_zod7.z.object({
|
|
1691
|
+
recipients: import_zod7.z.array(RecipientInput).min(1).max(MAX_RECIPIENTS_PER_BATCH)
|
|
1542
1692
|
});
|
|
1543
1693
|
var RECIPIENT_REJECTIONS = ["unknown_contact", "duplicate_in_batch"];
|
|
1544
|
-
var RecipientBatchResult =
|
|
1694
|
+
var RecipientBatchResult = import_zod7.z.object({
|
|
1545
1695
|
/** Newly queued. */
|
|
1546
|
-
added:
|
|
1696
|
+
added: import_zod7.z.number().int().min(0),
|
|
1547
1697
|
/** Already on the mailing from an earlier batch: left unchanged. */
|
|
1548
|
-
already_present:
|
|
1698
|
+
already_present: import_zod7.z.number().int().min(0),
|
|
1549
1699
|
/** Items not added, by their index in the request. */
|
|
1550
|
-
rejected:
|
|
1700
|
+
rejected: import_zod7.z.array(import_zod7.z.object({ index: import_zod7.z.number().int().min(0), reason: import_zod7.z.enum(RECIPIENT_REJECTIONS) }))
|
|
1551
1701
|
});
|
|
1552
1702
|
var RecipientListQuery = PageQuery.extend({
|
|
1553
1703
|
status: RecipientStatus.optional(),
|
|
1554
1704
|
skip_reason: SkipReason.optional()
|
|
1555
1705
|
});
|
|
1556
1706
|
var MESSAGE_OUTCOMES = ["sent", "failed"];
|
|
1557
|
-
var MessageOutcome =
|
|
1558
|
-
var MessageSummary =
|
|
1707
|
+
var MessageOutcome = import_zod7.z.enum(MESSAGE_OUTCOMES);
|
|
1708
|
+
var MessageSummary = import_zod7.z.object({
|
|
1559
1709
|
id: Id,
|
|
1560
1710
|
mailing_id: Id.nullable(),
|
|
1561
1711
|
recipient_id: Id.nullable(),
|
|
1562
1712
|
contact_id: Id.nullable(),
|
|
1563
1713
|
to: Email,
|
|
1564
|
-
subject:
|
|
1714
|
+
subject: import_zod7.z.string(),
|
|
1565
1715
|
provider_id: Id,
|
|
1566
|
-
provider_message_id:
|
|
1716
|
+
provider_message_id: import_zod7.z.string().nullable(),
|
|
1567
1717
|
outcome: MessageOutcome,
|
|
1568
|
-
error:
|
|
1569
|
-
is_test:
|
|
1570
|
-
recipient_count:
|
|
1718
|
+
error: import_zod7.z.string().nullable(),
|
|
1719
|
+
is_test: import_zod7.z.boolean(),
|
|
1720
|
+
recipient_count: import_zod7.z.number().int().min(1),
|
|
1571
1721
|
/** The language version the message was sent in; null for messages archived before languages existed. */
|
|
1572
1722
|
locale: LocaleTag.nullable(),
|
|
1573
1723
|
/**
|
|
@@ -1579,22 +1729,22 @@ var MessageSummary = import_zod6.z.object({
|
|
|
1579
1729
|
insertions: InsertionChoices.nullable(),
|
|
1580
1730
|
created_at: Timestamp
|
|
1581
1731
|
});
|
|
1582
|
-
var Message = MessageSummary.extend({ html:
|
|
1583
|
-
var MailingLanguageStatus =
|
|
1584
|
-
var MailingLanguage =
|
|
1732
|
+
var Message = MessageSummary.extend({ html: import_zod7.z.string() });
|
|
1733
|
+
var MailingLanguageStatus = import_zod7.z.enum(["main", "ready", "draft", "not_a_workspace_language"]);
|
|
1734
|
+
var MailingLanguage = import_zod7.z.object({
|
|
1585
1735
|
locale: LocaleTag,
|
|
1586
1736
|
status: MailingLanguageStatus,
|
|
1587
1737
|
/** Queued recipients who will get this version. */
|
|
1588
|
-
planned:
|
|
1738
|
+
planned: import_zod7.z.number().int().min(0),
|
|
1589
1739
|
/** Messages already sent in this language (tests excluded). */
|
|
1590
|
-
sent:
|
|
1740
|
+
sent: import_zod7.z.number().int().min(0)
|
|
1591
1741
|
});
|
|
1592
|
-
var MailingLanguageReport =
|
|
1742
|
+
var MailingLanguageReport = import_zod7.z.object({
|
|
1593
1743
|
main_locale: LocaleTag,
|
|
1594
1744
|
send_locale: LocaleTag.nullable(),
|
|
1595
|
-
languages:
|
|
1596
|
-
fallback:
|
|
1597
|
-
no_language:
|
|
1745
|
+
languages: import_zod7.z.array(MailingLanguage),
|
|
1746
|
+
fallback: import_zod7.z.array(import_zod7.z.object({ wanted: import_zod7.z.string(), planned: import_zod7.z.number().int().min(1) })),
|
|
1747
|
+
no_language: import_zod7.z.number().int().min(0),
|
|
1598
1748
|
/**
|
|
1599
1749
|
* Values that no choice of an insertion takes, so the "otherwise" text goes
|
|
1600
1750
|
* out (docs/plans/2026-09-25-translated-insertions.md, decision 3): per
|
|
@@ -1603,12 +1753,12 @@ var MailingLanguageReport = import_zod6.z.object({
|
|
|
1603
1753
|
* template's keys never produces one; a non-empty list means the client and
|
|
1604
1754
|
* the template have drifted apart.
|
|
1605
1755
|
*/
|
|
1606
|
-
insertion_misses:
|
|
1607
|
-
|
|
1608
|
-
insertion:
|
|
1609
|
-
value:
|
|
1610
|
-
planned:
|
|
1611
|
-
sent:
|
|
1756
|
+
insertion_misses: import_zod7.z.array(
|
|
1757
|
+
import_zod7.z.object({
|
|
1758
|
+
insertion: import_zod7.z.string(),
|
|
1759
|
+
value: import_zod7.z.string(),
|
|
1760
|
+
planned: import_zod7.z.number().int().min(0),
|
|
1761
|
+
sent: import_zod7.z.number().int().min(0)
|
|
1612
1762
|
})
|
|
1613
1763
|
)
|
|
1614
1764
|
});
|
|
@@ -1618,26 +1768,26 @@ var MessageListQuery = PageQuery.extend({
|
|
|
1618
1768
|
});
|
|
1619
1769
|
|
|
1620
1770
|
// src/platform.ts
|
|
1621
|
-
var Tag =
|
|
1771
|
+
var Tag = import_zod8.z.object({
|
|
1622
1772
|
id: Id,
|
|
1623
1773
|
slug: Slug,
|
|
1624
|
-
name:
|
|
1625
|
-
contact_count:
|
|
1774
|
+
name: import_zod8.z.string().min(1).max(120),
|
|
1775
|
+
contact_count: import_zod8.z.number().int().min(0),
|
|
1626
1776
|
created_at: Timestamp
|
|
1627
1777
|
});
|
|
1628
|
-
var TagCreate =
|
|
1629
|
-
var TagAssignment =
|
|
1630
|
-
contact_ids:
|
|
1778
|
+
var TagCreate = import_zod8.z.object({ slug: Slug, name: import_zod8.z.string().min(1).max(120) });
|
|
1779
|
+
var TagAssignment = import_zod8.z.object({
|
|
1780
|
+
contact_ids: import_zod8.z.array(Id).min(1).max(1e3)
|
|
1631
1781
|
});
|
|
1632
1782
|
var CONTACT_PROPERTY_TYPES = ["string", "number", "boolean", "date"];
|
|
1633
|
-
var ContactPropertyType =
|
|
1634
|
-
var ContactPropertyKey =
|
|
1635
|
-
var ContactPropertyDefinition =
|
|
1783
|
+
var ContactPropertyType = import_zod8.z.enum(CONTACT_PROPERTY_TYPES);
|
|
1784
|
+
var ContactPropertyKey = import_zod8.z.string().regex(/^[A-Za-z0-9_.-]{1,64}$/, "letters, digits, dot, underscore and hyphen, 1 to 64");
|
|
1785
|
+
var ContactPropertyDefinition = import_zod8.z.object({
|
|
1636
1786
|
key: ContactPropertyKey,
|
|
1637
|
-
label:
|
|
1787
|
+
label: import_zod8.z.string().min(1).max(120),
|
|
1638
1788
|
type: ContactPropertyType
|
|
1639
1789
|
});
|
|
1640
|
-
var ContactPropertyParams =
|
|
1790
|
+
var ContactPropertyParams = import_zod8.z.object({ key: ContactPropertyKey });
|
|
1641
1791
|
var FILTER_OPERATORS = [
|
|
1642
1792
|
"eq",
|
|
1643
1793
|
"neq",
|
|
@@ -1653,8 +1803,8 @@ var FILTER_OPERATORS = [
|
|
|
1653
1803
|
"in",
|
|
1654
1804
|
"not_in"
|
|
1655
1805
|
];
|
|
1656
|
-
var FilterOperator =
|
|
1657
|
-
var FilterField =
|
|
1806
|
+
var FilterOperator = import_zod8.z.enum(FILTER_OPERATORS);
|
|
1807
|
+
var FilterField = import_zod8.z.string().regex(
|
|
1658
1808
|
/^(email|first_name|last_name|locale|created_at|tag|topic|engagement:(opened|clicked)|property:[A-Za-z0-9_.-]{1,64})$/,
|
|
1659
1809
|
"unknown filter field"
|
|
1660
1810
|
);
|
|
@@ -1670,18 +1820,18 @@ var FILTER_FIELD_OPERATORS = {
|
|
|
1670
1820
|
property: FILTER_OPERATORS
|
|
1671
1821
|
};
|
|
1672
1822
|
var MAX_FILTER_DEPTH = 5;
|
|
1673
|
-
var FilterCondition =
|
|
1823
|
+
var FilterCondition = import_zod8.z.object({ field: FilterField, op: FilterOperator, value: JsonValue.optional() }).strict().refine((c) => (c.op === "exists" || c.op === "not_exists") === (c.value === void 0), {
|
|
1674
1824
|
message: "exists and not_exists take no value; every other operator needs one",
|
|
1675
1825
|
path: ["value"]
|
|
1676
1826
|
}).refine((c) => c.op !== "in" && c.op !== "not_in" || Array.isArray(c.value), {
|
|
1677
1827
|
message: "in and not_in take an array",
|
|
1678
1828
|
path: ["value"]
|
|
1679
1829
|
});
|
|
1680
|
-
var SegmentFilter =
|
|
1681
|
-
() =>
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1830
|
+
var SegmentFilter = import_zod8.z.lazy(
|
|
1831
|
+
() => import_zod8.z.union([
|
|
1832
|
+
import_zod8.z.object({ and: import_zod8.z.array(SegmentFilter).min(1).max(50) }).strict(),
|
|
1833
|
+
import_zod8.z.object({ or: import_zod8.z.array(SegmentFilter).min(1).max(50) }).strict(),
|
|
1834
|
+
import_zod8.z.object({ not: SegmentFilter }).strict(),
|
|
1685
1835
|
FilterCondition
|
|
1686
1836
|
])
|
|
1687
1837
|
);
|
|
@@ -1769,42 +1919,42 @@ function untypedValueProblem(c, every) {
|
|
|
1769
1919
|
if (orderOp) return every(isFilterNumber, "numbers (define the property to compare dates)");
|
|
1770
1920
|
return every((v) => isScalar(v) || v === null, "strings, numbers, booleans or null");
|
|
1771
1921
|
}
|
|
1772
|
-
var Segment =
|
|
1922
|
+
var Segment = import_zod8.z.object({
|
|
1773
1923
|
id: Id,
|
|
1774
|
-
name:
|
|
1924
|
+
name: import_zod8.z.string().min(1).max(120),
|
|
1775
1925
|
filter: SegmentFilter,
|
|
1776
1926
|
/** Computed on read. */
|
|
1777
|
-
contact_count:
|
|
1927
|
+
contact_count: import_zod8.z.number().int().min(0),
|
|
1778
1928
|
created_at: Timestamp,
|
|
1779
1929
|
updated_at: Timestamp
|
|
1780
1930
|
});
|
|
1781
|
-
var SegmentCreate =
|
|
1782
|
-
var SegmentPreviewRequest =
|
|
1783
|
-
var SegmentPreview =
|
|
1784
|
-
contact_count:
|
|
1931
|
+
var SegmentCreate = import_zod8.z.object({ name: import_zod8.z.string().min(1).max(120), filter: BoundedSegmentFilter });
|
|
1932
|
+
var SegmentPreviewRequest = import_zod8.z.object({ filter: BoundedSegmentFilter });
|
|
1933
|
+
var SegmentPreview = import_zod8.z.object({
|
|
1934
|
+
contact_count: import_zod8.z.number().int().min(0),
|
|
1785
1935
|
/** Up to five matching contacts, for a human to sanity-check the filter. */
|
|
1786
|
-
sample:
|
|
1936
|
+
sample: import_zod8.z.array(import_zod8.z.object({ id: Id, email: Email })).max(5)
|
|
1787
1937
|
});
|
|
1788
|
-
var MailingAudienceFromSegment =
|
|
1938
|
+
var MailingAudienceFromSegment = import_zod8.z.object({ segment_id: Id });
|
|
1789
1939
|
var SIGNUP_FORM_FIELDS = ["first_name", "last_name"];
|
|
1790
1940
|
var HOSTED_PAGE_LOCALES = ["en", "de", "it", "fr", "es"];
|
|
1791
|
-
var SignupFormTranslation =
|
|
1792
|
-
title:
|
|
1793
|
-
consent_text:
|
|
1941
|
+
var SignupFormTranslation = import_zod8.z.object({
|
|
1942
|
+
title: import_zod8.z.string().min(1).max(120),
|
|
1943
|
+
consent_text: import_zod8.z.string().min(1).max(2e3)
|
|
1794
1944
|
});
|
|
1795
|
-
var SignupForm =
|
|
1945
|
+
var SignupForm = import_zod8.z.object({
|
|
1796
1946
|
id: Id,
|
|
1797
|
-
name:
|
|
1947
|
+
name: import_zod8.z.string().min(1).max(120),
|
|
1798
1948
|
/** The heading of the hosted page. */
|
|
1799
|
-
title:
|
|
1949
|
+
title: import_zod8.z.string().min(1).max(120),
|
|
1800
1950
|
/** What the person agrees to, shown above the button and recorded with the confirmation. */
|
|
1801
|
-
consent_text:
|
|
1802
|
-
translations:
|
|
1803
|
-
topics:
|
|
1804
|
-
tags:
|
|
1951
|
+
consent_text: import_zod8.z.string().min(1).max(2e3),
|
|
1952
|
+
translations: import_zod8.z.record(SignupFormTranslation),
|
|
1953
|
+
topics: import_zod8.z.array(Slug).min(1),
|
|
1954
|
+
tags: import_zod8.z.array(Slug),
|
|
1805
1955
|
/** Fields shown besides email. */
|
|
1806
|
-
fields:
|
|
1807
|
-
double_opt_in:
|
|
1956
|
+
fields: import_zod8.z.array(import_zod8.z.enum(SIGNUP_FORM_FIELDS)),
|
|
1957
|
+
double_opt_in: import_zod8.z.literal(true),
|
|
1808
1958
|
/** The provider the confirmation mail is sent through. */
|
|
1809
1959
|
provider_id: Id,
|
|
1810
1960
|
/**
|
|
@@ -1813,11 +1963,11 @@ var SignupForm = import_zod7.z.object({
|
|
|
1813
1963
|
*/
|
|
1814
1964
|
confirmation_template_id: Id.nullable(),
|
|
1815
1965
|
/** Where the person lands after confirming. Null shows the hosted confirmation page. */
|
|
1816
|
-
redirect_url:
|
|
1966
|
+
redirect_url: import_zod8.z.string().url().nullable(),
|
|
1817
1967
|
/** Origins allowed to embed the form and call the submission route from a browser. */
|
|
1818
|
-
allowed_origins:
|
|
1968
|
+
allowed_origins: import_zod8.z.array(import_zod8.z.string().url()).max(20),
|
|
1819
1969
|
/** Bumped on every change; recorded with each confirmation. */
|
|
1820
|
-
version:
|
|
1970
|
+
version: import_zod8.z.number().int().min(1),
|
|
1821
1971
|
created_at: Timestamp,
|
|
1822
1972
|
updated_at: Timestamp
|
|
1823
1973
|
});
|
|
@@ -1832,56 +1982,56 @@ var SignupFormCreate = SignupForm.omit({
|
|
|
1832
1982
|
tags: SignupForm.shape.tags.optional(),
|
|
1833
1983
|
fields: SignupForm.shape.fields.optional(),
|
|
1834
1984
|
confirmation_template_id: Id.nullable().optional(),
|
|
1835
|
-
redirect_url:
|
|
1985
|
+
redirect_url: import_zod8.z.string().url().nullable().optional(),
|
|
1836
1986
|
allowed_origins: SignupForm.shape.allowed_origins.optional()
|
|
1837
1987
|
});
|
|
1838
|
-
var SignupFormEmbed =
|
|
1839
|
-
hosted_url:
|
|
1988
|
+
var SignupFormEmbed = import_zod8.z.object({
|
|
1989
|
+
hosted_url: import_zod8.z.string().url(),
|
|
1840
1990
|
/** A plain HTML form posting to the hosted page; works without JavaScript. */
|
|
1841
|
-
html:
|
|
1991
|
+
html: import_zod8.z.string(),
|
|
1842
1992
|
/** A small optional script that adds the time check and submits in place. */
|
|
1843
|
-
script_url:
|
|
1993
|
+
script_url: import_zod8.z.string().url(),
|
|
1844
1994
|
/** Where a browser fetches a fresh `form_token` from (GET, public, CORS for `allowed_origins`). */
|
|
1845
|
-
token_url:
|
|
1995
|
+
token_url: import_zod8.z.string().url()
|
|
1846
1996
|
});
|
|
1847
|
-
var SignupSubmission =
|
|
1997
|
+
var SignupSubmission = import_zod8.z.object({
|
|
1848
1998
|
email: Email,
|
|
1849
|
-
first_name:
|
|
1850
|
-
last_name:
|
|
1851
|
-
locale:
|
|
1999
|
+
first_name: import_zod8.z.string().max(200).optional(),
|
|
2000
|
+
last_name: import_zod8.z.string().max(200).optional(),
|
|
2001
|
+
locale: import_zod8.z.string().min(2).max(35).optional(),
|
|
1852
2002
|
/** Honeypot: must be empty. */
|
|
1853
|
-
website:
|
|
2003
|
+
website: import_zod8.z.string().max(0).optional(),
|
|
1854
2004
|
/**
|
|
1855
2005
|
* The signed render time from the form's token URL: a submission sooner than a
|
|
1856
2006
|
* human could fill the form, or long after, is refused.
|
|
1857
2007
|
*/
|
|
1858
|
-
form_token:
|
|
2008
|
+
form_token: import_zod8.z.string().min(1).max(512)
|
|
1859
2009
|
});
|
|
1860
2010
|
var IMPORT_STATUSES = ["uploaded", "validating", "validated", "committing", "completed", "failed", "cancelled"];
|
|
1861
|
-
var ImportStatus =
|
|
2011
|
+
var ImportStatus = import_zod8.z.enum(IMPORT_STATUSES);
|
|
1862
2012
|
var IMPORT_TERMINAL_STATUSES = ["completed", "failed", "cancelled"];
|
|
1863
|
-
var ImportColumnMapping =
|
|
1864
|
-
|
|
2013
|
+
var ImportColumnMapping = import_zod8.z.record(
|
|
2014
|
+
import_zod8.z.string().regex(/^(email|external_id|first_name|last_name|locale|property:[A-Za-z0-9_.-]{1,64}|ignore)$/)
|
|
1865
2015
|
);
|
|
1866
2016
|
var IMPORT_FILE_FIELD = "file";
|
|
1867
2017
|
var MAX_IMPORT_BYTES = 50 * 1024 * 1024;
|
|
1868
2018
|
var MAX_IMPORT_ROWS = 2e5;
|
|
1869
|
-
var ImportMappingRequest =
|
|
2019
|
+
var ImportMappingRequest = import_zod8.z.object({
|
|
1870
2020
|
/** CSV header -> contact field. Exactly one column maps to `email`. */
|
|
1871
2021
|
mapping: ImportColumnMapping.refine(
|
|
1872
2022
|
(m) => Object.values(m).filter((v) => v === "email").length === 1,
|
|
1873
2023
|
"exactly one column maps to email"
|
|
1874
2024
|
),
|
|
1875
|
-
topics:
|
|
1876
|
-
tags:
|
|
2025
|
+
topics: import_zod8.z.array(Slug).max(100),
|
|
2026
|
+
tags: import_zod8.z.array(Slug).max(100),
|
|
1877
2027
|
/** Existing contacts get the mapped fields overwritten; false only adds topics, tags and missing fields. */
|
|
1878
|
-
update_existing:
|
|
2028
|
+
update_existing: import_zod8.z.boolean().optional(),
|
|
1879
2029
|
/** The importer states the people consented; the service records who said so and when. */
|
|
1880
|
-
consent_confirmed:
|
|
2030
|
+
consent_confirmed: import_zod8.z.literal(true)
|
|
1881
2031
|
});
|
|
1882
|
-
var ImportCommitRequest =
|
|
2032
|
+
var ImportCommitRequest = import_zod8.z.object({ mapping_version: import_zod8.z.number().int().min(1) });
|
|
1883
2033
|
var IMPORT_ROW_OUTCOMES = ["created", "updated", "unchanged", "suppressed", "skipped"];
|
|
1884
|
-
var ImportRowOutcome =
|
|
2034
|
+
var ImportRowOutcome = import_zod8.z.enum(IMPORT_ROW_OUTCOMES);
|
|
1885
2035
|
var IMPORT_SKIP_REASONS = [
|
|
1886
2036
|
"missing_email",
|
|
1887
2037
|
"invalid_email",
|
|
@@ -1890,90 +2040,90 @@ var IMPORT_SKIP_REASONS = [
|
|
|
1890
2040
|
"external_id_conflict",
|
|
1891
2041
|
"wrong_column_count"
|
|
1892
2042
|
];
|
|
1893
|
-
var ImportSkipReason =
|
|
1894
|
-
var ImportReport =
|
|
1895
|
-
created:
|
|
1896
|
-
updated:
|
|
1897
|
-
unchanged:
|
|
1898
|
-
suppressed:
|
|
1899
|
-
skipped:
|
|
1900
|
-
skipped_by_reason:
|
|
2043
|
+
var ImportSkipReason = import_zod8.z.enum(IMPORT_SKIP_REASONS);
|
|
2044
|
+
var ImportReport = import_zod8.z.object({
|
|
2045
|
+
created: import_zod8.z.number().int().min(0),
|
|
2046
|
+
updated: import_zod8.z.number().int().min(0),
|
|
2047
|
+
unchanged: import_zod8.z.number().int().min(0),
|
|
2048
|
+
suppressed: import_zod8.z.number().int().min(0),
|
|
2049
|
+
skipped: import_zod8.z.number().int().min(0),
|
|
2050
|
+
skipped_by_reason: import_zod8.z.record(ImportSkipReason, import_zod8.z.number().int().min(0)),
|
|
1901
2051
|
/** Rows whose contact was subscribed without some topic, because the address is blocked for it. */
|
|
1902
|
-
topics_withheld:
|
|
2052
|
+
topics_withheld: import_zod8.z.number().int().min(0)
|
|
1903
2053
|
});
|
|
1904
|
-
var ImportJob =
|
|
2054
|
+
var ImportJob = import_zod8.z.object({
|
|
1905
2055
|
id: Id,
|
|
1906
2056
|
status: ImportStatus,
|
|
1907
|
-
file_name:
|
|
1908
|
-
file_bytes:
|
|
1909
|
-
total_rows:
|
|
2057
|
+
file_name: import_zod8.z.string().max(255).nullable(),
|
|
2058
|
+
file_bytes: import_zod8.z.number().int().min(0),
|
|
2059
|
+
total_rows: import_zod8.z.number().int().min(0),
|
|
1910
2060
|
/** The header row, as in the file. */
|
|
1911
|
-
columns:
|
|
2061
|
+
columns: import_zod8.z.array(import_zod8.z.string()),
|
|
1912
2062
|
/** The first rows, for the mapping screen. */
|
|
1913
|
-
sample:
|
|
2063
|
+
sample: import_zod8.z.array(import_zod8.z.array(import_zod8.z.string())).max(5),
|
|
1914
2064
|
/** A mapping guessed from the headers, for the mapping screen to start from. */
|
|
1915
2065
|
suggested_mapping: ImportColumnMapping,
|
|
1916
2066
|
mapping: ImportColumnMapping.nullable(),
|
|
1917
|
-
mapping_version:
|
|
1918
|
-
topics:
|
|
1919
|
-
tags:
|
|
1920
|
-
update_existing:
|
|
2067
|
+
mapping_version: import_zod8.z.number().int().min(0),
|
|
2068
|
+
topics: import_zod8.z.array(Slug),
|
|
2069
|
+
tags: import_zod8.z.array(Slug),
|
|
2070
|
+
update_existing: import_zod8.z.boolean(),
|
|
1921
2071
|
/** The dry run of the current mapping; null before one finished. */
|
|
1922
2072
|
dry_run: ImportReport.nullable(),
|
|
1923
2073
|
/** What the commit has written so far; null before it started. */
|
|
1924
2074
|
result: ImportReport.nullable(),
|
|
1925
2075
|
/** Rows the current phase (dry run or commit) has processed. */
|
|
1926
|
-
processed_rows:
|
|
2076
|
+
processed_rows: import_zod8.z.number().int().min(0),
|
|
1927
2077
|
/** Why a `failed` import failed. */
|
|
1928
|
-
error:
|
|
2078
|
+
error: import_zod8.z.string().nullable(),
|
|
1929
2079
|
created_at: Timestamp,
|
|
1930
2080
|
updated_at: Timestamp,
|
|
1931
2081
|
finished_at: Timestamp.nullable()
|
|
1932
2082
|
});
|
|
1933
|
-
var ImportRow =
|
|
2083
|
+
var ImportRow = import_zod8.z.object({
|
|
1934
2084
|
/** The line in the file, 1 being the first line after the header. */
|
|
1935
|
-
row:
|
|
1936
|
-
email:
|
|
2085
|
+
row: import_zod8.z.number().int().min(1),
|
|
2086
|
+
email: import_zod8.z.string().nullable(),
|
|
1937
2087
|
outcome: ImportRowOutcome,
|
|
1938
2088
|
reason: ImportSkipReason.nullable(),
|
|
1939
2089
|
/** A human explanation for a skipped row (which value, which column). */
|
|
1940
|
-
message:
|
|
2090
|
+
message: import_zod8.z.string().nullable(),
|
|
1941
2091
|
contact_id: Id.nullable()
|
|
1942
2092
|
});
|
|
1943
2093
|
var ImportRowListQuery = PageQuery.extend({ outcome: ImportRowOutcome.optional() });
|
|
1944
|
-
var MailingScheduleRequest =
|
|
1945
|
-
var TrackingSettings =
|
|
1946
|
-
var VariantAnalytics =
|
|
1947
|
-
key:
|
|
1948
|
-
sent:
|
|
1949
|
-
unique_opens:
|
|
1950
|
-
unique_clicks:
|
|
1951
|
-
});
|
|
1952
|
-
var MailingAnalytics =
|
|
2094
|
+
var MailingScheduleRequest = import_zod8.z.object({ send_at: Timestamp });
|
|
2095
|
+
var TrackingSettings = import_zod8.z.object({ opens: import_zod8.z.boolean(), clicks: import_zod8.z.boolean() });
|
|
2096
|
+
var VariantAnalytics = import_zod8.z.object({
|
|
2097
|
+
key: import_zod8.z.string(),
|
|
2098
|
+
sent: import_zod8.z.number().int().min(0),
|
|
2099
|
+
unique_opens: import_zod8.z.number().int().min(0).nullable(),
|
|
2100
|
+
unique_clicks: import_zod8.z.number().int().min(0).nullable()
|
|
2101
|
+
});
|
|
2102
|
+
var MailingAnalytics = import_zod8.z.object({
|
|
1953
2103
|
mailing_id: Id,
|
|
1954
2104
|
counts: MailingCounts,
|
|
1955
2105
|
/** What was tracked for this mailing, fixed when it started; null before it started. */
|
|
1956
2106
|
tracking: TrackingSettings.nullable(),
|
|
1957
2107
|
/** Unique recipients with a human open. Null when opens were not tracked. */
|
|
1958
|
-
unique_opens:
|
|
2108
|
+
unique_opens: import_zod8.z.number().int().min(0).nullable(),
|
|
1959
2109
|
/**
|
|
1960
2110
|
* Recipients whose only opens came from Apple Mail Privacy Protection, which
|
|
1961
2111
|
* loads every image on delivery: counted apart, never as human opens.
|
|
1962
2112
|
*/
|
|
1963
|
-
apple_mpp_opens:
|
|
2113
|
+
apple_mpp_opens: import_zod8.z.number().int().min(0).nullable(),
|
|
1964
2114
|
/** Opens and clicks from security scanners and prefetchers, filtered out of the unique counts. */
|
|
1965
|
-
machine_events:
|
|
1966
|
-
unique_clicks:
|
|
1967
|
-
unsubscribes:
|
|
1968
|
-
bounces:
|
|
1969
|
-
complaints:
|
|
1970
|
-
links:
|
|
1971
|
-
variants:
|
|
2115
|
+
machine_events: import_zod8.z.number().int().min(0).nullable(),
|
|
2116
|
+
unique_clicks: import_zod8.z.number().int().min(0).nullable(),
|
|
2117
|
+
unsubscribes: import_zod8.z.number().int().min(0),
|
|
2118
|
+
bounces: import_zod8.z.number().int().min(0),
|
|
2119
|
+
complaints: import_zod8.z.number().int().min(0),
|
|
2120
|
+
links: import_zod8.z.array(import_zod8.z.object({ url: import_zod8.z.string(), unique_clicks: import_zod8.z.number().int().min(0) })).nullable(),
|
|
2121
|
+
variants: import_zod8.z.array(VariantAnalytics).nullable()
|
|
1972
2122
|
});
|
|
1973
2123
|
|
|
1974
2124
|
// src/automations.ts
|
|
1975
2125
|
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})$/;
|
|
1976
|
-
var AutomationFilterField =
|
|
2126
|
+
var AutomationFilterField = import_zod9.z.string().regex(AUTOMATION_FILTER_FIELD_PATTERN, "unknown filter field");
|
|
1977
2127
|
var AUTOMATION_FILTER_FIELD_OPERATORS = {
|
|
1978
2128
|
...FILTER_FIELD_OPERATORS,
|
|
1979
2129
|
segment: ["eq", "neq", "in", "not_in"],
|
|
@@ -1983,18 +2133,18 @@ var AUTOMATION_FILTER_FIELD_OPERATORS = {
|
|
|
1983
2133
|
step: ["eq", "neq"]
|
|
1984
2134
|
};
|
|
1985
2135
|
var automationFilterFamily = filterFieldFamily;
|
|
1986
|
-
var AutomationFilterCondition =
|
|
2136
|
+
var AutomationFilterCondition = import_zod9.z.object({ field: AutomationFilterField, op: FilterOperator, value: JsonValue.optional() }).strict().refine((c) => (c.op === "exists" || c.op === "not_exists") === (c.value === void 0), {
|
|
1987
2137
|
message: "exists and not_exists take no value; every other operator needs one",
|
|
1988
2138
|
path: ["value"]
|
|
1989
2139
|
}).refine((c) => c.op !== "in" && c.op !== "not_in" || Array.isArray(c.value), {
|
|
1990
2140
|
message: "in and not_in take an array",
|
|
1991
2141
|
path: ["value"]
|
|
1992
2142
|
});
|
|
1993
|
-
var AutomationFilter =
|
|
1994
|
-
() =>
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2143
|
+
var AutomationFilter = import_zod9.z.lazy(
|
|
2144
|
+
() => import_zod9.z.union([
|
|
2145
|
+
import_zod9.z.object({ and: import_zod9.z.array(AutomationFilter).min(1).max(50) }).strict(),
|
|
2146
|
+
import_zod9.z.object({ or: import_zod9.z.array(AutomationFilter).min(1).max(50) }).strict(),
|
|
2147
|
+
import_zod9.z.object({ not: AutomationFilter }).strict(),
|
|
1998
2148
|
AutomationFilterCondition
|
|
1999
2149
|
])
|
|
2000
2150
|
);
|
|
@@ -2007,20 +2157,20 @@ function filterConditions(filter) {
|
|
|
2007
2157
|
if ("not" in filter) return filterConditions(filter.not);
|
|
2008
2158
|
return [filter];
|
|
2009
2159
|
}
|
|
2010
|
-
var StepId =
|
|
2011
|
-
var TimeOfDay =
|
|
2012
|
-
var Weekday =
|
|
2013
|
-
var TimezoneName =
|
|
2160
|
+
var StepId = import_zod9.z.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "letters, digits, underscore and hyphen, 1 to 64");
|
|
2161
|
+
var TimeOfDay = import_zod9.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "HH:MM in 24 hours");
|
|
2162
|
+
var Weekday = import_zod9.z.number().int().min(0).max(6);
|
|
2163
|
+
var TimezoneName = import_zod9.z.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");
|
|
2014
2164
|
var MAX_DELAY_SECONDS = 365 * 24 * 60 * 60;
|
|
2015
|
-
var DateOnly =
|
|
2165
|
+
var DateOnly = import_zod9.z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "a calendar date such as 2026-09-20").refine((v) => {
|
|
2016
2166
|
const [y, m, d] = v.split("-").map(Number);
|
|
2017
2167
|
const t = new Date(Date.UTC(y, m - 1, d));
|
|
2018
2168
|
return t.getUTCFullYear() === y && t.getUTCMonth() + 1 === m && t.getUTCDate() === d;
|
|
2019
2169
|
}, "a real calendar date");
|
|
2020
|
-
var OffsetDays =
|
|
2170
|
+
var OffsetDays = import_zod9.z.number().int().min(-365).max(365);
|
|
2021
2171
|
var DATE_PASSED_ANSWERS = ["skip", "stop", "continue"];
|
|
2022
|
-
var DatePassedAnswer =
|
|
2023
|
-
var EventName =
|
|
2172
|
+
var DatePassedAnswer = import_zod9.z.enum(DATE_PASSED_ANSWERS);
|
|
2173
|
+
var EventName = import_zod9.z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,79}$/, "letters, digits, dot, colon, underscore and hyphen, 1 to 80");
|
|
2024
2174
|
var AUTOMATION_TRIGGER_KINDS = [
|
|
2025
2175
|
"topic_subscribed",
|
|
2026
2176
|
"form_confirmed",
|
|
@@ -2033,17 +2183,17 @@ var AUTOMATION_TRIGGER_KINDS = [
|
|
|
2033
2183
|
"segment_entered",
|
|
2034
2184
|
"link_clicked"
|
|
2035
2185
|
];
|
|
2036
|
-
var AutomationTriggerKind =
|
|
2186
|
+
var AutomationTriggerKind = import_zod9.z.enum(AUTOMATION_TRIGGER_KINDS);
|
|
2037
2187
|
var exclude = { exclude: BoundedAutomationFilter.nullish() };
|
|
2038
|
-
var AutomationTrigger =
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2188
|
+
var AutomationTrigger = import_zod9.z.discriminatedUnion("kind", [
|
|
2189
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("topic_subscribed"), topic: Slug, ...exclude }).strict(),
|
|
2190
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("form_confirmed"), form_id: Id, ...exclude }).strict(),
|
|
2191
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("tag_added"), tag: Slug, ...exclude }).strict(),
|
|
2042
2192
|
/** `to` given: only a change that lands on this value fires. Absent: any change of the key. */
|
|
2043
|
-
|
|
2193
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("property_changed"), key: ContactPropertyKey, to: JsonValue.optional(), ...exclude }).strict(),
|
|
2044
2194
|
/** `where` filters the event's own data, so only `event:<key>` leaves belong in it. */
|
|
2045
|
-
|
|
2046
|
-
|
|
2195
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("event"), name: EventName, where: BoundedAutomationFilter.nullish(), ...exclude }).strict(),
|
|
2196
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("manual"), ...exclude }).strict(),
|
|
2047
2197
|
/**
|
|
2048
2198
|
* Every year on a date the contact carries: a birthday, a joining date, a
|
|
2049
2199
|
* renewal. `offset_days` moves it ("three days before"), `at` is the wall
|
|
@@ -2053,8 +2203,8 @@ var AutomationTrigger = import_zod8.z.discriminatedUnion("kind", [
|
|
|
2053
2203
|
* A 29 February date fires on 1 March in a year that has no 29 February, so
|
|
2054
2204
|
* nobody is skipped every fourth year.
|
|
2055
2205
|
*/
|
|
2056
|
-
|
|
2057
|
-
kind:
|
|
2206
|
+
import_zod9.z.object({
|
|
2207
|
+
kind: import_zod9.z.literal("date_anniversary"),
|
|
2058
2208
|
key: ContactPropertyKey,
|
|
2059
2209
|
offset_days: OffsetDays,
|
|
2060
2210
|
at: TimeOfDay,
|
|
@@ -2066,8 +2216,8 @@ var AutomationTrigger = import_zod8.z.discriminatedUnion("kind", [
|
|
|
2066
2216
|
* not ask about a segment or about an event's data: there is no event, and a
|
|
2067
2217
|
* segment is its own trigger kind.
|
|
2068
2218
|
*/
|
|
2069
|
-
|
|
2070
|
-
kind:
|
|
2219
|
+
import_zod9.z.object({
|
|
2220
|
+
kind: import_zod9.z.literal("exact_date"),
|
|
2071
2221
|
on: DateOnly,
|
|
2072
2222
|
at: TimeOfDay,
|
|
2073
2223
|
where: BoundedAutomationFilter.nullish(),
|
|
@@ -2086,7 +2236,7 @@ var AutomationTrigger = import_zod8.z.discriminatedUnion("kind", [
|
|
|
2086
2236
|
* already exists. Entering those people as well is the publish dialog's
|
|
2087
2237
|
* backfill question, answered by a person rather than by the sweep.
|
|
2088
2238
|
*/
|
|
2089
|
-
|
|
2239
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("segment_entered"), segment_id: Id, ...exclude }).strict(),
|
|
2090
2240
|
/**
|
|
2091
2241
|
* S4. When somebody clicks a link in a mailing that has already gone out.
|
|
2092
2242
|
*
|
|
@@ -2096,7 +2246,7 @@ var AutomationTrigger = import_zod8.z.discriminatedUnion("kind", [
|
|
|
2096
2246
|
* tracking off there is no click to hear about, so the validator refuses it
|
|
2097
2247
|
* rather than leaving an automation that can never fire.
|
|
2098
2248
|
*/
|
|
2099
|
-
|
|
2249
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("link_clicked"), mailing_id: Id, url: import_zod9.z.string().min(1).max(2048).optional(), ...exclude }).strict()
|
|
2100
2250
|
]);
|
|
2101
2251
|
var AUTOMATION_DATE_TRIGGER_KINDS = ["date_anniversary", "exact_date"];
|
|
2102
2252
|
var MAX_TRIGGERS = 10;
|
|
@@ -2115,45 +2265,45 @@ var AUTOMATION_STEP_KINDS = [
|
|
|
2115
2265
|
"webhook",
|
|
2116
2266
|
"notify"
|
|
2117
2267
|
];
|
|
2118
|
-
var AutomationStepKind =
|
|
2268
|
+
var AutomationStepKind = import_zod9.z.enum(AUTOMATION_STEP_KINDS);
|
|
2119
2269
|
var next = { next: StepId.nullable() };
|
|
2120
|
-
var Unchosen =
|
|
2121
|
-
var DraftSlug =
|
|
2122
|
-
var DraftPropertyKey =
|
|
2123
|
-
var
|
|
2124
|
-
var EmailStepLanguage =
|
|
2125
|
-
subject:
|
|
2126
|
-
preheader:
|
|
2270
|
+
var Unchosen = import_zod9.z.literal("");
|
|
2271
|
+
var DraftSlug = import_zod9.z.union([Slug, Unchosen]);
|
|
2272
|
+
var DraftPropertyKey = import_zod9.z.union([ContactPropertyKey, Unchosen]);
|
|
2273
|
+
var label2 = { label: import_zod9.z.string().max(120).nullish() };
|
|
2274
|
+
var EmailStepLanguage = import_zod9.z.object({
|
|
2275
|
+
subject: import_zod9.z.string().min(1).max(998),
|
|
2276
|
+
preheader: import_zod9.z.string().max(500).nullable(),
|
|
2127
2277
|
document: TemplateDocument,
|
|
2128
2278
|
/** This language's insertions, its texts over the template's structure (every text present). */
|
|
2129
2279
|
insertions: Insertions.nullish()
|
|
2130
2280
|
}).strict();
|
|
2131
|
-
var EmailStep =
|
|
2132
|
-
kind:
|
|
2133
|
-
...
|
|
2281
|
+
var EmailStep = import_zod9.z.object({
|
|
2282
|
+
kind: import_zod9.z.literal("email"),
|
|
2283
|
+
...label2,
|
|
2134
2284
|
...next,
|
|
2135
2285
|
template_id: Id.nullish(),
|
|
2136
2286
|
document: TemplateDocument.nullish(),
|
|
2137
2287
|
/** Empty while the step is being written, or when its template has a subject; else `not_chosen` refuses to publish it. */
|
|
2138
|
-
subject:
|
|
2139
|
-
preheader:
|
|
2288
|
+
subject: import_zod9.z.string().max(998),
|
|
2289
|
+
preheader: import_zod9.z.string().max(500).nullish(),
|
|
2140
2290
|
locale: LocaleTag.nullish(),
|
|
2141
|
-
languages:
|
|
2291
|
+
languages: import_zod9.z.record(LocaleTag, EmailStepLanguage).nullish(),
|
|
2142
2292
|
/** The main language's insertions, snapshot from the template when the automation is published. */
|
|
2143
2293
|
insertions: Insertions.nullish()
|
|
2144
2294
|
}).strict();
|
|
2145
|
-
var DelayStep =
|
|
2146
|
-
kind:
|
|
2147
|
-
...
|
|
2295
|
+
var DelayStep = import_zod9.z.object({
|
|
2296
|
+
kind: import_zod9.z.literal("delay"),
|
|
2297
|
+
...label2,
|
|
2148
2298
|
...next,
|
|
2149
|
-
wait:
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2299
|
+
wait: import_zod9.z.discriminatedUnion("mode", [
|
|
2300
|
+
import_zod9.z.object({ mode: import_zod9.z.literal("duration"), seconds: import_zod9.z.number().int().min(1).max(MAX_DELAY_SECONDS) }).strict(),
|
|
2301
|
+
import_zod9.z.object({ mode: import_zod9.z.literal("time_of_day"), at: TimeOfDay }).strict(),
|
|
2302
|
+
import_zod9.z.object({ mode: import_zod9.z.literal("weekday"), weekday: Weekday, at: TimeOfDay }).strict(),
|
|
2153
2303
|
/** A fixed day has no next occurrence, so `continue` is not one of its answers. */
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
mode:
|
|
2304
|
+
import_zod9.z.object({ mode: import_zod9.z.literal("date"), on: DateOnly, at: TimeOfDay, passed: import_zod9.z.enum(["skip", "stop"]) }).strict(),
|
|
2305
|
+
import_zod9.z.object({
|
|
2306
|
+
mode: import_zod9.z.literal("date_property"),
|
|
2157
2307
|
key: ContactPropertyKey,
|
|
2158
2308
|
offset_days: OffsetDays,
|
|
2159
2309
|
at: TimeOfDay,
|
|
@@ -2161,9 +2311,9 @@ var DelayStep = import_zod8.z.object({
|
|
|
2161
2311
|
}).strict()
|
|
2162
2312
|
])
|
|
2163
2313
|
}).strict();
|
|
2164
|
-
var ConditionStep =
|
|
2165
|
-
kind:
|
|
2166
|
-
...
|
|
2314
|
+
var ConditionStep = import_zod9.z.object({
|
|
2315
|
+
kind: import_zod9.z.literal("condition"),
|
|
2316
|
+
...label2,
|
|
2167
2317
|
filter: BoundedAutomationFilter,
|
|
2168
2318
|
yes: StepId.nullable(),
|
|
2169
2319
|
no: StepId.nullable(),
|
|
@@ -2173,49 +2323,49 @@ var ConditionStep = import_zod8.z.object({
|
|
|
2173
2323
|
* neither half means anything without the other: a window with no answer
|
|
2174
2324
|
* for the end of it would be a silent decision.
|
|
2175
2325
|
*/
|
|
2176
|
-
wait_for:
|
|
2326
|
+
wait_for: import_zod9.z.object({ up_to_s: import_zod9.z.number().int().min(1).max(MAX_DELAY_SECONDS), on_timeout: import_zod9.z.enum(["yes", "no"]) }).strict().nullish()
|
|
2177
2327
|
}).strict();
|
|
2178
|
-
var SplitBranchKey =
|
|
2328
|
+
var SplitBranchKey = import_zod9.z.string().regex(/^[A-Za-z0-9_-]{1,32}$/, "letters, digits, underscore and hyphen, 1 to 32");
|
|
2179
2329
|
var MIN_SPLIT_BRANCHES = 2;
|
|
2180
2330
|
var MAX_SPLIT_BRANCHES = 4;
|
|
2181
|
-
var SplitStep =
|
|
2182
|
-
kind:
|
|
2183
|
-
...
|
|
2184
|
-
branches:
|
|
2185
|
-
|
|
2331
|
+
var SplitStep = import_zod9.z.object({
|
|
2332
|
+
kind: import_zod9.z.literal("split"),
|
|
2333
|
+
...label2,
|
|
2334
|
+
branches: import_zod9.z.array(
|
|
2335
|
+
import_zod9.z.object({
|
|
2186
2336
|
key: SplitBranchKey,
|
|
2187
2337
|
/** A whole percentage. Zero is allowed: a branch kept in the flow but switched off. */
|
|
2188
|
-
weight:
|
|
2338
|
+
weight: import_zod9.z.number().int().min(0).max(100),
|
|
2189
2339
|
next: StepId.nullable()
|
|
2190
2340
|
}).strict()
|
|
2191
2341
|
).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")
|
|
2192
2342
|
}).strict();
|
|
2193
|
-
var GotoStep =
|
|
2343
|
+
var GotoStep = import_zod9.z.object({ kind: import_zod9.z.literal("goto"), ...label2, target: StepId.nullable() }).strict();
|
|
2194
2344
|
var MAX_STEP_VISITS = 20;
|
|
2195
|
-
var WebhookStep =
|
|
2196
|
-
var NotifyRecipients =
|
|
2197
|
-
var NotifyStep =
|
|
2198
|
-
kind:
|
|
2199
|
-
...
|
|
2345
|
+
var WebhookStep = import_zod9.z.object({ kind: import_zod9.z.literal("webhook"), ...label2, ...next, endpoint_id: Id.nullable(), payload: JsonValue.nullish() }).strict();
|
|
2346
|
+
var NotifyRecipients = import_zod9.z.union([import_zod9.z.literal("owner"), import_zod9.z.literal("admins"), import_zod9.z.object({ member_id: Id }).strict()]);
|
|
2347
|
+
var NotifyStep = import_zod9.z.object({
|
|
2348
|
+
kind: import_zod9.z.literal("notify"),
|
|
2349
|
+
...label2,
|
|
2200
2350
|
...next,
|
|
2201
2351
|
to: NotifyRecipients,
|
|
2202
2352
|
/** Empty while the step is being written; `not_chosen` refuses to publish it. */
|
|
2203
|
-
subject:
|
|
2204
|
-
body:
|
|
2353
|
+
subject: import_zod9.z.string().max(998),
|
|
2354
|
+
body: import_zod9.z.string().max(1e4)
|
|
2205
2355
|
}).strict();
|
|
2206
|
-
var AutomationStep =
|
|
2356
|
+
var AutomationStep = import_zod9.z.discriminatedUnion("kind", [
|
|
2207
2357
|
EmailStep,
|
|
2208
2358
|
DelayStep,
|
|
2209
2359
|
ConditionStep,
|
|
2210
|
-
|
|
2211
|
-
|
|
2360
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("add_tag"), ...label2, ...next, tag: DraftSlug }).strict(),
|
|
2361
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("remove_tag"), ...label2, ...next, tag: DraftSlug }).strict(),
|
|
2212
2362
|
/** `value` null clears the property, as a contact upsert does. */
|
|
2213
|
-
|
|
2363
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("set_property"), ...label2, ...next, key: DraftPropertyKey, value: JsonValue }).strict(),
|
|
2214
2364
|
/** Subscribing never lifts a suppression: the service checks that, not the flow. */
|
|
2215
|
-
|
|
2365
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("subscribe_topic"), ...label2, ...next, topic: DraftSlug }).strict(),
|
|
2216
2366
|
/** `topic` unsubscribes from the automation's own topic, `all` from every topic. */
|
|
2217
|
-
|
|
2218
|
-
|
|
2367
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("unsubscribe"), ...label2, ...next, scope: import_zod9.z.enum(["topic", "all"]) }).strict(),
|
|
2368
|
+
import_zod9.z.object({ kind: import_zod9.z.literal("exit"), ...label2 }).strict(),
|
|
2219
2369
|
SplitStep,
|
|
2220
2370
|
GotoStep,
|
|
2221
2371
|
WebhookStep,
|
|
@@ -2239,25 +2389,25 @@ function stepTargets(step) {
|
|
|
2239
2389
|
}
|
|
2240
2390
|
}
|
|
2241
2391
|
var MAX_STEPS = 200;
|
|
2242
|
-
var AutomationFlow =
|
|
2243
|
-
triggers:
|
|
2392
|
+
var AutomationFlow = import_zod9.z.object({
|
|
2393
|
+
triggers: import_zod9.z.array(AutomationTrigger).max(MAX_TRIGGERS),
|
|
2244
2394
|
/** The first step. `null` is an empty flow. */
|
|
2245
2395
|
entry: StepId.nullable(),
|
|
2246
|
-
steps:
|
|
2396
|
+
steps: import_zod9.z.record(StepId, AutomationStep)
|
|
2247
2397
|
}).strict();
|
|
2248
2398
|
var emptyFlow = () => ({ triggers: [], entry: null, steps: {} });
|
|
2249
|
-
var QuietHours =
|
|
2399
|
+
var QuietHours = import_zod9.z.object({
|
|
2250
2400
|
start: TimeOfDay,
|
|
2251
2401
|
end: TimeOfDay,
|
|
2252
|
-
weekdays:
|
|
2402
|
+
weekdays: import_zod9.z.array(Weekday).min(1).max(7).nullish()
|
|
2253
2403
|
}).strict();
|
|
2254
2404
|
var RE_ENTRY_MODES = ["off", "after_exit"];
|
|
2255
|
-
var ReEntryMode =
|
|
2405
|
+
var ReEntryMode = import_zod9.z.enum(RE_ENTRY_MODES);
|
|
2256
2406
|
var MAX_RE_ENTRY_COOLDOWN_SECONDS = 365 * 24 * 60 * 60;
|
|
2257
|
-
var AutomationSettings =
|
|
2407
|
+
var AutomationSettings = import_zod9.z.object({
|
|
2258
2408
|
re_entry: ReEntryMode,
|
|
2259
2409
|
/** Seconds a person must wait after leaving before entering again. Only with `after_exit`. */
|
|
2260
|
-
re_entry_cooldown_s:
|
|
2410
|
+
re_entry_cooldown_s: import_zod9.z.number().int().min(0).max(MAX_RE_ENTRY_COOLDOWN_SECONDS).nullish(),
|
|
2261
2411
|
quiet_hours: QuietHours.nullish(),
|
|
2262
2412
|
/** Checked before every step; when it matches, the run ends. */
|
|
2263
2413
|
exit_filter: BoundedAutomationFilter.nullish()
|
|
@@ -2315,18 +2465,18 @@ var AUTOMATION_ISSUE_CODES = [
|
|
|
2315
2465
|
/** A one-off date that is already over everywhere on earth, so it would fire for nobody. */
|
|
2316
2466
|
"date_in_the_past"
|
|
2317
2467
|
];
|
|
2318
|
-
var AutomationIssueCode =
|
|
2319
|
-
var AutomationIssue =
|
|
2468
|
+
var AutomationIssueCode = import_zod9.z.enum(AUTOMATION_ISSUE_CODES);
|
|
2469
|
+
var AutomationIssue = import_zod9.z.object({
|
|
2320
2470
|
code: AutomationIssueCode,
|
|
2321
2471
|
/** The step the issue is about, when it is about one. */
|
|
2322
2472
|
step_id: StepId.nullable(),
|
|
2323
2473
|
/** Where inside the document, for the canvas to point at a field. */
|
|
2324
|
-
path:
|
|
2325
|
-
message:
|
|
2474
|
+
path: import_zod9.z.array(import_zod9.z.union([import_zod9.z.string(), import_zod9.z.number()])),
|
|
2475
|
+
message: import_zod9.z.string()
|
|
2326
2476
|
});
|
|
2327
|
-
var AutomationValidation =
|
|
2328
|
-
ok:
|
|
2329
|
-
issues:
|
|
2477
|
+
var AutomationValidation = import_zod9.z.object({
|
|
2478
|
+
ok: import_zod9.z.boolean(),
|
|
2479
|
+
issues: import_zod9.z.array(AutomationIssue)
|
|
2330
2480
|
});
|
|
2331
2481
|
function validateAutomation(flow, settings, ctx) {
|
|
2332
2482
|
const issues = [];
|
|
@@ -2717,7 +2867,7 @@ function cycles(flow) {
|
|
|
2717
2867
|
return out.sort();
|
|
2718
2868
|
}
|
|
2719
2869
|
var AUTOMATION_STATUSES = ["draft", "active", "paused", "archived"];
|
|
2720
|
-
var AutomationStatus =
|
|
2870
|
+
var AutomationStatus = import_zod9.z.enum(AUTOMATION_STATUSES);
|
|
2721
2871
|
var AUTOMATION_END_REASONS = [
|
|
2722
2872
|
"completed",
|
|
2723
2873
|
"exit_filter",
|
|
@@ -2736,9 +2886,9 @@ var AUTOMATION_END_REASONS = [
|
|
|
2736
2886
|
"ended_by_hand",
|
|
2737
2887
|
"failed"
|
|
2738
2888
|
];
|
|
2739
|
-
var AutomationEndReason =
|
|
2889
|
+
var AutomationEndReason = import_zod9.z.enum(AUTOMATION_END_REASONS);
|
|
2740
2890
|
var AUTOMATION_WAIT_REASONS = ["delay", "quiet_hours", "plan_limit", "sending", "condition"];
|
|
2741
|
-
var AutomationWaitReason =
|
|
2891
|
+
var AutomationWaitReason = import_zod9.z.enum(AUTOMATION_WAIT_REASONS);
|
|
2742
2892
|
var AUTOMATION_ENTRY_REFUSALS = [
|
|
2743
2893
|
"already_inside",
|
|
2744
2894
|
"re_entry_off",
|
|
@@ -2749,26 +2899,26 @@ var AUTOMATION_ENTRY_REFUSALS = [
|
|
|
2749
2899
|
"not_active",
|
|
2750
2900
|
"unknown_contact"
|
|
2751
2901
|
];
|
|
2752
|
-
var AutomationEntryRefusal =
|
|
2753
|
-
var AutomationCounts =
|
|
2754
|
-
started:
|
|
2755
|
-
in_progress:
|
|
2756
|
-
completed:
|
|
2757
|
-
exited:
|
|
2758
|
-
failed:
|
|
2759
|
-
});
|
|
2760
|
-
var Automation =
|
|
2902
|
+
var AutomationEntryRefusal = import_zod9.z.enum(AUTOMATION_ENTRY_REFUSALS);
|
|
2903
|
+
var AutomationCounts = import_zod9.z.object({
|
|
2904
|
+
started: import_zod9.z.number().int().min(0),
|
|
2905
|
+
in_progress: import_zod9.z.number().int().min(0),
|
|
2906
|
+
completed: import_zod9.z.number().int().min(0),
|
|
2907
|
+
exited: import_zod9.z.number().int().min(0),
|
|
2908
|
+
failed: import_zod9.z.number().int().min(0)
|
|
2909
|
+
});
|
|
2910
|
+
var Automation = import_zod9.z.object({
|
|
2761
2911
|
id: Id,
|
|
2762
|
-
name:
|
|
2912
|
+
name: import_zod9.z.string().min(1).max(200),
|
|
2763
2913
|
status: AutomationStatus,
|
|
2764
2914
|
/** Every automation sends under one topic; there are no transactional automations. */
|
|
2765
2915
|
topic: Slug,
|
|
2766
2916
|
provider_id: Id.nullable(),
|
|
2767
2917
|
settings: AutomationSettings,
|
|
2768
2918
|
/** The flow people are running through, null while the automation has never been published. */
|
|
2769
|
-
published_version:
|
|
2919
|
+
published_version: import_zod9.z.number().int().min(1).nullable(),
|
|
2770
2920
|
/** The flow being edited. Increments on every save, like a template's version. */
|
|
2771
|
-
draft_version:
|
|
2921
|
+
draft_version: import_zod9.z.number().int().min(1),
|
|
2772
2922
|
counts: AutomationCounts,
|
|
2773
2923
|
created_at: Timestamp,
|
|
2774
2924
|
updated_at: Timestamp,
|
|
@@ -2781,16 +2931,16 @@ var AutomationWithFlow = Automation.extend({
|
|
|
2781
2931
|
published_flow: AutomationFlow.nullable()
|
|
2782
2932
|
});
|
|
2783
2933
|
var AutomationSummary = Automation;
|
|
2784
|
-
var AutomationCreate =
|
|
2785
|
-
name:
|
|
2934
|
+
var AutomationCreate = import_zod9.z.object({
|
|
2935
|
+
name: import_zod9.z.string().min(1).max(200),
|
|
2786
2936
|
topic: Slug,
|
|
2787
2937
|
provider_id: Id.optional(),
|
|
2788
2938
|
flow: AutomationFlow.optional(),
|
|
2789
2939
|
settings: AutomationSettings.partial().optional()
|
|
2790
2940
|
});
|
|
2791
|
-
var AutomationUpdate =
|
|
2792
|
-
base_version:
|
|
2793
|
-
name:
|
|
2941
|
+
var AutomationUpdate = import_zod9.z.object({
|
|
2942
|
+
base_version: import_zod9.z.number().int().min(1),
|
|
2943
|
+
name: import_zod9.z.string().min(1).max(200).optional(),
|
|
2794
2944
|
topic: Slug.optional(),
|
|
2795
2945
|
provider_id: Id.optional(),
|
|
2796
2946
|
flow: AutomationFlow.optional(),
|
|
@@ -2803,42 +2953,42 @@ var AutomationListQuery = PageQuery.extend({
|
|
|
2803
2953
|
status: AutomationStatus.optional(),
|
|
2804
2954
|
topic: Slug.optional()
|
|
2805
2955
|
});
|
|
2806
|
-
var AutomationPublishRequest =
|
|
2956
|
+
var AutomationPublishRequest = import_zod9.z.object({
|
|
2807
2957
|
/**
|
|
2808
2958
|
* Answer what this publish would do and change nothing, whatever the flow
|
|
2809
2959
|
* looks like. Without it a publish that has no issues and moves nobody
|
|
2810
2960
|
* simply goes ahead, which is right for an API caller and wrong for a
|
|
2811
2961
|
* dialog that is only showing somebody what they are about to do.
|
|
2812
2962
|
*/
|
|
2813
|
-
dry_run:
|
|
2814
|
-
confirm:
|
|
2963
|
+
dry_run: import_zod9.z.boolean().optional(),
|
|
2964
|
+
confirm: import_zod9.z.boolean().optional(),
|
|
2815
2965
|
/** Per removed step: the step its waiting runs move to, or null to end them. */
|
|
2816
|
-
moves:
|
|
2966
|
+
moves: import_zod9.z.record(StepId, StepId.nullable()).optional(),
|
|
2817
2967
|
/** Also enter everyone who matches a trigger's condition right now. */
|
|
2818
|
-
backfill:
|
|
2968
|
+
backfill: import_zod9.z.boolean().optional()
|
|
2819
2969
|
}).strict();
|
|
2820
|
-
var AutomationPublishMove =
|
|
2970
|
+
var AutomationPublishMove = import_zod9.z.object({
|
|
2821
2971
|
step_id: StepId,
|
|
2822
2972
|
/** Why the step's people have to move: it is gone, or its delay changed. */
|
|
2823
|
-
change:
|
|
2824
|
-
waiting:
|
|
2973
|
+
change: import_zod9.z.enum(["removed", "delay_changed", "kind_changed"]),
|
|
2974
|
+
waiting: import_zod9.z.number().int().min(0),
|
|
2825
2975
|
/** Where they go: a step id, or null to end their runs with `step_removed`. */
|
|
2826
2976
|
destination: StepId.nullable()
|
|
2827
2977
|
});
|
|
2828
|
-
var AutomationPublishResult =
|
|
2829
|
-
published:
|
|
2978
|
+
var AutomationPublishResult = import_zod9.z.object({
|
|
2979
|
+
published: import_zod9.z.boolean(),
|
|
2830
2980
|
automation: Automation,
|
|
2831
2981
|
/** Empty when nothing has to move; non-empty and `published` false asks for a confirmation. */
|
|
2832
|
-
moves:
|
|
2982
|
+
moves: import_zod9.z.array(AutomationPublishMove),
|
|
2833
2983
|
/** How many people the backfill entered, or would enter when it is not confirmed. */
|
|
2834
|
-
backfill:
|
|
2835
|
-
issues:
|
|
2836
|
-
});
|
|
2837
|
-
var AutomationDuplicateRequest =
|
|
2838
|
-
var AutomationEnrollRequest =
|
|
2839
|
-
contact_ids:
|
|
2840
|
-
external_ids:
|
|
2841
|
-
emails:
|
|
2984
|
+
backfill: import_zod9.z.number().int().min(0),
|
|
2985
|
+
issues: import_zod9.z.array(AutomationIssue)
|
|
2986
|
+
});
|
|
2987
|
+
var AutomationDuplicateRequest = import_zod9.z.object({ name: import_zod9.z.string().min(1).max(200).optional() }).strict();
|
|
2988
|
+
var AutomationEnrollRequest = import_zod9.z.object({
|
|
2989
|
+
contact_ids: import_zod9.z.array(Id).min(1).max(1e3).optional(),
|
|
2990
|
+
external_ids: import_zod9.z.array(import_zod9.z.string().min(1).max(255)).min(1).max(1e3).optional(),
|
|
2991
|
+
emails: import_zod9.z.array(Email).min(1).max(1e3).optional(),
|
|
2842
2992
|
segment_id: Id.optional(),
|
|
2843
2993
|
/** Where to start them; the flow's entry step by default. */
|
|
2844
2994
|
step_id: StepId.optional()
|
|
@@ -2846,34 +2996,34 @@ var AutomationEnrollRequest = import_zod8.z.object({
|
|
|
2846
2996
|
(v) => [v.contact_ids, v.external_ids, v.emails, v.segment_id].filter((x) => x !== void 0).length === 1,
|
|
2847
2997
|
"name the people exactly one way: contact_ids, external_ids, emails or segment_id"
|
|
2848
2998
|
);
|
|
2849
|
-
var AutomationEnrollment =
|
|
2999
|
+
var AutomationEnrollment = import_zod9.z.object({
|
|
2850
3000
|
contact_id: Id.nullable(),
|
|
2851
3001
|
email: Email.nullable(),
|
|
2852
|
-
external_id:
|
|
2853
|
-
entered:
|
|
3002
|
+
external_id: import_zod9.z.string().nullable(),
|
|
3003
|
+
entered: import_zod9.z.boolean(),
|
|
2854
3004
|
run_id: Id.nullable(),
|
|
2855
3005
|
reason: AutomationEntryRefusal.nullable()
|
|
2856
3006
|
});
|
|
2857
|
-
var AutomationEnrollResult =
|
|
2858
|
-
entered:
|
|
2859
|
-
refused:
|
|
2860
|
-
results:
|
|
3007
|
+
var AutomationEnrollResult = import_zod9.z.object({
|
|
3008
|
+
entered: import_zod9.z.number().int().min(0),
|
|
3009
|
+
refused: import_zod9.z.number().int().min(0),
|
|
3010
|
+
results: import_zod9.z.array(AutomationEnrollment)
|
|
2861
3011
|
});
|
|
2862
|
-
var AutomationRunStatus =
|
|
2863
|
-
var AutomationRun =
|
|
3012
|
+
var AutomationRunStatus = import_zod9.z.enum(["active", "waiting", "completed", "exited", "failed"]);
|
|
3013
|
+
var AutomationRun = import_zod9.z.object({
|
|
2864
3014
|
id: Id,
|
|
2865
3015
|
automation_id: Id,
|
|
2866
3016
|
contact_id: Id,
|
|
2867
3017
|
email: Email,
|
|
2868
|
-
external_id:
|
|
2869
|
-
version:
|
|
3018
|
+
external_id: import_zod9.z.string().nullable(),
|
|
3019
|
+
version: import_zod9.z.number().int().min(1),
|
|
2870
3020
|
status: AutomationRunStatus,
|
|
2871
3021
|
current_step_id: StepId.nullable(),
|
|
2872
3022
|
next_action_at: Timestamp.nullable(),
|
|
2873
3023
|
wait_reason: AutomationWaitReason.nullable(),
|
|
2874
|
-
end_reason:
|
|
3024
|
+
end_reason: import_zod9.z.string().nullable(),
|
|
2875
3025
|
/** What went wrong the last time this run tried to take its step, null otherwise. */
|
|
2876
|
-
last_error:
|
|
3026
|
+
last_error: import_zod9.z.string().nullable(),
|
|
2877
3027
|
entered_at: Timestamp,
|
|
2878
3028
|
ended_at: Timestamp.nullable()
|
|
2879
3029
|
});
|
|
@@ -2883,13 +3033,13 @@ var AutomationRunListQuery = PageQuery.extend({
|
|
|
2883
3033
|
contact_id: Id.optional()
|
|
2884
3034
|
});
|
|
2885
3035
|
var ContactAutomationRun = AutomationRun.extend({
|
|
2886
|
-
automation:
|
|
3036
|
+
automation: import_zod9.z.object({ id: Id, name: import_zod9.z.string(), status: AutomationStatus })
|
|
2887
3037
|
});
|
|
2888
3038
|
var ContactAutomationListQuery = PageQuery.extend({ status: AutomationRunStatus.optional() });
|
|
2889
|
-
var AutomationStepExecution =
|
|
3039
|
+
var AutomationStepExecution = import_zod9.z.object({
|
|
2890
3040
|
step_id: StepId,
|
|
2891
3041
|
step_kind: AutomationStepKind,
|
|
2892
|
-
visit:
|
|
3042
|
+
visit: import_zod9.z.number().int().min(1),
|
|
2893
3043
|
started_at: Timestamp,
|
|
2894
3044
|
finished_at: Timestamp.nullable(),
|
|
2895
3045
|
/**
|
|
@@ -2901,48 +3051,48 @@ var AutomationStepExecution = import_zod8.z.object({
|
|
|
2901
3051
|
*/
|
|
2902
3052
|
outcome: Properties
|
|
2903
3053
|
});
|
|
2904
|
-
var AutomationJourney =
|
|
3054
|
+
var AutomationJourney = import_zod9.z.object({
|
|
2905
3055
|
run: AutomationRun,
|
|
2906
|
-
steps:
|
|
3056
|
+
steps: import_zod9.z.array(AutomationStepExecution)
|
|
2907
3057
|
});
|
|
2908
|
-
var AutomationStepReport =
|
|
3058
|
+
var AutomationStepReport = import_zod9.z.object({
|
|
2909
3059
|
step_id: StepId,
|
|
2910
3060
|
step_kind: AutomationStepKind,
|
|
2911
|
-
reached:
|
|
2912
|
-
waiting:
|
|
2913
|
-
passed:
|
|
3061
|
+
reached: import_zod9.z.number().int().min(0),
|
|
3062
|
+
waiting: import_zod9.z.number().int().min(0),
|
|
3063
|
+
passed: import_zod9.z.number().int().min(0),
|
|
2914
3064
|
/** Per branch, for a condition: how many took `yes` and how many `no`. */
|
|
2915
|
-
branches:
|
|
3065
|
+
branches: import_zod9.z.record(import_zod9.z.number().int().min(0)),
|
|
2916
3066
|
/** The step mailing's id, for an email step of the published version. */
|
|
2917
3067
|
mailing_id: Id.nullable()
|
|
2918
3068
|
});
|
|
2919
|
-
var AutomationReport =
|
|
3069
|
+
var AutomationReport = import_zod9.z.object({
|
|
2920
3070
|
counts: AutomationCounts,
|
|
2921
3071
|
/** Why the people who left did: `AutomationEndReason` to a count. */
|
|
2922
|
-
end_reasons:
|
|
3072
|
+
end_reasons: import_zod9.z.record(import_zod9.z.number().int().min(0)),
|
|
2923
3073
|
/**
|
|
2924
3074
|
* Why people a trigger named did not enter: `AutomationEntryRefusal` to a
|
|
2925
3075
|
* count. Without it a pause, an exclusion or a missing subscription would
|
|
2926
3076
|
* turn people away invisibly.
|
|
2927
3077
|
*/
|
|
2928
|
-
refusals:
|
|
2929
|
-
steps:
|
|
2930
|
-
});
|
|
2931
|
-
var AutomationExitRunRequest =
|
|
2932
|
-
var AutomationRunParams =
|
|
2933
|
-
var AutomationStepParams =
|
|
2934
|
-
var AutomationTestEmailRequest =
|
|
2935
|
-
var AutomationValidateRequest =
|
|
2936
|
-
var EventCreate =
|
|
3078
|
+
refusals: import_zod9.z.record(import_zod9.z.number().int().min(0)),
|
|
3079
|
+
steps: import_zod9.z.array(AutomationStepReport)
|
|
3080
|
+
});
|
|
3081
|
+
var AutomationExitRunRequest = import_zod9.z.object({ reason: import_zod9.z.string().max(200).optional() }).strict();
|
|
3082
|
+
var AutomationRunParams = import_zod9.z.object({ id: Id, run_id: Id });
|
|
3083
|
+
var AutomationStepParams = import_zod9.z.object({ id: Id, step_id: StepId });
|
|
3084
|
+
var AutomationTestEmailRequest = import_zod9.z.object({ to: Email, merge: Properties.optional(), locale: LocaleTag.optional() }).strict();
|
|
3085
|
+
var AutomationValidateRequest = import_zod9.z.object({ flow: AutomationFlow.optional(), settings: AutomationSettings.partial().optional() }).strict();
|
|
3086
|
+
var EventCreate = import_zod9.z.object({
|
|
2937
3087
|
name: EventName,
|
|
2938
|
-
contact:
|
|
3088
|
+
contact: import_zod9.z.object({ external_id: import_zod9.z.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"),
|
|
2939
3089
|
data: Properties.optional(),
|
|
2940
3090
|
occurred_at: Timestamp.optional(),
|
|
2941
|
-
id:
|
|
3091
|
+
id: import_zod9.z.string().min(1).max(128).optional()
|
|
2942
3092
|
}).strict();
|
|
2943
|
-
var Event =
|
|
3093
|
+
var Event = import_zod9.z.object({
|
|
2944
3094
|
id: Id,
|
|
2945
|
-
client_event_id:
|
|
3095
|
+
client_event_id: import_zod9.z.string().nullable(),
|
|
2946
3096
|
name: EventName,
|
|
2947
3097
|
contact_id: Id,
|
|
2948
3098
|
data: Properties,
|
|
@@ -2953,7 +3103,7 @@ var Event = import_zod8.z.object({
|
|
|
2953
3103
|
* hand. Each is then admitted or refused by the runtime on its own rules
|
|
2954
3104
|
* (consent, exclusions, re-entry), which `automations.report` counts.
|
|
2955
3105
|
*/
|
|
2956
|
-
entered:
|
|
3106
|
+
entered: import_zod9.z.number().int().min(0)
|
|
2957
3107
|
});
|
|
2958
3108
|
var EventListQuery = PageQuery.extend({
|
|
2959
3109
|
name: EventName.optional(),
|
|
@@ -2961,19 +3111,19 @@ var EventListQuery = PageQuery.extend({
|
|
|
2961
3111
|
});
|
|
2962
3112
|
|
|
2963
3113
|
// src/workspace.ts
|
|
2964
|
-
var CompanyId =
|
|
3114
|
+
var CompanyId = import_zod10.z.string().min(1).max(64);
|
|
2965
3115
|
var ASSET_POLICIES = ["any", "service_only"];
|
|
2966
|
-
var AssetPolicy =
|
|
3116
|
+
var AssetPolicy = import_zod10.z.enum(ASSET_POLICIES);
|
|
2967
3117
|
var MAX_ALLOWED_ASSET_HOSTS = 20;
|
|
2968
|
-
var AssetHost =
|
|
3118
|
+
var AssetHost = import_zod10.z.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");
|
|
2969
3119
|
var MAX_MERGE_DEFAULTS = 50;
|
|
2970
3120
|
var MAX_MERGE_DEFAULT_LENGTH = 2e3;
|
|
2971
|
-
var MergeFieldName =
|
|
2972
|
-
var MergeDefaultValue =
|
|
2973
|
-
var MergeDefaults =
|
|
3121
|
+
var MergeFieldName = import_zod10.z.string().regex(/^[a-z][a-z0-9_]*$/, "a merge-field name: lowercase letters, digits and underscores, starting with a letter").max(64);
|
|
3122
|
+
var MergeDefaultValue = import_zod10.z.union([import_zod10.z.string().max(MAX_MERGE_DEFAULT_LENGTH), import_zod10.z.number().finite(), import_zod10.z.boolean()]);
|
|
3123
|
+
var MergeDefaults = import_zod10.z.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), {
|
|
2974
3124
|
message: `a reserved merge field (${Object.keys(RESERVED_MERGE_FIELDS).join(", ")}) cannot have a workspace default`
|
|
2975
3125
|
});
|
|
2976
|
-
var WorkspaceSettings =
|
|
3126
|
+
var WorkspaceSettings = import_zod10.z.object({
|
|
2977
3127
|
/**
|
|
2978
3128
|
* The workspace's main language (a {@link LocaleTag}, e.g. "de"): the main
|
|
2979
3129
|
* language of a new template, and of the hosted pages when nothing else
|
|
@@ -2986,9 +3136,9 @@ var WorkspaceSettings = import_zod9.z.object({
|
|
|
2986
3136
|
* each; the hosted pages are offered in those of them they support (English,
|
|
2987
3137
|
* German, French, Italian, Spanish). The service stores canonical casing.
|
|
2988
3138
|
*/
|
|
2989
|
-
locales:
|
|
3139
|
+
locales: import_zod10.z.array(LocaleTag).min(1).max(20).refine((l) => new Set(l.map((t) => t.toLowerCase())).size === l.length, "a language appears twice"),
|
|
2990
3140
|
/** Open and click tracking. Off by default, opt-in per workspace. */
|
|
2991
|
-
tracking_enabled:
|
|
3141
|
+
tracking_enabled: import_zod10.z.boolean(),
|
|
2992
3142
|
/** Where images, stylesheets and fonts in a mail may load from. `any` by default. */
|
|
2993
3143
|
asset_policy: AssetPolicy,
|
|
2994
3144
|
/**
|
|
@@ -2996,7 +3146,7 @@ var WorkspaceSettings = import_zod9.z.object({
|
|
|
2996
3146
|
* the workspace's website, typically. Empty by default. Ignored under `any`,
|
|
2997
3147
|
* which allows everything already.
|
|
2998
3148
|
*/
|
|
2999
|
-
allowed_asset_hosts:
|
|
3149
|
+
allowed_asset_hosts: import_zod10.z.array(AssetHost).max(MAX_ALLOWED_ASSET_HOSTS),
|
|
3000
3150
|
/** Merge values every mail of the workspace can use (see `MergeDefaults`). Empty by default. */
|
|
3001
3151
|
merge_defaults: MergeDefaults,
|
|
3002
3152
|
/**
|
|
@@ -3005,10 +3155,10 @@ var WorkspaceSettings = import_zod9.z.object({
|
|
|
3005
3155
|
*/
|
|
3006
3156
|
default_timezone: TimezoneName.nullable()
|
|
3007
3157
|
});
|
|
3008
|
-
var Workspace =
|
|
3158
|
+
var Workspace = import_zod10.z.object({
|
|
3009
3159
|
id: Id,
|
|
3010
3160
|
slug: Slug,
|
|
3011
|
-
name:
|
|
3161
|
+
name: import_zod10.z.string().min(1).max(120),
|
|
3012
3162
|
settings: WorkspaceSettings,
|
|
3013
3163
|
/**
|
|
3014
3164
|
* The auth-brain company (tenant) the workspace was created for, or null for
|
|
@@ -3019,13 +3169,13 @@ var Workspace = import_zod9.z.object({
|
|
|
3019
3169
|
created_at: Timestamp,
|
|
3020
3170
|
updated_at: Timestamp
|
|
3021
3171
|
});
|
|
3022
|
-
var WorkspaceCreate =
|
|
3172
|
+
var WorkspaceCreate = import_zod10.z.object({
|
|
3023
3173
|
slug: Slug,
|
|
3024
|
-
name:
|
|
3174
|
+
name: import_zod10.z.string().min(1).max(120),
|
|
3025
3175
|
settings: WorkspaceSettings.partial().optional(),
|
|
3026
|
-
owner:
|
|
3176
|
+
owner: import_zod10.z.object({
|
|
3027
3177
|
email: Email,
|
|
3028
|
-
name:
|
|
3178
|
+
name: import_zod10.z.string().max(200).nullable().optional()
|
|
3029
3179
|
}),
|
|
3030
3180
|
/**
|
|
3031
3181
|
* The signed-in person's active auth-brain company. The dashboard always
|
|
@@ -3035,49 +3185,49 @@ var WorkspaceCreate = import_zod9.z.object({
|
|
|
3035
3185
|
*/
|
|
3036
3186
|
company_id: CompanyId.optional()
|
|
3037
3187
|
});
|
|
3038
|
-
var WorkspaceUpdate =
|
|
3039
|
-
name:
|
|
3188
|
+
var WorkspaceUpdate = import_zod10.z.object({
|
|
3189
|
+
name: import_zod10.z.string().min(1).max(120),
|
|
3040
3190
|
settings: WorkspaceSettings.partial()
|
|
3041
3191
|
}).partial().refine((v) => Object.keys(v).length > 0, "at least one field");
|
|
3042
|
-
var WorkspaceMove =
|
|
3192
|
+
var WorkspaceMove = import_zod10.z.object({ company_id: CompanyId });
|
|
3043
3193
|
var MEMBER_ROLES = ["owner", "admin", "editor", "viewer"];
|
|
3044
|
-
var MemberRole =
|
|
3194
|
+
var MemberRole = import_zod10.z.enum(MEMBER_ROLES);
|
|
3045
3195
|
var WorkspaceMembership = Workspace.extend({ role: MemberRole });
|
|
3046
|
-
var Member =
|
|
3196
|
+
var Member = import_zod10.z.object({
|
|
3047
3197
|
id: Id,
|
|
3048
3198
|
/** The person's auth-brain subject. */
|
|
3049
|
-
subject:
|
|
3199
|
+
subject: import_zod10.z.string().min(1).max(255),
|
|
3050
3200
|
email: Email,
|
|
3051
|
-
name:
|
|
3201
|
+
name: import_zod10.z.string().max(200).nullable(),
|
|
3052
3202
|
role: MemberRole,
|
|
3053
3203
|
created_at: Timestamp
|
|
3054
3204
|
});
|
|
3055
|
-
var MemberCreate =
|
|
3056
|
-
subject:
|
|
3205
|
+
var MemberCreate = import_zod10.z.object({
|
|
3206
|
+
subject: import_zod10.z.string().min(1).max(255),
|
|
3057
3207
|
email: Email,
|
|
3058
|
-
name:
|
|
3208
|
+
name: import_zod10.z.string().max(200).nullable().optional(),
|
|
3059
3209
|
role: MemberRole
|
|
3060
3210
|
});
|
|
3061
|
-
var MemberUpdate =
|
|
3211
|
+
var MemberUpdate = import_zod10.z.object({ role: MemberRole });
|
|
3062
3212
|
var API_KEY_SCOPES = ["full", "read", "send"];
|
|
3063
|
-
var ApiKeyScope =
|
|
3064
|
-
var ApiKey =
|
|
3213
|
+
var ApiKeyScope = import_zod10.z.enum(API_KEY_SCOPES);
|
|
3214
|
+
var ApiKey = import_zod10.z.object({
|
|
3065
3215
|
id: Id,
|
|
3066
|
-
name:
|
|
3216
|
+
name: import_zod10.z.string().min(1).max(120),
|
|
3067
3217
|
/** The first characters of the key, for recognising it in a list. */
|
|
3068
|
-
prefix:
|
|
3218
|
+
prefix: import_zod10.z.string().min(4).max(32),
|
|
3069
3219
|
scope: ApiKeyScope,
|
|
3070
3220
|
last_used_at: Timestamp.nullable(),
|
|
3071
3221
|
revoked_at: Timestamp.nullable(),
|
|
3072
3222
|
created_at: Timestamp
|
|
3073
3223
|
});
|
|
3074
|
-
var ApiKeyCreate =
|
|
3075
|
-
name:
|
|
3224
|
+
var ApiKeyCreate = import_zod10.z.object({
|
|
3225
|
+
name: import_zod10.z.string().min(1).max(120),
|
|
3076
3226
|
/** Omitted means `full`. */
|
|
3077
3227
|
scope: ApiKeyScope.optional()
|
|
3078
3228
|
});
|
|
3079
|
-
var ApiKeyCreated =
|
|
3080
|
-
key:
|
|
3229
|
+
var ApiKeyCreated = import_zod10.z.object({
|
|
3230
|
+
key: import_zod10.z.string().min(20),
|
|
3081
3231
|
api_key: ApiKey
|
|
3082
3232
|
});
|
|
3083
3233
|
var AUDIT_ACTIONS = [
|
|
@@ -3104,6 +3254,7 @@ var AUDIT_ACTIONS = [
|
|
|
3104
3254
|
"template.created",
|
|
3105
3255
|
"template.updated",
|
|
3106
3256
|
"template.deleted",
|
|
3257
|
+
"template.inputs_declared",
|
|
3107
3258
|
"saved_section.created",
|
|
3108
3259
|
"saved_section.updated",
|
|
3109
3260
|
"saved_section.deleted",
|
|
@@ -3165,29 +3316,29 @@ var AUDIT_ACTIONS = [
|
|
|
3165
3316
|
"automation.run_ended",
|
|
3166
3317
|
"event.recorded"
|
|
3167
3318
|
];
|
|
3168
|
-
var AuditAction =
|
|
3169
|
-
var AuditActor =
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
type:
|
|
3319
|
+
var AuditAction = import_zod10.z.enum(AUDIT_ACTIONS);
|
|
3320
|
+
var AuditActor = import_zod10.z.discriminatedUnion("type", [
|
|
3321
|
+
import_zod10.z.object({ type: import_zod10.z.literal("member"), member_id: Id, subject: import_zod10.z.string().min(1) }),
|
|
3322
|
+
import_zod10.z.object({
|
|
3323
|
+
type: import_zod10.z.literal("api_key"),
|
|
3173
3324
|
api_key_id: Id,
|
|
3174
3325
|
/**
|
|
3175
3326
|
* Who the client's application said it was acting for, from
|
|
3176
3327
|
* `ON_BEHALF_OF_HEADER`: reported by that application, never verified.
|
|
3177
3328
|
* Absent when the call carried no label.
|
|
3178
3329
|
*/
|
|
3179
|
-
on_behalf_of:
|
|
3330
|
+
on_behalf_of: import_zod10.z.string().min(1).max(ON_BEHALF_OF_MAX_LENGTH).optional()
|
|
3180
3331
|
}),
|
|
3181
3332
|
/** The service itself (the worker, a bounce, the hosted unsubscribe page). */
|
|
3182
|
-
|
|
3333
|
+
import_zod10.z.object({ type: import_zod10.z.literal("system"), reason: import_zod10.z.string().min(1).max(200) })
|
|
3183
3334
|
]);
|
|
3184
|
-
var AuditEntry =
|
|
3335
|
+
var AuditEntry = import_zod10.z.object({
|
|
3185
3336
|
id: Id,
|
|
3186
3337
|
action: AuditAction,
|
|
3187
3338
|
actor: AuditActor,
|
|
3188
|
-
target_type:
|
|
3339
|
+
target_type: import_zod10.z.string().min(1).max(64),
|
|
3189
3340
|
target_id: Id.nullable(),
|
|
3190
|
-
details:
|
|
3341
|
+
details: import_zod10.z.record(import_zod10.z.unknown()),
|
|
3191
3342
|
created_at: Timestamp
|
|
3192
3343
|
});
|
|
3193
3344
|
var AuditQuery = PageQuery.extend({
|
|
@@ -3196,12 +3347,12 @@ var AuditQuery = PageQuery.extend({
|
|
|
3196
3347
|
});
|
|
3197
3348
|
|
|
3198
3349
|
// src/invites.ts
|
|
3199
|
-
var
|
|
3350
|
+
var import_zod11 = require("zod");
|
|
3200
3351
|
var INVITE_STATUSES = ["pending", "accepted", "revoked", "expired"];
|
|
3201
|
-
var InviteStatus =
|
|
3352
|
+
var InviteStatus = import_zod11.z.enum(INVITE_STATUSES);
|
|
3202
3353
|
var DEFAULT_INVITE_TTL_DAYS = 7;
|
|
3203
3354
|
var MAX_INVITE_TTL_DAYS = 30;
|
|
3204
|
-
var Invite =
|
|
3355
|
+
var Invite = import_zod11.z.object({
|
|
3205
3356
|
id: Id,
|
|
3206
3357
|
/** Stored lowercased; only a person signed in with this address can accept. */
|
|
3207
3358
|
email: Email,
|
|
@@ -3209,32 +3360,32 @@ var Invite = import_zod10.z.object({
|
|
|
3209
3360
|
/** Derived from the timestamps below, never stored. */
|
|
3210
3361
|
status: InviteStatus,
|
|
3211
3362
|
/** The member who invited, while they are still one (null after they left). */
|
|
3212
|
-
invited_by:
|
|
3363
|
+
invited_by: import_zod11.z.object({ member_id: Id.nullable(), email: Email.nullable() }),
|
|
3213
3364
|
expires_at: Timestamp,
|
|
3214
3365
|
accepted_at: Timestamp.nullable(),
|
|
3215
3366
|
revoked_at: Timestamp.nullable(),
|
|
3216
3367
|
created_at: Timestamp
|
|
3217
3368
|
});
|
|
3218
|
-
var InviteCreate =
|
|
3369
|
+
var InviteCreate = import_zod11.z.object({
|
|
3219
3370
|
email: Email,
|
|
3220
3371
|
role: MemberRole,
|
|
3221
|
-
expires_in_days:
|
|
3372
|
+
expires_in_days: import_zod11.z.number().int().min(1).max(MAX_INVITE_TTL_DAYS).optional()
|
|
3222
3373
|
});
|
|
3223
|
-
var InviteCreated =
|
|
3374
|
+
var InviteCreated = import_zod11.z.object({
|
|
3224
3375
|
invite: Invite,
|
|
3225
|
-
token:
|
|
3376
|
+
token: import_zod11.z.string().min(32).max(128)
|
|
3226
3377
|
});
|
|
3227
3378
|
var InviteListQuery = PageQuery.extend({
|
|
3228
3379
|
status: InviteStatus.optional()
|
|
3229
3380
|
});
|
|
3230
|
-
var InviteAccept =
|
|
3231
|
-
token:
|
|
3381
|
+
var InviteAccept = import_zod11.z.object({
|
|
3382
|
+
token: import_zod11.z.string().min(32).max(128),
|
|
3232
3383
|
email: Email,
|
|
3233
|
-
name:
|
|
3384
|
+
name: import_zod11.z.string().max(200).nullable().optional()
|
|
3234
3385
|
});
|
|
3235
|
-
var InviteAccepted =
|
|
3386
|
+
var InviteAccepted = import_zod11.z.object({
|
|
3236
3387
|
workspace: WorkspaceMembership,
|
|
3237
|
-
already_member:
|
|
3388
|
+
already_member: import_zod11.z.boolean()
|
|
3238
3389
|
});
|
|
3239
3390
|
var INVITE_REFUSAL_REASONS = ["email_mismatch", "inviter_lacks_role", "expired", "revoked", "accepted"];
|
|
3240
3391
|
|
|
@@ -3541,14 +3692,14 @@ function insertionNameProblem(insertions, name, current) {
|
|
|
3541
3692
|
if (has(RESERVED_MERGE_FIELDS, name)) return `"${name}" is a merge field the service fills itself. Choose another name.`;
|
|
3542
3693
|
if (name !== current && has(insertions, name)) return `There is already an insertion called "${name}".`;
|
|
3543
3694
|
const chooser = Object.entries(insertions).find(([other, def]) => def.by === name && other !== current);
|
|
3544
|
-
if (chooser) return `"${name}" is the value
|
|
3545
|
-
if (current !== void 0 && ownEntry(insertions, current)?.by === name) return `"${name}" is the value
|
|
3695
|
+
if (chooser) return `"${name}" is the value "${chooser[0]}" depends on. Choose another name.`;
|
|
3696
|
+
if (current !== void 0 && ownEntry(insertions, current)?.by === name) return `"${name}" is the value this insertion depends on. Choose another name.`;
|
|
3546
3697
|
return null;
|
|
3547
3698
|
}
|
|
3548
3699
|
function insertionByProblem(insertions, by, self) {
|
|
3549
|
-
if (!InsertionName.safeParse(by).success) return "The value
|
|
3550
|
-
if (by === "unsubscribe_url") return "
|
|
3551
|
-
if (by === self || has(insertions, by)) return `"${by}" is an insertion, not a value. Use a value the
|
|
3700
|
+
if (!InsertionName.safeParse(by).success) return "The value it depends on is a merge-field name: lowercase letters, digits and underscores (like reason).";
|
|
3701
|
+
if (by === "unsubscribe_url") return "An insertion cannot depend on the unsubscribe link. Use a value the app or the contact provides.";
|
|
3702
|
+
if (by === self || has(insertions, by)) return `"${by}" is an insertion, not a value. Use a value the app or the contact provides.`;
|
|
3552
3703
|
return null;
|
|
3553
3704
|
}
|
|
3554
3705
|
function normalizeChoiceKey(raw) {
|
|
@@ -3556,10 +3707,10 @@ function normalizeChoiceKey(raw) {
|
|
|
3556
3707
|
}
|
|
3557
3708
|
function choiceKeyProblem(insertion, key, current) {
|
|
3558
3709
|
if (INSERTION_TEXT_SLOTS.includes(key)) {
|
|
3559
|
-
return `"${key}"
|
|
3710
|
+
return `"${key}" is kept for the insertion's ${key === "filled" ? '"any other value"' : '"empty"'} sentence, so it cannot be a value. Choose another value.`;
|
|
3560
3711
|
}
|
|
3561
|
-
if (!InsertionChoiceKey.safeParse(key).success) return "A
|
|
3562
|
-
if (key !== current && has(insertion.choices, key)) return `
|
|
3712
|
+
if (!InsertionChoiceKey.safeParse(key).success) return "A value is written exactly as the app sends it: lowercase letters, digits, hyphens and underscores (like venue).";
|
|
3713
|
+
if (key !== current && has(insertion.choices, key)) return `"${key}" already has its own sentence.`;
|
|
3563
3714
|
return null;
|
|
3564
3715
|
}
|
|
3565
3716
|
function addInsertion(drafts, rawName, rawBy) {
|
|
@@ -3568,7 +3719,7 @@ function addInsertion(drafts, rawName, rawBy) {
|
|
|
3568
3719
|
if (Object.keys(drafts.insertions).length >= MAX_INSERTIONS) return { ok: false, message: `A template holds at most ${MAX_INSERTIONS} insertions.` };
|
|
3569
3720
|
const nameProblem = insertionNameProblem(drafts.insertions, name);
|
|
3570
3721
|
if (nameProblem) return { ok: false, message: nameProblem };
|
|
3571
|
-
if (name === by) return { ok: false, message: "An insertion cannot
|
|
3722
|
+
if (name === by) return { ok: false, message: "An insertion cannot depend on its own name. Use the value the app sends (like reason)." };
|
|
3572
3723
|
const byProblem = insertionByProblem(drafts.insertions, by);
|
|
3573
3724
|
if (byProblem) return { ok: false, message: byProblem };
|
|
3574
3725
|
return { ok: true, drafts: editMainInsertions(drafts, { ...drafts.insertions, [name]: { by, choices: {}, filled: null, otherwise: "" } }) };
|
|
@@ -3751,6 +3902,25 @@ var REFUSAL_HINTS = {
|
|
|
3751
3902
|
insertion_with_fallback: 'Remove the |fallback from the insertion in the subject, preheader or body: its "otherwise" text is the fallback.',
|
|
3752
3903
|
unknown_translation_insertion: "A language names an insertion or a choice the main language no longer has. Reload the template and try again."
|
|
3753
3904
|
};
|
|
3905
|
+
function unknownRefusedInsertions(details, drafts) {
|
|
3906
|
+
const reason = details?.reason;
|
|
3907
|
+
if (typeof reason !== "string" || !has(REFUSAL_HINTS, reason)) return [];
|
|
3908
|
+
const names = [];
|
|
3909
|
+
const add = (name) => {
|
|
3910
|
+
if (typeof name === "string" && name !== "" && ownEntry(drafts.insertions, name) === void 0 && !names.includes(name)) names.push(name);
|
|
3911
|
+
};
|
|
3912
|
+
if (Array.isArray(details?.missing)) {
|
|
3913
|
+
for (const m of details.missing) if (typeof m === "object" && m !== null) add(m.insertion);
|
|
3914
|
+
}
|
|
3915
|
+
if (Array.isArray(details?.issues)) {
|
|
3916
|
+
for (const issue of details.issues) {
|
|
3917
|
+
const path = typeof issue === "object" && issue !== null && Array.isArray(issue.path) ? issue.path.map(String) : [];
|
|
3918
|
+
const at = path.indexOf("insertions");
|
|
3919
|
+
if (at >= 0) add(path[at + 1]);
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
return names;
|
|
3923
|
+
}
|
|
3754
3924
|
function insertionRefusalHint(details) {
|
|
3755
3925
|
const reason = details?.reason;
|
|
3756
3926
|
if (typeof reason !== "string" || !has(REFUSAL_HINTS, reason)) return null;
|
|
@@ -3759,15 +3929,99 @@ function insertionRefusalHint(details) {
|
|
|
3759
3929
|
const missing = Array.isArray(details?.missing) ? details.missing.filter((m) => typeof m === "object" && m !== null && typeof m.insertion === "string" && typeof m.choice === "string") : [];
|
|
3760
3930
|
return missing.length > 0 ? `The language has ${describeMissingTexts(missing)}. ${hint}` : hint;
|
|
3761
3931
|
}
|
|
3932
|
+
function tokenOf(name) {
|
|
3933
|
+
return new RegExp(String.raw`\{\{(\s*)${name}(\s*)(\|[^}]*)?\}\}`, "g");
|
|
3934
|
+
}
|
|
3935
|
+
function renameTokenInText(text, from, to) {
|
|
3936
|
+
let count = 0;
|
|
3937
|
+
const out = text.replace(tokenOf(from), (_m, before, after, fallback) => {
|
|
3938
|
+
count++;
|
|
3939
|
+
return `{{${before}${to}${after}${fallback ?? ""}}}`;
|
|
3940
|
+
});
|
|
3941
|
+
return { text: count > 0 ? out : text, count };
|
|
3942
|
+
}
|
|
3943
|
+
function renameTokenInDocument(document, from, to) {
|
|
3944
|
+
let count = 0;
|
|
3945
|
+
const walk = (value) => {
|
|
3946
|
+
if (typeof value === "string") {
|
|
3947
|
+
const r = renameTokenInText(value, from, to);
|
|
3948
|
+
count += r.count;
|
|
3949
|
+
return r.text;
|
|
3950
|
+
}
|
|
3951
|
+
if (Array.isArray(value)) {
|
|
3952
|
+
let changed = false;
|
|
3953
|
+
const next2 = value.map((v) => {
|
|
3954
|
+
const w = walk(v);
|
|
3955
|
+
if (w !== v) changed = true;
|
|
3956
|
+
return w;
|
|
3957
|
+
});
|
|
3958
|
+
return changed ? next2 : value;
|
|
3959
|
+
}
|
|
3960
|
+
if (value && typeof value === "object") {
|
|
3961
|
+
let changed = false;
|
|
3962
|
+
const next2 = {};
|
|
3963
|
+
for (const [k, v] of Object.entries(value)) {
|
|
3964
|
+
const w = walk(v);
|
|
3965
|
+
if (w !== v) changed = true;
|
|
3966
|
+
next2[k] = w;
|
|
3967
|
+
}
|
|
3968
|
+
return changed ? next2 : value;
|
|
3969
|
+
}
|
|
3970
|
+
return value;
|
|
3971
|
+
};
|
|
3972
|
+
const renamed = walk(document);
|
|
3973
|
+
return { document: renamed, count };
|
|
3974
|
+
}
|
|
3975
|
+
function renameInsertionPlacements(drafts, from, to) {
|
|
3976
|
+
if (from === to) return { drafts, places: [] };
|
|
3977
|
+
const places = [];
|
|
3978
|
+
const note2 = (locale, where, count) => {
|
|
3979
|
+
if (count > 0) places.push({ locale, where, count });
|
|
3980
|
+
};
|
|
3981
|
+
const body = renameTokenInDocument(drafts.main.document, from, to);
|
|
3982
|
+
const subject = renameTokenInText(drafts.main.subject, from, to);
|
|
3983
|
+
const preheader = renameTokenInText(drafts.main.preheader, from, to);
|
|
3984
|
+
note2(null, "body", body.count);
|
|
3985
|
+
note2(null, "subject", subject.count);
|
|
3986
|
+
note2(null, "preheader", preheader.count);
|
|
3987
|
+
let out = {
|
|
3988
|
+
...drafts,
|
|
3989
|
+
main: { subject: subject.text, preheader: preheader.text, document: body.document },
|
|
3990
|
+
mainDocumentChanged: drafts.mainDocumentChanged || body.count > 0
|
|
3991
|
+
};
|
|
3992
|
+
for (const [tag, version] of Object.entries(drafts.translations)) {
|
|
3993
|
+
const b = renameTokenInDocument(version.document, from, to);
|
|
3994
|
+
const s = version.subject === null ? { text: null, count: 0 } : renameTokenInText(version.subject, from, to);
|
|
3995
|
+
const p = version.preheader === null ? { text: null, count: 0 } : renameTokenInText(version.preheader, from, to);
|
|
3996
|
+
note2(tag, "body", b.count);
|
|
3997
|
+
note2(tag, "subject", s.count);
|
|
3998
|
+
note2(tag, "preheader", p.count);
|
|
3999
|
+
if (b.count + s.count + p.count === 0) continue;
|
|
4000
|
+
out = {
|
|
4001
|
+
...out,
|
|
4002
|
+
translations: { ...out.translations, [tag]: { ...version, document: b.document, subject: s.text, preheader: p.text } },
|
|
4003
|
+
changed: out.changed.includes(tag) ? out.changed : [...out.changed, tag]
|
|
4004
|
+
};
|
|
4005
|
+
}
|
|
4006
|
+
return { drafts: out, places };
|
|
4007
|
+
}
|
|
4008
|
+
function renameInsertionEverywhere(drafts, from, rawTo) {
|
|
4009
|
+
const renamed = renameInsertionChecked(drafts, from, rawTo);
|
|
4010
|
+
if (!renamed.ok) return renamed;
|
|
4011
|
+
const to = rawTo.trim();
|
|
4012
|
+
if (to === from) return { ok: true, drafts, places: [] };
|
|
4013
|
+
const placed = renameInsertionPlacements(renamed.drafts, from, to);
|
|
4014
|
+
return { ok: true, drafts: placed.drafts, places: placed.places };
|
|
4015
|
+
}
|
|
3762
4016
|
|
|
3763
4017
|
// src/providers.ts
|
|
3764
|
-
var
|
|
4018
|
+
var import_zod12 = require("zod");
|
|
3765
4019
|
var PROVIDER_KINDS = ["smtp", "resend"];
|
|
3766
|
-
var ProviderKind =
|
|
3767
|
-
var ProviderPolicy =
|
|
3768
|
-
daily_recipient_budget:
|
|
3769
|
-
min_interval_ms:
|
|
3770
|
-
max_recipients_per_message:
|
|
4020
|
+
var ProviderKind = import_zod12.z.enum(PROVIDER_KINDS);
|
|
4021
|
+
var ProviderPolicy = import_zod12.z.object({
|
|
4022
|
+
daily_recipient_budget: import_zod12.z.number().int().min(1).max(1e7),
|
|
4023
|
+
min_interval_ms: import_zod12.z.number().int().min(0).max(36e5),
|
|
4024
|
+
max_recipients_per_message: import_zod12.z.number().int().min(1).max(1e3)
|
|
3771
4025
|
});
|
|
3772
4026
|
var ICLOUD_SMTP_POLICY = {
|
|
3773
4027
|
daily_recipient_budget: 800,
|
|
@@ -3775,17 +4029,17 @@ var ICLOUD_SMTP_POLICY = {
|
|
|
3775
4029
|
max_recipients_per_message: 1
|
|
3776
4030
|
};
|
|
3777
4031
|
var SMTP_SECURITY = ["tls", "starttls"];
|
|
3778
|
-
var SmtpConfigBase =
|
|
3779
|
-
host:
|
|
3780
|
-
port:
|
|
4032
|
+
var SmtpConfigBase = import_zod12.z.object({
|
|
4033
|
+
host: import_zod12.z.string().min(1).max(255),
|
|
4034
|
+
port: import_zod12.z.number().int().min(1).max(65535),
|
|
3781
4035
|
/** `tls` connects encrypted (465), `starttls` upgrades (587). Never plaintext. */
|
|
3782
|
-
security:
|
|
3783
|
-
username:
|
|
4036
|
+
security: import_zod12.z.enum(SMTP_SECURITY),
|
|
4037
|
+
username: import_zod12.z.string().min(1).max(255)
|
|
3784
4038
|
});
|
|
3785
|
-
var ResendConfigBase =
|
|
3786
|
-
var ProviderRejections =
|
|
3787
|
-
count:
|
|
3788
|
-
last_error:
|
|
4039
|
+
var ResendConfigBase = import_zod12.z.object({});
|
|
4040
|
+
var ProviderRejections = import_zod12.z.object({
|
|
4041
|
+
count: import_zod12.z.number().int().min(0),
|
|
4042
|
+
last_error: import_zod12.z.string().nullable(),
|
|
3789
4043
|
last_at: Timestamp.nullable(),
|
|
3790
4044
|
/**
|
|
3791
4045
|
* The last time the bounce circuit breaker tripped on one of this provider's
|
|
@@ -3795,59 +4049,59 @@ var ProviderRejections = import_zod11.z.object({
|
|
|
3795
4049
|
* run's bounce blocks were undone and the mailing paused. `sample` is the
|
|
3796
4050
|
* reply that tripped it.
|
|
3797
4051
|
*/
|
|
3798
|
-
anomaly:
|
|
4052
|
+
anomaly: import_zod12.z.object({
|
|
3799
4053
|
at: Timestamp,
|
|
3800
4054
|
/** `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. */
|
|
3801
|
-
scope:
|
|
4055
|
+
scope: import_zod12.z.enum(["mailing", "provider"]),
|
|
3802
4056
|
/**
|
|
3803
4057
|
* True while the provider-wide breaker is open: no bounce blocks through
|
|
3804
4058
|
* this provider, test sends refused and mailings not started or resumed
|
|
3805
4059
|
* (`provider_anomaly`), until an admin clears it (`providers.clearAnomaly`).
|
|
3806
4060
|
*/
|
|
3807
|
-
blocking:
|
|
4061
|
+
blocking: import_zod12.z.boolean(),
|
|
3808
4062
|
mailing_id: Id.nullable(),
|
|
3809
|
-
reason:
|
|
3810
|
-
sample:
|
|
4063
|
+
reason: import_zod12.z.string(),
|
|
4064
|
+
sample: import_zod12.z.string()
|
|
3811
4065
|
}).nullable()
|
|
3812
4066
|
});
|
|
3813
|
-
var ProviderEvents =
|
|
3814
|
-
status:
|
|
3815
|
-
source:
|
|
3816
|
-
url:
|
|
3817
|
-
error:
|
|
4067
|
+
var ProviderEvents = import_zod12.z.object({
|
|
4068
|
+
status: import_zod12.z.enum(["active", "needs_secret"]),
|
|
4069
|
+
source: import_zod12.z.enum(["automatic", "manual"]).nullable(),
|
|
4070
|
+
url: import_zod12.z.string().url(),
|
|
4071
|
+
error: import_zod12.z.string().nullable(),
|
|
3818
4072
|
/**
|
|
3819
4073
|
* Events that named an email this provider never sent through the service
|
|
3820
4074
|
* (another workspace or system sharing the Resend account): counted, never
|
|
3821
4075
|
* acted on.
|
|
3822
4076
|
*/
|
|
3823
|
-
unmatched:
|
|
4077
|
+
unmatched: import_zod12.z.number().int().min(0)
|
|
3824
4078
|
});
|
|
3825
4079
|
var RESEND_EVENT_TYPES = ["email.bounced", "email.complained", "email.delivery_delayed"];
|
|
3826
|
-
var ResendSigningSecret =
|
|
3827
|
-
var ProviderEventsSecret =
|
|
4080
|
+
var ResendSigningSecret = import_zod12.z.string().max(200).regex(/^whsec_[A-Za-z0-9+/]{16,}={0,2}$/, "must be the signing secret Resend shows, starting with whsec_");
|
|
4081
|
+
var ProviderEventsSecret = import_zod12.z.object({ signing_secret: ResendSigningSecret });
|
|
3828
4082
|
var ProviderFields = {
|
|
3829
|
-
name:
|
|
3830
|
-
from_name:
|
|
4083
|
+
name: import_zod12.z.string().min(1).max(120),
|
|
4084
|
+
from_name: import_zod12.z.string().min(1).max(120),
|
|
3831
4085
|
from_email: Email,
|
|
3832
4086
|
reply_to: Email.nullable(),
|
|
3833
4087
|
policy: ProviderPolicy
|
|
3834
4088
|
};
|
|
3835
|
-
var Provider =
|
|
3836
|
-
|
|
4089
|
+
var Provider = import_zod12.z.discriminatedUnion("kind", [
|
|
4090
|
+
import_zod12.z.object({
|
|
3837
4091
|
id: Id,
|
|
3838
|
-
kind:
|
|
4092
|
+
kind: import_zod12.z.literal("smtp"),
|
|
3839
4093
|
config: SmtpConfigBase,
|
|
3840
|
-
has_secret:
|
|
4094
|
+
has_secret: import_zod12.z.boolean(),
|
|
3841
4095
|
...ProviderFields,
|
|
3842
4096
|
rejections: ProviderRejections,
|
|
3843
4097
|
created_at: Timestamp,
|
|
3844
4098
|
updated_at: Timestamp
|
|
3845
4099
|
}),
|
|
3846
|
-
|
|
4100
|
+
import_zod12.z.object({
|
|
3847
4101
|
id: Id,
|
|
3848
|
-
kind:
|
|
4102
|
+
kind: import_zod12.z.literal("resend"),
|
|
3849
4103
|
config: ResendConfigBase,
|
|
3850
|
-
has_secret:
|
|
4104
|
+
has_secret: import_zod12.z.boolean(),
|
|
3851
4105
|
...ProviderFields,
|
|
3852
4106
|
rejections: ProviderRejections,
|
|
3853
4107
|
events: ProviderEvents,
|
|
@@ -3855,31 +4109,31 @@ var Provider = import_zod11.z.discriminatedUnion("kind", [
|
|
|
3855
4109
|
updated_at: Timestamp
|
|
3856
4110
|
})
|
|
3857
4111
|
]);
|
|
3858
|
-
var ProviderCreate =
|
|
3859
|
-
|
|
3860
|
-
kind:
|
|
3861
|
-
config: SmtpConfigBase.extend({ password:
|
|
4112
|
+
var ProviderCreate = import_zod12.z.discriminatedUnion("kind", [
|
|
4113
|
+
import_zod12.z.object({
|
|
4114
|
+
kind: import_zod12.z.literal("smtp"),
|
|
4115
|
+
config: SmtpConfigBase.extend({ password: import_zod12.z.string().min(1).max(1024) }),
|
|
3862
4116
|
...ProviderFields
|
|
3863
4117
|
}),
|
|
3864
|
-
|
|
3865
|
-
kind:
|
|
3866
|
-
config: ResendConfigBase.extend({ api_key:
|
|
4118
|
+
import_zod12.z.object({
|
|
4119
|
+
kind: import_zod12.z.literal("resend"),
|
|
4120
|
+
config: ResendConfigBase.extend({ api_key: import_zod12.z.string().min(1).max(1024) }),
|
|
3867
4121
|
...ProviderFields
|
|
3868
4122
|
})
|
|
3869
4123
|
]);
|
|
3870
|
-
var ProviderUpdate =
|
|
3871
|
-
|
|
3872
|
-
kind:
|
|
3873
|
-
config: SmtpConfigBase.extend({ password:
|
|
4124
|
+
var ProviderUpdate = import_zod12.z.discriminatedUnion("kind", [
|
|
4125
|
+
import_zod12.z.object({
|
|
4126
|
+
kind: import_zod12.z.literal("smtp"),
|
|
4127
|
+
config: SmtpConfigBase.extend({ password: import_zod12.z.string().min(1).max(1024).optional() }).partial().optional(),
|
|
3874
4128
|
name: ProviderFields.name.optional(),
|
|
3875
4129
|
from_name: ProviderFields.from_name.optional(),
|
|
3876
4130
|
from_email: ProviderFields.from_email.optional(),
|
|
3877
4131
|
reply_to: ProviderFields.reply_to.optional(),
|
|
3878
4132
|
policy: ProviderPolicy.partial().optional()
|
|
3879
4133
|
}),
|
|
3880
|
-
|
|
3881
|
-
kind:
|
|
3882
|
-
config: ResendConfigBase.extend({ api_key:
|
|
4134
|
+
import_zod12.z.object({
|
|
4135
|
+
kind: import_zod12.z.literal("resend"),
|
|
4136
|
+
config: ResendConfigBase.extend({ api_key: import_zod12.z.string().min(1).max(1024).optional() }).optional(),
|
|
3883
4137
|
name: ProviderFields.name.optional(),
|
|
3884
4138
|
from_name: ProviderFields.from_name.optional(),
|
|
3885
4139
|
from_email: ProviderFields.from_email.optional(),
|
|
@@ -3887,48 +4141,48 @@ var ProviderUpdate = import_zod11.z.discriminatedUnion("kind", [
|
|
|
3887
4141
|
policy: ProviderPolicy.partial().optional()
|
|
3888
4142
|
})
|
|
3889
4143
|
]);
|
|
3890
|
-
var ProviderUsage =
|
|
4144
|
+
var ProviderUsage = import_zod12.z.object({
|
|
3891
4145
|
provider_id: Id,
|
|
3892
|
-
recipients_last_24h:
|
|
3893
|
-
remaining_budget:
|
|
4146
|
+
recipients_last_24h: import_zod12.z.number().int().min(0),
|
|
4147
|
+
remaining_budget: import_zod12.z.number().int().min(0),
|
|
3894
4148
|
/** When enough of the rolling window frees up to send again, if exhausted. */
|
|
3895
4149
|
next_capacity_at: Timestamp.nullable()
|
|
3896
4150
|
});
|
|
3897
|
-
var ProviderVerifyResult =
|
|
3898
|
-
ok:
|
|
3899
|
-
error:
|
|
4151
|
+
var ProviderVerifyResult = import_zod12.z.object({
|
|
4152
|
+
ok: import_zod12.z.boolean(),
|
|
4153
|
+
error: import_zod12.z.string().nullable()
|
|
3900
4154
|
});
|
|
3901
4155
|
|
|
3902
4156
|
// src/contacts.ts
|
|
3903
|
-
var
|
|
3904
|
-
var Topic =
|
|
4157
|
+
var import_zod13 = require("zod");
|
|
4158
|
+
var Topic = import_zod13.z.object({
|
|
3905
4159
|
id: Id,
|
|
3906
4160
|
slug: Slug,
|
|
3907
|
-
name:
|
|
4161
|
+
name: import_zod13.z.string().min(1).max(120),
|
|
3908
4162
|
/** Shown on the hosted preference page. */
|
|
3909
|
-
description:
|
|
4163
|
+
description: import_zod13.z.string().max(1e3).nullable(),
|
|
3910
4164
|
/** Translations of `name` and `description` per locale, for the hosted page. */
|
|
3911
|
-
translations:
|
|
3912
|
-
|
|
4165
|
+
translations: import_zod13.z.record(
|
|
4166
|
+
import_zod13.z.object({ name: import_zod13.z.string().min(1).max(120), description: import_zod13.z.string().max(1e3).nullable() })
|
|
3913
4167
|
),
|
|
3914
4168
|
created_at: Timestamp,
|
|
3915
4169
|
updated_at: Timestamp
|
|
3916
4170
|
});
|
|
3917
|
-
var TopicCreate =
|
|
4171
|
+
var TopicCreate = import_zod13.z.object({
|
|
3918
4172
|
slug: Slug,
|
|
3919
|
-
name:
|
|
3920
|
-
description:
|
|
4173
|
+
name: import_zod13.z.string().min(1).max(120),
|
|
4174
|
+
description: import_zod13.z.string().max(1e3).optional(),
|
|
3921
4175
|
translations: Topic.shape.translations.optional()
|
|
3922
4176
|
});
|
|
3923
4177
|
var TopicUpdate = TopicCreate.omit({ slug: true }).partial().refine((v) => Object.keys(v).length > 0, "at least one field");
|
|
3924
|
-
var Contact =
|
|
4178
|
+
var Contact = import_zod13.z.object({
|
|
3925
4179
|
id: Id,
|
|
3926
|
-
external_id:
|
|
4180
|
+
external_id: import_zod13.z.string().min(1).max(255).nullable(),
|
|
3927
4181
|
/** Stored lowercased, unique per workspace. */
|
|
3928
4182
|
email: Email,
|
|
3929
|
-
first_name:
|
|
3930
|
-
last_name:
|
|
3931
|
-
locale:
|
|
4183
|
+
first_name: import_zod13.z.string().max(200).nullable(),
|
|
4184
|
+
last_name: import_zod13.z.string().max(200).nullable(),
|
|
4185
|
+
locale: import_zod13.z.string().min(2).max(35).nullable(),
|
|
3932
4186
|
/**
|
|
3933
4187
|
* The person's own time zone, an IANA name such as `Europe/Berlin` (A1).
|
|
3934
4188
|
* Every wall clock an automation waits for is read in it, falling back to the
|
|
@@ -3936,65 +4190,65 @@ var Contact = import_zod12.z.object({
|
|
|
3936
4190
|
*/
|
|
3937
4191
|
timezone: TimezoneName.nullable(),
|
|
3938
4192
|
properties: Properties,
|
|
3939
|
-
topics:
|
|
4193
|
+
topics: import_zod13.z.array(Slug),
|
|
3940
4194
|
/** The slugs of the contact's tags (S4). */
|
|
3941
|
-
tags:
|
|
4195
|
+
tags: import_zod13.z.array(Slug),
|
|
3942
4196
|
created_at: Timestamp,
|
|
3943
4197
|
updated_at: Timestamp
|
|
3944
4198
|
});
|
|
3945
|
-
var ContactUpsert =
|
|
3946
|
-
external_id:
|
|
4199
|
+
var ContactUpsert = import_zod13.z.object({
|
|
4200
|
+
external_id: import_zod13.z.string().min(1).max(255).optional(),
|
|
3947
4201
|
email: Email.optional(),
|
|
3948
|
-
first_name:
|
|
3949
|
-
last_name:
|
|
3950
|
-
locale:
|
|
4202
|
+
first_name: import_zod13.z.string().max(200).nullable().optional(),
|
|
4203
|
+
last_name: import_zod13.z.string().max(200).nullable().optional(),
|
|
4204
|
+
locale: import_zod13.z.string().min(2).max(35).nullable().optional(),
|
|
3951
4205
|
timezone: TimezoneName.nullable().optional(),
|
|
3952
4206
|
properties: Properties.optional(),
|
|
3953
|
-
topics:
|
|
4207
|
+
topics: import_zod13.z.array(Slug).max(100).optional()
|
|
3954
4208
|
}).refine((v) => v.external_id !== void 0 || v.email !== void 0, {
|
|
3955
4209
|
message: "external_id or email is required",
|
|
3956
4210
|
path: ["email"]
|
|
3957
4211
|
});
|
|
3958
|
-
var ContactUpsertResult =
|
|
4212
|
+
var ContactUpsertResult = import_zod13.z.object({
|
|
3959
4213
|
contact: Contact,
|
|
3960
|
-
created:
|
|
4214
|
+
created: import_zod13.z.boolean()
|
|
3961
4215
|
});
|
|
3962
4216
|
var ContactListQuery = PageQuery.extend({
|
|
3963
|
-
email:
|
|
3964
|
-
external_id:
|
|
4217
|
+
email: import_zod13.z.string().min(1).max(254).optional(),
|
|
4218
|
+
external_id: import_zod13.z.string().min(1).max(255).optional(),
|
|
3965
4219
|
topic: Slug.optional()
|
|
3966
4220
|
});
|
|
3967
|
-
var ContactErased =
|
|
3968
|
-
ok:
|
|
3969
|
-
erased_messages:
|
|
3970
|
-
erased_recipients:
|
|
3971
|
-
suppressions_kept:
|
|
4221
|
+
var ContactErased = import_zod13.z.object({
|
|
4222
|
+
ok: import_zod13.z.literal(true),
|
|
4223
|
+
erased_messages: import_zod13.z.number().int().min(0),
|
|
4224
|
+
erased_recipients: import_zod13.z.number().int().min(0),
|
|
4225
|
+
suppressions_kept: import_zod13.z.number().int().min(0)
|
|
3972
4226
|
});
|
|
3973
4227
|
var SUPPRESSION_REASONS = ["unsubscribed", "bounced", "complained", "manual"];
|
|
3974
|
-
var SuppressionReason =
|
|
3975
|
-
var Suppression =
|
|
4228
|
+
var SuppressionReason = import_zod13.z.enum(SUPPRESSION_REASONS);
|
|
4229
|
+
var Suppression = import_zod13.z.object({
|
|
3976
4230
|
id: Id,
|
|
3977
4231
|
email: Email,
|
|
3978
4232
|
reason: SuppressionReason,
|
|
3979
4233
|
topic: Slug.nullable(),
|
|
3980
4234
|
source_message_id: Id.nullable(),
|
|
3981
|
-
note:
|
|
4235
|
+
note: import_zod13.z.string().max(1e3).nullable(),
|
|
3982
4236
|
created_at: Timestamp
|
|
3983
4237
|
});
|
|
3984
|
-
var SuppressionCreate =
|
|
4238
|
+
var SuppressionCreate = import_zod13.z.object({
|
|
3985
4239
|
email: Email,
|
|
3986
|
-
reason:
|
|
4240
|
+
reason: import_zod13.z.enum(["manual", "unsubscribed"]),
|
|
3987
4241
|
topic: Slug.nullable().optional(),
|
|
3988
|
-
note:
|
|
4242
|
+
note: import_zod13.z.string().max(1e3).optional()
|
|
3989
4243
|
});
|
|
3990
4244
|
var SuppressionListQuery = PageQuery.extend({
|
|
3991
|
-
email:
|
|
4245
|
+
email: import_zod13.z.string().min(1).max(254).optional(),
|
|
3992
4246
|
reason: SuppressionReason.optional(),
|
|
3993
4247
|
topic: Slug.optional()
|
|
3994
4248
|
});
|
|
3995
4249
|
|
|
3996
4250
|
// src/webhooks.ts
|
|
3997
|
-
var
|
|
4251
|
+
var import_zod14 = require("zod");
|
|
3998
4252
|
var WEBHOOK_EVENT_TYPES = [
|
|
3999
4253
|
"message.sent",
|
|
4000
4254
|
"message.failed",
|
|
@@ -4038,23 +4292,23 @@ var WEBHOOK_EVENT_TYPES = [
|
|
|
4038
4292
|
"template.updated",
|
|
4039
4293
|
"template.deleted"
|
|
4040
4294
|
];
|
|
4041
|
-
var WebhookEventType =
|
|
4042
|
-
var MessageEventBase =
|
|
4295
|
+
var WebhookEventType = import_zod14.z.enum(WEBHOOK_EVENT_TYPES);
|
|
4296
|
+
var MessageEventBase = import_zod14.z.object({
|
|
4043
4297
|
message_id: Id,
|
|
4044
4298
|
mailing_id: Id.nullable(),
|
|
4045
4299
|
mailing_metadata: MailingMetadata.nullable(),
|
|
4046
4300
|
recipient_id: Id.nullable(),
|
|
4047
4301
|
contact_id: Id.nullable(),
|
|
4048
|
-
external_id:
|
|
4302
|
+
external_id: import_zod14.z.string().nullable(),
|
|
4049
4303
|
email: Email,
|
|
4050
|
-
subject:
|
|
4304
|
+
subject: import_zod14.z.string(),
|
|
4051
4305
|
topic: Slug.nullable(),
|
|
4052
|
-
provider_message_id:
|
|
4053
|
-
is_test:
|
|
4306
|
+
provider_message_id: import_zod14.z.string().nullable(),
|
|
4307
|
+
is_test: import_zod14.z.boolean()
|
|
4054
4308
|
});
|
|
4055
4309
|
var MessageSentData = MessageEventBase.extend({
|
|
4056
4310
|
/** The final HTML as sent, so the client can archive it beside its own records. */
|
|
4057
|
-
html:
|
|
4311
|
+
html: import_zod14.z.string(),
|
|
4058
4312
|
sent_at: Timestamp,
|
|
4059
4313
|
/**
|
|
4060
4314
|
* The choice each insertion made (see `Message.insertions`); absent or null
|
|
@@ -4065,20 +4319,20 @@ var MessageSentData = MessageEventBase.extend({
|
|
|
4065
4319
|
insertions: InsertionChoices.nullish()
|
|
4066
4320
|
});
|
|
4067
4321
|
var MessageFailedData = MessageEventBase.extend({
|
|
4068
|
-
error:
|
|
4322
|
+
error: import_zod14.z.string(),
|
|
4069
4323
|
/** False when the provider rejected permanently; the recipient will not be retried. */
|
|
4070
|
-
retryable:
|
|
4324
|
+
retryable: import_zod14.z.boolean(),
|
|
4071
4325
|
failed_at: Timestamp
|
|
4072
4326
|
});
|
|
4073
4327
|
var UNSUBSCRIBE_SOURCES = ["hosted_page", "one_click", "api", "dashboard", "signup_form"];
|
|
4074
|
-
var ContactUnsubscribedData =
|
|
4328
|
+
var ContactUnsubscribedData = import_zod14.z.object({
|
|
4075
4329
|
contact_id: Id.nullable(),
|
|
4076
|
-
external_id:
|
|
4330
|
+
external_id: import_zod14.z.string().nullable(),
|
|
4077
4331
|
email: Email,
|
|
4078
4332
|
/** The topic unsubscribed from; null means every topic. */
|
|
4079
4333
|
topic: Slug.nullable(),
|
|
4080
4334
|
mailing_id: Id.nullable(),
|
|
4081
|
-
source:
|
|
4335
|
+
source: import_zod14.z.enum(UNSUBSCRIBE_SOURCES),
|
|
4082
4336
|
unsubscribed_at: Timestamp
|
|
4083
4337
|
});
|
|
4084
4338
|
var ContactResubscribedData = ContactUnsubscribedData.omit({ unsubscribed_at: true }).extend({
|
|
@@ -4087,142 +4341,142 @@ var ContactResubscribedData = ContactUnsubscribedData.omit({ unsubscribed_at: tr
|
|
|
4087
4341
|
* breaker undid a `bounced` block it judged false (a `contact.bounced` for the
|
|
4088
4342
|
* same address and mailing went out before); `topic` is null.
|
|
4089
4343
|
*/
|
|
4090
|
-
source:
|
|
4344
|
+
source: import_zod14.z.enum([...UNSUBSCRIBE_SOURCES, "bounce_reverted"]),
|
|
4091
4345
|
resubscribed_at: Timestamp
|
|
4092
4346
|
});
|
|
4093
|
-
var ContactBouncedData =
|
|
4347
|
+
var ContactBouncedData = import_zod14.z.object({
|
|
4094
4348
|
contact_id: Id.nullable(),
|
|
4095
|
-
external_id:
|
|
4349
|
+
external_id: import_zod14.z.string().nullable(),
|
|
4096
4350
|
email: Email,
|
|
4097
4351
|
/** `bounced` for a hard bounce, `complained` for a spam complaint. */
|
|
4098
|
-
reason:
|
|
4352
|
+
reason: import_zod14.z.enum(["bounced", "complained"]),
|
|
4099
4353
|
message_id: Id.nullable(),
|
|
4100
|
-
diagnostic:
|
|
4354
|
+
diagnostic: import_zod14.z.string().nullable(),
|
|
4101
4355
|
bounced_at: Timestamp
|
|
4102
4356
|
});
|
|
4103
|
-
var MailingFinishedData =
|
|
4357
|
+
var MailingFinishedData = import_zod14.z.object({
|
|
4104
4358
|
mailing_id: Id,
|
|
4105
4359
|
mailing_metadata: MailingMetadata,
|
|
4106
|
-
status:
|
|
4360
|
+
status: import_zod14.z.enum(["sent", "partially_failed", "cancelled"]),
|
|
4107
4361
|
counts: MailingCounts,
|
|
4108
4362
|
finished_at: Timestamp
|
|
4109
4363
|
});
|
|
4110
|
-
var ContactSubscribedData =
|
|
4364
|
+
var ContactSubscribedData = import_zod14.z.object({
|
|
4111
4365
|
contact_id: Id,
|
|
4112
|
-
external_id:
|
|
4366
|
+
external_id: import_zod14.z.string().nullable(),
|
|
4113
4367
|
email: Email,
|
|
4114
|
-
topics:
|
|
4115
|
-
source:
|
|
4368
|
+
topics: import_zod14.z.array(Slug),
|
|
4369
|
+
source: import_zod14.z.enum(["signup_form", "api", "dashboard", "automation"]),
|
|
4116
4370
|
signup_form_id: Id.nullable(),
|
|
4117
|
-
signup_form_version:
|
|
4371
|
+
signup_form_version: import_zod14.z.number().int().min(1).nullable(),
|
|
4118
4372
|
subscribed_at: Timestamp
|
|
4119
4373
|
});
|
|
4120
|
-
var ImportFinishedData =
|
|
4374
|
+
var ImportFinishedData = import_zod14.z.object({
|
|
4121
4375
|
import_id: Id,
|
|
4122
|
-
status:
|
|
4376
|
+
status: import_zod14.z.enum(["completed", "cancelled", "failed"]),
|
|
4123
4377
|
result: ImportReport.nullable(),
|
|
4124
|
-
error:
|
|
4378
|
+
error: import_zod14.z.string().nullable(),
|
|
4125
4379
|
finished_at: Timestamp
|
|
4126
4380
|
});
|
|
4127
|
-
var MailingScheduledData =
|
|
4381
|
+
var MailingScheduledData = import_zod14.z.object({
|
|
4128
4382
|
mailing_id: Id,
|
|
4129
4383
|
mailing_metadata: MailingMetadata,
|
|
4130
4384
|
scheduled_at: Timestamp.nullable()
|
|
4131
4385
|
});
|
|
4132
|
-
var MailingStartedData =
|
|
4386
|
+
var MailingStartedData = import_zod14.z.object({
|
|
4133
4387
|
mailing_id: Id,
|
|
4134
4388
|
mailing_metadata: MailingMetadata,
|
|
4135
|
-
trigger:
|
|
4136
|
-
recipients:
|
|
4389
|
+
trigger: import_zod14.z.enum(["send", "schedule"]),
|
|
4390
|
+
recipients: import_zod14.z.number().int().min(0),
|
|
4137
4391
|
started_at: Timestamp
|
|
4138
4392
|
});
|
|
4139
|
-
var MailingScheduleFailedData =
|
|
4393
|
+
var MailingScheduleFailedData = import_zod14.z.object({
|
|
4140
4394
|
mailing_id: Id,
|
|
4141
4395
|
mailing_metadata: MailingMetadata,
|
|
4142
|
-
code:
|
|
4143
|
-
message:
|
|
4396
|
+
code: import_zod14.z.string(),
|
|
4397
|
+
message: import_zod14.z.string(),
|
|
4144
4398
|
failed_at: Timestamp
|
|
4145
4399
|
});
|
|
4146
|
-
var MailingAbWinnerSelectedData =
|
|
4400
|
+
var MailingAbWinnerSelectedData = import_zod14.z.object({
|
|
4147
4401
|
mailing_id: Id,
|
|
4148
4402
|
mailing_metadata: MailingMetadata,
|
|
4149
|
-
winner:
|
|
4150
|
-
decided_by:
|
|
4151
|
-
variants:
|
|
4403
|
+
winner: import_zod14.z.string(),
|
|
4404
|
+
decided_by: import_zod14.z.enum(["metric", "manual"]),
|
|
4405
|
+
variants: import_zod14.z.array(VariantAnalytics),
|
|
4152
4406
|
decided_at: Timestamp
|
|
4153
4407
|
});
|
|
4154
|
-
var ContactTaggedData =
|
|
4408
|
+
var ContactTaggedData = import_zod14.z.object({
|
|
4155
4409
|
contact_id: Id,
|
|
4156
|
-
external_id:
|
|
4410
|
+
external_id: import_zod14.z.string().nullable(),
|
|
4157
4411
|
email: Email,
|
|
4158
4412
|
/** Only the tags the contact did not already have. */
|
|
4159
|
-
tags:
|
|
4160
|
-
source:
|
|
4413
|
+
tags: import_zod14.z.array(Slug).min(1),
|
|
4414
|
+
source: import_zod14.z.enum(["api", "dashboard", "signup_form", "import", "automation"]),
|
|
4161
4415
|
tagged_at: Timestamp
|
|
4162
4416
|
});
|
|
4163
|
-
var ContactUpdatedData =
|
|
4417
|
+
var ContactUpdatedData = import_zod14.z.object({
|
|
4164
4418
|
contact_id: Id,
|
|
4165
|
-
external_id:
|
|
4419
|
+
external_id: import_zod14.z.string().nullable(),
|
|
4166
4420
|
email: Email,
|
|
4167
|
-
changed:
|
|
4421
|
+
changed: import_zod14.z.array(import_zod14.z.string()).min(1),
|
|
4168
4422
|
properties: Properties,
|
|
4169
|
-
source:
|
|
4423
|
+
source: import_zod14.z.enum(["api", "dashboard", "signup_form", "import", "automation"]),
|
|
4170
4424
|
updated_at: Timestamp
|
|
4171
4425
|
});
|
|
4172
|
-
var AutomationRunEventBase =
|
|
4426
|
+
var AutomationRunEventBase = import_zod14.z.object({
|
|
4173
4427
|
automation_id: Id,
|
|
4174
|
-
automation_name:
|
|
4428
|
+
automation_name: import_zod14.z.string(),
|
|
4175
4429
|
run_id: Id,
|
|
4176
|
-
version:
|
|
4430
|
+
version: import_zod14.z.number().int().min(1),
|
|
4177
4431
|
contact_id: Id,
|
|
4178
|
-
external_id:
|
|
4432
|
+
external_id: import_zod14.z.string().nullable(),
|
|
4179
4433
|
email: Email
|
|
4180
4434
|
});
|
|
4181
4435
|
var AutomationRunStartedData = AutomationRunEventBase.extend({
|
|
4182
4436
|
/** What let them in: a trigger kind, or `manual` for an explicit enrolment. */
|
|
4183
|
-
trigger_kind:
|
|
4437
|
+
trigger_kind: import_zod14.z.string(),
|
|
4184
4438
|
entered_at: Timestamp
|
|
4185
4439
|
});
|
|
4186
4440
|
var AutomationRunCompletedData = AutomationRunEventBase.extend({
|
|
4187
|
-
last_step_id:
|
|
4441
|
+
last_step_id: import_zod14.z.string().nullable(),
|
|
4188
4442
|
completed_at: Timestamp
|
|
4189
4443
|
});
|
|
4190
4444
|
var AutomationRunExitedData = AutomationRunEventBase.extend({
|
|
4191
4445
|
reason: AutomationEndReason,
|
|
4192
|
-
step_id:
|
|
4446
|
+
step_id: import_zod14.z.string().nullable(),
|
|
4193
4447
|
exited_at: Timestamp
|
|
4194
4448
|
});
|
|
4195
4449
|
var AutomationRunFailedData = AutomationRunEventBase.extend({
|
|
4196
|
-
reason:
|
|
4197
|
-
step_id:
|
|
4450
|
+
reason: import_zod14.z.string(),
|
|
4451
|
+
step_id: import_zod14.z.string().nullable(),
|
|
4198
4452
|
failed_at: Timestamp
|
|
4199
4453
|
});
|
|
4200
4454
|
var AutomationWebhookData = AutomationRunEventBase.extend({
|
|
4201
|
-
step_id:
|
|
4455
|
+
step_id: import_zod14.z.string(),
|
|
4202
4456
|
/** The step's own label, when it was given one, so a receiver can branch on a name. */
|
|
4203
|
-
step_label:
|
|
4457
|
+
step_label: import_zod14.z.string().nullable(),
|
|
4204
4458
|
/** Whatever the step carries, verbatim. Null when it carries nothing. */
|
|
4205
4459
|
payload: JsonValue.nullable(),
|
|
4206
4460
|
fired_at: Timestamp
|
|
4207
4461
|
});
|
|
4208
|
-
var TemplateEventBase =
|
|
4462
|
+
var TemplateEventBase = import_zod14.z.object({
|
|
4209
4463
|
template_id: Id,
|
|
4210
|
-
name:
|
|
4211
|
-
version:
|
|
4212
|
-
source:
|
|
4464
|
+
name: import_zod14.z.string().min(1).max(200),
|
|
4465
|
+
version: import_zod14.z.number().int().min(1),
|
|
4466
|
+
source: import_zod14.z.enum(["api", "dashboard", "mjml_import"])
|
|
4213
4467
|
});
|
|
4214
4468
|
var TemplateCreatedData = TemplateEventBase.extend({ created_at: Timestamp });
|
|
4215
|
-
var TemplateUpdatedData = TemplateEventBase.extend({ archived:
|
|
4469
|
+
var TemplateUpdatedData = TemplateEventBase.extend({ archived: import_zod14.z.boolean(), updated_at: Timestamp });
|
|
4216
4470
|
var TemplateDeletedData = TemplateEventBase.extend({ deleted_at: Timestamp });
|
|
4217
|
-
var envelope = (type, data) =>
|
|
4471
|
+
var envelope = (type, data) => import_zod14.z.object({
|
|
4218
4472
|
/** Unique per event: a receiver deduplicates on it (deliveries may repeat). */
|
|
4219
4473
|
id: Id,
|
|
4220
|
-
type:
|
|
4474
|
+
type: import_zod14.z.literal(type),
|
|
4221
4475
|
created_at: Timestamp,
|
|
4222
4476
|
workspace_id: Id,
|
|
4223
4477
|
data
|
|
4224
4478
|
});
|
|
4225
|
-
var WebhookEvent =
|
|
4479
|
+
var WebhookEvent = import_zod14.z.discriminatedUnion("type", [
|
|
4226
4480
|
envelope("message.sent", MessageSentData),
|
|
4227
4481
|
envelope("message.failed", MessageFailedData),
|
|
4228
4482
|
envelope("contact.unsubscribed", ContactUnsubscribedData),
|
|
@@ -4246,16 +4500,16 @@ var WebhookEvent = import_zod13.z.discriminatedUnion("type", [
|
|
|
4246
4500
|
envelope("template.updated", TemplateUpdatedData),
|
|
4247
4501
|
envelope("template.deleted", TemplateDeletedData)
|
|
4248
4502
|
]);
|
|
4249
|
-
var WebhookEndpoint =
|
|
4503
|
+
var WebhookEndpoint = import_zod14.z.object({
|
|
4250
4504
|
id: Id,
|
|
4251
|
-
url:
|
|
4252
|
-
description:
|
|
4253
|
-
events:
|
|
4254
|
-
enabled:
|
|
4505
|
+
url: import_zod14.z.string().url(),
|
|
4506
|
+
description: import_zod14.z.string().max(500).nullable(),
|
|
4507
|
+
events: import_zod14.z.array(WebhookEventType).min(1),
|
|
4508
|
+
enabled: import_zod14.z.boolean(),
|
|
4255
4509
|
created_at: Timestamp,
|
|
4256
4510
|
updated_at: Timestamp
|
|
4257
4511
|
});
|
|
4258
|
-
var WebhookUrl =
|
|
4512
|
+
var WebhookUrl = import_zod14.z.string().url().refine((u) => {
|
|
4259
4513
|
try {
|
|
4260
4514
|
const url = new URL(u);
|
|
4261
4515
|
if (url.protocol === "https:") return true;
|
|
@@ -4264,35 +4518,35 @@ var WebhookUrl = import_zod13.z.string().url().refine((u) => {
|
|
|
4264
4518
|
return false;
|
|
4265
4519
|
}
|
|
4266
4520
|
}, "must be https (http only for localhost)");
|
|
4267
|
-
var WebhookEndpointCreate =
|
|
4521
|
+
var WebhookEndpointCreate = import_zod14.z.object({
|
|
4268
4522
|
url: WebhookUrl,
|
|
4269
|
-
description:
|
|
4270
|
-
events:
|
|
4271
|
-
enabled:
|
|
4523
|
+
description: import_zod14.z.string().max(500).optional(),
|
|
4524
|
+
events: import_zod14.z.array(WebhookEventType).min(1),
|
|
4525
|
+
enabled: import_zod14.z.boolean().optional()
|
|
4272
4526
|
});
|
|
4273
|
-
var WebhookEndpointUpdate =
|
|
4527
|
+
var WebhookEndpointUpdate = import_zod14.z.object({
|
|
4274
4528
|
url: WebhookUrl,
|
|
4275
|
-
description:
|
|
4276
|
-
events:
|
|
4277
|
-
enabled:
|
|
4529
|
+
description: import_zod14.z.string().max(500).nullable(),
|
|
4530
|
+
events: import_zod14.z.array(WebhookEventType).min(1),
|
|
4531
|
+
enabled: import_zod14.z.boolean()
|
|
4278
4532
|
}).partial().refine((v) => Object.keys(v).length > 0, "at least one field");
|
|
4279
|
-
var WebhookEndpointWithSecret =
|
|
4533
|
+
var WebhookEndpointWithSecret = import_zod14.z.object({
|
|
4280
4534
|
endpoint: WebhookEndpoint,
|
|
4281
|
-
secret:
|
|
4535
|
+
secret: import_zod14.z.string().min(32)
|
|
4282
4536
|
});
|
|
4283
4537
|
var WEBHOOK_DELIVERY_STATUSES = ["pending", "succeeded", "failed"];
|
|
4284
|
-
var WebhookDeliveryStatus =
|
|
4538
|
+
var WebhookDeliveryStatus = import_zod14.z.enum(WEBHOOK_DELIVERY_STATUSES);
|
|
4285
4539
|
var WEBHOOK_MAX_ATTEMPTS = 8;
|
|
4286
4540
|
var WEBHOOK_RETRY_DELAYS_SECONDS = [30, 120, 600, 1800, 3600, 7200, 21600];
|
|
4287
|
-
var WebhookDelivery =
|
|
4541
|
+
var WebhookDelivery = import_zod14.z.object({
|
|
4288
4542
|
id: Id,
|
|
4289
4543
|
endpoint_id: Id,
|
|
4290
4544
|
event_id: Id,
|
|
4291
4545
|
event_type: WebhookEventType,
|
|
4292
4546
|
status: WebhookDeliveryStatus,
|
|
4293
|
-
attempts:
|
|
4294
|
-
last_status_code:
|
|
4295
|
-
last_error:
|
|
4547
|
+
attempts: import_zod14.z.number().int().min(0),
|
|
4548
|
+
last_status_code: import_zod14.z.number().int().min(100).max(599).nullable(),
|
|
4549
|
+
last_error: import_zod14.z.string().nullable(),
|
|
4296
4550
|
next_attempt_at: Timestamp.nullable(),
|
|
4297
4551
|
delivered_at: Timestamp.nullable(),
|
|
4298
4552
|
created_at: Timestamp
|
|
@@ -4301,7 +4555,7 @@ var WebhookDeliveryListQuery = PageQuery.extend({
|
|
|
4301
4555
|
status: WebhookDeliveryStatus.optional(),
|
|
4302
4556
|
event_type: WebhookEventType.optional()
|
|
4303
4557
|
});
|
|
4304
|
-
var WebhookDeliveryParams =
|
|
4558
|
+
var WebhookDeliveryParams = import_zod14.z.object({ id: Id, delivery_id: Id });
|
|
4305
4559
|
|
|
4306
4560
|
// src/webhook-signing.ts
|
|
4307
4561
|
var WEBHOOK_SIGNATURE_HEADER = "x-mail-signature";
|
|
@@ -4368,79 +4622,79 @@ async function verifyWebhook(input) {
|
|
|
4368
4622
|
}
|
|
4369
4623
|
|
|
4370
4624
|
// src/billing.ts
|
|
4371
|
-
var
|
|
4625
|
+
var import_zod15 = require("zod");
|
|
4372
4626
|
var PLAN_IDS = ["free", "starter", "growth", "design_partner"];
|
|
4373
|
-
var PlanId =
|
|
4374
|
-
var PlanLimits =
|
|
4375
|
-
monthly_messages:
|
|
4376
|
-
contacts:
|
|
4377
|
-
members:
|
|
4378
|
-
providers:
|
|
4379
|
-
webhook_endpoints:
|
|
4627
|
+
var PlanId = import_zod15.z.enum(PLAN_IDS);
|
|
4628
|
+
var PlanLimits = import_zod15.z.object({
|
|
4629
|
+
monthly_messages: import_zod15.z.number().int().min(0).nullable(),
|
|
4630
|
+
contacts: import_zod15.z.number().int().min(0).nullable(),
|
|
4631
|
+
members: import_zod15.z.number().int().min(1).nullable(),
|
|
4632
|
+
providers: import_zod15.z.number().int().min(1).nullable(),
|
|
4633
|
+
webhook_endpoints: import_zod15.z.number().int().min(0).nullable(),
|
|
4380
4634
|
/** Automations that may be live (active or paused) at once (A1). */
|
|
4381
|
-
active_automations:
|
|
4635
|
+
active_automations: import_zod15.z.number().int().min(0).nullable(),
|
|
4382
4636
|
/** Steps one automation may hold. Null is the flow's own cap, not unlimited. */
|
|
4383
|
-
automation_steps:
|
|
4637
|
+
automation_steps: import_zod15.z.number().int().min(1).nullable()
|
|
4384
4638
|
});
|
|
4385
|
-
var PlanFeatures =
|
|
4639
|
+
var PlanFeatures = import_zod15.z.object({
|
|
4386
4640
|
/** A/B tests of subject and content on a mailing (S4). */
|
|
4387
|
-
ab_testing:
|
|
4641
|
+
ab_testing: import_zod15.z.boolean(),
|
|
4388
4642
|
/** Open and click tracking, still off until a workspace turns it on (`settings.tracking_enabled`). */
|
|
4389
|
-
tracking:
|
|
4643
|
+
tracking: import_zod15.z.boolean(),
|
|
4390
4644
|
/** Sending and hosting pages on the workspace's own domain (a later phase). */
|
|
4391
|
-
custom_domains:
|
|
4645
|
+
custom_domains: import_zod15.z.boolean(),
|
|
4392
4646
|
/** Automations: flows a person walks through step by step (A1). */
|
|
4393
|
-
automations:
|
|
4647
|
+
automations: import_zod15.z.boolean()
|
|
4394
4648
|
});
|
|
4395
4649
|
var PLAN_FEATURES = ["ab_testing", "tracking", "custom_domains", "automations"];
|
|
4396
|
-
var Plan =
|
|
4650
|
+
var Plan = import_zod15.z.object({
|
|
4397
4651
|
id: PlanId,
|
|
4398
|
-
name:
|
|
4652
|
+
name: import_zod15.z.string().min(1),
|
|
4399
4653
|
/** Price in the smallest currency unit per month; null for plans not sold. */
|
|
4400
|
-
monthly_price_cents:
|
|
4401
|
-
currency:
|
|
4654
|
+
monthly_price_cents: import_zod15.z.number().int().min(0).nullable(),
|
|
4655
|
+
currency: import_zod15.z.string().length(3),
|
|
4402
4656
|
limits: PlanLimits,
|
|
4403
4657
|
features: PlanFeatures
|
|
4404
4658
|
});
|
|
4405
|
-
var CatalogPlan = Plan.extend({ sellable:
|
|
4406
|
-
var Subscription =
|
|
4659
|
+
var CatalogPlan = Plan.extend({ sellable: import_zod15.z.boolean() });
|
|
4660
|
+
var Subscription = import_zod15.z.object({
|
|
4407
4661
|
workspace_id: Id,
|
|
4408
4662
|
plan: PlanId,
|
|
4409
|
-
status:
|
|
4663
|
+
status: import_zod15.z.enum(["active", "trialing", "past_due", "cancelled"]),
|
|
4410
4664
|
current_period_start: Timestamp,
|
|
4411
4665
|
current_period_end: Timestamp,
|
|
4412
|
-
cancel_at_period_end:
|
|
4666
|
+
cancel_at_period_end: import_zod15.z.boolean(),
|
|
4413
4667
|
/**
|
|
4414
4668
|
* A design partner outside billing (plan `design_partner`, no limits, no
|
|
4415
4669
|
* checkout). Set only by an operator, never through the API.
|
|
4416
4670
|
*/
|
|
4417
|
-
billing_exempt:
|
|
4671
|
+
billing_exempt: import_zod15.z.boolean()
|
|
4418
4672
|
});
|
|
4419
4673
|
var USAGE_METRICS = ["messages", "contacts", "members", "providers", "webhook_endpoints", "active_automations"];
|
|
4420
|
-
var UsageMetric =
|
|
4674
|
+
var UsageMetric = import_zod15.z.enum(USAGE_METRICS);
|
|
4421
4675
|
var USAGE_WARNING_RATIO = 0.8;
|
|
4422
|
-
var UsageWarning =
|
|
4676
|
+
var UsageWarning = import_zod15.z.object({
|
|
4423
4677
|
metric: UsageMetric,
|
|
4424
|
-
used:
|
|
4425
|
-
limit:
|
|
4678
|
+
used: import_zod15.z.number().int().min(0),
|
|
4679
|
+
limit: import_zod15.z.number().int().min(0),
|
|
4426
4680
|
/** `approaching`: at or above USAGE_WARNING_RATIO of the limit; `reached`: at or above the limit. */
|
|
4427
|
-
level:
|
|
4681
|
+
level: import_zod15.z.enum(["approaching", "reached"])
|
|
4428
4682
|
});
|
|
4429
|
-
var Usage =
|
|
4683
|
+
var Usage = import_zod15.z.object({
|
|
4430
4684
|
plan: PlanId,
|
|
4431
4685
|
period_start: Timestamp,
|
|
4432
4686
|
period_end: Timestamp,
|
|
4433
|
-
metrics:
|
|
4434
|
-
warnings:
|
|
4687
|
+
metrics: import_zod15.z.record(UsageMetric, import_zod15.z.object({ used: import_zod15.z.number().int().min(0), limit: import_zod15.z.number().int().min(0).nullable() })),
|
|
4688
|
+
warnings: import_zod15.z.array(UsageWarning)
|
|
4435
4689
|
});
|
|
4436
|
-
var CheckoutRequest =
|
|
4437
|
-
plan:
|
|
4438
|
-
success_url:
|
|
4439
|
-
cancel_url:
|
|
4690
|
+
var CheckoutRequest = import_zod15.z.object({
|
|
4691
|
+
plan: import_zod15.z.enum(["starter", "growth"]),
|
|
4692
|
+
success_url: import_zod15.z.string().url(),
|
|
4693
|
+
cancel_url: import_zod15.z.string().url()
|
|
4440
4694
|
});
|
|
4441
|
-
var CheckoutSession =
|
|
4442
|
-
var PortalRequest =
|
|
4443
|
-
var PortalSession =
|
|
4695
|
+
var CheckoutSession = import_zod15.z.object({ url: import_zod15.z.string().url() });
|
|
4696
|
+
var PortalRequest = import_zod15.z.object({ return_url: import_zod15.z.string().url() });
|
|
4697
|
+
var PortalSession = import_zod15.z.object({ url: import_zod15.z.string().url() });
|
|
4444
4698
|
var USAGE_WARNING_HEADER = "x-mail-usage-warning";
|
|
4445
4699
|
function formatUsageWarningHeader(warnings) {
|
|
4446
4700
|
if (warnings.length === 0) return null;
|
|
@@ -4460,7 +4714,7 @@ function parseUsageWarningHeader(value) {
|
|
|
4460
4714
|
}
|
|
4461
4715
|
|
|
4462
4716
|
// src/routes.ts
|
|
4463
|
-
var
|
|
4717
|
+
var import_zod16 = require("zod");
|
|
4464
4718
|
var list = (item) => page(item);
|
|
4465
4719
|
var foundationRoutes = {
|
|
4466
4720
|
"workspaces.create": {
|
|
@@ -4617,6 +4871,25 @@ var templateRoutes = {
|
|
|
4617
4871
|
access: "write",
|
|
4618
4872
|
phase: "S1"
|
|
4619
4873
|
},
|
|
4874
|
+
/**
|
|
4875
|
+
* The sending app declares what the template receives (see
|
|
4876
|
+
* `template-inputs.ts`). Replaces the stored declaration whole; the same
|
|
4877
|
+
* declaration again is a no-op (`changed: false`). Creates no template
|
|
4878
|
+
* version and takes no `base_version`, so it never conflicts with an edit.
|
|
4879
|
+
* An API key only (`send` or `full`): the declaration is the app's statement
|
|
4880
|
+
* about its own code, so a dashboard member is refused with `forbidden`, a
|
|
4881
|
+
* rule the access levels cannot express and the service enforces itself.
|
|
4882
|
+
*/
|
|
4883
|
+
"templates.declareInputs": {
|
|
4884
|
+
method: "PUT",
|
|
4885
|
+
path: "/v1/templates/:id/inputs",
|
|
4886
|
+
params: IdParams,
|
|
4887
|
+
body: TemplateInputsDeclaration,
|
|
4888
|
+
response: TemplateInputsDeclared,
|
|
4889
|
+
status: 200,
|
|
4890
|
+
access: "write",
|
|
4891
|
+
phase: "S1"
|
|
4892
|
+
},
|
|
4620
4893
|
"templates.delete": {
|
|
4621
4894
|
method: "DELETE",
|
|
4622
4895
|
path: "/v1/templates/:id",
|
|
@@ -4668,7 +4941,7 @@ var templateRoutes = {
|
|
|
4668
4941
|
path: "/v1/templates/:id/export",
|
|
4669
4942
|
params: IdParams,
|
|
4670
4943
|
query: TemplateExportQuery,
|
|
4671
|
-
response:
|
|
4944
|
+
response: import_zod16.z.string(),
|
|
4672
4945
|
responseType: "text",
|
|
4673
4946
|
status: 200,
|
|
4674
4947
|
access: "read",
|
|
@@ -4992,7 +5265,7 @@ var sendingRoutes = {
|
|
|
4992
5265
|
path: "/v1/mailings/:id/export",
|
|
4993
5266
|
params: IdParams,
|
|
4994
5267
|
query: MailingExportQuery,
|
|
4995
|
-
response:
|
|
5268
|
+
response: import_zod16.z.string(),
|
|
4996
5269
|
responseType: "text",
|
|
4997
5270
|
status: 200,
|
|
4998
5271
|
access: "read",
|
|
@@ -5225,7 +5498,7 @@ var platformRoutes = {
|
|
|
5225
5498
|
"contactProperties.list": {
|
|
5226
5499
|
method: "GET",
|
|
5227
5500
|
path: "/v1/contact-properties",
|
|
5228
|
-
response:
|
|
5501
|
+
response: import_zod16.z.object({ data: import_zod16.z.array(ContactPropertyDefinition) }),
|
|
5229
5502
|
status: 200,
|
|
5230
5503
|
access: "read",
|
|
5231
5504
|
phase: "S4"
|
|
@@ -5524,7 +5797,7 @@ var billingRoutes = {
|
|
|
5524
5797
|
"billing.plans": {
|
|
5525
5798
|
method: "GET",
|
|
5526
5799
|
path: "/v1/billing/plans",
|
|
5527
|
-
response:
|
|
5800
|
+
response: import_zod16.z.object({ data: import_zod16.z.array(CatalogPlan) }),
|
|
5528
5801
|
status: 200,
|
|
5529
5802
|
access: "read",
|
|
5530
5803
|
phase: "S5"
|
|
@@ -5666,7 +5939,7 @@ var automationRoutes = {
|
|
|
5666
5939
|
method: "POST",
|
|
5667
5940
|
path: "/v1/automations/:id/pause",
|
|
5668
5941
|
params: IdParams,
|
|
5669
|
-
body:
|
|
5942
|
+
body: import_zod16.z.object({}).strict(),
|
|
5670
5943
|
response: Automation,
|
|
5671
5944
|
status: 200,
|
|
5672
5945
|
access: "write",
|
|
@@ -5676,7 +5949,7 @@ var automationRoutes = {
|
|
|
5676
5949
|
method: "POST",
|
|
5677
5950
|
path: "/v1/automations/:id/resume",
|
|
5678
5951
|
params: IdParams,
|
|
5679
|
-
body:
|
|
5952
|
+
body: import_zod16.z.object({}).strict(),
|
|
5680
5953
|
response: Automation,
|
|
5681
5954
|
status: 200,
|
|
5682
5955
|
access: "write",
|
|
@@ -5686,7 +5959,7 @@ var automationRoutes = {
|
|
|
5686
5959
|
method: "POST",
|
|
5687
5960
|
path: "/v1/automations/:id/archive",
|
|
5688
5961
|
params: IdParams,
|
|
5689
|
-
body:
|
|
5962
|
+
body: import_zod16.z.object({}).strict(),
|
|
5690
5963
|
response: Automation,
|
|
5691
5964
|
status: 200,
|
|
5692
5965
|
access: "write",
|
|
@@ -6066,6 +6339,13 @@ function matchRoute(method, pathname) {
|
|
|
6066
6339
|
MAX_STEPS,
|
|
6067
6340
|
MAX_STEP_VISITS,
|
|
6068
6341
|
MAX_SUBJECT_LENGTH,
|
|
6342
|
+
MAX_TEMPLATE_INPUTS,
|
|
6343
|
+
MAX_TEMPLATE_INPUTS_APP_LENGTH,
|
|
6344
|
+
MAX_TEMPLATE_INPUTS_SEEN,
|
|
6345
|
+
MAX_TEMPLATE_INPUT_EXAMPLE_LENGTH,
|
|
6346
|
+
MAX_TEMPLATE_INPUT_LABEL_LENGTH,
|
|
6347
|
+
MAX_TEMPLATE_INPUT_NOTE_LENGTH,
|
|
6348
|
+
MAX_TEMPLATE_INPUT_VALUES,
|
|
6069
6349
|
MAX_TEMPLATE_TRANSLATIONS,
|
|
6070
6350
|
MAX_TRIGGERS,
|
|
6071
6351
|
MEMBER_ROLES,
|
|
@@ -6160,6 +6440,7 @@ function matchRoute(method, pathname) {
|
|
|
6160
6440
|
RecipientStatus,
|
|
6161
6441
|
RemoteImage,
|
|
6162
6442
|
ResendSigningSecret,
|
|
6443
|
+
SERVICE_FILLED_MERGE_FIELDS,
|
|
6163
6444
|
SIGNUP_FORM_FIELDS,
|
|
6164
6445
|
SKIP_REASONS,
|
|
6165
6446
|
SMTP_SECURITY,
|
|
@@ -6189,6 +6470,8 @@ function matchRoute(method, pathname) {
|
|
|
6189
6470
|
SuppressionCreate,
|
|
6190
6471
|
SuppressionListQuery,
|
|
6191
6472
|
SuppressionReason,
|
|
6473
|
+
TEMPLATE_INPUT_KINDS,
|
|
6474
|
+
TEMPLATE_INPUT_PROBLEM_REASONS,
|
|
6192
6475
|
TEMPLATE_LANGUAGE_STATUSES,
|
|
6193
6476
|
TERMINAL_MAILING_STATUSES,
|
|
6194
6477
|
Tag,
|
|
@@ -6205,6 +6488,16 @@ function matchRoute(method, pathname) {
|
|
|
6205
6488
|
TemplateImportPreview,
|
|
6206
6489
|
TemplateImportPreviewRequest,
|
|
6207
6490
|
TemplateImportResult,
|
|
6491
|
+
TemplateInput,
|
|
6492
|
+
TemplateInputMap,
|
|
6493
|
+
TemplateInputName,
|
|
6494
|
+
TemplateInputSeen,
|
|
6495
|
+
TemplateInputSource,
|
|
6496
|
+
TemplateInputValue,
|
|
6497
|
+
TemplateInputs,
|
|
6498
|
+
TemplateInputsDeclaration,
|
|
6499
|
+
TemplateInputsDeclared,
|
|
6500
|
+
TemplateInputsSeen,
|
|
6208
6501
|
TemplateLanguageStatus,
|
|
6209
6502
|
TemplateListQuery,
|
|
6210
6503
|
TemplatePreheader,
|
|
@@ -6358,6 +6651,10 @@ function matchRoute(method, pathname) {
|
|
|
6358
6651
|
renameInsertionChecked,
|
|
6359
6652
|
renameInsertionChoice,
|
|
6360
6653
|
renameInsertionChoiceChecked,
|
|
6654
|
+
renameInsertionEverywhere,
|
|
6655
|
+
renameInsertionPlacements,
|
|
6656
|
+
renameTokenInDocument,
|
|
6657
|
+
renameTokenInText,
|
|
6361
6658
|
resolveInsertions,
|
|
6362
6659
|
restorePatch,
|
|
6363
6660
|
routes,
|
|
@@ -6373,6 +6670,8 @@ function matchRoute(method, pathname) {
|
|
|
6373
6670
|
stepTargets,
|
|
6374
6671
|
storedTemplateLine,
|
|
6375
6672
|
stronglyConnected,
|
|
6673
|
+
templateInputDrift,
|
|
6674
|
+
templateInputProblems,
|
|
6376
6675
|
templateRoutes,
|
|
6377
6676
|
testGroupSize,
|
|
6378
6677
|
tightLoops,
|
|
@@ -6380,6 +6679,7 @@ function matchRoute(method, pathname) {
|
|
|
6380
6679
|
translationInsertionProblems,
|
|
6381
6680
|
translationTextFromInput,
|
|
6382
6681
|
triggerKey,
|
|
6682
|
+
unknownRefusedInsertions,
|
|
6383
6683
|
unplacedInsertions,
|
|
6384
6684
|
validateAutomation,
|
|
6385
6685
|
verifyWebhook
|