@primitivedotdev/sdk 0.24.0 → 0.25.1

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.
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./chunk-pbuEa-1d.js";
2
- import { r as formatAddress } from "./received-email-D6tKtWwW.js";
2
+ import { c as WebhookVerificationError, d as formatAddress } from "./errors-x91I_yEt.js";
3
3
  //#region src/api/generated/core/bodySerializer.gen.ts
4
4
  const jsonBodySerializer = { bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value) };
5
5
  //#endregion
@@ -689,7 +689,7 @@ const pollCliLogin = (options) => (options.client ?? client$1).post({
689
689
  url: "/cli/login/poll",
690
690
  ...options,
691
691
  headers: {
692
- "Content-Type": "application/json",
692
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
693
693
  ...options.headers
694
694
  }
695
695
  });
@@ -734,7 +734,7 @@ const updateAccount = (options) => (options.client ?? client$1).patch({
734
734
  url: "/account",
735
735
  ...options,
736
736
  headers: {
737
- "Content-Type": "application/json",
737
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
738
738
  ...options.headers
739
739
  }
740
740
  });
@@ -828,7 +828,7 @@ const addDomain = (options) => (options.client ?? client$1).post({
828
828
  url: "/domains",
829
829
  ...options,
830
830
  headers: {
831
- "Content-Type": "application/json",
831
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
832
832
  ...options.headers
833
833
  }
834
834
  });
@@ -860,7 +860,7 @@ const updateDomain = (options) => (options.client ?? client$1).patch({
860
860
  url: "/domains/{id}",
861
861
  ...options,
862
862
  headers: {
863
- "Content-Type": "application/json",
863
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
864
864
  ...options.headers
865
865
  }
866
866
  });
@@ -1042,7 +1042,7 @@ const replyToEmail = (options) => (options.client ?? client$1).post({
1042
1042
  url: "/emails/{id}/reply",
1043
1043
  ...options,
1044
1044
  headers: {
1045
- "Content-Type": "application/json",
1045
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1046
1046
  ...options.headers
1047
1047
  }
1048
1048
  });
@@ -1132,7 +1132,7 @@ const createEndpoint = (options) => (options.client ?? client$1).post({
1132
1132
  url: "/endpoints",
1133
1133
  ...options,
1134
1134
  headers: {
1135
- "Content-Type": "application/json",
1135
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1136
1136
  ...options.headers
1137
1137
  }
1138
1138
  });
@@ -1167,7 +1167,7 @@ const updateEndpoint = (options) => (options.client ?? client$1).patch({
1167
1167
  url: "/endpoints/{id}",
1168
1168
  ...options,
1169
1169
  headers: {
1170
- "Content-Type": "application/json",
1170
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1171
1171
  ...options.headers
1172
1172
  }
1173
1173
  });
@@ -1217,7 +1217,7 @@ const createFilter = (options) => (options.client ?? client$1).post({
1217
1217
  url: "/filters",
1218
1218
  ...options,
1219
1219
  headers: {
1220
- "Content-Type": "application/json",
1220
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1221
1221
  ...options.headers
1222
1222
  }
1223
1223
  });
@@ -1245,7 +1245,7 @@ const updateFilter = (options) => (options.client ?? client$1).patch({
1245
1245
  url: "/filters/{id}",
1246
1246
  ...options,
1247
1247
  headers: {
1248
- "Content-Type": "application/json",
1248
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1249
1249
  ...options.headers
1250
1250
  }
1251
1251
  });
@@ -1350,7 +1350,7 @@ const sendEmail = (options) => (options.client ?? client$1).post({
1350
1350
  url: "/send-mail",
1351
1351
  ...options,
1352
1352
  headers: {
1353
- "Content-Type": "application/json",
1353
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1354
1354
  ...options.headers
1355
1355
  }
1356
1356
  });
@@ -1455,7 +1455,7 @@ const createFunction = (options) => (options.client ?? client$1).post({
1455
1455
  url: "/functions",
1456
1456
  ...options,
1457
1457
  headers: {
1458
- "Content-Type": "application/json",
1458
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1459
1459
  ...options.headers
1460
1460
  }
1461
1461
  });
@@ -1520,7 +1520,7 @@ const updateFunction = (options) => (options.client ?? client$1).put({
1520
1520
  url: "/functions/{id}",
1521
1521
  ...options,
1522
1522
  headers: {
1523
- "Content-Type": "application/json",
1523
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1524
1524
  ...options.headers
1525
1525
  }
1526
1526
  });
@@ -1528,8 +1528,14 @@ const updateFunction = (options) => (options.client ?? client$1).put({
1528
1528
  * Send a test invocation
1529
1529
  *
1530
1530
  * Sends a real test email from a Primitive-controlled sender to a
1531
- * synthetic local-part on one of the org's verified inbound
1532
- * domains. The function fires through the normal MX delivery
1531
+ * local-part on one of the org's verified inbound domains. By
1532
+ * default the recipient is a synthetic
1533
+ * `__primitive_function_test+<random>@<domain>` address that
1534
+ * every handler's catch-all routing receives identically; pass
1535
+ * `local_part` to override and exercise routing logic that
1536
+ * branches on a specific recipient (the common pattern when one
1537
+ * function handles multiple inboxes like `summarize@` and
1538
+ * `action@`). The function fires through the normal MX delivery
1533
1539
  * path, so reply / send-mail calls from inside the handler
1534
1540
  * against the inbound's `email.id` work the same as in
1535
1541
  * production. Returns immediately after the send is queued; the
@@ -1539,6 +1545,8 @@ const updateFunction = (options) => (options.client ?? client$1).put({
1539
1545
  * Requires that the function is currently `deployed`. Returns 422
1540
1546
  * if the function is in `pending` or `failed` state, or if the
1541
1547
  * org has no verified inbound domain to receive the test mail.
1548
+ * Returns 400 if `local_part` is set to a value that does not
1549
+ * match the local-part character set.
1542
1550
  *
1543
1551
  */
1544
1552
  const testFunction = (options) => (options.client ?? client$1).post({
@@ -1547,7 +1555,11 @@ const testFunction = (options) => (options.client ?? client$1).post({
1547
1555
  type: "http"
1548
1556
  }],
1549
1557
  url: "/functions/{id}/test",
1550
- ...options
1558
+ ...options,
1559
+ headers: {
1560
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1561
+ ...options.headers
1562
+ }
1551
1563
  });
1552
1564
  /**
1553
1565
  * List a function's secrets
@@ -1594,7 +1606,7 @@ const createFunctionSecret = (options) => (options.client ?? client$1).post({
1594
1606
  url: "/functions/{id}/secrets",
1595
1607
  ...options,
1596
1608
  headers: {
1597
- "Content-Type": "application/json",
1609
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1598
1610
  ...options.headers
1599
1611
  }
1600
1612
  });
@@ -1634,11 +1646,150 @@ const setFunctionSecret = (options) => (options.client ?? client$1).put({
1634
1646
  url: "/functions/{id}/secrets/{key}",
1635
1647
  ...options,
1636
1648
  headers: {
1637
- "Content-Type": "application/json",
1649
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1638
1650
  ...options.headers
1639
1651
  }
1640
1652
  });
1641
1653
  //#endregion
1654
+ //#region src/api/verify-signature.ts
1655
+ /**
1656
+ * Workers-safe webhook signature verification.
1657
+ *
1658
+ * Mirrors `verifyWebhookSignature` from `@primitivedotdev/sdk` but
1659
+ * implements the HMAC-SHA256 step with the Web Crypto API
1660
+ * (`crypto.subtle`) instead of `node:crypto`. The Node version is
1661
+ * still the right choice for server-side handlers running on Node
1662
+ * (it's measurably faster and supports Buffer bodies); this one
1663
+ * exists so a Primitive Function handler can bundle the verifier
1664
+ * without dragging in a `node:crypto` polyfill that inflates the
1665
+ * deploy artifact past the size cap.
1666
+ *
1667
+ * Available natively in Workers, Node 22+, browsers, Deno, and Bun.
1668
+ * Zero polyfill weight, zero new runtime dependencies.
1669
+ *
1670
+ * Surface contract matches the Node verifier exactly: same input
1671
+ * shape, same `WebhookVerificationError` class, same set of error
1672
+ * codes. Existing callers can swap the import path with no other
1673
+ * code changes:
1674
+ *
1675
+ * // Node (existing):
1676
+ * import { verifyWebhookSignature } from '@primitivedotdev/sdk';
1677
+ *
1678
+ * // Workers / in-handler (this file):
1679
+ * import { verifyWebhookSignature } from '@primitivedotdev/sdk/api';
1680
+ */
1681
+ const PRIMITIVE_SIGNATURE_HEADER = "Primitive-Signature";
1682
+ const DEFAULT_TOLERANCE_SECONDS = 300;
1683
+ const FUTURE_TOLERANCE_SECONDS = 60;
1684
+ const HEX_PATTERN = /^[0-9a-f]+$/i;
1685
+ const HEX_LENGTH = 64;
1686
+ const UNIX_SECONDS_PATTERN = /^\d{1,10}$/;
1687
+ function parseSignatureHeader(signatureHeader) {
1688
+ if (!signatureHeader || typeof signatureHeader !== "string") return null;
1689
+ const parts = signatureHeader.split(",");
1690
+ let timestamp = null;
1691
+ const signatures = [];
1692
+ for (const part of parts) {
1693
+ const idx = part.indexOf("=");
1694
+ if (idx === -1) continue;
1695
+ const key = part.slice(0, idx).trim();
1696
+ const value = part.slice(idx + 1).trim();
1697
+ if (!key || !value) continue;
1698
+ if (key === "t") {
1699
+ if (!UNIX_SECONDS_PATTERN.test(value)) continue;
1700
+ const parsed = Number(value);
1701
+ if (Number.isSafeInteger(parsed)) timestamp = parsed;
1702
+ } else if (key === "v1") signatures.push(value);
1703
+ }
1704
+ if (timestamp === null || signatures.length === 0) return null;
1705
+ return {
1706
+ timestamp,
1707
+ signatures
1708
+ };
1709
+ }
1710
+ function isValidHex(str) {
1711
+ return str.length === HEX_LENGTH && HEX_PATTERN.test(str);
1712
+ }
1713
+ function arrayBufferToHex(buffer) {
1714
+ const bytes = new Uint8Array(buffer);
1715
+ let hex = "";
1716
+ for (let i = 0; i < bytes.length; i++) hex += bytes[i].toString(16).padStart(2, "0");
1717
+ return hex;
1718
+ }
1719
+ /**
1720
+ * Constant-time comparison of two equal-length hex strings. Returns
1721
+ * false if lengths differ (intentionally not a security issue: lengths
1722
+ * are public). Iterates the full length regardless of mismatch so the
1723
+ * timing signal does not reveal the position of the first divergence.
1724
+ */
1725
+ function timingSafeEqualHex(a, b) {
1726
+ if (a.length !== b.length) return false;
1727
+ let diff = 0;
1728
+ for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
1729
+ return diff === 0;
1730
+ }
1731
+ async function computeHmacHex(secret, payload) {
1732
+ const encoder = new TextEncoder();
1733
+ const keyData = encoder.encode(secret);
1734
+ const key = await crypto.subtle.importKey("raw", keyData, {
1735
+ name: "HMAC",
1736
+ hash: "SHA-256"
1737
+ }, false, ["sign"]);
1738
+ return arrayBufferToHex(await crypto.subtle.sign("HMAC", key, encoder.encode(payload)));
1739
+ }
1740
+ /**
1741
+ * Verify a webhook signature using the Web Crypto API.
1742
+ *
1743
+ * Throws `WebhookVerificationError` on failure with a specific error
1744
+ * code matching the Node verifier's set. Returns `true` on success.
1745
+ *
1746
+ * @example
1747
+ * ```typescript
1748
+ * import {
1749
+ * verifyWebhookSignature,
1750
+ * WebhookVerificationError,
1751
+ * PRIMITIVE_SIGNATURE_HEADER,
1752
+ * } from '@primitivedotdev/sdk/api';
1753
+ *
1754
+ * export default {
1755
+ * async fetch(request: Request, env: { PRIMITIVE_WEBHOOK_SECRET: string }) {
1756
+ * const rawBody = await request.text();
1757
+ * try {
1758
+ * await verifyWebhookSignature({
1759
+ * rawBody,
1760
+ * signatureHeader: request.headers.get(PRIMITIVE_SIGNATURE_HEADER) ?? '',
1761
+ * secret: env.PRIMITIVE_WEBHOOK_SECRET,
1762
+ * });
1763
+ * } catch (err) {
1764
+ * if (err instanceof WebhookVerificationError) {
1765
+ * return new Response('invalid signature', { status: 401 });
1766
+ * }
1767
+ * throw err;
1768
+ * }
1769
+ * // ... process the webhook
1770
+ * },
1771
+ * };
1772
+ * ```
1773
+ */
1774
+ async function verifyWebhookSignature(opts) {
1775
+ const { rawBody, signatureHeader, secret, toleranceSeconds = DEFAULT_TOLERANCE_SECONDS, nowSeconds } = opts;
1776
+ if (!secret) throw new WebhookVerificationError("MISSING_SECRET", "Webhook secret is required but was empty or not provided");
1777
+ const parsed = parseSignatureHeader(signatureHeader);
1778
+ if (!parsed) throw new WebhookVerificationError("INVALID_SIGNATURE_HEADER", "Invalid Primitive-Signature header format. Expected: t={timestamp},v1={signature}");
1779
+ const { timestamp, signatures } = parsed;
1780
+ const age = (nowSeconds ?? Math.floor(Date.now() / 1e3)) - timestamp;
1781
+ if (age > toleranceSeconds) throw new WebhookVerificationError("TIMESTAMP_OUT_OF_RANGE", `Webhook timestamp too old (${age}s). Max age is ${toleranceSeconds}s.`);
1782
+ if (age < -FUTURE_TOLERANCE_SECONDS) throw new WebhookVerificationError("TIMESTAMP_OUT_OF_RANGE", "Webhook timestamp is too far in the future. Check server clock sync.");
1783
+ const expectedHex = await computeHmacHex(secret, `${timestamp}.${rawBody}`);
1784
+ let anyMatch = false;
1785
+ for (const candidate of signatures) {
1786
+ if (!isValidHex(candidate)) continue;
1787
+ if (timingSafeEqualHex(candidate.toLowerCase(), expectedHex)) anyMatch = true;
1788
+ }
1789
+ if (!anyMatch) throw new WebhookVerificationError("SIGNATURE_MISMATCH", "Webhook signature did not match. The body may have been modified in transit, or the secret may be out of date.");
1790
+ return true;
1791
+ }
1792
+ //#endregion
1642
1793
  //#region src/api/index.ts
1643
1794
  const DEFAULT_API_BASE_URL_1 = "https://www.primitive.dev/api/v1";
1644
1795
  const DEFAULT_API_BASE_URL_2 = "https://api.primitive.dev/v1";
@@ -1927,4 +2078,4 @@ function client(options = {}) {
1927
2078
  }
1928
2079
  const operations = sdk_gen_exports;
1929
2080
  //#endregion
1930
- export { updateEndpoint as $, getStorageStats as A, pollCliLogin as B, downloadAttachments as C, getFunction as D, getEmail as E, listEndpoints as F, searchEmails as G, replayEmailWebhooks as H, listFilters as I, startCliLogin as J, sendEmail as K, listFunctionSecrets as L, listDeliveries as M, listDomains as N, getSendPermissions as O, listEmails as P, updateDomain as Q, listFunctions as R, discardEmailContent as S, getAccount as T, replyToEmail as U, replayDelivery as V, rotateWebhookSecret as W, testFunction as X, testEndpoint as Y, updateAccount as Z, deleteEmail as _, PrimitiveClient as a, deleteFunction as b, createPrimitiveClient as c, cliLogout as d, updateFilter as et, createEndpoint as f, deleteDomain as g, createFunctionSecret as h, PrimitiveApiError as i, getWebhookSecret as j, getSentEmail as k, operations as l, createFunction as m, DEFAULT_API_BASE_URL_2 as n, verifyDomain as nt, client as o, createFilter as p, setFunctionSecret as q, PrimitiveApiClient as r, createPrimitiveApiClient as s, DEFAULT_API_BASE_URL_1 as t, updateFunction as tt, addDomain as u, deleteEndpoint as v, downloadRawEmail as w, deleteFunctionSecret as x, deleteFilter as y, listSentEmails as z };
2081
+ export { updateAccount as $, getSendPermissions as A, listFunctions as B, deleteFunctionSecret as C, getAccount as D, downloadRawEmail as E, listDomains as F, replyToEmail as G, pollCliLogin as H, listEmails as I, sendEmail as J, rotateWebhookSecret as K, listEndpoints as L, getStorageStats as M, getWebhookSecret as N, getEmail as O, listDeliveries as P, testFunction as Q, listFilters as R, deleteFunction as S, downloadAttachments as T, replayDelivery as U, listSentEmails as V, replayEmailWebhooks as W, startCliLogin as X, setFunctionSecret as Y, testEndpoint as Z, createFunctionSecret as _, PrimitiveClient as a, deleteEndpoint as b, createPrimitiveClient as c, verifyWebhookSignature as d, updateDomain as et, addDomain as f, createFunction as g, createFilter as h, PrimitiveApiError as i, verifyDomain as it, getSentEmail as j, getFunction as k, operations as l, createEndpoint as m, DEFAULT_API_BASE_URL_2 as n, updateFilter as nt, client as o, cliLogout as p, searchEmails as q, PrimitiveApiClient as r, updateFunction as rt, createPrimitiveApiClient as s, DEFAULT_API_BASE_URL_1 as t, updateEndpoint as tt, PRIMITIVE_SIGNATURE_HEADER as u, deleteDomain as v, discardEmailContent as w, deleteFilter as x, deleteEmail as y, listFunctionSecrets as z };
@@ -1,5 +1,5 @@
1
1
  import { C as ParsedDataFailed, D as RawContentDownloadOnly, M as WebhookAttachment, O as RawContentInline, S as ParsedDataComplete, c as EmailAnalysis, l as EmailAuth, s as EmailAddress, u as EmailReceivedEvent, w as ParsedError } from "../types-9vXGZjPd.js";
2
- import { C as signStandardWebhooksPayload, h as WEBHOOK_VERSION, j as signWebhookPayload, k as SignResult, x as StandardWebhooksSignResult } from "../index-CDlwyxdp.js";
2
+ import { C as signStandardWebhooksPayload, h as WEBHOOK_VERSION, j as signWebhookPayload, k as SignResult, x as StandardWebhooksSignResult } from "../index-Dbx9udpX.js";
3
3
 
4
4
  //#region src/contract/contract.d.ts
5
5
  /** Maximum raw email size for inline inclusion (256 KB). */
@@ -1,4 +1,4 @@
1
- import { E as signStandardWebhooksPayload, L as validateEmailReceivedEvent, M as signWebhookPayload, d as WEBHOOK_VERSION } from "../webhook-rUjGV6Zu.js";
1
+ import { E as signStandardWebhooksPayload, L as validateEmailReceivedEvent, M as signWebhookPayload, d as WEBHOOK_VERSION } from "../webhook-DJkfUnFZ.js";
2
2
  import { createHash } from "node:crypto";
3
3
  //#region src/contract/contract.ts
4
4
  /**
@@ -0,0 +1,245 @@
1
+ import { M as WebhookAttachment, c as EmailAnalysis, l as EmailAuth, u as EmailReceivedEvent } from "./types-9vXGZjPd.js";
2
+ import { ErrorObject } from "ajv";
3
+
4
+ //#region src/webhook/received-email.d.ts
5
+ interface ReceivedEmailAddress {
6
+ address: string;
7
+ name: string | null;
8
+ }
9
+ interface ReceivedEmailThread {
10
+ messageId: string | null;
11
+ inReplyTo: string[];
12
+ references: string[];
13
+ }
14
+ interface ReceivedEmail {
15
+ id: string;
16
+ eventId: string;
17
+ receivedAt: string;
18
+ sender: ReceivedEmailAddress;
19
+ replyTarget: ReceivedEmailAddress;
20
+ receivedBy: string;
21
+ receivedByAll: string[];
22
+ subject: string | null;
23
+ replySubject: string;
24
+ forwardSubject: string;
25
+ text: string | null;
26
+ thread: ReceivedEmailThread;
27
+ attachments: WebhookAttachment[];
28
+ auth: EmailAuth;
29
+ analysis: EmailAnalysis;
30
+ raw: EmailReceivedEvent;
31
+ }
32
+ declare function normalizeReceivedEmail(event: EmailReceivedEvent): ReceivedEmail;
33
+ declare function buildReplySubject(subject: string | null | undefined): string;
34
+ declare function buildForwardSubject(subject: string | null | undefined): string;
35
+ declare function formatAddress(address: ReceivedEmailAddress): string;
36
+ declare function parseHeaderAddress(value: string | null | undefined): ReceivedEmailAddress | null;
37
+ //#endregion
38
+ //#region src/webhook/errors.d.ts
39
+ /**
40
+ * Verification error definitions.
41
+ * Use these for documentation, dashboards, and i18n.
42
+ */
43
+ declare const VERIFICATION_ERRORS: {
44
+ readonly INVALID_SIGNATURE_HEADER: {
45
+ readonly message: "Missing or malformed Primitive-Signature header";
46
+ readonly suggestion: "Check that you're reading the correct header (Primitive-Signature) and it's being passed correctly from your web framework.";
47
+ };
48
+ readonly TIMESTAMP_OUT_OF_RANGE: {
49
+ readonly message: "Timestamp is too old (possible replay attack)";
50
+ readonly suggestion: "This could indicate a replay attack, network delay, or server clock drift. Check your server's time is synced.";
51
+ };
52
+ readonly SIGNATURE_MISMATCH: {
53
+ readonly message: "Signature doesn't match expected value";
54
+ readonly suggestion: "Verify the webhook secret matches and you're using the raw request body (not re-serialized JSON).";
55
+ };
56
+ readonly MISSING_SECRET: {
57
+ readonly message: "No webhook secret was provided";
58
+ readonly suggestion: "Pass your webhook secret from the Primitive dashboard. Check that the environment variable is set.";
59
+ };
60
+ };
61
+ /**
62
+ * Payload parsing error definitions.
63
+ * Use these for documentation, dashboards, and i18n.
64
+ */
65
+ declare const PAYLOAD_ERRORS: {
66
+ readonly PAYLOAD_NULL: {
67
+ readonly message: "Webhook payload is null";
68
+ readonly suggestion: "Ensure you're passing the parsed JSON body, not null. Check your framework's body parsing middleware.";
69
+ };
70
+ readonly PAYLOAD_UNDEFINED: {
71
+ readonly message: "Webhook payload is undefined";
72
+ readonly suggestion: "The payload was not provided. Make sure you're passing the request body to the handler.";
73
+ };
74
+ readonly PAYLOAD_WRONG_TYPE: {
75
+ readonly message: "Webhook payload must be an object";
76
+ readonly suggestion: "The payload should be a parsed JSON object. Check that you're not passing a string or other primitive.";
77
+ };
78
+ readonly PAYLOAD_IS_ARRAY: {
79
+ readonly message: "Webhook payload is an array, expected object";
80
+ readonly suggestion: "Primitive webhooks are single event objects, not arrays. Check the payload structure.";
81
+ };
82
+ readonly PAYLOAD_MISSING_EVENT: {
83
+ readonly message: "Webhook payload missing 'event' field";
84
+ readonly suggestion: "All webhook payloads must have an 'event' field. This may not be a valid Primitive webhook.";
85
+ };
86
+ readonly PAYLOAD_UNKNOWN_EVENT: {
87
+ readonly message: "Unknown webhook event type";
88
+ readonly suggestion: "This event type is not recognized. You may need to update your SDK or handle unknown events gracefully.";
89
+ };
90
+ readonly PAYLOAD_EMPTY_BODY: {
91
+ readonly message: "Request body is empty";
92
+ readonly suggestion: "The request body was empty. Ensure the webhook is sending data and your framework is parsing it correctly.";
93
+ };
94
+ readonly JSON_PARSE_FAILED: {
95
+ readonly message: "Failed to parse JSON body";
96
+ readonly suggestion: "The request body is not valid JSON. Check the raw body content and Content-Type header.";
97
+ };
98
+ readonly INVALID_ENCODING: {
99
+ readonly message: "Invalid body encoding";
100
+ readonly suggestion: "The request body encoding is not supported. Primitive webhooks use UTF-8 encoded JSON.";
101
+ };
102
+ };
103
+ /**
104
+ * Raw email decode error definitions.
105
+ * Use these for documentation, dashboards, and i18n.
106
+ */
107
+ declare const RAW_EMAIL_ERRORS: {
108
+ readonly NOT_INCLUDED: {
109
+ readonly message: "Raw email content not included inline";
110
+ readonly suggestion: "Use the download URL at event.email.content.download.url to fetch the raw email.";
111
+ };
112
+ readonly INVALID_BASE64: {
113
+ readonly message: "Raw email content is not valid base64";
114
+ readonly suggestion: "The raw email data is malformed. Fetch the raw email from the download URL or regenerate the webhook payload.";
115
+ };
116
+ readonly HASH_MISMATCH: {
117
+ readonly message: "SHA-256 hash verification failed";
118
+ readonly suggestion: "The raw email data may be corrupted. Try downloading from the URL instead.";
119
+ };
120
+ };
121
+ /**
122
+ * All error codes that can be thrown by the SDK.
123
+ */
124
+ type WebhookErrorCode = WebhookVerificationErrorCode | WebhookPayloadErrorCode | WebhookValidationErrorCode | RawEmailDecodeErrorCode;
125
+ type RawEmailDecodeErrorCode = keyof typeof RAW_EMAIL_ERRORS;
126
+ /**
127
+ * Base class for all Primitive webhook errors.
128
+ *
129
+ * Catch this to handle any error from the SDK in a single catch block.
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * import { handleWebhook, PrimitiveWebhookError } from '@primitivedotdev/sdk';
134
+ *
135
+ * try {
136
+ * const event = handleWebhook({ body, headers, secret });
137
+ * } catch (err) {
138
+ * if (err instanceof PrimitiveWebhookError) {
139
+ * console.error(`[${err.code}] ${err.message}`);
140
+ * return res.status(400).json({ error: err.code });
141
+ * }
142
+ * throw err;
143
+ * }
144
+ * ```
145
+ */
146
+ declare abstract class PrimitiveWebhookError extends Error {
147
+ /** Programmatic error code for monitoring and handling */
148
+ abstract readonly code: WebhookErrorCode;
149
+ /** Actionable guidance for fixing the issue */
150
+ abstract readonly suggestion: string;
151
+ /**
152
+ * Formats the error for logging/display.
153
+ */
154
+ toString(): string;
155
+ /**
156
+ * Serializes cleanly for structured logging (Datadog, CloudWatch, etc.)
157
+ */
158
+ toJSON(): {
159
+ name: string;
160
+ code: WebhookErrorCode;
161
+ message: string;
162
+ suggestion: string;
163
+ };
164
+ }
165
+ /**
166
+ * Error codes for webhook verification failures.
167
+ * Derived from VERIFICATION_ERRORS keys.
168
+ */
169
+ type WebhookVerificationErrorCode = keyof typeof VERIFICATION_ERRORS;
170
+ /**
171
+ * Error thrown when webhook signature verification fails.
172
+ *
173
+ * Use the `code` property to programmatically handle specific error cases.
174
+ */
175
+ declare class WebhookVerificationError extends PrimitiveWebhookError {
176
+ readonly code: WebhookVerificationErrorCode;
177
+ readonly suggestion: string;
178
+ constructor(code: WebhookVerificationErrorCode, message?: string, suggestion?: string);
179
+ }
180
+ /**
181
+ * Error codes for webhook payload parsing failures.
182
+ * Derived from PAYLOAD_ERRORS keys.
183
+ */
184
+ type WebhookPayloadErrorCode = keyof typeof PAYLOAD_ERRORS;
185
+ /**
186
+ * Error thrown when webhook payload parsing fails (lightweight parser).
187
+ *
188
+ * Use the `code` property for programmatic handling and monitoring.
189
+ * The `suggestion` property contains actionable guidance for fixing the issue.
190
+ */
191
+ declare class WebhookPayloadError extends PrimitiveWebhookError {
192
+ readonly code: WebhookPayloadErrorCode;
193
+ readonly suggestion: string;
194
+ /** Original error if this wraps another error (e.g., JSON.parse failure) */
195
+ readonly cause?: Error;
196
+ constructor(code: WebhookPayloadErrorCode, message?: string, suggestion?: string, cause?: Error);
197
+ }
198
+ /**
199
+ * Error code for schema validation failures.
200
+ */
201
+ type WebhookValidationErrorCode = "SCHEMA_VALIDATION_FAILED";
202
+ /**
203
+ * Error thrown when schema validation fails.
204
+ */
205
+ declare class WebhookValidationError extends PrimitiveWebhookError {
206
+ readonly code: WebhookValidationErrorCode;
207
+ readonly suggestion: string;
208
+ /** The specific field path that failed (e.g., "email.headers.from") */
209
+ readonly field: string;
210
+ /** Original schema validation errors for advanced debugging */
211
+ readonly validationErrors: readonly ErrorObject[];
212
+ /** Number of additional validation errors beyond the first */
213
+ readonly additionalErrorCount: number;
214
+ constructor(field: string, message: string, suggestion: string, validationErrors: readonly ErrorObject[]);
215
+ /**
216
+ * Formats the error for logging/display.
217
+ * Includes error count and suggestion.
218
+ */
219
+ toString(): string;
220
+ /**
221
+ * Serializes cleanly for structured logging (Datadog, CloudWatch, etc.)
222
+ */
223
+ toJSON(): {
224
+ name: string;
225
+ code: "SCHEMA_VALIDATION_FAILED";
226
+ field: string;
227
+ message: string;
228
+ suggestion: string;
229
+ additionalErrorCount: number;
230
+ };
231
+ }
232
+ /**
233
+ * Error thrown when raw email decoding or verification fails.
234
+ *
235
+ * Use the `code` property to determine the failure reason:
236
+ * - `NOT_INCLUDED`: Raw email not inline, must download from URL
237
+ * - `HASH_MISMATCH`: SHA-256 verification failed, content may be corrupted
238
+ */
239
+ declare class RawEmailDecodeError extends PrimitiveWebhookError {
240
+ readonly code: RawEmailDecodeErrorCode;
241
+ readonly suggestion: string;
242
+ constructor(code: RawEmailDecodeErrorCode, message?: string);
243
+ }
244
+ //#endregion
245
+ export { buildForwardSubject as _, RawEmailDecodeErrorCode as a, normalizeReceivedEmail as b, WebhookPayloadError as c, WebhookValidationErrorCode as d, WebhookVerificationError as f, ReceivedEmailThread as g, ReceivedEmailAddress as h, RawEmailDecodeError as i, WebhookPayloadErrorCode as l, ReceivedEmail as m, PrimitiveWebhookError as n, VERIFICATION_ERRORS as o, WebhookVerificationErrorCode as p, RAW_EMAIL_ERRORS as r, WebhookErrorCode as s, PAYLOAD_ERRORS as t, WebhookValidationError as u, buildReplySubject as v, parseHeaderAddress as x, formatAddress as y };