@primitivedotdev/sdk 1.22.0 → 1.23.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.
@@ -0,0 +1,409 @@
1
+ //#region src/payloads/node-fs.ts
2
+ /**
3
+ * Load a Node builtin at call time. Exported for direct testing of the
4
+ * unavailable-module error path; production code goes through the typed
5
+ * wrappers below.
6
+ */
7
+ async function loadBuiltin(specifier, operation) {
8
+ let builtin;
9
+ if (typeof process !== "undefined" && typeof process.getBuiltinModule === "function") try {
10
+ builtin = process.getBuiltinModule(specifier);
11
+ } catch {
12
+ builtin = void 0;
13
+ }
14
+ if (builtin !== void 0 && builtin !== null) return builtin;
15
+ try {
16
+ return await import(specifier);
17
+ } catch (cause) {
18
+ throw new Error(`${operation} requires file system access, which this runtime does not provide. Pass in-memory bytes instead of a file path when running outside Node.js.`, { cause });
19
+ }
20
+ }
21
+ /** `node:fs`, loaded lazily so Workers-style bundles never see the import. */
22
+ function loadNodeFs(operation) {
23
+ return loadBuiltin("node:fs", operation);
24
+ }
25
+ /** `node:fs/promises`, loaded lazily for the same reason as {@link loadNodeFs}. */
26
+ function loadNodeFsPromises(operation) {
27
+ return loadBuiltin("node:fs/promises", operation);
28
+ }
29
+ //#endregion
30
+ //#region src/payloads/index.ts
31
+ const CHUNK_SIZE = 64 * 1024 * 1024;
32
+ const MANIFEST_VERSION = 1;
33
+ const CHUNK_KDF_INFO = "payloads-chunk";
34
+ const OBJECT_ID_BYTES = 16;
35
+ const CEK_BYTES = 32;
36
+ const subtle = globalThis.crypto.subtle;
37
+ const textEncoder = new TextEncoder();
38
+ function randomBytes(length) {
39
+ const out = new Uint8Array(length);
40
+ globalThis.crypto.getRandomValues(out);
41
+ return out;
42
+ }
43
+ function toHex(bytes) {
44
+ let out = "";
45
+ for (const b of bytes) out += b.toString(16).padStart(2, "0");
46
+ return out;
47
+ }
48
+ function fromHex(hex) {
49
+ if (hex.length % 2 !== 0) throw new Error("invalid hex: odd length");
50
+ if (hex.length > 0 && !/^[0-9a-fA-F]+$/.test(hex)) throw new Error("invalid hex: non-hex character");
51
+ const out = new Uint8Array(hex.length / 2);
52
+ for (let i = 0; i < out.length; i++) out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
53
+ return out;
54
+ }
55
+ async function contentHash(bytes) {
56
+ return new Uint8Array(await subtle.digest("SHA-256", bytes));
57
+ }
58
+ async function contentHashHex(bytes) {
59
+ return toHex(await contentHash(bytes));
60
+ }
61
+ /** Binary Merkle root over ordered hex leaf hashes (odd node promoted). */
62
+ async function merkleRoot(leafHashesHex) {
63
+ if (leafHashesHex.length === 0) return contentHashHex(new Uint8Array(0));
64
+ let level = leafHashesHex.map(fromHex);
65
+ while (level.length > 1) {
66
+ const next = [];
67
+ for (let i = 0; i < level.length; i += 2) {
68
+ const left = level[i];
69
+ const right = i + 1 < level.length ? level[i + 1] : level[i];
70
+ const combined = new Uint8Array(left.length + right.length);
71
+ combined.set(left, 0);
72
+ combined.set(right, left.length);
73
+ next.push(await contentHash(combined));
74
+ }
75
+ level = next;
76
+ }
77
+ return toHex(level[0]);
78
+ }
79
+ async function deriveChunkKey(cek, index) {
80
+ const baseKey = await subtle.importKey("raw", cek, "HKDF", false, ["deriveKey"]);
81
+ const info = textEncoder.encode(`${CHUNK_KDF_INFO}:${index}`);
82
+ return subtle.deriveKey({
83
+ name: "HKDF",
84
+ hash: "SHA-256",
85
+ salt: new Uint8Array(32),
86
+ info
87
+ }, baseKey, {
88
+ name: "AES-GCM",
89
+ length: 256
90
+ }, false, ["encrypt", "decrypt"]);
91
+ }
92
+ function chunkNonce(index) {
93
+ const nonce = new Uint8Array(12);
94
+ new DataView(nonce.buffer).setUint32(8, index >>> 0, false);
95
+ return nonce;
96
+ }
97
+ function chunkAad(objectId, index) {
98
+ const aad = new Uint8Array(objectId.length + 4);
99
+ aad.set(objectId, 0);
100
+ new DataView(aad.buffer).setUint32(objectId.length, index >>> 0, false);
101
+ return aad;
102
+ }
103
+ async function encryptChunk(cek, objectId, index, plaintext) {
104
+ const key = await deriveChunkKey(cek, index);
105
+ const ct = await subtle.encrypt({
106
+ name: "AES-GCM",
107
+ iv: chunkNonce(index),
108
+ additionalData: chunkAad(objectId, index)
109
+ }, key, plaintext);
110
+ return new Uint8Array(ct);
111
+ }
112
+ async function decryptChunk(cek, objectId, index, ciphertext) {
113
+ const key = await deriveChunkKey(cek, index);
114
+ const pt = await subtle.decrypt({
115
+ name: "AES-GCM",
116
+ iv: chunkNonce(index),
117
+ additionalData: chunkAad(objectId, index)
118
+ }, key, ciphertext);
119
+ return new Uint8Array(pt);
120
+ }
121
+ function authHeaders(apiKey) {
122
+ return { authorization: `Bearer ${apiKey}` };
123
+ }
124
+ function apiRoot(baseUrl) {
125
+ let end = baseUrl.length;
126
+ while (end > 0 && baseUrl.charCodeAt(end - 1) === 47) end--;
127
+ return `${baseUrl.slice(0, end)}/v1/payloads`;
128
+ }
129
+ const DEFAULT_MAX_RETRIES = 6;
130
+ const RETRYABLE_STATUS = new Set([
131
+ 429,
132
+ 500,
133
+ 502,
134
+ 503,
135
+ 504
136
+ ]);
137
+ function sleep(ms) {
138
+ return new Promise((resolve) => setTimeout(resolve, ms));
139
+ }
140
+ /**
141
+ * fetch with exponential backoff on transient failures. Request bodies here are
142
+ * in-memory buffers/strings (never streams), so a retry can safely resend them.
143
+ */
144
+ async function retryingFetch(url, init, label, maxRetries = DEFAULT_MAX_RETRIES) {
145
+ for (let attempt = 0;; attempt++) {
146
+ let res;
147
+ let networkError;
148
+ try {
149
+ res = await fetch(url, init);
150
+ } catch (err) {
151
+ networkError = err;
152
+ }
153
+ if (res && !RETRYABLE_STATUS.has(res.status)) return res;
154
+ if (attempt >= maxRetries) {
155
+ if (res) return res;
156
+ throw new Error(`${label}: network error after ${attempt} retries: ${networkError?.message ?? networkError}`);
157
+ }
158
+ if (res) await res.text().catch(() => {});
159
+ await sleep(Math.min(1e3 * 2 ** attempt, 15e3) + Math.floor(Math.random() * 250));
160
+ }
161
+ }
162
+ async function initiate(opts, manifest) {
163
+ const res = await retryingFetch(apiRoot(opts.baseUrl), {
164
+ method: "POST",
165
+ headers: {
166
+ ...authHeaders(opts.apiKey),
167
+ "content-type": "application/json"
168
+ },
169
+ body: JSON.stringify({ manifest })
170
+ }, "initiate");
171
+ if (!res.ok) throw new Error(`initiate failed: HTTP ${res.status} ${await res.text()}`);
172
+ await res.text();
173
+ }
174
+ async function putChunk(opts, root, d, bytes) {
175
+ const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/chunks/${d.ciphertextHash}`, {
176
+ method: "PUT",
177
+ headers: {
178
+ ...authHeaders(opts.apiKey),
179
+ "content-type": "application/octet-stream"
180
+ },
181
+ body: bytes
182
+ }, `chunk ${d.index} upload`);
183
+ if (!res.ok) throw new Error(`chunk ${d.index} upload failed: HTTP ${res.status} ${await res.text()}`);
184
+ await res.text();
185
+ }
186
+ async function finalize(opts, root) {
187
+ const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/finalize`, {
188
+ method: "POST",
189
+ headers: authHeaders(opts.apiKey)
190
+ }, "finalize");
191
+ if (!res.ok) throw new Error(`finalize failed: HTTP ${res.status} ${await res.text()}`);
192
+ await res.text();
193
+ }
194
+ async function fetchManifest(opts, root) {
195
+ const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/manifest`, { headers: authHeaders(opts.apiKey) }, "get manifest");
196
+ if (!res.ok) throw new Error(`get manifest failed: HTTP ${res.status} ${await res.text()}`);
197
+ return (await res.json()).data.manifest;
198
+ }
199
+ async function getChunkBytes(opts, root, d) {
200
+ const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/chunks/${d.ciphertextHash}`, { headers: authHeaders(opts.apiKey) }, `get chunk ${d.index}`);
201
+ if (!res.ok) throw new Error(`get chunk ${d.index} failed: HTTP ${res.status}`);
202
+ return new Uint8Array(await res.arrayBuffer());
203
+ }
204
+ /**
205
+ * Encode in-memory bytes into a payload manifest (no upload). Exposed for
206
+ * precomputing an object's content address and for conformance-testing the
207
+ * chunk/crypto/Merkle construction against the server object model. With a fixed
208
+ * cek/objectId the output is fully deterministic.
209
+ */
210
+ async function encodeManifest(bytes, opts = {}) {
211
+ const chunkSize = opts.chunkSize ?? CHUNK_SIZE;
212
+ const cek = opts.cekHex ? fromHex(opts.cekHex) : randomBytes(CEK_BYTES);
213
+ const objectId = opts.objectIdHex ? fromHex(opts.objectIdHex) : randomBytes(OBJECT_ID_BYTES);
214
+ const chunkCount = bytes.length === 0 ? 0 : Math.ceil(bytes.length / chunkSize);
215
+ const descriptors = [];
216
+ for (let index = 0; index < chunkCount; index++) {
217
+ const start = index * chunkSize;
218
+ const plaintext = bytes.subarray(start, Math.min(start + chunkSize, bytes.length));
219
+ const ciphertext = await encryptChunk(cek, objectId, index, plaintext);
220
+ descriptors.push({
221
+ index,
222
+ ciphertextHash: await contentHashHex(ciphertext),
223
+ plaintextSize: plaintext.length,
224
+ ciphertextSize: ciphertext.length
225
+ });
226
+ }
227
+ return {
228
+ version: MANIFEST_VERSION,
229
+ objectId: toHex(objectId),
230
+ chunkSize,
231
+ totalPlaintextSize: bytes.length,
232
+ chunkCount,
233
+ chunks: descriptors,
234
+ merkleRoot: await merkleRoot(descriptors.map((d) => d.ciphertextHash))
235
+ };
236
+ }
237
+ async function readWindow(fh, position, length) {
238
+ const buf = Buffer.allocUnsafe(length);
239
+ let read = 0;
240
+ while (read < length) {
241
+ const { bytesRead } = await fh.read(buf, read, length - read, position + read);
242
+ if (bytesRead === 0) break;
243
+ read += bytesRead;
244
+ }
245
+ return read === length ? buf : buf.subarray(0, read);
246
+ }
247
+ async function mapLimit(items, limit, fn) {
248
+ let next = 0;
249
+ let firstError;
250
+ const worker = async () => {
251
+ while (next < items.length && firstError === void 0) {
252
+ const i = next++;
253
+ try {
254
+ await fn(items[i], i);
255
+ } catch (err) {
256
+ if (firstError === void 0) firstError = err;
257
+ return;
258
+ }
259
+ }
260
+ };
261
+ await Promise.all(Array.from({ length: Math.min(limit, items.length || 1) }, () => worker()));
262
+ if (firstError !== void 0) throw firstError;
263
+ }
264
+ /**
265
+ * Stream a file up as a Primitive Payload. Two passes over the file (encrypt to
266
+ * build the manifest, then encrypt again to upload) keep memory bounded to a few
267
+ * chunks — the whole object is never resident. Returns the content address
268
+ * (merkleRoot) and the hex CEK needed to download it.
269
+ */
270
+ async function pushFile(filePath, opts) {
271
+ const { open, stat } = await loadNodeFsPromises("pushFile");
272
+ const chunkSize = opts.chunkSize ?? CHUNK_SIZE;
273
+ const concurrency = opts.concurrency ?? 3;
274
+ const { size } = await stat(filePath);
275
+ const cek = randomBytes(CEK_BYTES);
276
+ const objectId = randomBytes(OBJECT_ID_BYTES);
277
+ const chunkCount = size === 0 ? 0 : Math.ceil(size / chunkSize);
278
+ const fh = await open(filePath, "r");
279
+ try {
280
+ const descriptors = [];
281
+ for (let index = 0; index < chunkCount; index++) {
282
+ const position = index * chunkSize;
283
+ const length = Math.min(chunkSize, size - position);
284
+ const plaintext = await readWindow(fh, position, length);
285
+ const ciphertext = await encryptChunk(cek, objectId, index, plaintext);
286
+ descriptors.push({
287
+ index,
288
+ ciphertextHash: await contentHashHex(ciphertext),
289
+ plaintextSize: length,
290
+ ciphertextSize: ciphertext.length
291
+ });
292
+ opts.onProgress?.("encrypt", index + 1, chunkCount);
293
+ }
294
+ const root = await merkleRoot(descriptors.map((d) => d.ciphertextHash));
295
+ await initiate(opts, {
296
+ version: MANIFEST_VERSION,
297
+ objectId: toHex(objectId),
298
+ chunkSize,
299
+ totalPlaintextSize: size,
300
+ chunkCount,
301
+ chunks: descriptors,
302
+ merkleRoot: root
303
+ });
304
+ let uploaded = 0;
305
+ await mapLimit(descriptors, concurrency, async (d) => {
306
+ const plaintext = await readWindow(fh, d.index * chunkSize, d.plaintextSize);
307
+ await putChunk(opts, root, d, await encryptChunk(cek, objectId, d.index, plaintext));
308
+ opts.onProgress?.("upload", ++uploaded, chunkCount);
309
+ });
310
+ await finalize(opts, root);
311
+ return {
312
+ merkleRoot: root,
313
+ cek: toHex(cek),
314
+ chunkCount,
315
+ totalBytes: size
316
+ };
317
+ } finally {
318
+ await fh.close();
319
+ }
320
+ }
321
+ /**
322
+ * Push an in-memory buffer as a Primitive Payload. Same chunked, content-
323
+ * addressed, per-chunk-AEAD encoding as {@link pushFile}, over a Uint8Array
324
+ * (chunks are views, so no copy). Use {@link pushFile} for large objects that
325
+ * shouldn't be resident in memory. Returns the content address (merkleRoot) and
326
+ * the hex CEK.
327
+ */
328
+ async function pushBytes(bytes, opts) {
329
+ const chunkSize = opts.chunkSize ?? CHUNK_SIZE;
330
+ const concurrency = opts.concurrency ?? 3;
331
+ const size = bytes.length;
332
+ const cek = randomBytes(CEK_BYTES);
333
+ const objectId = randomBytes(OBJECT_ID_BYTES);
334
+ const chunkCount = size === 0 ? 0 : Math.ceil(size / chunkSize);
335
+ const chunkOf = (index) => bytes.subarray(index * chunkSize, Math.min(size, (index + 1) * chunkSize));
336
+ const descriptors = [];
337
+ for (let index = 0; index < chunkCount; index++) {
338
+ const plaintext = chunkOf(index);
339
+ const ciphertext = await encryptChunk(cek, objectId, index, plaintext);
340
+ descriptors.push({
341
+ index,
342
+ ciphertextHash: await contentHashHex(ciphertext),
343
+ plaintextSize: plaintext.length,
344
+ ciphertextSize: ciphertext.length
345
+ });
346
+ opts.onProgress?.("encrypt", index + 1, chunkCount);
347
+ }
348
+ const root = await merkleRoot(descriptors.map((d) => d.ciphertextHash));
349
+ await initiate(opts, {
350
+ version: MANIFEST_VERSION,
351
+ objectId: toHex(objectId),
352
+ chunkSize,
353
+ totalPlaintextSize: size,
354
+ chunkCount,
355
+ chunks: descriptors,
356
+ merkleRoot: root
357
+ });
358
+ let uploaded = 0;
359
+ await mapLimit(descriptors, concurrency, async (d) => {
360
+ await putChunk(opts, root, d, await encryptChunk(cek, objectId, d.index, chunkOf(d.index)));
361
+ opts.onProgress?.("upload", ++uploaded, chunkCount);
362
+ });
363
+ await finalize(opts, root);
364
+ return {
365
+ merkleRoot: root,
366
+ cek: toHex(cek),
367
+ chunkCount,
368
+ totalBytes: size
369
+ };
370
+ }
371
+ /**
372
+ * Stream a Primitive Payload down to a file, verifying and decrypting one chunk
373
+ * at a time (bounded memory). Every chunk is content-address-checked before
374
+ * decryption, so a corrupt or substituted chunk throws.
375
+ */
376
+ async function pullFile(root, outPath, opts) {
377
+ const { createWriteStream } = await loadNodeFs("pullFile");
378
+ const { rm } = await loadNodeFsPromises("pullFile");
379
+ const manifest = await fetchManifest(opts, root);
380
+ const computedRoot = await merkleRoot(manifest.chunks.map((c) => c.ciphertextHash));
381
+ if (computedRoot !== root) throw new Error(`manifest does not match the requested content address (got ${computedRoot})`);
382
+ const cek = fromHex(opts.cek);
383
+ const objectId = fromHex(manifest.objectId);
384
+ const out = createWriteStream(outPath);
385
+ let onWriteError = () => {};
386
+ const writeErrored = new Promise((_, reject) => {
387
+ onWriteError = reject;
388
+ });
389
+ writeErrored.catch(() => {});
390
+ out.on("error", (err) => onWriteError(err));
391
+ try {
392
+ let done = 0;
393
+ for (const d of manifest.chunks) {
394
+ const ciphertext = await getChunkBytes(opts, root, d);
395
+ if (await contentHashHex(ciphertext) !== d.ciphertextHash) throw new Error(`chunk ${d.index} failed integrity check`);
396
+ const plaintext = await decryptChunk(cek, objectId, d.index, ciphertext);
397
+ if (!out.write(plaintext)) await Promise.race([new Promise((resolve) => out.once("drain", resolve)), writeErrored]);
398
+ opts.onProgress?.("download", ++done, manifest.chunkCount);
399
+ }
400
+ await Promise.race([new Promise((resolve) => out.end(resolve)), writeErrored]);
401
+ } catch (err) {
402
+ out.destroy();
403
+ await rm(outPath, { force: true });
404
+ throw err;
405
+ }
406
+ return manifest;
407
+ }
408
+ //#endregion
409
+ export { pushFile as a, pushBytes as i, fetchManifest as n, loadNodeFsPromises as o, pullFile as r, encodeManifest as t };
@@ -459,7 +459,7 @@ interface EmailAnalysis$1 {
459
459
  /**
460
460
  * Forward detection and analysis results.
461
461
  *
462
- * 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.
462
+ * Optional. In Primitive's managed service this object is included only when forwarded content is detected; when absent, no forward was detected or forward detection was not performed.
463
463
  */
464
464
  interface ForwardAnalysis$1 {
465
465
  /**
@@ -1,4 +1,4 @@
1
- import { $ as isPaymentFailedEvent, A as RawContentInline, B as InteractionEvent, C as ParsedData, D as ParsedStatus, E as ParsedError, F as ValidateEmailAuthResult, G as PaymentFailedEvent, H as InteractionX402Suffix, I as WebhookAttachment, J as WebhookEventType, K as PaymentSettledEvent, L as WebhookEvent, M as SpfResult, N as TlsReportAnalysis, O as RawContent, P as UnknownEvent, Q as isPaymentEvent, R as EMAIL_EVENT_TYPES, S as KnownWebhookEvent, T as ParsedDataFailed, U as PAYMENT_EVENT_TYPES, V as InteractionX402Event, W as PaymentEvent, X as isInteractionX402Event, Y as isEmailReceivedEvent, Z as isKnownWebhookEventType, _ as ForwardResultAttachmentAnalyzed, a as DkimSignature, b as ForwardVerdict, c as DmarcResult, d as EmailAuth, et as isPaymentSettledEvent, f as EmailReceivedEvent, g as ForwardResult, h as ForwardOriginalSender, i as DkimResult, j as RoutingDecision, k as RawContentDownloadOnly, l as EmailAddress, m as ForwardAnalysis, n as AuthVerdict, o as DmarcPolicy, p as EventType, q as WEBHOOK_EVENT_TYPES, r as BounceAnalysis, s as DmarcReportAnalysis, t as AuthConfidence, u as EmailAnalysis, v as ForwardResultAttachmentSkipped, w as ParsedDataComplete, x as ForwardVerification, y as ForwardResultInline, z as INTERACTION_EVENT_TYPES } from "../types-DVjBmOg0.js";
2
- import { C as buildReplySubject, E as parseHeaderAddress, S as buildForwardSubject, T as normalizeReceivedEmail, _ as isTrustedSender, a as RawEmailDecodeErrorCode, b as ReceivedEmailAddress, c as WebhookPayloadError, d as WebhookValidationErrorCode, f as WebhookVerificationError, g as TrustedSenderResult, h as TrustedSenderOptions, i as RawEmailDecodeError, l as WebhookPayloadErrorCode, m as TrustReason, n as PrimitiveWebhookError, o as VERIFICATION_ERRORS, p as WebhookVerificationErrorCode, r as RAW_EMAIL_ERRORS, s as WebhookErrorCode, t as PAYLOAD_ERRORS, u as WebhookValidationError, v as validateEmailAuth, w as formatAddress, x as ReceivedEmailThread, y as ReceivedEmail } from "../errors-DAHv2kWU.js";
3
- import { A as SignResult, B as validateEmailReceivedEvent, C as StandardWebhooksVerifyOptions, D as LEGACY_SIGNATURE_HEADER, E as LEGACY_CONFIRMED_HEADER, F as VerifyDownloadTokenOptions, I as VerifyDownloadTokenResult, L as generateDownloadToken, M as signWebhookPayload, N as verifyWebhookSignature, O as PRIMITIVE_CONFIRMED_HEADER, P as GenerateDownloadTokenOptions, R as verifyDownloadToken, S as StandardWebhooksSignResult, T as verifyStandardWebhooksSignature, _ as WEBHOOK_VERSION, a as WebhookHeaders, b as STANDARD_WEBHOOK_SIGNATURE_HEADER, c as getDownloadTimeRemaining, d as handleWebhookEvent, f as isDownloadExpired, g as verifyRawEmailDownload, h as receive, i as WEBHOOK_EVENT_HEADER, j as VerifyOptions, k as PRIMITIVE_SIGNATURE_HEADER, l as getEventHeader, m as parseWebhookEvent, n as HandleWebhookOptions, o as confirmedHeaders, p as isRawIncluded, r as ReceiveRequestOptions, s as decodeRawEmail, t as DecodeRawEmailOptions, u as handleWebhook, v as emailReceivedEventJsonSchema, w as signStandardWebhooksPayload, x as STANDARD_WEBHOOK_TIMESTAMP_HEADER, y as STANDARD_WEBHOOK_ID_HEADER, z as safeValidateEmailReceivedEvent } from "../index-DJeUuGUn.js";
1
+ import { $ as isPaymentFailedEvent, A as RawContentInline, B as InteractionEvent, C as ParsedData, D as ParsedStatus, E as ParsedError, F as ValidateEmailAuthResult, G as PaymentFailedEvent, H as InteractionX402Suffix, I as WebhookAttachment, J as WebhookEventType, K as PaymentSettledEvent, L as WebhookEvent, M as SpfResult, N as TlsReportAnalysis, O as RawContent, P as UnknownEvent, Q as isPaymentEvent, R as EMAIL_EVENT_TYPES, S as KnownWebhookEvent, T as ParsedDataFailed, U as PAYMENT_EVENT_TYPES, V as InteractionX402Event, W as PaymentEvent, X as isInteractionX402Event, Y as isEmailReceivedEvent, Z as isKnownWebhookEventType, _ as ForwardResultAttachmentAnalyzed, a as DkimSignature, b as ForwardVerdict, c as DmarcResult, d as EmailAuth, et as isPaymentSettledEvent, f as EmailReceivedEvent, g as ForwardResult, h as ForwardOriginalSender, i as DkimResult, j as RoutingDecision, k as RawContentDownloadOnly, l as EmailAddress, m as ForwardAnalysis, n as AuthVerdict, o as DmarcPolicy, p as EventType, q as WEBHOOK_EVENT_TYPES, r as BounceAnalysis, s as DmarcReportAnalysis, t as AuthConfidence, u as EmailAnalysis, v as ForwardResultAttachmentSkipped, w as ParsedDataComplete, x as ForwardVerification, y as ForwardResultInline, z as INTERACTION_EVENT_TYPES } from "../types-DcXtFARL.js";
2
+ import { C as buildReplySubject, E as parseHeaderAddress, S as buildForwardSubject, T as normalizeReceivedEmail, _ as isTrustedSender, a as RawEmailDecodeErrorCode, b as ReceivedEmailAddress, c as WebhookPayloadError, d as WebhookValidationErrorCode, f as WebhookVerificationError, g as TrustedSenderResult, h as TrustedSenderOptions, i as RawEmailDecodeError, l as WebhookPayloadErrorCode, m as TrustReason, n as PrimitiveWebhookError, o as VERIFICATION_ERRORS, p as WebhookVerificationErrorCode, r as RAW_EMAIL_ERRORS, s as WebhookErrorCode, t as PAYLOAD_ERRORS, u as WebhookValidationError, v as validateEmailAuth, w as formatAddress, x as ReceivedEmailThread, y as ReceivedEmail } from "../errors-BgI9FKHK.js";
3
+ import { A as SignResult, B as validateEmailReceivedEvent, C as StandardWebhooksVerifyOptions, D as LEGACY_SIGNATURE_HEADER, E as LEGACY_CONFIRMED_HEADER, F as VerifyDownloadTokenOptions, I as VerifyDownloadTokenResult, L as generateDownloadToken, M as signWebhookPayload, N as verifyWebhookSignature, O as PRIMITIVE_CONFIRMED_HEADER, P as GenerateDownloadTokenOptions, R as verifyDownloadToken, S as StandardWebhooksSignResult, T as verifyStandardWebhooksSignature, _ as WEBHOOK_VERSION, a as WebhookHeaders, b as STANDARD_WEBHOOK_SIGNATURE_HEADER, c as getDownloadTimeRemaining, d as handleWebhookEvent, f as isDownloadExpired, g as verifyRawEmailDownload, h as receive, i as WEBHOOK_EVENT_HEADER, j as VerifyOptions, k as PRIMITIVE_SIGNATURE_HEADER, l as getEventHeader, m as parseWebhookEvent, n as HandleWebhookOptions, o as confirmedHeaders, p as isRawIncluded, r as ReceiveRequestOptions, s as decodeRawEmail, t as DecodeRawEmailOptions, u as handleWebhook, v as emailReceivedEventJsonSchema, w as signStandardWebhooksPayload, x as STANDARD_WEBHOOK_TIMESTAMP_HEADER, y as STANDARD_WEBHOOK_ID_HEADER, z as safeValidateEmailReceivedEvent } from "../index-fS1gVNuX.js";
4
4
  export { AuthConfidence, AuthVerdict, BounceAnalysis, DecodeRawEmailOptions, DkimResult, DkimSignature, DmarcPolicy, DmarcReportAnalysis, DmarcResult, EMAIL_EVENT_TYPES, EmailAddress, EmailAnalysis, EmailAuth, EmailReceivedEvent, EventType, ForwardAnalysis, ForwardOriginalSender, ForwardResult, ForwardResultAttachmentAnalyzed, ForwardResultAttachmentSkipped, ForwardResultInline, ForwardVerdict, ForwardVerification, GenerateDownloadTokenOptions, HandleWebhookOptions, INTERACTION_EVENT_TYPES, InteractionEvent, InteractionX402Event, InteractionX402Suffix, KnownWebhookEvent, LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS, PAYMENT_EVENT_TYPES, PRIMITIVE_CONFIRMED_HEADER, PRIMITIVE_SIGNATURE_HEADER, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus, PaymentEvent, PaymentFailedEvent, PaymentSettledEvent, PrimitiveWebhookError, RAW_EMAIL_ERRORS, RawContent, RawContentDownloadOnly, RawContentInline, RawEmailDecodeError, RawEmailDecodeErrorCode, ReceiveRequestOptions, ReceivedEmail, ReceivedEmailAddress, ReceivedEmailThread, RoutingDecision, STANDARD_WEBHOOK_ID_HEADER, STANDARD_WEBHOOK_SIGNATURE_HEADER, STANDARD_WEBHOOK_TIMESTAMP_HEADER, SignResult, SpfResult, StandardWebhooksSignResult, StandardWebhooksVerifyOptions, TlsReportAnalysis, TrustReason, TrustedSenderOptions, TrustedSenderResult, UnknownEvent, VERIFICATION_ERRORS, ValidateEmailAuthResult, VerifyDownloadTokenOptions, VerifyDownloadTokenResult, VerifyOptions, WEBHOOK_EVENT_HEADER, WEBHOOK_EVENT_TYPES, WEBHOOK_VERSION, WebhookAttachment, WebhookErrorCode, WebhookEvent, WebhookEventType, WebhookHeaders, WebhookPayloadError, WebhookPayloadErrorCode, WebhookValidationError, WebhookValidationErrorCode, WebhookVerificationError, WebhookVerificationErrorCode, buildForwardSubject, buildReplySubject, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, formatAddress, generateDownloadToken, getDownloadTimeRemaining, getEventHeader, handleWebhook, handleWebhookEvent, isDownloadExpired, isEmailReceivedEvent, isInteractionX402Event, isKnownWebhookEventType, isPaymentEvent, isPaymentFailedEvent, isPaymentSettledEvent, isRawIncluded, isTrustedSender, normalizeReceivedEmail, parseHeaderAddress, parseWebhookEvent, receive, safeValidateEmailReceivedEvent, signStandardWebhooksPayload, signWebhookPayload, validateEmailAuth, validateEmailReceivedEvent, verifyDownloadToken, verifyRawEmailDownload, verifyStandardWebhooksSignature, verifyWebhookSignature };
@@ -1,3 +1,3 @@
1
- import { C as normalizeReceivedEmail, S as formatAddress, _ as ForwardVerdict, a as VERIFICATION_ERRORS, b as buildForwardSubject, c as WebhookVerificationError, d as AuthConfidence, f as AuthVerdict, g as EventType, h as DmarcResult, i as RawEmailDecodeError, l as isTrustedSender, m as DmarcPolicy, n as PrimitiveWebhookError, o as WebhookPayloadError, p as DkimResult, r as RAW_EMAIL_ERRORS, s as WebhookValidationError, t as PAYLOAD_ERRORS, u as validateEmailAuth, v as ParsedStatus, w as parseHeaderAddress, x as buildReplySubject, y as SpfResult } from "../errors-D6WjisrD.js";
2
- import { A as isEmailReceivedEvent, C as PRIMITIVE_SIGNATURE_HEADER, D as INTERACTION_EVENT_TYPES, E as EMAIL_EVENT_TYPES, F as isPaymentSettledEvent, I as generateDownloadToken, L as verifyDownloadToken, M as isKnownWebhookEventType, N as isPaymentEvent, O as PAYMENT_EVENT_TYPES, P as isPaymentFailedEvent, R as safeValidateEmailReceivedEvent, S as PRIMITIVE_CONFIRMED_HEADER, T as verifyWebhookSignature, _ as STANDARD_WEBHOOK_TIMESTAMP_HEADER, a as getEventHeader, b as LEGACY_CONFIRMED_HEADER, c as isDownloadExpired, d as receive, f as verifyRawEmailDownload, g as STANDARD_WEBHOOK_SIGNATURE_HEADER, h as STANDARD_WEBHOOK_ID_HEADER, i as getDownloadTimeRemaining, j as isInteractionX402Event, k as WEBHOOK_EVENT_TYPES, l as isRawIncluded, m as emailReceivedEventJsonSchema, n as confirmedHeaders, o as handleWebhook, p as WEBHOOK_VERSION, r as decodeRawEmail, s as handleWebhookEvent, t as WEBHOOK_EVENT_HEADER, u as parseWebhookEvent, v as signStandardWebhooksPayload, w as signWebhookPayload, x as LEGACY_SIGNATURE_HEADER, y as verifyStandardWebhooksSignature, z as validateEmailReceivedEvent } from "../webhook-1cz088XC.js";
1
+ import { C as normalizeReceivedEmail, S as formatAddress, _ as ForwardVerdict, a as VERIFICATION_ERRORS, b as buildForwardSubject, c as WebhookVerificationError, d as AuthConfidence, f as AuthVerdict, g as EventType, h as DmarcResult, i as RawEmailDecodeError, l as isTrustedSender, m as DmarcPolicy, n as PrimitiveWebhookError, o as WebhookPayloadError, p as DkimResult, r as RAW_EMAIL_ERRORS, s as WebhookValidationError, t as PAYLOAD_ERRORS, u as validateEmailAuth, v as ParsedStatus, w as parseHeaderAddress, x as buildReplySubject, y as SpfResult } from "../errors-Bo0f7eId.js";
2
+ import { A as isEmailReceivedEvent, C as PRIMITIVE_SIGNATURE_HEADER, D as INTERACTION_EVENT_TYPES, E as EMAIL_EVENT_TYPES, F as isPaymentSettledEvent, I as generateDownloadToken, L as verifyDownloadToken, M as isKnownWebhookEventType, N as isPaymentEvent, O as PAYMENT_EVENT_TYPES, P as isPaymentFailedEvent, R as safeValidateEmailReceivedEvent, S as PRIMITIVE_CONFIRMED_HEADER, T as verifyWebhookSignature, _ as STANDARD_WEBHOOK_TIMESTAMP_HEADER, a as getEventHeader, b as LEGACY_CONFIRMED_HEADER, c as isDownloadExpired, d as receive, f as verifyRawEmailDownload, g as STANDARD_WEBHOOK_SIGNATURE_HEADER, h as STANDARD_WEBHOOK_ID_HEADER, i as getDownloadTimeRemaining, j as isInteractionX402Event, k as WEBHOOK_EVENT_TYPES, l as isRawIncluded, m as emailReceivedEventJsonSchema, n as confirmedHeaders, o as handleWebhook, p as WEBHOOK_VERSION, r as decodeRawEmail, s as handleWebhookEvent, t as WEBHOOK_EVENT_HEADER, u as parseWebhookEvent, v as signStandardWebhooksPayload, w as signWebhookPayload, x as LEGACY_SIGNATURE_HEADER, y as verifyStandardWebhooksSignature, z as validateEmailReceivedEvent } from "../webhook-BtyuJtUF.js";
3
3
  export { AuthConfidence, AuthVerdict, DkimResult, DmarcPolicy, DmarcResult, EMAIL_EVENT_TYPES, EventType, ForwardVerdict, INTERACTION_EVENT_TYPES, LEGACY_CONFIRMED_HEADER, LEGACY_SIGNATURE_HEADER, PAYLOAD_ERRORS, PAYMENT_EVENT_TYPES, 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_EVENT_HEADER, WEBHOOK_EVENT_TYPES, WEBHOOK_VERSION, WebhookPayloadError, WebhookValidationError, WebhookVerificationError, buildForwardSubject, buildReplySubject, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, formatAddress, generateDownloadToken, getDownloadTimeRemaining, getEventHeader, handleWebhook, handleWebhookEvent, isDownloadExpired, isEmailReceivedEvent, isInteractionX402Event, isKnownWebhookEventType, isPaymentEvent, isPaymentFailedEvent, isPaymentSettledEvent, isRawIncluded, isTrustedSender, normalizeReceivedEmail, parseHeaderAddress, parseWebhookEvent, receive, safeValidateEmailReceivedEvent, signStandardWebhooksPayload, signWebhookPayload, validateEmailAuth, validateEmailReceivedEvent, verifyDownloadToken, verifyRawEmailDownload, verifyStandardWebhooksSignature, verifyWebhookSignature };
@@ -1,4 +1,4 @@
1
- import { C as normalizeReceivedEmail, c as WebhookVerificationError, i as RawEmailDecodeError, o as WebhookPayloadError, s as WebhookValidationError } from "./errors-D6WjisrD.js";
1
+ import { C as normalizeReceivedEmail, c as WebhookVerificationError, i as RawEmailDecodeError, o as WebhookPayloadError, s as WebhookValidationError } from "./errors-Bo0f7eId.js";
2
2
  import { createHash, createHmac, timingSafeEqual } from "node:crypto";
3
3
  //#region src/generated/email-received-event.validator.generated.ts
4
4
  /**
@@ -9343,7 +9343,7 @@ const emailReceivedEventJsonSchema = {
9343
9343
  },
9344
9344
  "forward": {
9345
9345
  "$ref": "#/definitions/ForwardAnalysis",
9346
- "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."
9346
+ "description": "Forward detection and analysis results.\n\nOptional. In Primitive's managed service this object is included only when forwarded content is detected; when absent, no forward was detected or forward detection was not performed."
9347
9347
  },
9348
9348
  "bounce": {
9349
9349
  "$ref": "#/definitions/BounceAnalysis",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primitivedotdev/sdk",
3
- "version": "1.22.0",
3
+ "version": "1.23.0",
4
4
  "description": "Official Primitive Node.js SDK: webhook, api, openapi, contract, and parser runtime modules.",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
@@ -61,13 +61,13 @@
61
61
  "generate:types": "tsx scripts/generate-types.ts",
62
62
  "generate:validator": "tsx scripts/generate-validator.ts",
63
63
  "generate": "pnpm --filter @primitivedotdev/api-core generate && pnpm generate:schema && pnpm generate:types && pnpm generate:validator",
64
- "build": "pnpm generate && NODE_OPTIONS=\"--max-old-space-size=4096\" tsdown",
64
+ "build": "pnpm generate && NODE_OPTIONS=\"--max-old-space-size=6144\" tsdown",
65
65
  "test": "vitest run",
66
66
  "test:coverage": "vitest run --coverage",
67
67
  "test:watch": "vitest",
68
68
  "typecheck": "pnpm generate && tsc --noEmit -p tsconfig.typecheck.json",
69
- "lint": "biome check --error-on-warnings src/index.ts src/validation.ts src/types.ts src/webhook src/contract src/parser src/api/index.ts src/x402 src/openapi/index.ts src/payloads/index.ts tests/",
70
- "lint:fix": "biome check --write --error-on-warnings src/index.ts src/validation.ts src/types.ts src/webhook src/contract src/parser src/api/index.ts src/x402 src/openapi/index.ts tests/",
69
+ "lint": "biome check --error-on-warnings src/index.ts src/validation.ts src/types.ts src/webhook src/contract src/parser src/api/index.ts src/x402 src/openapi/index.ts src/payloads tests/",
70
+ "lint:fix": "biome check --write --error-on-warnings src/index.ts src/validation.ts src/types.ts src/webhook src/contract src/parser src/api/index.ts src/x402 src/openapi/index.ts src/payloads tests/",
71
71
  "prepublishOnly": "pnpm build"
72
72
  },
73
73
  "keywords": [
@@ -97,9 +97,9 @@
97
97
  },
98
98
  "dependencies": {
99
99
  "ajv": "^8.17.1",
100
- "mailparser": "^3.9.0",
100
+ "mailparser": "^3.9.14",
101
101
  "nodemailer": "^9.0.1",
102
- "sanitize-html": "^2.14.0",
102
+ "sanitize-html": "^2.17.5",
103
103
  "tar-stream": "^3.1.8",
104
104
  "validator": "^13.15.35",
105
105
  "viem": "^2.21.0"
@@ -115,6 +115,7 @@
115
115
  "@types/tar-stream": "^3.1.4",
116
116
  "@types/validator": "^13.15.10",
117
117
  "@vitest/coverage-v8": "^4.1.4",
118
+ "esbuild": "^0.27.0",
118
119
  "json-schema-to-typescript": "^15.0.4",
119
120
  "tsdown": "^0.21.10",
120
121
  "tsx": "^4.21.0",