@primitivedotdev/sdk 0.2.4 → 0.4.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/README.md +65 -2
- package/bin/run.js +5 -0
- package/dist/api/generated/client/client.gen.js +235 -0
- package/dist/api/generated/client/index.js +6 -0
- package/dist/api/generated/client/types.gen.js +2 -0
- package/dist/api/generated/client/utils.gen.js +228 -0
- package/dist/api/generated/client.gen.js +3 -0
- package/dist/api/generated/core/auth.gen.js +14 -0
- package/dist/api/generated/core/bodySerializer.gen.js +57 -0
- package/dist/api/generated/core/params.gen.js +100 -0
- package/dist/api/generated/core/pathSerializer.gen.js +106 -0
- package/dist/api/generated/core/queryKeySerializer.gen.js +92 -0
- package/dist/api/generated/core/serverSentEvents.gen.js +132 -0
- package/dist/api/generated/core/types.gen.js +2 -0
- package/dist/api/generated/core/utils.gen.js +87 -0
- package/dist/api/generated/index.js +2 -0
- package/dist/api/generated/sdk.gen.js +347 -0
- package/dist/api/generated/types.gen.js +2 -0
- package/dist/api/index.d.ts +1877 -0
- package/dist/api/index.js +39 -0
- package/dist/chunk-Cl8Af3a2.js +11 -0
- package/dist/contract/index.d.ts +3 -3
- package/dist/contract/index.js +2 -2
- package/dist/{index-BdRIHaXz.d.ts → index-D2OuDGVz.d.ts} +81 -5
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/oclif/api-command.js +218 -0
- package/dist/oclif/fish-completion.js +87 -0
- package/dist/oclif/index.js +42 -0
- package/dist/openapi/index.d.ts +42 -0
- package/dist/openapi/index.js +8 -0
- package/dist/openapi/openapi.generated.js +2715 -0
- package/dist/openapi/operations.generated.js +671 -0
- package/dist/parser/index.d.ts +1 -1
- package/dist/parser/index.js +40 -25
- package/dist/{types-B5IgP-Zx.d.ts → types-C3ms4R0d.d.ts} +4 -0
- package/dist/webhook/index.d.ts +3 -3
- package/dist/webhook/index.js +2 -2
- package/dist/{webhook-Be2vM0F-.js → webhook-uSco6pyX.js} +176 -11
- package/oclif.manifest.json +1267 -0
- package/package.json +81 -13
package/dist/parser/index.js
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
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 =
|
|
22
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
/**
|
package/dist/webhook/index.d.ts
CHANGED
|
@@ -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-
|
|
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-
|
|
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 };
|
package/dist/webhook/index.js
CHANGED
|
@@ -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-
|
|
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
|
|
7562
|
-
|
|
7715
|
+
const swHeaders = getStandardWebhooksHeaders(headers);
|
|
7716
|
+
if (swHeaders) verifyStandardWebhooksSignature({
|
|
7563
7717
|
rawBody: body,
|
|
7564
|
-
|
|
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 };
|