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