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