@marlinjai/mail-contract 0.2.0 → 0.3.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 +675 -105
- package/dist/index.d.ts +675 -105
- package/dist/index.js +107 -29
- package/dist/index.mjs +94 -29
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -49,6 +49,7 @@ __export(index_exports, {
|
|
|
49
49
|
ApiKeyScope: () => ApiKeyScope,
|
|
50
50
|
Asset: () => Asset,
|
|
51
51
|
AssetContentType: () => AssetContentType,
|
|
52
|
+
AssetHost: () => AssetHost,
|
|
52
53
|
AssetImport: () => AssetImport,
|
|
53
54
|
AssetPolicy: () => AssetPolicy,
|
|
54
55
|
AuditAction: () => AuditAction,
|
|
@@ -203,6 +204,7 @@ __export(index_exports, {
|
|
|
203
204
|
MAILING_KINDS: () => MAILING_KINDS,
|
|
204
205
|
MAILING_STATUSES: () => MAILING_STATUSES,
|
|
205
206
|
MAILING_TRANSITIONS: () => MAILING_TRANSITIONS,
|
|
207
|
+
MAX_ALLOWED_ASSET_HOSTS: () => MAX_ALLOWED_ASSET_HOSTS,
|
|
206
208
|
MAX_ASSET_BYTES: () => MAX_ASSET_BYTES,
|
|
207
209
|
MAX_DELAY_SECONDS: () => MAX_DELAY_SECONDS,
|
|
208
210
|
MAX_DOCUMENT_BYTES: () => MAX_DOCUMENT_BYTES,
|
|
@@ -215,6 +217,8 @@ __export(index_exports, {
|
|
|
215
217
|
MAX_IMPORT_URL_LENGTH: () => MAX_IMPORT_URL_LENGTH,
|
|
216
218
|
MAX_INVITE_TTL_DAYS: () => MAX_INVITE_TTL_DAYS,
|
|
217
219
|
MAX_IN_LIST: () => MAX_IN_LIST,
|
|
220
|
+
MAX_MERGE_DEFAULTS: () => MAX_MERGE_DEFAULTS,
|
|
221
|
+
MAX_MERGE_DEFAULT_LENGTH: () => MAX_MERGE_DEFAULT_LENGTH,
|
|
218
222
|
MAX_MJML_DEPTH: () => MAX_MJML_DEPTH,
|
|
219
223
|
MAX_MJML_ELEMENTS: () => MAX_MJML_ELEMENTS,
|
|
220
224
|
MAX_MJML_IMPORT_BYTES: () => MAX_MJML_IMPORT_BYTES,
|
|
@@ -259,6 +263,9 @@ __export(index_exports, {
|
|
|
259
263
|
MemberCreate: () => MemberCreate,
|
|
260
264
|
MemberRole: () => MemberRole,
|
|
261
265
|
MemberUpdate: () => MemberUpdate,
|
|
266
|
+
MergeDefaultValue: () => MergeDefaultValue,
|
|
267
|
+
MergeDefaults: () => MergeDefaults,
|
|
268
|
+
MergeFieldName: () => MergeFieldName,
|
|
262
269
|
Message: () => Message,
|
|
263
270
|
MessageFailedData: () => MessageFailedData,
|
|
264
271
|
MessageListQuery: () => MessageListQuery,
|
|
@@ -267,6 +274,8 @@ __export(index_exports, {
|
|
|
267
274
|
MessageSummary: () => MessageSummary,
|
|
268
275
|
NotifyRecipients: () => NotifyRecipients,
|
|
269
276
|
NotifyStep: () => NotifyStep,
|
|
277
|
+
ON_BEHALF_OF_HEADER: () => ON_BEHALF_OF_HEADER,
|
|
278
|
+
ON_BEHALF_OF_MAX_LENGTH: () => ON_BEHALF_OF_MAX_LENGTH,
|
|
270
279
|
OffsetDays: () => OffsetDays,
|
|
271
280
|
Ok: () => Ok,
|
|
272
281
|
PLAN_FEATURES: () => PLAN_FEATURES,
|
|
@@ -344,6 +353,8 @@ __export(index_exports, {
|
|
|
344
353
|
Template: () => Template,
|
|
345
354
|
TemplateCompileRequest: () => TemplateCompileRequest,
|
|
346
355
|
TemplateCreate: () => TemplateCreate,
|
|
356
|
+
TemplateCreatedData: () => TemplateCreatedData,
|
|
357
|
+
TemplateDeletedData: () => TemplateDeletedData,
|
|
347
358
|
TemplateDocument: () => TemplateDocument,
|
|
348
359
|
TemplateExportQuery: () => TemplateExportQuery,
|
|
349
360
|
TemplateImport: () => TemplateImport,
|
|
@@ -353,6 +364,7 @@ __export(index_exports, {
|
|
|
353
364
|
TemplateListQuery: () => TemplateListQuery,
|
|
354
365
|
TemplateSummary: () => TemplateSummary,
|
|
355
366
|
TemplateUpdate: () => TemplateUpdate,
|
|
367
|
+
TemplateUpdatedData: () => TemplateUpdatedData,
|
|
356
368
|
TemplateVersion: () => TemplateVersion,
|
|
357
369
|
TemplateVersionParams: () => TemplateVersionParams,
|
|
358
370
|
TimeOfDay: () => TimeOfDay,
|
|
@@ -429,6 +441,7 @@ __export(index_exports, {
|
|
|
429
441
|
matchRoute: () => matchRoute,
|
|
430
442
|
missingRequiredMergeFields: () => missingRequiredMergeFields,
|
|
431
443
|
nowSeconds: () => nowSeconds,
|
|
444
|
+
operationsWithAccess: () => operationsWithAccess,
|
|
432
445
|
page: () => page,
|
|
433
446
|
parseExportWarningsHeader: () => parseExportWarningsHeader,
|
|
434
447
|
parseUsageWarningHeader: () => parseUsageWarningHeader,
|
|
@@ -566,6 +579,8 @@ var IDEMPOTENCY_KEY_MAX_LENGTH = 255;
|
|
|
566
579
|
var IDEMPOTENCY_KEY_RETENTION_HOURS = 24;
|
|
567
580
|
var SUBJECT_HEADER = "x-mail-subject";
|
|
568
581
|
var WORKSPACE_HEADER = "x-mail-workspace";
|
|
582
|
+
var ON_BEHALF_OF_HEADER = "x-mail-on-behalf-of";
|
|
583
|
+
var ON_BEHALF_OF_MAX_LENGTH = 200;
|
|
569
584
|
var REQUEST_ID_HEADER = "x-request-id";
|
|
570
585
|
var RETRY_AFTER_HEADER = "retry-after";
|
|
571
586
|
var API_VERSION_PREFIX = "/v1";
|
|
@@ -2358,10 +2373,45 @@ var EventListQuery = PageQuery.extend({
|
|
|
2358
2373
|
contact_id: Id.optional()
|
|
2359
2374
|
});
|
|
2360
2375
|
|
|
2376
|
+
// src/unsubscribe.ts
|
|
2377
|
+
var UNSUBSCRIBE_PATH_PREFIX = "/u/";
|
|
2378
|
+
var LIST_UNSUBSCRIBE_POST_VALUE = "List-Unsubscribe=One-Click";
|
|
2379
|
+
var RESERVED_MERGE_FIELDS = {
|
|
2380
|
+
/** From the recipient's merge values, else the contact. Supports a fallback. */
|
|
2381
|
+
first_name: { source: "contact", fallback: true, required_for_broadcast: false },
|
|
2382
|
+
last_name: { source: "contact", fallback: true, required_for_broadcast: false },
|
|
2383
|
+
email: { source: "contact", fallback: false, required_for_broadcast: false },
|
|
2384
|
+
/** The hosted unsubscribe page for this recipient. A broadcast without it is refused. */
|
|
2385
|
+
unsubscribe_url: { source: "service", fallback: false, required_for_broadcast: true }
|
|
2386
|
+
};
|
|
2387
|
+
var MERGE_FIELD_PATTERN = /\{\{\s*([a-z][a-z0-9_]*)\s*(?:\|([^}]*))?\}\}/g;
|
|
2388
|
+
function findMergeFields(html) {
|
|
2389
|
+
const uses = [];
|
|
2390
|
+
for (const match of html.matchAll(MERGE_FIELD_PATTERN)) {
|
|
2391
|
+
uses.push({ name: match[1], fallback: match[2] === void 0 ? null : match[2].trim() });
|
|
2392
|
+
}
|
|
2393
|
+
return uses;
|
|
2394
|
+
}
|
|
2395
|
+
function missingRequiredMergeFields(html) {
|
|
2396
|
+
const used = new Set(findMergeFields(html).map((u) => u.name));
|
|
2397
|
+
return Object.keys(RESERVED_MERGE_FIELDS).filter(
|
|
2398
|
+
(name) => RESERVED_MERGE_FIELDS[name].required_for_broadcast && !used.has(name)
|
|
2399
|
+
);
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2361
2402
|
// src/workspace.ts
|
|
2362
2403
|
var CompanyId = import_zod8.z.string().min(1).max(64);
|
|
2363
2404
|
var ASSET_POLICIES = ["any", "service_only"];
|
|
2364
2405
|
var AssetPolicy = import_zod8.z.enum(ASSET_POLICIES);
|
|
2406
|
+
var MAX_ALLOWED_ASSET_HOSTS = 20;
|
|
2407
|
+
var AssetHost = import_zod8.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");
|
|
2408
|
+
var MAX_MERGE_DEFAULTS = 50;
|
|
2409
|
+
var MAX_MERGE_DEFAULT_LENGTH = 2e3;
|
|
2410
|
+
var MergeFieldName = import_zod8.z.string().regex(/^[a-z][a-z0-9_]*$/, "a merge-field name: lowercase letters, digits and underscores, starting with a letter").max(64);
|
|
2411
|
+
var MergeDefaultValue = import_zod8.z.union([import_zod8.z.string().max(MAX_MERGE_DEFAULT_LENGTH), import_zod8.z.number().finite(), import_zod8.z.boolean()]);
|
|
2412
|
+
var MergeDefaults = import_zod8.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), {
|
|
2413
|
+
message: `a reserved merge field (${Object.keys(RESERVED_MERGE_FIELDS).join(", ")}) cannot have a workspace default`
|
|
2414
|
+
});
|
|
2365
2415
|
var WorkspaceSettings = import_zod8.z.object({
|
|
2366
2416
|
/** Default language of the hosted unsubscribe page (BCP 47 tag, e.g. "de"). */
|
|
2367
2417
|
default_locale: import_zod8.z.string().min(2).max(35),
|
|
@@ -2371,6 +2421,14 @@ var WorkspaceSettings = import_zod8.z.object({
|
|
|
2371
2421
|
tracking_enabled: import_zod8.z.boolean(),
|
|
2372
2422
|
/** Where images, stylesheets and fonts in a mail may load from. `any` by default. */
|
|
2373
2423
|
asset_policy: AssetPolicy,
|
|
2424
|
+
/**
|
|
2425
|
+
* Hosts a `service_only` workspace also allows, besides the service's own:
|
|
2426
|
+
* the workspace's website, typically. Empty by default. Ignored under `any`,
|
|
2427
|
+
* which allows everything already.
|
|
2428
|
+
*/
|
|
2429
|
+
allowed_asset_hosts: import_zod8.z.array(AssetHost).max(MAX_ALLOWED_ASSET_HOSTS),
|
|
2430
|
+
/** Merge values every mail of the workspace can use (see `MergeDefaults`). Empty by default. */
|
|
2431
|
+
merge_defaults: MergeDefaults,
|
|
2374
2432
|
/**
|
|
2375
2433
|
* The time zone an automation reads a wall clock in for a contact who has
|
|
2376
2434
|
* none of their own (A1). Null falls back to UTC.
|
|
@@ -2539,7 +2597,16 @@ var AUDIT_ACTIONS = [
|
|
|
2539
2597
|
var AuditAction = import_zod8.z.enum(AUDIT_ACTIONS);
|
|
2540
2598
|
var AuditActor = import_zod8.z.discriminatedUnion("type", [
|
|
2541
2599
|
import_zod8.z.object({ type: import_zod8.z.literal("member"), member_id: Id, subject: import_zod8.z.string().min(1) }),
|
|
2542
|
-
import_zod8.z.object({
|
|
2600
|
+
import_zod8.z.object({
|
|
2601
|
+
type: import_zod8.z.literal("api_key"),
|
|
2602
|
+
api_key_id: Id,
|
|
2603
|
+
/**
|
|
2604
|
+
* Who the client's application said it was acting for, from
|
|
2605
|
+
* `ON_BEHALF_OF_HEADER`: reported by that application, never verified.
|
|
2606
|
+
* Absent when the call carried no label.
|
|
2607
|
+
*/
|
|
2608
|
+
on_behalf_of: import_zod8.z.string().min(1).max(ON_BEHALF_OF_MAX_LENGTH).optional()
|
|
2609
|
+
}),
|
|
2543
2610
|
/** The service itself (the worker, a bounce, the hosted unsubscribe page). */
|
|
2544
2611
|
import_zod8.z.object({ type: import_zod8.z.literal("system"), reason: import_zod8.z.string().min(1).max(200) })
|
|
2545
2612
|
]);
|
|
@@ -2867,7 +2934,16 @@ var WEBHOOK_EVENT_TYPES = [
|
|
|
2867
2934
|
* `webhook_events.type` is checked against it, not because an endpoint can ask
|
|
2868
2935
|
* to receive everybody's.
|
|
2869
2936
|
*/
|
|
2870
|
-
"automation.webhook"
|
|
2937
|
+
"automation.webhook",
|
|
2938
|
+
/**
|
|
2939
|
+
* E4 of the embeddable-mail plan: a template changed, so a client that
|
|
2940
|
+
* caches or mirrors templates (an app embedding the editor beside the
|
|
2941
|
+
* hosted dashboard) can invalidate. The service stays the source of truth:
|
|
2942
|
+
* the event carries the new version, never the document.
|
|
2943
|
+
*/
|
|
2944
|
+
"template.created",
|
|
2945
|
+
"template.updated",
|
|
2946
|
+
"template.deleted"
|
|
2871
2947
|
];
|
|
2872
2948
|
var WebhookEventType = import_zod12.z.enum(WEBHOOK_EVENT_TYPES);
|
|
2873
2949
|
var MessageEventBase = import_zod12.z.object({
|
|
@@ -3029,6 +3105,15 @@ var AutomationWebhookData = AutomationRunEventBase.extend({
|
|
|
3029
3105
|
payload: JsonValue.nullable(),
|
|
3030
3106
|
fired_at: Timestamp
|
|
3031
3107
|
});
|
|
3108
|
+
var TemplateEventBase = import_zod12.z.object({
|
|
3109
|
+
template_id: Id,
|
|
3110
|
+
name: import_zod12.z.string().min(1).max(200),
|
|
3111
|
+
version: import_zod12.z.number().int().min(1),
|
|
3112
|
+
source: import_zod12.z.enum(["api", "dashboard", "mjml_import"])
|
|
3113
|
+
});
|
|
3114
|
+
var TemplateCreatedData = TemplateEventBase.extend({ created_at: Timestamp });
|
|
3115
|
+
var TemplateUpdatedData = TemplateEventBase.extend({ archived: import_zod12.z.boolean(), updated_at: Timestamp });
|
|
3116
|
+
var TemplateDeletedData = TemplateEventBase.extend({ deleted_at: Timestamp });
|
|
3032
3117
|
var envelope = (type, data) => import_zod12.z.object({
|
|
3033
3118
|
/** Unique per event: a receiver deduplicates on it (deliveries may repeat). */
|
|
3034
3119
|
id: Id,
|
|
@@ -3056,7 +3141,10 @@ var WebhookEvent = import_zod12.z.discriminatedUnion("type", [
|
|
|
3056
3141
|
envelope("automation.run_completed", AutomationRunCompletedData),
|
|
3057
3142
|
envelope("automation.run_exited", AutomationRunExitedData),
|
|
3058
3143
|
envelope("automation.run_failed", AutomationRunFailedData),
|
|
3059
|
-
envelope("automation.webhook", AutomationWebhookData)
|
|
3144
|
+
envelope("automation.webhook", AutomationWebhookData),
|
|
3145
|
+
envelope("template.created", TemplateCreatedData),
|
|
3146
|
+
envelope("template.updated", TemplateUpdatedData),
|
|
3147
|
+
envelope("template.deleted", TemplateDeletedData)
|
|
3060
3148
|
]);
|
|
3061
3149
|
var WebhookEndpoint = import_zod12.z.object({
|
|
3062
3150
|
id: Id,
|
|
@@ -3179,32 +3267,6 @@ async function verifyWebhook(input) {
|
|
|
3179
3267
|
return matched ? { ok: true, timestamp } : { ok: false, reason: "signature_mismatch" };
|
|
3180
3268
|
}
|
|
3181
3269
|
|
|
3182
|
-
// src/unsubscribe.ts
|
|
3183
|
-
var UNSUBSCRIBE_PATH_PREFIX = "/u/";
|
|
3184
|
-
var LIST_UNSUBSCRIBE_POST_VALUE = "List-Unsubscribe=One-Click";
|
|
3185
|
-
var RESERVED_MERGE_FIELDS = {
|
|
3186
|
-
/** From the recipient's merge values, else the contact. Supports a fallback. */
|
|
3187
|
-
first_name: { source: "contact", fallback: true, required_for_broadcast: false },
|
|
3188
|
-
last_name: { source: "contact", fallback: true, required_for_broadcast: false },
|
|
3189
|
-
email: { source: "contact", fallback: false, required_for_broadcast: false },
|
|
3190
|
-
/** The hosted unsubscribe page for this recipient. A broadcast without it is refused. */
|
|
3191
|
-
unsubscribe_url: { source: "service", fallback: false, required_for_broadcast: true }
|
|
3192
|
-
};
|
|
3193
|
-
var MERGE_FIELD_PATTERN = /\{\{\s*([a-z][a-z0-9_]*)\s*(?:\|([^}]*))?\}\}/g;
|
|
3194
|
-
function findMergeFields(html) {
|
|
3195
|
-
const uses = [];
|
|
3196
|
-
for (const match of html.matchAll(MERGE_FIELD_PATTERN)) {
|
|
3197
|
-
uses.push({ name: match[1], fallback: match[2] === void 0 ? null : match[2].trim() });
|
|
3198
|
-
}
|
|
3199
|
-
return uses;
|
|
3200
|
-
}
|
|
3201
|
-
function missingRequiredMergeFields(html) {
|
|
3202
|
-
const used = new Set(findMergeFields(html).map((u) => u.name));
|
|
3203
|
-
return Object.keys(RESERVED_MERGE_FIELDS).filter(
|
|
3204
|
-
(name) => RESERVED_MERGE_FIELDS[name].required_for_broadcast && !used.has(name)
|
|
3205
|
-
);
|
|
3206
|
-
}
|
|
3207
|
-
|
|
3208
3270
|
// src/billing.ts
|
|
3209
3271
|
var import_zod13 = require("zod");
|
|
3210
3272
|
var PLAN_IDS = ["free", "starter", "growth", "design_partner"];
|
|
@@ -4632,6 +4694,9 @@ function buildPath(path, params = {}) {
|
|
|
4632
4694
|
return encodeURIComponent(String(value));
|
|
4633
4695
|
});
|
|
4634
4696
|
}
|
|
4697
|
+
function operationsWithAccess(access) {
|
|
4698
|
+
return Object.entries(routes).filter(([, route]) => route.access === access).map(([id]) => id).sort();
|
|
4699
|
+
}
|
|
4635
4700
|
function matchRoute(method, pathname) {
|
|
4636
4701
|
for (const [id, route] of Object.entries(routes)) {
|
|
4637
4702
|
if (route.method !== method) continue;
|
|
@@ -4683,6 +4748,7 @@ function matchRoute(method, pathname) {
|
|
|
4683
4748
|
ApiKeyScope,
|
|
4684
4749
|
Asset,
|
|
4685
4750
|
AssetContentType,
|
|
4751
|
+
AssetHost,
|
|
4686
4752
|
AssetImport,
|
|
4687
4753
|
AssetPolicy,
|
|
4688
4754
|
AuditAction,
|
|
@@ -4837,6 +4903,7 @@ function matchRoute(method, pathname) {
|
|
|
4837
4903
|
MAILING_KINDS,
|
|
4838
4904
|
MAILING_STATUSES,
|
|
4839
4905
|
MAILING_TRANSITIONS,
|
|
4906
|
+
MAX_ALLOWED_ASSET_HOSTS,
|
|
4840
4907
|
MAX_ASSET_BYTES,
|
|
4841
4908
|
MAX_DELAY_SECONDS,
|
|
4842
4909
|
MAX_DOCUMENT_BYTES,
|
|
@@ -4849,6 +4916,8 @@ function matchRoute(method, pathname) {
|
|
|
4849
4916
|
MAX_IMPORT_URL_LENGTH,
|
|
4850
4917
|
MAX_INVITE_TTL_DAYS,
|
|
4851
4918
|
MAX_IN_LIST,
|
|
4919
|
+
MAX_MERGE_DEFAULTS,
|
|
4920
|
+
MAX_MERGE_DEFAULT_LENGTH,
|
|
4852
4921
|
MAX_MJML_DEPTH,
|
|
4853
4922
|
MAX_MJML_ELEMENTS,
|
|
4854
4923
|
MAX_MJML_IMPORT_BYTES,
|
|
@@ -4893,6 +4962,9 @@ function matchRoute(method, pathname) {
|
|
|
4893
4962
|
MemberCreate,
|
|
4894
4963
|
MemberRole,
|
|
4895
4964
|
MemberUpdate,
|
|
4965
|
+
MergeDefaultValue,
|
|
4966
|
+
MergeDefaults,
|
|
4967
|
+
MergeFieldName,
|
|
4896
4968
|
Message,
|
|
4897
4969
|
MessageFailedData,
|
|
4898
4970
|
MessageListQuery,
|
|
@@ -4901,6 +4973,8 @@ function matchRoute(method, pathname) {
|
|
|
4901
4973
|
MessageSummary,
|
|
4902
4974
|
NotifyRecipients,
|
|
4903
4975
|
NotifyStep,
|
|
4976
|
+
ON_BEHALF_OF_HEADER,
|
|
4977
|
+
ON_BEHALF_OF_MAX_LENGTH,
|
|
4904
4978
|
OffsetDays,
|
|
4905
4979
|
Ok,
|
|
4906
4980
|
PLAN_FEATURES,
|
|
@@ -4978,6 +5052,8 @@ function matchRoute(method, pathname) {
|
|
|
4978
5052
|
Template,
|
|
4979
5053
|
TemplateCompileRequest,
|
|
4980
5054
|
TemplateCreate,
|
|
5055
|
+
TemplateCreatedData,
|
|
5056
|
+
TemplateDeletedData,
|
|
4981
5057
|
TemplateDocument,
|
|
4982
5058
|
TemplateExportQuery,
|
|
4983
5059
|
TemplateImport,
|
|
@@ -4987,6 +5063,7 @@ function matchRoute(method, pathname) {
|
|
|
4987
5063
|
TemplateListQuery,
|
|
4988
5064
|
TemplateSummary,
|
|
4989
5065
|
TemplateUpdate,
|
|
5066
|
+
TemplateUpdatedData,
|
|
4990
5067
|
TemplateVersion,
|
|
4991
5068
|
TemplateVersionParams,
|
|
4992
5069
|
TimeOfDay,
|
|
@@ -5063,6 +5140,7 @@ function matchRoute(method, pathname) {
|
|
|
5063
5140
|
matchRoute,
|
|
5064
5141
|
missingRequiredMergeFields,
|
|
5065
5142
|
nowSeconds,
|
|
5143
|
+
operationsWithAccess,
|
|
5066
5144
|
page,
|
|
5067
5145
|
parseExportWarningsHeader,
|
|
5068
5146
|
parseUsageWarningHeader,
|
package/dist/index.mjs
CHANGED
|
@@ -114,6 +114,8 @@ var IDEMPOTENCY_KEY_MAX_LENGTH = 255;
|
|
|
114
114
|
var IDEMPOTENCY_KEY_RETENTION_HOURS = 24;
|
|
115
115
|
var SUBJECT_HEADER = "x-mail-subject";
|
|
116
116
|
var WORKSPACE_HEADER = "x-mail-workspace";
|
|
117
|
+
var ON_BEHALF_OF_HEADER = "x-mail-on-behalf-of";
|
|
118
|
+
var ON_BEHALF_OF_MAX_LENGTH = 200;
|
|
117
119
|
var REQUEST_ID_HEADER = "x-request-id";
|
|
118
120
|
var RETRY_AFTER_HEADER = "retry-after";
|
|
119
121
|
var API_VERSION_PREFIX = "/v1";
|
|
@@ -1906,10 +1908,45 @@ var EventListQuery = PageQuery.extend({
|
|
|
1906
1908
|
contact_id: Id.optional()
|
|
1907
1909
|
});
|
|
1908
1910
|
|
|
1911
|
+
// src/unsubscribe.ts
|
|
1912
|
+
var UNSUBSCRIBE_PATH_PREFIX = "/u/";
|
|
1913
|
+
var LIST_UNSUBSCRIBE_POST_VALUE = "List-Unsubscribe=One-Click";
|
|
1914
|
+
var RESERVED_MERGE_FIELDS = {
|
|
1915
|
+
/** From the recipient's merge values, else the contact. Supports a fallback. */
|
|
1916
|
+
first_name: { source: "contact", fallback: true, required_for_broadcast: false },
|
|
1917
|
+
last_name: { source: "contact", fallback: true, required_for_broadcast: false },
|
|
1918
|
+
email: { source: "contact", fallback: false, required_for_broadcast: false },
|
|
1919
|
+
/** The hosted unsubscribe page for this recipient. A broadcast without it is refused. */
|
|
1920
|
+
unsubscribe_url: { source: "service", fallback: false, required_for_broadcast: true }
|
|
1921
|
+
};
|
|
1922
|
+
var MERGE_FIELD_PATTERN = /\{\{\s*([a-z][a-z0-9_]*)\s*(?:\|([^}]*))?\}\}/g;
|
|
1923
|
+
function findMergeFields(html) {
|
|
1924
|
+
const uses = [];
|
|
1925
|
+
for (const match of html.matchAll(MERGE_FIELD_PATTERN)) {
|
|
1926
|
+
uses.push({ name: match[1], fallback: match[2] === void 0 ? null : match[2].trim() });
|
|
1927
|
+
}
|
|
1928
|
+
return uses;
|
|
1929
|
+
}
|
|
1930
|
+
function missingRequiredMergeFields(html) {
|
|
1931
|
+
const used = new Set(findMergeFields(html).map((u) => u.name));
|
|
1932
|
+
return Object.keys(RESERVED_MERGE_FIELDS).filter(
|
|
1933
|
+
(name) => RESERVED_MERGE_FIELDS[name].required_for_broadcast && !used.has(name)
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1909
1937
|
// src/workspace.ts
|
|
1910
1938
|
var CompanyId = z8.string().min(1).max(64);
|
|
1911
1939
|
var ASSET_POLICIES = ["any", "service_only"];
|
|
1912
1940
|
var AssetPolicy = z8.enum(ASSET_POLICIES);
|
|
1941
|
+
var MAX_ALLOWED_ASSET_HOSTS = 20;
|
|
1942
|
+
var AssetHost = z8.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");
|
|
1943
|
+
var MAX_MERGE_DEFAULTS = 50;
|
|
1944
|
+
var MAX_MERGE_DEFAULT_LENGTH = 2e3;
|
|
1945
|
+
var MergeFieldName = z8.string().regex(/^[a-z][a-z0-9_]*$/, "a merge-field name: lowercase letters, digits and underscores, starting with a letter").max(64);
|
|
1946
|
+
var MergeDefaultValue = z8.union([z8.string().max(MAX_MERGE_DEFAULT_LENGTH), z8.number().finite(), z8.boolean()]);
|
|
1947
|
+
var MergeDefaults = z8.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), {
|
|
1948
|
+
message: `a reserved merge field (${Object.keys(RESERVED_MERGE_FIELDS).join(", ")}) cannot have a workspace default`
|
|
1949
|
+
});
|
|
1913
1950
|
var WorkspaceSettings = z8.object({
|
|
1914
1951
|
/** Default language of the hosted unsubscribe page (BCP 47 tag, e.g. "de"). */
|
|
1915
1952
|
default_locale: z8.string().min(2).max(35),
|
|
@@ -1919,6 +1956,14 @@ var WorkspaceSettings = z8.object({
|
|
|
1919
1956
|
tracking_enabled: z8.boolean(),
|
|
1920
1957
|
/** Where images, stylesheets and fonts in a mail may load from. `any` by default. */
|
|
1921
1958
|
asset_policy: AssetPolicy,
|
|
1959
|
+
/**
|
|
1960
|
+
* Hosts a `service_only` workspace also allows, besides the service's own:
|
|
1961
|
+
* the workspace's website, typically. Empty by default. Ignored under `any`,
|
|
1962
|
+
* which allows everything already.
|
|
1963
|
+
*/
|
|
1964
|
+
allowed_asset_hosts: z8.array(AssetHost).max(MAX_ALLOWED_ASSET_HOSTS),
|
|
1965
|
+
/** Merge values every mail of the workspace can use (see `MergeDefaults`). Empty by default. */
|
|
1966
|
+
merge_defaults: MergeDefaults,
|
|
1922
1967
|
/**
|
|
1923
1968
|
* The time zone an automation reads a wall clock in for a contact who has
|
|
1924
1969
|
* none of their own (A1). Null falls back to UTC.
|
|
@@ -2087,7 +2132,16 @@ var AUDIT_ACTIONS = [
|
|
|
2087
2132
|
var AuditAction = z8.enum(AUDIT_ACTIONS);
|
|
2088
2133
|
var AuditActor = z8.discriminatedUnion("type", [
|
|
2089
2134
|
z8.object({ type: z8.literal("member"), member_id: Id, subject: z8.string().min(1) }),
|
|
2090
|
-
z8.object({
|
|
2135
|
+
z8.object({
|
|
2136
|
+
type: z8.literal("api_key"),
|
|
2137
|
+
api_key_id: Id,
|
|
2138
|
+
/**
|
|
2139
|
+
* Who the client's application said it was acting for, from
|
|
2140
|
+
* `ON_BEHALF_OF_HEADER`: reported by that application, never verified.
|
|
2141
|
+
* Absent when the call carried no label.
|
|
2142
|
+
*/
|
|
2143
|
+
on_behalf_of: z8.string().min(1).max(ON_BEHALF_OF_MAX_LENGTH).optional()
|
|
2144
|
+
}),
|
|
2091
2145
|
/** The service itself (the worker, a bounce, the hosted unsubscribe page). */
|
|
2092
2146
|
z8.object({ type: z8.literal("system"), reason: z8.string().min(1).max(200) })
|
|
2093
2147
|
]);
|
|
@@ -2415,7 +2469,16 @@ var WEBHOOK_EVENT_TYPES = [
|
|
|
2415
2469
|
* `webhook_events.type` is checked against it, not because an endpoint can ask
|
|
2416
2470
|
* to receive everybody's.
|
|
2417
2471
|
*/
|
|
2418
|
-
"automation.webhook"
|
|
2472
|
+
"automation.webhook",
|
|
2473
|
+
/**
|
|
2474
|
+
* E4 of the embeddable-mail plan: a template changed, so a client that
|
|
2475
|
+
* caches or mirrors templates (an app embedding the editor beside the
|
|
2476
|
+
* hosted dashboard) can invalidate. The service stays the source of truth:
|
|
2477
|
+
* the event carries the new version, never the document.
|
|
2478
|
+
*/
|
|
2479
|
+
"template.created",
|
|
2480
|
+
"template.updated",
|
|
2481
|
+
"template.deleted"
|
|
2419
2482
|
];
|
|
2420
2483
|
var WebhookEventType = z12.enum(WEBHOOK_EVENT_TYPES);
|
|
2421
2484
|
var MessageEventBase = z12.object({
|
|
@@ -2577,6 +2640,15 @@ var AutomationWebhookData = AutomationRunEventBase.extend({
|
|
|
2577
2640
|
payload: JsonValue.nullable(),
|
|
2578
2641
|
fired_at: Timestamp
|
|
2579
2642
|
});
|
|
2643
|
+
var TemplateEventBase = z12.object({
|
|
2644
|
+
template_id: Id,
|
|
2645
|
+
name: z12.string().min(1).max(200),
|
|
2646
|
+
version: z12.number().int().min(1),
|
|
2647
|
+
source: z12.enum(["api", "dashboard", "mjml_import"])
|
|
2648
|
+
});
|
|
2649
|
+
var TemplateCreatedData = TemplateEventBase.extend({ created_at: Timestamp });
|
|
2650
|
+
var TemplateUpdatedData = TemplateEventBase.extend({ archived: z12.boolean(), updated_at: Timestamp });
|
|
2651
|
+
var TemplateDeletedData = TemplateEventBase.extend({ deleted_at: Timestamp });
|
|
2580
2652
|
var envelope = (type, data) => z12.object({
|
|
2581
2653
|
/** Unique per event: a receiver deduplicates on it (deliveries may repeat). */
|
|
2582
2654
|
id: Id,
|
|
@@ -2604,7 +2676,10 @@ var WebhookEvent = z12.discriminatedUnion("type", [
|
|
|
2604
2676
|
envelope("automation.run_completed", AutomationRunCompletedData),
|
|
2605
2677
|
envelope("automation.run_exited", AutomationRunExitedData),
|
|
2606
2678
|
envelope("automation.run_failed", AutomationRunFailedData),
|
|
2607
|
-
envelope("automation.webhook", AutomationWebhookData)
|
|
2679
|
+
envelope("automation.webhook", AutomationWebhookData),
|
|
2680
|
+
envelope("template.created", TemplateCreatedData),
|
|
2681
|
+
envelope("template.updated", TemplateUpdatedData),
|
|
2682
|
+
envelope("template.deleted", TemplateDeletedData)
|
|
2608
2683
|
]);
|
|
2609
2684
|
var WebhookEndpoint = z12.object({
|
|
2610
2685
|
id: Id,
|
|
@@ -2727,32 +2802,6 @@ async function verifyWebhook(input) {
|
|
|
2727
2802
|
return matched ? { ok: true, timestamp } : { ok: false, reason: "signature_mismatch" };
|
|
2728
2803
|
}
|
|
2729
2804
|
|
|
2730
|
-
// src/unsubscribe.ts
|
|
2731
|
-
var UNSUBSCRIBE_PATH_PREFIX = "/u/";
|
|
2732
|
-
var LIST_UNSUBSCRIBE_POST_VALUE = "List-Unsubscribe=One-Click";
|
|
2733
|
-
var RESERVED_MERGE_FIELDS = {
|
|
2734
|
-
/** From the recipient's merge values, else the contact. Supports a fallback. */
|
|
2735
|
-
first_name: { source: "contact", fallback: true, required_for_broadcast: false },
|
|
2736
|
-
last_name: { source: "contact", fallback: true, required_for_broadcast: false },
|
|
2737
|
-
email: { source: "contact", fallback: false, required_for_broadcast: false },
|
|
2738
|
-
/** The hosted unsubscribe page for this recipient. A broadcast without it is refused. */
|
|
2739
|
-
unsubscribe_url: { source: "service", fallback: false, required_for_broadcast: true }
|
|
2740
|
-
};
|
|
2741
|
-
var MERGE_FIELD_PATTERN = /\{\{\s*([a-z][a-z0-9_]*)\s*(?:\|([^}]*))?\}\}/g;
|
|
2742
|
-
function findMergeFields(html) {
|
|
2743
|
-
const uses = [];
|
|
2744
|
-
for (const match of html.matchAll(MERGE_FIELD_PATTERN)) {
|
|
2745
|
-
uses.push({ name: match[1], fallback: match[2] === void 0 ? null : match[2].trim() });
|
|
2746
|
-
}
|
|
2747
|
-
return uses;
|
|
2748
|
-
}
|
|
2749
|
-
function missingRequiredMergeFields(html) {
|
|
2750
|
-
const used = new Set(findMergeFields(html).map((u) => u.name));
|
|
2751
|
-
return Object.keys(RESERVED_MERGE_FIELDS).filter(
|
|
2752
|
-
(name) => RESERVED_MERGE_FIELDS[name].required_for_broadcast && !used.has(name)
|
|
2753
|
-
);
|
|
2754
|
-
}
|
|
2755
|
-
|
|
2756
2805
|
// src/billing.ts
|
|
2757
2806
|
import { z as z13 } from "zod";
|
|
2758
2807
|
var PLAN_IDS = ["free", "starter", "growth", "design_partner"];
|
|
@@ -4180,6 +4229,9 @@ function buildPath(path, params = {}) {
|
|
|
4180
4229
|
return encodeURIComponent(String(value));
|
|
4181
4230
|
});
|
|
4182
4231
|
}
|
|
4232
|
+
function operationsWithAccess(access) {
|
|
4233
|
+
return Object.entries(routes).filter(([, route]) => route.access === access).map(([id]) => id).sort();
|
|
4234
|
+
}
|
|
4183
4235
|
function matchRoute(method, pathname) {
|
|
4184
4236
|
for (const [id, route] of Object.entries(routes)) {
|
|
4185
4237
|
if (route.method !== method) continue;
|
|
@@ -4230,6 +4282,7 @@ export {
|
|
|
4230
4282
|
ApiKeyScope,
|
|
4231
4283
|
Asset,
|
|
4232
4284
|
AssetContentType,
|
|
4285
|
+
AssetHost,
|
|
4233
4286
|
AssetImport,
|
|
4234
4287
|
AssetPolicy,
|
|
4235
4288
|
AuditAction,
|
|
@@ -4384,6 +4437,7 @@ export {
|
|
|
4384
4437
|
MAILING_KINDS,
|
|
4385
4438
|
MAILING_STATUSES,
|
|
4386
4439
|
MAILING_TRANSITIONS,
|
|
4440
|
+
MAX_ALLOWED_ASSET_HOSTS,
|
|
4387
4441
|
MAX_ASSET_BYTES,
|
|
4388
4442
|
MAX_DELAY_SECONDS,
|
|
4389
4443
|
MAX_DOCUMENT_BYTES,
|
|
@@ -4396,6 +4450,8 @@ export {
|
|
|
4396
4450
|
MAX_IMPORT_URL_LENGTH,
|
|
4397
4451
|
MAX_INVITE_TTL_DAYS,
|
|
4398
4452
|
MAX_IN_LIST,
|
|
4453
|
+
MAX_MERGE_DEFAULTS,
|
|
4454
|
+
MAX_MERGE_DEFAULT_LENGTH,
|
|
4399
4455
|
MAX_MJML_DEPTH,
|
|
4400
4456
|
MAX_MJML_ELEMENTS,
|
|
4401
4457
|
MAX_MJML_IMPORT_BYTES,
|
|
@@ -4440,6 +4496,9 @@ export {
|
|
|
4440
4496
|
MemberCreate,
|
|
4441
4497
|
MemberRole,
|
|
4442
4498
|
MemberUpdate,
|
|
4499
|
+
MergeDefaultValue,
|
|
4500
|
+
MergeDefaults,
|
|
4501
|
+
MergeFieldName,
|
|
4443
4502
|
Message,
|
|
4444
4503
|
MessageFailedData,
|
|
4445
4504
|
MessageListQuery,
|
|
@@ -4448,6 +4507,8 @@ export {
|
|
|
4448
4507
|
MessageSummary,
|
|
4449
4508
|
NotifyRecipients,
|
|
4450
4509
|
NotifyStep,
|
|
4510
|
+
ON_BEHALF_OF_HEADER,
|
|
4511
|
+
ON_BEHALF_OF_MAX_LENGTH,
|
|
4451
4512
|
OffsetDays,
|
|
4452
4513
|
Ok,
|
|
4453
4514
|
PLAN_FEATURES,
|
|
@@ -4525,6 +4586,8 @@ export {
|
|
|
4525
4586
|
Template,
|
|
4526
4587
|
TemplateCompileRequest,
|
|
4527
4588
|
TemplateCreate,
|
|
4589
|
+
TemplateCreatedData,
|
|
4590
|
+
TemplateDeletedData,
|
|
4528
4591
|
TemplateDocument,
|
|
4529
4592
|
TemplateExportQuery,
|
|
4530
4593
|
TemplateImport,
|
|
@@ -4534,6 +4597,7 @@ export {
|
|
|
4534
4597
|
TemplateListQuery,
|
|
4535
4598
|
TemplateSummary,
|
|
4536
4599
|
TemplateUpdate,
|
|
4600
|
+
TemplateUpdatedData,
|
|
4537
4601
|
TemplateVersion,
|
|
4538
4602
|
TemplateVersionParams,
|
|
4539
4603
|
TimeOfDay,
|
|
@@ -4610,6 +4674,7 @@ export {
|
|
|
4610
4674
|
matchRoute,
|
|
4611
4675
|
missingRequiredMergeFields,
|
|
4612
4676
|
nowSeconds,
|
|
4677
|
+
operationsWithAccess,
|
|
4613
4678
|
page,
|
|
4614
4679
|
parseExportWarningsHeader,
|
|
4615
4680
|
parseUsageWarningHeader,
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.3.0",
|
|
7
7
|
"description": "The v1 API contract of the mail service: zod schemas and types for every request, response, error and webhook, the route table, and webhook signing helpers",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"repository": {
|