@primitivedotdev/sdk 0.2.3 → 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.
Files changed (41) hide show
  1. package/README.md +65 -2
  2. package/bin/run.js +5 -0
  3. package/dist/api/generated/client/client.gen.js +235 -0
  4. package/dist/api/generated/client/index.js +6 -0
  5. package/dist/api/generated/client/types.gen.js +2 -0
  6. package/dist/api/generated/client/utils.gen.js +228 -0
  7. package/dist/api/generated/client.gen.js +3 -0
  8. package/dist/api/generated/core/auth.gen.js +14 -0
  9. package/dist/api/generated/core/bodySerializer.gen.js +57 -0
  10. package/dist/api/generated/core/params.gen.js +100 -0
  11. package/dist/api/generated/core/pathSerializer.gen.js +106 -0
  12. package/dist/api/generated/core/queryKeySerializer.gen.js +92 -0
  13. package/dist/api/generated/core/serverSentEvents.gen.js +132 -0
  14. package/dist/api/generated/core/types.gen.js +2 -0
  15. package/dist/api/generated/core/utils.gen.js +87 -0
  16. package/dist/api/generated/index.js +2 -0
  17. package/dist/api/generated/sdk.gen.js +344 -0
  18. package/dist/api/generated/types.gen.js +2 -0
  19. package/dist/api/index.d.ts +1832 -0
  20. package/dist/api/index.js +39 -0
  21. package/dist/chunk-Cl8Af3a2.js +11 -0
  22. package/dist/contract/index.d.ts +3 -3
  23. package/dist/contract/index.js +2 -2
  24. package/dist/{index-BdRIHaXz.d.ts → index-D2OuDGVz.d.ts} +81 -5
  25. package/dist/index.d.ts +3 -3
  26. package/dist/index.js +2 -2
  27. package/dist/oclif/api-command.js +154 -0
  28. package/dist/oclif/fish-completion.js +87 -0
  29. package/dist/oclif/index.js +42 -0
  30. package/dist/openapi/index.d.ts +41 -0
  31. package/dist/openapi/index.js +8 -0
  32. package/dist/openapi/openapi.generated.js +2644 -0
  33. package/dist/openapi/operations.generated.js +632 -0
  34. package/dist/parser/index.d.ts +1 -1
  35. package/dist/parser/index.js +40 -25
  36. package/dist/{types-B5IgP-Zx.d.ts → types-C3ms4R0d.d.ts} +4 -0
  37. package/dist/webhook/index.d.ts +3 -3
  38. package/dist/webhook/index.js +2 -2
  39. package/dist/{webhook-Be2vM0F-.js → webhook-uSco6pyX.js} +176 -11
  40. package/oclif.manifest.json +1255 -0
  41. package/package.json +81 -13
@@ -1,10 +1,26 @@
1
1
  import { createHash } from "node:crypto";
2
- import { PassThrough } from "node:stream";
3
- import archiver from "archiver";
2
+ import { createGzip } from "node:zlib";
3
+ import { pack } from "tar-stream";
4
4
  import { simpleParser } from "mailparser";
5
5
  import DOMPurify from "isomorphic-dompurify";
6
6
 
7
7
  //#region src/parser/attachment-bundler.ts
8
+ function appendTarEntry(archive, name, content) {
9
+ return new Promise((resolve, reject) => {
10
+ archive.entry({
11
+ mode: 420,
12
+ name,
13
+ size: content.length,
14
+ type: "file"
15
+ }, content, (error) => {
16
+ if (error) {
17
+ reject(error);
18
+ return;
19
+ }
20
+ resolve();
21
+ });
22
+ });
23
+ }
8
24
  /**
9
25
  * Bundle downloadable attachments into a tar.gz archive.
10
26
  *
@@ -18,30 +34,28 @@ import DOMPurify from "isomorphic-dompurify";
18
34
  async function bundleAttachments(attachments) {
19
35
  const downloadable = attachments.filter((att) => att.isDownloadable);
20
36
  if (downloadable.length === 0) return null;
21
- const archive = archiver("tar", {
22
- gzip: true,
23
- gzipOptions: { level: 6 }
24
- });
37
+ const archive = pack();
38
+ const gzip = createGzip({ level: 6 });
25
39
  const chunks = [];
26
- const passThrough = new PassThrough();
27
40
  let totalAttachmentBytes = 0;
28
41
  const tarGzBuffer = await new Promise((resolve, reject) => {
29
42
  let settled = false;
30
43
  const cleanup = () => {
31
44
  archive.off("error", rejectArchive);
32
- archive.off("warning", rejectArchive);
33
- passThrough.off("data", handleData);
34
- passThrough.off("end", handleEnd);
35
- passThrough.off("error", rejectArchive);
45
+ gzip.off("data", handleData);
46
+ gzip.off("end", handleEnd);
47
+ gzip.off("error", rejectArchive);
36
48
  };
37
49
  const rejectArchive = (error) => {
38
50
  if (settled) return;
39
51
  settled = true;
40
52
  cleanup();
53
+ archive.destroy();
54
+ gzip.destroy();
41
55
  reject(error);
42
56
  };
43
57
  const handleData = (chunk) => {
44
- chunks.push(chunk);
58
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
45
59
  };
46
60
  const handleEnd = () => {
47
61
  if (settled) return;
@@ -50,20 +64,21 @@ async function bundleAttachments(attachments) {
50
64
  resolve(Buffer.concat(chunks));
51
65
  };
52
66
  archive.on("error", rejectArchive);
53
- archive.on("warning", rejectArchive);
54
- passThrough.on("data", handleData);
55
- passThrough.on("end", handleEnd);
56
- passThrough.on("error", rejectArchive);
57
- archive.pipe(passThrough);
58
- try {
59
- for (const att of downloadable) {
60
- archive.append(att.content, { name: att.tarPath });
61
- totalAttachmentBytes += att.sizeBytes;
67
+ gzip.on("data", handleData);
68
+ gzip.on("end", handleEnd);
69
+ gzip.on("error", rejectArchive);
70
+ archive.pipe(gzip);
71
+ (async () => {
72
+ try {
73
+ for (const att of downloadable) {
74
+ await appendTarEntry(archive, att.tarPath, att.content);
75
+ totalAttachmentBytes += att.sizeBytes;
76
+ }
77
+ archive.finalize();
78
+ } catch (error) {
79
+ rejectArchive(error instanceof Error ? error : new Error(String(error)));
62
80
  }
63
- archive.finalize().catch(rejectArchive);
64
- } catch (error) {
65
- rejectArchive(error instanceof Error ? error : new Error(String(error)));
66
- }
81
+ })();
67
82
  });
68
83
  const sha256 = createHash("sha256").update(tarGzBuffer).digest("hex");
69
84
  return {
@@ -399,6 +399,8 @@ interface ParsedError$1 {
399
399
  interface EmailAnalysis$1 {
400
400
  /**
401
401
  * SpamAssassin analysis results.
402
+ *
403
+ * Optional. Present when the email was processed by a SpamAssassin-equipped pipeline (always present in Primitive's managed service). When absent, spam scoring was not performed on this email.
402
404
  */
403
405
  spamassassin?: {
404
406
  /**
@@ -410,6 +412,8 @@ interface EmailAnalysis$1 {
410
412
  }
411
413
  /**
412
414
  * Forward detection and analysis results.
415
+ *
416
+ * Optional. Present when the email was processed by a forward-detection pipeline (always present in Primitive's managed service). When absent, forward detection was not performed on this email.
413
417
  */
414
418
  interface ForwardAnalysis$1 {
415
419
  /**
@@ -1,3 +1,3 @@
1
- import { AuthConfidence$1 as AuthConfidence, AuthVerdict$1 as AuthVerdict, DkimResult$1 as DkimResult, DkimSignature, DmarcPolicy$1 as DmarcPolicy, DmarcResult$1 as DmarcResult, EmailAddress, EmailAnalysis, EmailAuth, EmailReceivedEvent, EventType$1 as EventType, ForwardAnalysis, ForwardOriginalSender, ForwardResult, ForwardResultAttachmentAnalyzed, ForwardResultAttachmentSkipped, ForwardResultInline, ForwardVerdict$1 as ForwardVerdict, ForwardVerification, KnownWebhookEvent, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus$1 as ParsedStatus, RawContent, RawContentDownloadOnly, RawContentInline, SpfResult$1 as SpfResult, UnknownEvent, ValidateEmailAuthResult, WebhookAttachment, WebhookEvent } from "../types-B5IgP-Zx.js";
2
- import { DecodeRawEmailOptions, HandleWebhookOptions, LEGACY_CONFIRMED_HEADER$1 as LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER$1 as LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS$1 as PAYLOAD_ERRORS, PRIMITIVE_CONFIRMED_HEADER$1 as PRIMITIVE_CONFIRMED_HEADER, PRIMITIVE_SIGNATURE_HEADER$1 as PRIMITIVE_SIGNATURE_HEADER, PrimitiveWebhookError$1 as PrimitiveWebhookError, RAW_EMAIL_ERRORS$1 as RAW_EMAIL_ERRORS, RawEmailDecodeError$1 as RawEmailDecodeError, RawEmailDecodeErrorCode, SignResult, VERIFICATION_ERRORS$1 as VERIFICATION_ERRORS, VerifyOptions, WEBHOOK_VERSION$1 as WEBHOOK_VERSION, WebhookErrorCode, WebhookHeaders, WebhookPayloadError$1 as WebhookPayloadError, WebhookPayloadErrorCode, WebhookValidationError$1 as WebhookValidationError, WebhookValidationErrorCode, WebhookVerificationError$1 as WebhookVerificationError, WebhookVerificationErrorCode, confirmedHeaders$1 as confirmedHeaders, decodeRawEmail$1 as decodeRawEmail, emailReceivedEventJsonSchema$1 as emailReceivedEventJsonSchema, getDownloadTimeRemaining$1 as getDownloadTimeRemaining, handleWebhook$1 as handleWebhook, isDownloadExpired$1 as isDownloadExpired, isEmailReceivedEvent$1 as isEmailReceivedEvent, isRawIncluded$1 as isRawIncluded, parseWebhookEvent$1 as parseWebhookEvent, safeValidateEmailReceivedEvent$1 as safeValidateEmailReceivedEvent, signWebhookPayload$1 as signWebhookPayload, validateEmailAuth$1 as validateEmailAuth, validateEmailReceivedEvent$1 as validateEmailReceivedEvent, verifyRawEmailDownload$1 as verifyRawEmailDownload, verifyWebhookSignature$1 as verifyWebhookSignature } from "../index-BdRIHaXz.js";
3
- export { AuthConfidence, AuthVerdict, DecodeRawEmailOptions, DkimResult, DkimSignature, DmarcPolicy, DmarcResult, EmailAddress, EmailAnalysis, EmailAuth, EmailReceivedEvent, EventType, ForwardAnalysis, ForwardOriginalSender, ForwardResult, ForwardResultAttachmentAnalyzed, ForwardResultAttachmentSkipped, ForwardResultInline, ForwardVerdict, ForwardVerification, HandleWebhookOptions, KnownWebhookEvent, LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS, PRIMITIVE_CONFIRMED_HEADER, PRIMITIVE_SIGNATURE_HEADER, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus, PrimitiveWebhookError, RAW_EMAIL_ERRORS, RawContent, RawContentDownloadOnly, RawContentInline, RawEmailDecodeError, RawEmailDecodeErrorCode, SignResult, SpfResult, UnknownEvent, VERIFICATION_ERRORS, ValidateEmailAuthResult, VerifyOptions, WEBHOOK_VERSION, WebhookAttachment, WebhookErrorCode, WebhookEvent, WebhookHeaders, WebhookPayloadError, WebhookPayloadErrorCode, WebhookValidationError, WebhookValidationErrorCode, WebhookVerificationError, WebhookVerificationErrorCode, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, safeValidateEmailReceivedEvent, signWebhookPayload, validateEmailAuth, validateEmailReceivedEvent, verifyRawEmailDownload, verifyWebhookSignature };
1
+ import { AuthConfidence$1 as AuthConfidence, AuthVerdict$1 as AuthVerdict, DkimResult$1 as DkimResult, DkimSignature, DmarcPolicy$1 as DmarcPolicy, DmarcResult$1 as DmarcResult, EmailAddress, EmailAnalysis, EmailAuth, EmailReceivedEvent, EventType$1 as EventType, ForwardAnalysis, ForwardOriginalSender, ForwardResult, ForwardResultAttachmentAnalyzed, ForwardResultAttachmentSkipped, ForwardResultInline, ForwardVerdict$1 as ForwardVerdict, ForwardVerification, KnownWebhookEvent, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus$1 as ParsedStatus, RawContent, RawContentDownloadOnly, RawContentInline, SpfResult$1 as SpfResult, UnknownEvent, ValidateEmailAuthResult, WebhookAttachment, WebhookEvent } from "../types-C3ms4R0d.js";
2
+ import { DecodeRawEmailOptions, HandleWebhookOptions, LEGACY_CONFIRMED_HEADER$1 as LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER$1 as LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS$1 as PAYLOAD_ERRORS, PRIMITIVE_CONFIRMED_HEADER$1 as PRIMITIVE_CONFIRMED_HEADER, PRIMITIVE_SIGNATURE_HEADER$1 as PRIMITIVE_SIGNATURE_HEADER, PrimitiveWebhookError$1 as PrimitiveWebhookError, RAW_EMAIL_ERRORS$1 as RAW_EMAIL_ERRORS, RawEmailDecodeError$1 as RawEmailDecodeError, RawEmailDecodeErrorCode, STANDARD_WEBHOOK_ID_HEADER$1 as STANDARD_WEBHOOK_ID_HEADER, STANDARD_WEBHOOK_SIGNATURE_HEADER$1 as STANDARD_WEBHOOK_SIGNATURE_HEADER, STANDARD_WEBHOOK_TIMESTAMP_HEADER$1 as STANDARD_WEBHOOK_TIMESTAMP_HEADER, SignResult, StandardWebhooksSignResult, StandardWebhooksVerifyOptions, VERIFICATION_ERRORS$1 as VERIFICATION_ERRORS, VerifyOptions, WEBHOOK_VERSION$1 as WEBHOOK_VERSION, WebhookErrorCode, WebhookHeaders, WebhookPayloadError$1 as WebhookPayloadError, WebhookPayloadErrorCode, WebhookValidationError$1 as WebhookValidationError, WebhookValidationErrorCode, WebhookVerificationError$1 as WebhookVerificationError, WebhookVerificationErrorCode, confirmedHeaders$1 as confirmedHeaders, decodeRawEmail$1 as decodeRawEmail, emailReceivedEventJsonSchema$1 as emailReceivedEventJsonSchema, getDownloadTimeRemaining$1 as getDownloadTimeRemaining, handleWebhook$1 as handleWebhook, isDownloadExpired$1 as isDownloadExpired, isEmailReceivedEvent$1 as isEmailReceivedEvent, isRawIncluded$1 as isRawIncluded, parseWebhookEvent$1 as parseWebhookEvent, safeValidateEmailReceivedEvent$1 as safeValidateEmailReceivedEvent, signStandardWebhooksPayload$1 as signStandardWebhooksPayload, signWebhookPayload$1 as signWebhookPayload, validateEmailAuth$1 as validateEmailAuth, validateEmailReceivedEvent$1 as validateEmailReceivedEvent, verifyRawEmailDownload$1 as verifyRawEmailDownload, verifyStandardWebhooksSignature$1 as verifyStandardWebhooksSignature, verifyWebhookSignature$1 as verifyWebhookSignature } from "../index-D2OuDGVz.js";
3
+ export { AuthConfidence, AuthVerdict, DecodeRawEmailOptions, DkimResult, DkimSignature, DmarcPolicy, DmarcResult, EmailAddress, EmailAnalysis, EmailAuth, EmailReceivedEvent, EventType, ForwardAnalysis, ForwardOriginalSender, ForwardResult, ForwardResultAttachmentAnalyzed, ForwardResultAttachmentSkipped, ForwardResultInline, ForwardVerdict, ForwardVerification, HandleWebhookOptions, KnownWebhookEvent, LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS, PRIMITIVE_CONFIRMED_HEADER, PRIMITIVE_SIGNATURE_HEADER, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus, PrimitiveWebhookError, RAW_EMAIL_ERRORS, RawContent, RawContentDownloadOnly, RawContentInline, RawEmailDecodeError, RawEmailDecodeErrorCode, STANDARD_WEBHOOK_ID_HEADER, STANDARD_WEBHOOK_SIGNATURE_HEADER, STANDARD_WEBHOOK_TIMESTAMP_HEADER, SignResult, SpfResult, StandardWebhooksSignResult, StandardWebhooksVerifyOptions, UnknownEvent, VERIFICATION_ERRORS, ValidateEmailAuthResult, VerifyOptions, WEBHOOK_VERSION, WebhookAttachment, WebhookErrorCode, WebhookEvent, WebhookHeaders, WebhookPayloadError, WebhookPayloadErrorCode, WebhookValidationError, WebhookValidationErrorCode, WebhookVerificationError, WebhookVerificationErrorCode, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, safeValidateEmailReceivedEvent, signStandardWebhooksPayload, signWebhookPayload, validateEmailAuth, validateEmailReceivedEvent, verifyRawEmailDownload, verifyStandardWebhooksSignature, verifyWebhookSignature };
@@ -1,3 +1,3 @@
1
- import { AuthConfidence, AuthVerdict, DkimResult, DmarcPolicy, DmarcResult, EventType, ForwardVerdict, LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS, PRIMITIVE_CONFIRMED_HEADER, PRIMITIVE_SIGNATURE_HEADER, ParsedStatus, PrimitiveWebhookError, RAW_EMAIL_ERRORS, RawEmailDecodeError, SpfResult, VERIFICATION_ERRORS, WEBHOOK_VERSION, WebhookPayloadError, WebhookValidationError, WebhookVerificationError, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, safeValidateEmailReceivedEvent, signWebhookPayload, validateEmailAuth, validateEmailReceivedEvent, verifyRawEmailDownload, verifyWebhookSignature } from "../webhook-Be2vM0F-.js";
1
+ import { AuthConfidence, AuthVerdict, DkimResult, DmarcPolicy, DmarcResult, EventType, ForwardVerdict, LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS, PRIMITIVE_CONFIRMED_HEADER, PRIMITIVE_SIGNATURE_HEADER, ParsedStatus, PrimitiveWebhookError, RAW_EMAIL_ERRORS, RawEmailDecodeError, STANDARD_WEBHOOK_ID_HEADER, STANDARD_WEBHOOK_SIGNATURE_HEADER, STANDARD_WEBHOOK_TIMESTAMP_HEADER, SpfResult, VERIFICATION_ERRORS, WEBHOOK_VERSION, WebhookPayloadError, WebhookValidationError, WebhookVerificationError, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, safeValidateEmailReceivedEvent, signStandardWebhooksPayload, signWebhookPayload, validateEmailAuth, validateEmailReceivedEvent, verifyRawEmailDownload, verifyStandardWebhooksSignature, verifyWebhookSignature } from "../webhook-uSco6pyX.js";
2
2
 
3
- export { AuthConfidence, AuthVerdict, DkimResult, DmarcPolicy, DmarcResult, EventType, ForwardVerdict, LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS, PRIMITIVE_CONFIRMED_HEADER, PRIMITIVE_SIGNATURE_HEADER, ParsedStatus, PrimitiveWebhookError, RAW_EMAIL_ERRORS, RawEmailDecodeError, SpfResult, VERIFICATION_ERRORS, WEBHOOK_VERSION, WebhookPayloadError, WebhookValidationError, WebhookVerificationError, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, safeValidateEmailReceivedEvent, signWebhookPayload, validateEmailAuth, validateEmailReceivedEvent, verifyRawEmailDownload, verifyWebhookSignature };
3
+ export { AuthConfidence, AuthVerdict, DkimResult, DmarcPolicy, DmarcResult, EventType, ForwardVerdict, LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS, PRIMITIVE_CONFIRMED_HEADER, PRIMITIVE_SIGNATURE_HEADER, ParsedStatus, PrimitiveWebhookError, RAW_EMAIL_ERRORS, RawEmailDecodeError, STANDARD_WEBHOOK_ID_HEADER, STANDARD_WEBHOOK_SIGNATURE_HEADER, STANDARD_WEBHOOK_TIMESTAMP_HEADER, SpfResult, VERIFICATION_ERRORS, WEBHOOK_VERSION, WebhookPayloadError, WebhookValidationError, WebhookVerificationError, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, safeValidateEmailReceivedEvent, signStandardWebhooksPayload, signWebhookPayload, validateEmailAuth, validateEmailReceivedEvent, verifyRawEmailDownload, verifyStandardWebhooksSignature, verifyWebhookSignature };
@@ -6090,9 +6090,9 @@ const PRIMITIVE_CONFIRMED_HEADER = "X-Primitive-Confirmed";
6090
6090
  /** Legacy confirmed header name kept for backward compatibility */
6091
6091
  const LEGACY_CONFIRMED_HEADER = "X-MyMX-Confirmed";
6092
6092
  /** Default max age for webhook requests (5 minutes) */
6093
- const DEFAULT_TOLERANCE_SECONDS = 5 * 60;
6093
+ const DEFAULT_TOLERANCE_SECONDS$1 = 5 * 60;
6094
6094
  /** Future clock skew tolerance (1 minute) */
6095
- const FUTURE_TOLERANCE_SECONDS = 60;
6095
+ const FUTURE_TOLERANCE_SECONDS$1 = 60;
6096
6096
  /** Valid hex pattern for signature verification */
6097
6097
  const HEX_PATTERN = /^[0-9a-f]+$/i;
6098
6098
  /** Strict unix-seconds pattern for the timestamp component */
@@ -6186,7 +6186,7 @@ function isValidHex(str, expectedLength) {
6186
6186
  * ```
6187
6187
  */
6188
6188
  function verifyWebhookSignature(opts) {
6189
- const { rawBody, signatureHeader, secret, toleranceSeconds = DEFAULT_TOLERANCE_SECONDS, nowSeconds } = opts;
6189
+ const { rawBody, signatureHeader, secret, toleranceSeconds = DEFAULT_TOLERANCE_SECONDS$1, nowSeconds } = opts;
6190
6190
  if (!secret || typeof secret === "string" && secret.length === 0 || Buffer.isBuffer(secret) && secret.length === 0) throw new WebhookVerificationError("MISSING_SECRET", "Webhook secret is required but was empty or not provided");
6191
6191
  const parsed = parseSignatureHeader(signatureHeader);
6192
6192
  if (!parsed) throw new WebhookVerificationError("INVALID_SIGNATURE_HEADER", "Invalid Primitive-Signature header format. Expected: t={timestamp},v1={signature}");
@@ -6194,7 +6194,7 @@ function verifyWebhookSignature(opts) {
6194
6194
  const now = nowSeconds ?? Math.floor(Date.now() / 1e3);
6195
6195
  const age = now - timestamp;
6196
6196
  if (age > toleranceSeconds) throw new WebhookVerificationError("TIMESTAMP_OUT_OF_RANGE", `Webhook timestamp too old (${age}s). Max age is ${toleranceSeconds}s.`);
6197
- if (age < -FUTURE_TOLERANCE_SECONDS) throw new WebhookVerificationError("TIMESTAMP_OUT_OF_RANGE", "Webhook timestamp is too far in the future. Check server clock sync.");
6197
+ if (age < -FUTURE_TOLERANCE_SECONDS$1) throw new WebhookVerificationError("TIMESTAMP_OUT_OF_RANGE", "Webhook timestamp is too far in the future. Check server clock sync.");
6198
6198
  const body = typeof rawBody === "string" ? rawBody : bufferToString(rawBody, "request body");
6199
6199
  let expectedHex;
6200
6200
  if (Buffer.isBuffer(rawBody)) {
@@ -6239,6 +6239,111 @@ function detectReserializedBody(body) {
6239
6239
  return null;
6240
6240
  }
6241
6241
 
6242
+ //#endregion
6243
+ //#region src/webhook/standard-webhooks.ts
6244
+ const WHSEC_PREFIX = "whsec_";
6245
+ const BASE64_PATTERN$1 = /^[A-Za-z0-9+/]*={0,2}$/;
6246
+ /** Default max age for webhook requests (5 minutes) */
6247
+ const DEFAULT_TOLERANCE_SECONDS = 5 * 60;
6248
+ /** Future clock skew tolerance (1 minute) */
6249
+ const FUTURE_TOLERANCE_SECONDS = 60;
6250
+ /** Standard Webhooks header names */
6251
+ const STANDARD_WEBHOOK_ID_HEADER = "webhook-id";
6252
+ const STANDARD_WEBHOOK_TIMESTAMP_HEADER = "webhook-timestamp";
6253
+ const STANDARD_WEBHOOK_SIGNATURE_HEADER = "webhook-signature";
6254
+ /**
6255
+ * Prepare a Standard Webhooks secret for HMAC computation.
6256
+ *
6257
+ * Strips the "whsec_" prefix if present, then base64-decodes the remainder
6258
+ * to produce the raw HMAC key bytes.
6259
+ *
6260
+ * @internal
6261
+ */
6262
+ function prepareStandardWebhooksSecret(secret) {
6263
+ if (Buffer.isBuffer(secret)) {
6264
+ if (secret.length === 0) throw new WebhookVerificationError("MISSING_SECRET", "Webhook secret is required but was empty or not provided");
6265
+ return secret;
6266
+ }
6267
+ let keyStr = secret;
6268
+ if (keyStr.startsWith(WHSEC_PREFIX)) keyStr = keyStr.slice(WHSEC_PREFIX.length);
6269
+ if (!keyStr || !BASE64_PATTERN$1.test(keyStr)) throw new WebhookVerificationError("MISSING_SECRET", "Standard Webhooks secret must be base64-encoded (optionally with whsec_ prefix)");
6270
+ const decoded = Buffer.from(keyStr, "base64");
6271
+ if (decoded.length === 0) throw new WebhookVerificationError("MISSING_SECRET", "Webhook secret is required but was empty or not provided");
6272
+ return decoded;
6273
+ }
6274
+ /**
6275
+ * Parse the webhook-signature header into base64 signature strings.
6276
+ *
6277
+ * The header format is space-delimited entries like "v1,<base64>".
6278
+ * Only v1 entries are returned.
6279
+ *
6280
+ * @internal
6281
+ */
6282
+ function parseStandardWebhooksSignatures(header) {
6283
+ if (!header) return [];
6284
+ const signatures = [];
6285
+ for (const entry of header.split(" ")) {
6286
+ const trimmed = entry.trim();
6287
+ if (!trimmed) continue;
6288
+ const commaIdx = trimmed.indexOf(",");
6289
+ if (commaIdx === -1) continue;
6290
+ const version = trimmed.slice(0, commaIdx);
6291
+ const sig = trimmed.slice(commaIdx + 1);
6292
+ if (version === "v1" && sig) signatures.push(sig);
6293
+ }
6294
+ return signatures;
6295
+ }
6296
+ /**
6297
+ * Sign a webhook payload using the Standard Webhooks format.
6298
+ *
6299
+ * @param rawBody - The raw JSON body string to sign
6300
+ * @param secret - The webhook secret (with or without whsec_ prefix)
6301
+ * @param msgId - The message ID (used in webhook-id header)
6302
+ * @param timestamp - Unix timestamp in seconds (defaults to current time)
6303
+ */
6304
+ function signStandardWebhooksPayload(rawBody, secret, msgId, timestamp) {
6305
+ const ts = timestamp ?? Math.floor(Date.now() / 1e3);
6306
+ const body = typeof rawBody === "string" ? rawBody : bufferToString(rawBody, "rawBody");
6307
+ const key = prepareStandardWebhooksSecret(secret);
6308
+ if (key.length === 0) throw new WebhookVerificationError("MISSING_SECRET", "Webhook secret is required but was empty or not provided");
6309
+ const signedPayload = `${msgId}.${ts}.${body}`;
6310
+ const sig = createHmac("sha256", key).update(signedPayload).digest("base64");
6311
+ return {
6312
+ signature: `v1,${sig}`,
6313
+ msgId,
6314
+ timestamp: ts
6315
+ };
6316
+ }
6317
+ /**
6318
+ * Verify a Standard Webhooks signature.
6319
+ *
6320
+ * Throws `WebhookVerificationError` on failure with a specific error code.
6321
+ */
6322
+ function verifyStandardWebhooksSignature(opts) {
6323
+ const { rawBody, msgId, timestamp: timestampStr, signatureHeader, secret, toleranceSeconds = DEFAULT_TOLERANCE_SECONDS, nowSeconds } = opts;
6324
+ const key = prepareStandardWebhooksSecret(secret);
6325
+ if (key.length === 0) throw new WebhookVerificationError("MISSING_SECRET", "Webhook secret is required but was empty or not provided");
6326
+ if (!timestampStr || !/^\d+$/.test(timestampStr)) throw new WebhookVerificationError("INVALID_SIGNATURE_HEADER", `Invalid webhook-timestamp header: "${timestampStr}". Expected a unix timestamp in seconds`);
6327
+ const timestamp = Number(timestampStr);
6328
+ if (!Number.isInteger(timestamp) || timestamp < 0) throw new WebhookVerificationError("INVALID_SIGNATURE_HEADER", `Invalid webhook-timestamp header: "${timestampStr}". Expected a unix timestamp in seconds`);
6329
+ const now = nowSeconds ?? Math.floor(Date.now() / 1e3);
6330
+ const age = now - timestamp;
6331
+ if (age > toleranceSeconds) throw new WebhookVerificationError("TIMESTAMP_OUT_OF_RANGE", `Webhook timestamp too old (${age}s). Max age is ${toleranceSeconds}s.`);
6332
+ if (age < -FUTURE_TOLERANCE_SECONDS) throw new WebhookVerificationError("TIMESTAMP_OUT_OF_RANGE", "Webhook timestamp is too far in the future. Check server clock sync.");
6333
+ const body = typeof rawBody === "string" ? rawBody : bufferToString(rawBody, "request body");
6334
+ const signedPayload = `${msgId}.${timestamp}.${body}`;
6335
+ const expectedSig = createHmac("sha256", key).update(signedPayload).digest("base64");
6336
+ const signatures = parseStandardWebhooksSignatures(signatureHeader);
6337
+ if (signatures.length === 0) throw new WebhookVerificationError("INVALID_SIGNATURE_HEADER", "Invalid webhook-signature header format. Expected: \"v1,<base64>\"");
6338
+ const expectedBytes = Buffer.from(expectedSig, "base64");
6339
+ for (const sig of signatures) {
6340
+ if (!BASE64_PATTERN$1.test(sig)) continue;
6341
+ const sigBytes = Buffer.from(sig, "base64");
6342
+ if (sigBytes.length === expectedBytes.length && timingSafeEqual(sigBytes, expectedBytes)) return true;
6343
+ }
6344
+ throw new WebhookVerificationError("SIGNATURE_MISMATCH", "No valid signature found. Verify the webhook secret matches and you're using the raw request body (not re-serialized JSON).");
6345
+ }
6346
+
6242
6347
  //#endregion
6243
6348
  //#region src/schema.generated.ts
6244
6349
  const emailReceivedEventJsonSchema = {
@@ -6758,14 +6863,14 @@ const emailReceivedEventJsonSchema = {
6758
6863
  "description": "Overall spam score (sum of all rule scores). Higher scores indicate higher likelihood of spam. Unbounded - can be negative (ham) or very high (spam)."
6759
6864
  } },
6760
6865
  "required": ["score"],
6761
- "description": "SpamAssassin analysis results."
6866
+ "description": "SpamAssassin analysis results.\n\nOptional. Present when the email was processed by a SpamAssassin-equipped pipeline (always present in Primitive's managed service). When absent, spam scoring was not performed on this email."
6762
6867
  },
6763
6868
  "forward": {
6764
6869
  "$ref": "#/definitions/ForwardAnalysis",
6765
- "description": "Forward detection and analysis results."
6870
+ "description": "Forward detection and analysis results.\n\nOptional. Present when the email was processed by a forward-detection pipeline (always present in Primitive's managed service). When absent, forward detection was not performed on this email."
6766
6871
  }
6767
6872
  },
6768
- "description": "Email analysis and classification results."
6873
+ "description": "Email analysis and classification results.\n\nAll properties in this object are optional. Which fields are present depends on the analysis pipeline processing the email. Primitive's managed service populates all fields. Self-hosted or third-party deployments may include some, all, or none of these fields depending on their pipeline configuration.\n\nWhen a field is absent, it means that particular analysis was not performed, not that the analysis produced no results. For example, a missing `spamassassin` field means SpamAssassin was not run, not that the email scored 0.\n\nThese fields may be omitted from the payload entirely but must not be set to null."
6769
6874
  },
6770
6875
  "ForwardAnalysis": {
6771
6876
  "type": "object",
@@ -7493,6 +7598,11 @@ function isEmailReceivedEvent(event) {
7493
7598
  }
7494
7599
  }
7495
7600
  const SIGNATURE_HEADER_NAMES = ["primitive-signature", "mymx-signature"];
7601
+ const STANDARD_WEBHOOKS_HEADER_NAMES = [
7602
+ "webhook-signature",
7603
+ "webhook-id",
7604
+ "webhook-timestamp"
7605
+ ];
7496
7606
  /**
7497
7607
  * Extract signature header from various header formats.
7498
7608
  * Checks for Primitive-Signature first, then falls back to the legacy
@@ -7518,6 +7628,50 @@ function getSignatureHeader(headers) {
7518
7628
  return "";
7519
7629
  }
7520
7630
  /**
7631
+ * Extract Standard Webhooks headers from various header formats.
7632
+ *
7633
+ * Returns all three required headers if `webhook-signature` is present.
7634
+ * If `webhook-signature` is found but `webhook-id` or `webhook-timestamp`
7635
+ * is missing, throws an error (partial Standard Webhooks headers indicate
7636
+ * a misconfiguration, not a Primitive-format webhook).
7637
+ *
7638
+ * Returns null if `webhook-signature` is not present (fall through to Primitive).
7639
+ */
7640
+ function getStandardWebhooksHeaders(headers) {
7641
+ let signature = "";
7642
+ let msgId = "";
7643
+ let timestamp = "";
7644
+ let hasSignatureKey = false;
7645
+ if (headers instanceof Headers) {
7646
+ hasSignatureKey = headers.has("webhook-signature");
7647
+ if (!hasSignatureKey) return null;
7648
+ signature = headers.get("webhook-signature") ?? "";
7649
+ msgId = headers.get("webhook-id") ?? "";
7650
+ timestamp = headers.get("webhook-timestamp") ?? "";
7651
+ } else {
7652
+ const obj = headers;
7653
+ const keys = Object.keys(obj);
7654
+ for (const name of STANDARD_WEBHOOKS_HEADER_NAMES) {
7655
+ const key = keys.find((k) => k.toLowerCase() === name);
7656
+ const raw = key ? obj[key] : void 0;
7657
+ const value = Array.isArray(raw) ? raw[0] ?? "" : raw ?? "";
7658
+ if (name === "webhook-signature") {
7659
+ hasSignatureKey = key !== void 0;
7660
+ signature = value;
7661
+ } else if (name === "webhook-id") msgId = value;
7662
+ else if (name === "webhook-timestamp") timestamp = value;
7663
+ }
7664
+ if (!hasSignatureKey) return null;
7665
+ }
7666
+ if (!signature) throw new WebhookVerificationError("INVALID_SIGNATURE_HEADER", "Empty webhook-signature header. Expected: \"v1,<base64>\"");
7667
+ if (!msgId || !timestamp) throw new WebhookVerificationError("INVALID_SIGNATURE_HEADER", `Found webhook-signature header but missing ${!msgId ? "webhook-id" : "webhook-timestamp"} header. Standard Webhooks requires all three headers: webhook-id, webhook-timestamp, webhook-signature.`);
7668
+ return {
7669
+ msgId,
7670
+ timestamp,
7671
+ signature
7672
+ };
7673
+ }
7674
+ /**
7521
7675
  * Verify, parse, and validate a webhook in one call.
7522
7676
  *
7523
7677
  * This is the recommended way to handle Primitive webhooks. It:
@@ -7558,13 +7712,24 @@ function getSignatureHeader(headers) {
7558
7712
  */
7559
7713
  function handleWebhook(options) {
7560
7714
  const { body, headers, secret, toleranceSeconds } = options;
7561
- const signature = getSignatureHeader(headers);
7562
- verifyWebhookSignature({
7715
+ const swHeaders = getStandardWebhooksHeaders(headers);
7716
+ if (swHeaders) verifyStandardWebhooksSignature({
7563
7717
  rawBody: body,
7564
- signatureHeader: signature,
7718
+ msgId: swHeaders.msgId,
7719
+ timestamp: swHeaders.timestamp,
7720
+ signatureHeader: swHeaders.signature,
7565
7721
  secret,
7566
7722
  toleranceSeconds
7567
7723
  });
7724
+ else {
7725
+ const signature = getSignatureHeader(headers);
7726
+ verifyWebhookSignature({
7727
+ rawBody: body,
7728
+ signatureHeader: signature,
7729
+ secret,
7730
+ toleranceSeconds
7731
+ });
7732
+ }
7568
7733
  const parsed = parseJsonBody(body);
7569
7734
  return validateEmailReceivedEvent(parsed);
7570
7735
  }
@@ -7742,4 +7907,4 @@ function verifyRawEmailDownload(downloaded, event) {
7742
7907
  }
7743
7908
 
7744
7909
  //#endregion
7745
- export { AuthConfidence, AuthVerdict, DkimResult, DmarcPolicy, DmarcResult, EventType, ForwardVerdict, LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS, PRIMITIVE_CONFIRMED_HEADER, PRIMITIVE_SIGNATURE_HEADER, ParsedStatus, PrimitiveWebhookError, RAW_EMAIL_ERRORS, RawEmailDecodeError, SpfResult, VERIFICATION_ERRORS, WEBHOOK_VERSION, WebhookPayloadError, WebhookValidationError, WebhookVerificationError, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, safeValidateEmailReceivedEvent, signWebhookPayload, validateEmailAuth, validateEmailReceivedEvent, verifyRawEmailDownload, verifyWebhookSignature };
7910
+ export { AuthConfidence, AuthVerdict, DkimResult, DmarcPolicy, DmarcResult, EventType, ForwardVerdict, LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS, PRIMITIVE_CONFIRMED_HEADER, PRIMITIVE_SIGNATURE_HEADER, ParsedStatus, PrimitiveWebhookError, RAW_EMAIL_ERRORS, RawEmailDecodeError, STANDARD_WEBHOOK_ID_HEADER, STANDARD_WEBHOOK_SIGNATURE_HEADER, STANDARD_WEBHOOK_TIMESTAMP_HEADER, SpfResult, VERIFICATION_ERRORS, WEBHOOK_VERSION, WebhookPayloadError, WebhookValidationError, WebhookVerificationError, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, safeValidateEmailReceivedEvent, signStandardWebhooksPayload, signWebhookPayload, validateEmailAuth, validateEmailReceivedEvent, verifyRawEmailDownload, verifyStandardWebhooksSignature, verifyWebhookSignature };