@bernierllc/email-headers 0.0.1 → 0.2.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/LICENSE +7 -0
- package/dist/errors.d.ts +40 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +48 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/mdn.d.ts +41 -0
- package/dist/mdn.d.ts.map +1 -0
- package/dist/mdn.js +160 -0
- package/dist/mdn.js.map +1 -0
- package/dist/merge.d.ts +21 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +41 -0
- package/dist/merge.js.map +1 -0
- package/dist/threading.d.ts +34 -0
- package/dist/threading.d.ts.map +1 -0
- package/dist/threading.js +128 -0
- package/dist/threading.js.map +1 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/dist/unsubscribe.d.ts +25 -0
- package/dist/unsubscribe.d.ts.map +1 -0
- package/dist/unsubscribe.js +69 -0
- package/dist/unsubscribe.js.map +1 -0
- package/package.json +50 -7
- package/README.md +0 -45
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2025 Bernier LLC
|
|
3
|
+
|
|
4
|
+
This file is licensed to the client under a limited-use license.
|
|
5
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
6
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
7
|
+
*/
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error codes for programmatic error handling in email header operations.
|
|
3
|
+
*/
|
|
4
|
+
export declare const HeaderErrorCode: {
|
|
5
|
+
readonly INVALID_HEADER: "INVALID_HEADER";
|
|
6
|
+
readonly INVALID_MESSAGE_ID: "INVALID_MESSAGE_ID";
|
|
7
|
+
readonly INVALID_EMAIL: "INVALID_EMAIL";
|
|
8
|
+
readonly PARSE_ERROR: "PARSE_ERROR";
|
|
9
|
+
};
|
|
10
|
+
export type HeaderErrorCodeType = typeof HeaderErrorCode[keyof typeof HeaderErrorCode];
|
|
11
|
+
/**
|
|
12
|
+
* Options for creating a HeaderError.
|
|
13
|
+
*/
|
|
14
|
+
export interface HeaderErrorOptions {
|
|
15
|
+
/** The underlying error that caused this error */
|
|
16
|
+
cause?: Error;
|
|
17
|
+
/** Machine-readable error code for programmatic handling */
|
|
18
|
+
code?: HeaderErrorCodeType;
|
|
19
|
+
/** Additional context about the error */
|
|
20
|
+
context?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Error class for email header operations.
|
|
24
|
+
*
|
|
25
|
+
* Supports ES2022 Error.cause for error chaining.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* throw new HeaderError('Invalid message ID format', {
|
|
30
|
+
* code: HeaderErrorCode.INVALID_MESSAGE_ID,
|
|
31
|
+
* context: { messageId: 'bad-id' }
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare class HeaderError extends Error {
|
|
36
|
+
readonly code: HeaderErrorCodeType;
|
|
37
|
+
readonly context: Record<string, unknown> | undefined;
|
|
38
|
+
constructor(message: string, options?: HeaderErrorOptions);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;CAKlB,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,OAAO,eAAe,CAAC,MAAM,OAAO,eAAe,CAAC,CAAC;AAEvF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,4DAA4D;IAC5D,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;gBAE1C,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB;CAc1D"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.HeaderError = exports.HeaderErrorCode = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* Error codes for programmatic error handling in email header operations.
|
|
13
|
+
*/
|
|
14
|
+
exports.HeaderErrorCode = {
|
|
15
|
+
INVALID_HEADER: 'INVALID_HEADER',
|
|
16
|
+
INVALID_MESSAGE_ID: 'INVALID_MESSAGE_ID',
|
|
17
|
+
INVALID_EMAIL: 'INVALID_EMAIL',
|
|
18
|
+
PARSE_ERROR: 'PARSE_ERROR',
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Error class for email header operations.
|
|
22
|
+
*
|
|
23
|
+
* Supports ES2022 Error.cause for error chaining.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* throw new HeaderError('Invalid message ID format', {
|
|
28
|
+
* code: HeaderErrorCode.INVALID_MESSAGE_ID,
|
|
29
|
+
* context: { messageId: 'bad-id' }
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
class HeaderError extends Error {
|
|
34
|
+
constructor(message, options) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'HeaderError';
|
|
37
|
+
this.code = options?.code ?? exports.HeaderErrorCode.INVALID_HEADER;
|
|
38
|
+
this.context = options?.context;
|
|
39
|
+
// Set cause using ES2022 pattern (works at runtime in Node 16.9+)
|
|
40
|
+
if (options?.cause) {
|
|
41
|
+
this.cause = options.cause;
|
|
42
|
+
}
|
|
43
|
+
// Maintains proper stack trace in V8 engines
|
|
44
|
+
Error.captureStackTrace?.(this, this.constructor);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.HeaderError = HeaderError;
|
|
48
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AAEF;;GAEG;AACU,QAAA,eAAe,GAAG;IAC7B,cAAc,EAAE,gBAAgB;IAChC,kBAAkB,EAAE,oBAAoB;IACxC,aAAa,EAAE,eAAe;IAC9B,WAAW,EAAE,aAAa;CAClB,CAAC;AAgBX;;;;;;;;;;;;GAYG;AACH,MAAa,WAAY,SAAQ,KAAK;IAIpC,YAAY,OAAe,EAAE,OAA4B;QACvD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,uBAAe,CAAC,cAAc,CAAC;QAC5D,IAAI,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,CAAC;QAEhC,kEAAkE;QAClE,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;YAClB,IAAoC,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC9D,CAAC;QAED,6CAA6C;QAC7C,KAAK,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACpD,CAAC;CACF;AAlBD,kCAkBC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
21
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
__exportStar(require("./types"), exports);
|
|
25
|
+
__exportStar(require("./errors"), exports);
|
|
26
|
+
__exportStar(require("./threading"), exports);
|
|
27
|
+
__exportStar(require("./unsubscribe"), exports);
|
|
28
|
+
__exportStar(require("./mdn"), exports);
|
|
29
|
+
__exportStar(require("./merge"), exports);
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;;;;;;;;;;;;;;AAEF,0CAAwB;AACxB,2CAAyB;AACzB,8CAA4B;AAC5B,gDAA8B;AAC9B,wCAAsB;AACtB,0CAAwB"}
|
package/dist/mdn.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { MdnDisposition } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Create a Disposition-Notification-To header for requesting read receipts.
|
|
4
|
+
*
|
|
5
|
+
* @param notifyEmail - Email address to send the MDN notification to
|
|
6
|
+
* @returns Record with Disposition-Notification-To header
|
|
7
|
+
*
|
|
8
|
+
* @throws {HeaderError} If the email format is invalid
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const headers = createMdnRequestHeader('sender@example.com');
|
|
13
|
+
* // { 'Disposition-Notification-To': 'sender@example.com' }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function createMdnRequestHeader(notifyEmail: string): Record<string, string>;
|
|
17
|
+
/**
|
|
18
|
+
* Parse an MDN (Message Disposition Notification) response body per RFC 3798.
|
|
19
|
+
*
|
|
20
|
+
* MDN bodies contain fields like:
|
|
21
|
+
* - Original-Message-ID: <message-id>
|
|
22
|
+
* - Final-Recipient: rfc822; user@example.com
|
|
23
|
+
* - Disposition: automatic-action/MDN-sent-automatically; displayed
|
|
24
|
+
*
|
|
25
|
+
* @param mdnContent - The MDN body content to parse
|
|
26
|
+
* @returns Parsed MDN disposition information
|
|
27
|
+
*
|
|
28
|
+
* @throws {HeaderError} If required fields are missing or disposition is invalid
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const mdn = parseMdnResponse(
|
|
33
|
+
* 'Original-Message-ID: <abc@example.com>\r\n' +
|
|
34
|
+
* 'Final-Recipient: rfc822; user@example.com\r\n' +
|
|
35
|
+
* 'Disposition: manual-action/MDN-sent-manually; displayed\r\n'
|
|
36
|
+
* );
|
|
37
|
+
* // { originalMessageId: '<abc@example.com>', recipient: 'user@example.com', disposition: 'displayed' }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseMdnResponse(mdnContent: string): MdnDisposition;
|
|
41
|
+
//# sourceMappingURL=mdn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mdn.d.ts","sourceRoot":"","sources":["../src/mdn.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAsB9C;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,GAClB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBxB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,CAkFnE"}
|
package/dist/mdn.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.createMdnRequestHeader = createMdnRequestHeader;
|
|
11
|
+
exports.parseMdnResponse = parseMdnResponse;
|
|
12
|
+
const errors_1 = require("./errors");
|
|
13
|
+
/**
|
|
14
|
+
* Basic email format validation.
|
|
15
|
+
*/
|
|
16
|
+
function isValidEmail(email) {
|
|
17
|
+
// Simple but reasonable email regex
|
|
18
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Valid MDN disposition types per RFC 3798.
|
|
22
|
+
*/
|
|
23
|
+
const VALID_DISPOSITIONS = new Set([
|
|
24
|
+
'displayed',
|
|
25
|
+
'deleted',
|
|
26
|
+
'dispatched',
|
|
27
|
+
'processed',
|
|
28
|
+
'denied',
|
|
29
|
+
'failed',
|
|
30
|
+
]);
|
|
31
|
+
/**
|
|
32
|
+
* Create a Disposition-Notification-To header for requesting read receipts.
|
|
33
|
+
*
|
|
34
|
+
* @param notifyEmail - Email address to send the MDN notification to
|
|
35
|
+
* @returns Record with Disposition-Notification-To header
|
|
36
|
+
*
|
|
37
|
+
* @throws {HeaderError} If the email format is invalid
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const headers = createMdnRequestHeader('sender@example.com');
|
|
42
|
+
* // { 'Disposition-Notification-To': 'sender@example.com' }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
function createMdnRequestHeader(notifyEmail) {
|
|
46
|
+
if (!notifyEmail || notifyEmail.trim().length === 0) {
|
|
47
|
+
throw new errors_1.HeaderError('Notification email is required', {
|
|
48
|
+
code: errors_1.HeaderErrorCode.INVALID_EMAIL,
|
|
49
|
+
context: { notifyEmail },
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const trimmed = notifyEmail.trim();
|
|
53
|
+
if (!isValidEmail(trimmed)) {
|
|
54
|
+
throw new errors_1.HeaderError('Invalid email format for MDN notification', {
|
|
55
|
+
code: errors_1.HeaderErrorCode.INVALID_EMAIL,
|
|
56
|
+
context: { notifyEmail: trimmed },
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
'Disposition-Notification-To': trimmed,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Parse an MDN (Message Disposition Notification) response body per RFC 3798.
|
|
65
|
+
*
|
|
66
|
+
* MDN bodies contain fields like:
|
|
67
|
+
* - Original-Message-ID: <message-id>
|
|
68
|
+
* - Final-Recipient: rfc822; user@example.com
|
|
69
|
+
* - Disposition: automatic-action/MDN-sent-automatically; displayed
|
|
70
|
+
*
|
|
71
|
+
* @param mdnContent - The MDN body content to parse
|
|
72
|
+
* @returns Parsed MDN disposition information
|
|
73
|
+
*
|
|
74
|
+
* @throws {HeaderError} If required fields are missing or disposition is invalid
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* const mdn = parseMdnResponse(
|
|
79
|
+
* 'Original-Message-ID: <abc@example.com>\r\n' +
|
|
80
|
+
* 'Final-Recipient: rfc822; user@example.com\r\n' +
|
|
81
|
+
* 'Disposition: manual-action/MDN-sent-manually; displayed\r\n'
|
|
82
|
+
* );
|
|
83
|
+
* // { originalMessageId: '<abc@example.com>', recipient: 'user@example.com', disposition: 'displayed' }
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
function parseMdnResponse(mdnContent) {
|
|
87
|
+
if (!mdnContent || mdnContent.trim().length === 0) {
|
|
88
|
+
throw new errors_1.HeaderError('MDN content is required', {
|
|
89
|
+
code: errors_1.HeaderErrorCode.PARSE_ERROR,
|
|
90
|
+
context: { mdnContent },
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Parse fields from MDN body (case-insensitive)
|
|
94
|
+
const lines = mdnContent.split(/\r?\n/);
|
|
95
|
+
const fields = {};
|
|
96
|
+
for (const line of lines) {
|
|
97
|
+
const colonIndex = line.indexOf(':');
|
|
98
|
+
if (colonIndex > 0) {
|
|
99
|
+
const key = line.substring(0, colonIndex).trim().toLowerCase();
|
|
100
|
+
const value = line.substring(colonIndex + 1).trim();
|
|
101
|
+
fields[key] = value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Extract Original-Message-ID
|
|
105
|
+
const originalMessageId = fields['original-message-id'];
|
|
106
|
+
if (originalMessageId === undefined || originalMessageId.length === 0) {
|
|
107
|
+
throw new errors_1.HeaderError('Missing Original-Message-ID in MDN response', {
|
|
108
|
+
code: errors_1.HeaderErrorCode.PARSE_ERROR,
|
|
109
|
+
context: { fields },
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// Extract Final-Recipient (format: rfc822; user@example.com)
|
|
113
|
+
const finalRecipientRaw = fields['final-recipient'];
|
|
114
|
+
if (finalRecipientRaw === undefined || finalRecipientRaw.length === 0) {
|
|
115
|
+
throw new errors_1.HeaderError('Missing Final-Recipient in MDN response', {
|
|
116
|
+
code: errors_1.HeaderErrorCode.PARSE_ERROR,
|
|
117
|
+
context: { fields },
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
let recipient = finalRecipientRaw;
|
|
121
|
+
// Strip "rfc822;" or "rfc822; " prefix if present
|
|
122
|
+
const semicolonIndex = finalRecipientRaw.indexOf(';');
|
|
123
|
+
if (semicolonIndex >= 0) {
|
|
124
|
+
recipient = finalRecipientRaw.substring(semicolonIndex + 1).trim();
|
|
125
|
+
}
|
|
126
|
+
// Extract Disposition (format: action-mode/sending-mode; disposition-type)
|
|
127
|
+
const dispositionRaw = fields['disposition'];
|
|
128
|
+
if (dispositionRaw === undefined || dispositionRaw.length === 0) {
|
|
129
|
+
throw new errors_1.HeaderError('Missing Disposition in MDN response', {
|
|
130
|
+
code: errors_1.HeaderErrorCode.PARSE_ERROR,
|
|
131
|
+
context: { fields },
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// Parse disposition type from the value after the last semicolon
|
|
135
|
+
const dispositionSemicolonIndex = dispositionRaw.lastIndexOf(';');
|
|
136
|
+
let dispositionType;
|
|
137
|
+
if (dispositionSemicolonIndex >= 0) {
|
|
138
|
+
dispositionType = dispositionRaw.substring(dispositionSemicolonIndex + 1).trim().toLowerCase();
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
dispositionType = dispositionRaw.trim().toLowerCase();
|
|
142
|
+
}
|
|
143
|
+
// Handle composite disposition types like "displayed/error" - take the first part
|
|
144
|
+
const slashIndex = dispositionType.indexOf('/');
|
|
145
|
+
if (slashIndex >= 0) {
|
|
146
|
+
dispositionType = dispositionType.substring(0, slashIndex).trim();
|
|
147
|
+
}
|
|
148
|
+
if (!VALID_DISPOSITIONS.has(dispositionType)) {
|
|
149
|
+
throw new errors_1.HeaderError(`Invalid disposition type: ${dispositionType}`, {
|
|
150
|
+
code: errors_1.HeaderErrorCode.PARSE_ERROR,
|
|
151
|
+
context: { dispositionType, raw: dispositionRaw },
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
originalMessageId,
|
|
156
|
+
recipient,
|
|
157
|
+
disposition: dispositionType,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=mdn.js.map
|
package/dist/mdn.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mdn.js","sourceRoot":"","sources":["../src/mdn.ts"],"names":[],"mappings":"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"}
|
package/dist/merge.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { EmailHeaders } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Merge multiple email header objects into one.
|
|
4
|
+
*
|
|
5
|
+
* Later values override earlier ones for the same key.
|
|
6
|
+
*
|
|
7
|
+
* @param base - The base headers to start with
|
|
8
|
+
* @param additional - Additional header sets to merge in order
|
|
9
|
+
* @returns Merged headers
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const merged = mergeHeaders(
|
|
14
|
+
* { 'From': 'a@example.com', 'Subject': 'Hello' },
|
|
15
|
+
* { 'Subject': 'Updated', 'X-Custom': 'value' }
|
|
16
|
+
* );
|
|
17
|
+
* // { 'From': 'a@example.com', 'Subject': 'Updated', 'X-Custom': 'value' }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function mergeHeaders(base: EmailHeaders, ...additional: EmailHeaders[]): EmailHeaders;
|
|
21
|
+
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,YAAY,EAClB,GAAG,UAAU,EAAE,YAAY,EAAE,GAC5B,YAAY,CAad"}
|
package/dist/merge.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.mergeHeaders = mergeHeaders;
|
|
11
|
+
/**
|
|
12
|
+
* Merge multiple email header objects into one.
|
|
13
|
+
*
|
|
14
|
+
* Later values override earlier ones for the same key.
|
|
15
|
+
*
|
|
16
|
+
* @param base - The base headers to start with
|
|
17
|
+
* @param additional - Additional header sets to merge in order
|
|
18
|
+
* @returns Merged headers
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const merged = mergeHeaders(
|
|
23
|
+
* { 'From': 'a@example.com', 'Subject': 'Hello' },
|
|
24
|
+
* { 'Subject': 'Updated', 'X-Custom': 'value' }
|
|
25
|
+
* );
|
|
26
|
+
* // { 'From': 'a@example.com', 'Subject': 'Updated', 'X-Custom': 'value' }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
function mergeHeaders(base, ...additional) {
|
|
30
|
+
const result = { ...base };
|
|
31
|
+
for (const headers of additional) {
|
|
32
|
+
for (const key of Object.keys(headers)) {
|
|
33
|
+
const value = headers[key];
|
|
34
|
+
if (value !== undefined) {
|
|
35
|
+
result[key] = value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;AAsBF,oCAgBC;AAlCD;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,YAAY,CAC1B,IAAkB,EAClB,GAAG,UAA0B;IAE7B,MAAM,MAAM,GAAiB,EAAE,GAAG,IAAI,EAAE,CAAC;IAEzC,KAAK,MAAM,OAAO,IAAI,UAAU,EAAE,CAAC;QACjC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;YAC3B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ParsedThreadingInfo } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Create threading headers (In-Reply-To and References) for email replies.
|
|
4
|
+
*
|
|
5
|
+
* @param inReplyTo - The message ID being replied to
|
|
6
|
+
* @param references - Optional array of previous message IDs in the thread
|
|
7
|
+
* @returns Record with 'In-Reply-To' and 'References' headers
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const headers = createThreadingHeaders('abc@example.com', ['root@example.com']);
|
|
12
|
+
* // { 'In-Reply-To': '<abc@example.com>', 'References': '<root@example.com> <abc@example.com>' }
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function createThreadingHeaders(inReplyTo: string, references?: string[]): Record<string, string>;
|
|
16
|
+
/**
|
|
17
|
+
* Parse threading headers from an email message.
|
|
18
|
+
*
|
|
19
|
+
* Performs case-insensitive header lookup to handle different email systems.
|
|
20
|
+
*
|
|
21
|
+
* @param headers - Raw email headers as key-value pairs
|
|
22
|
+
* @returns Parsed threading information
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const info = parseThreadingHeaders({
|
|
27
|
+
* 'In-Reply-To': '<reply@example.com>',
|
|
28
|
+
* 'References': '<root@example.com> <reply@example.com>'
|
|
29
|
+
* });
|
|
30
|
+
* // { inReplyTo: '<reply@example.com>', references: ['<root@example.com>', '<reply@example.com>'], threadId: '<root@example.com>' }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseThreadingHeaders(headers: Record<string, string>): ParsedThreadingInfo;
|
|
34
|
+
//# sourceMappingURL=threading.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threading.d.ts","sourceRoot":"","sources":["../src/threading.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AA6BnD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM,EAAE,GACpB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA8BxB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,mBAAmB,CAqCrB"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.createThreadingHeaders = createThreadingHeaders;
|
|
11
|
+
exports.parseThreadingHeaders = parseThreadingHeaders;
|
|
12
|
+
const errors_1 = require("./errors");
|
|
13
|
+
/**
|
|
14
|
+
* Wrap a message ID in angle brackets if not already wrapped.
|
|
15
|
+
*/
|
|
16
|
+
function ensureAngleBrackets(messageId) {
|
|
17
|
+
const trimmed = messageId.trim();
|
|
18
|
+
if (trimmed.startsWith('<') && trimmed.endsWith('>')) {
|
|
19
|
+
return trimmed;
|
|
20
|
+
}
|
|
21
|
+
return `<${trimmed}>`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Extract message IDs from a header value (space or comma separated, angle-bracketed).
|
|
25
|
+
*/
|
|
26
|
+
function extractMessageIds(headerValue) {
|
|
27
|
+
const matches = headerValue.match(/<[^>]+>/g);
|
|
28
|
+
if (!matches) {
|
|
29
|
+
// Try treating the whole value as a single message ID
|
|
30
|
+
const trimmed = headerValue.trim();
|
|
31
|
+
if (trimmed.length > 0) {
|
|
32
|
+
return [trimmed];
|
|
33
|
+
}
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
return matches;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Create threading headers (In-Reply-To and References) for email replies.
|
|
40
|
+
*
|
|
41
|
+
* @param inReplyTo - The message ID being replied to
|
|
42
|
+
* @param references - Optional array of previous message IDs in the thread
|
|
43
|
+
* @returns Record with 'In-Reply-To' and 'References' headers
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const headers = createThreadingHeaders('abc@example.com', ['root@example.com']);
|
|
48
|
+
* // { 'In-Reply-To': '<abc@example.com>', 'References': '<root@example.com> <abc@example.com>' }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
function createThreadingHeaders(inReplyTo, references) {
|
|
52
|
+
if (!inReplyTo || inReplyTo.trim().length === 0) {
|
|
53
|
+
throw new errors_1.HeaderError('inReplyTo message ID is required', {
|
|
54
|
+
code: errors_1.HeaderErrorCode.INVALID_MESSAGE_ID,
|
|
55
|
+
context: { inReplyTo },
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const wrappedInReplyTo = ensureAngleBrackets(inReplyTo);
|
|
59
|
+
// Build references: existing refs + the inReplyTo at the end
|
|
60
|
+
const refIds = [];
|
|
61
|
+
if (references && references.length > 0) {
|
|
62
|
+
for (const ref of references) {
|
|
63
|
+
const wrapped = ensureAngleBrackets(ref);
|
|
64
|
+
if (!refIds.includes(wrapped)) {
|
|
65
|
+
refIds.push(wrapped);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Add inReplyTo at the end if not already present
|
|
70
|
+
if (!refIds.includes(wrappedInReplyTo)) {
|
|
71
|
+
refIds.push(wrappedInReplyTo);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
'In-Reply-To': wrappedInReplyTo,
|
|
75
|
+
'References': refIds.join(' '),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse threading headers from an email message.
|
|
80
|
+
*
|
|
81
|
+
* Performs case-insensitive header lookup to handle different email systems.
|
|
82
|
+
*
|
|
83
|
+
* @param headers - Raw email headers as key-value pairs
|
|
84
|
+
* @returns Parsed threading information
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const info = parseThreadingHeaders({
|
|
89
|
+
* 'In-Reply-To': '<reply@example.com>',
|
|
90
|
+
* 'References': '<root@example.com> <reply@example.com>'
|
|
91
|
+
* });
|
|
92
|
+
* // { inReplyTo: '<reply@example.com>', references: ['<root@example.com>', '<reply@example.com>'], threadId: '<root@example.com>' }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
function parseThreadingHeaders(headers) {
|
|
96
|
+
// Case-insensitive header lookup
|
|
97
|
+
const normalizedHeaders = {};
|
|
98
|
+
for (const key of Object.keys(headers)) {
|
|
99
|
+
normalizedHeaders[key.toLowerCase()] = headers[key];
|
|
100
|
+
}
|
|
101
|
+
const inReplyToValue = normalizedHeaders['in-reply-to'];
|
|
102
|
+
const referencesValue = normalizedHeaders['references'];
|
|
103
|
+
// Parse In-Reply-To
|
|
104
|
+
let inReplyTo = null;
|
|
105
|
+
if (inReplyToValue !== undefined && inReplyToValue.trim().length > 0) {
|
|
106
|
+
const ids = extractMessageIds(inReplyToValue);
|
|
107
|
+
// In-Reply-To typically contains a single message ID
|
|
108
|
+
inReplyTo = ids[0] ?? null;
|
|
109
|
+
}
|
|
110
|
+
// Parse References
|
|
111
|
+
const references = [];
|
|
112
|
+
if (referencesValue !== undefined && referencesValue.trim().length > 0) {
|
|
113
|
+
const ids = extractMessageIds(referencesValue);
|
|
114
|
+
for (const id of ids) {
|
|
115
|
+
if (!references.includes(id)) {
|
|
116
|
+
references.push(id);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Thread ID is the first message in the references chain
|
|
121
|
+
const threadId = references.length > 0 ? references[0] : null;
|
|
122
|
+
return {
|
|
123
|
+
inReplyTo,
|
|
124
|
+
references,
|
|
125
|
+
threadId,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=threading.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threading.js","sourceRoot":"","sources":["../src/threading.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic email headers as key-value pairs.
|
|
3
|
+
* Values can be a single string or an array of strings for multi-value headers.
|
|
4
|
+
*/
|
|
5
|
+
export type EmailHeaders = Record<string, string | string[]>;
|
|
6
|
+
/**
|
|
7
|
+
* Headers for email threading (In-Reply-To and References).
|
|
8
|
+
*/
|
|
9
|
+
export interface ThreadingHeaders {
|
|
10
|
+
readonly inReplyTo: string;
|
|
11
|
+
readonly references?: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Headers for list unsubscribe functionality (RFC 8058).
|
|
15
|
+
*/
|
|
16
|
+
export interface ListUnsubscribeHeaders {
|
|
17
|
+
readonly listUnsubscribe: string;
|
|
18
|
+
readonly listUnsubscribePost?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parsed MDN (Message Disposition Notification) disposition.
|
|
22
|
+
*/
|
|
23
|
+
export interface MdnDisposition {
|
|
24
|
+
readonly originalMessageId: string;
|
|
25
|
+
readonly recipient: string;
|
|
26
|
+
readonly disposition: 'displayed' | 'deleted' | 'dispatched' | 'processed' | 'denied' | 'failed';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Options for requesting an MDN read receipt.
|
|
30
|
+
*/
|
|
31
|
+
export interface MdnRequestOptions {
|
|
32
|
+
readonly notifyEmail: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Parsed threading information from email headers.
|
|
36
|
+
*/
|
|
37
|
+
export interface ParsedThreadingInfo {
|
|
38
|
+
readonly inReplyTo: string | null;
|
|
39
|
+
readonly references: readonly string[];
|
|
40
|
+
readonly threadId: string | null;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;CAClG;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;EAME"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create List-Unsubscribe headers per RFC 2369 and optionally RFC 8058 (one-click).
|
|
3
|
+
*
|
|
4
|
+
* @param url - The HTTPS unsubscribe URL (must be HTTPS)
|
|
5
|
+
* @param mailto - Optional mailto address for unsubscribe
|
|
6
|
+
* @param oneClick - If true, adds List-Unsubscribe-Post header for RFC 8058 one-click unsubscribe
|
|
7
|
+
* @returns Record with List-Unsubscribe and optionally List-Unsubscribe-Post headers
|
|
8
|
+
*
|
|
9
|
+
* @throws {HeaderError} If the URL is not HTTPS
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const headers = createListUnsubscribeHeaders(
|
|
14
|
+
* 'https://example.com/unsubscribe?token=abc',
|
|
15
|
+
* 'unsubscribe@example.com',
|
|
16
|
+
* true
|
|
17
|
+
* );
|
|
18
|
+
* // {
|
|
19
|
+
* // 'List-Unsubscribe': '<https://example.com/unsubscribe?token=abc>, <mailto:unsubscribe@example.com>',
|
|
20
|
+
* // 'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click'
|
|
21
|
+
* // }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function createListUnsubscribeHeaders(url: string, mailto?: string, oneClick?: boolean): Record<string, string>;
|
|
25
|
+
//# sourceMappingURL=unsubscribe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsubscribe.d.ts","sourceRoot":"","sources":["../src/unsubscribe.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,OAAO,GACjB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsCxB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.createListUnsubscribeHeaders = createListUnsubscribeHeaders;
|
|
11
|
+
const errors_1 = require("./errors");
|
|
12
|
+
/**
|
|
13
|
+
* Create List-Unsubscribe headers per RFC 2369 and optionally RFC 8058 (one-click).
|
|
14
|
+
*
|
|
15
|
+
* @param url - The HTTPS unsubscribe URL (must be HTTPS)
|
|
16
|
+
* @param mailto - Optional mailto address for unsubscribe
|
|
17
|
+
* @param oneClick - If true, adds List-Unsubscribe-Post header for RFC 8058 one-click unsubscribe
|
|
18
|
+
* @returns Record with List-Unsubscribe and optionally List-Unsubscribe-Post headers
|
|
19
|
+
*
|
|
20
|
+
* @throws {HeaderError} If the URL is not HTTPS
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const headers = createListUnsubscribeHeaders(
|
|
25
|
+
* 'https://example.com/unsubscribe?token=abc',
|
|
26
|
+
* 'unsubscribe@example.com',
|
|
27
|
+
* true
|
|
28
|
+
* );
|
|
29
|
+
* // {
|
|
30
|
+
* // 'List-Unsubscribe': '<https://example.com/unsubscribe?token=abc>, <mailto:unsubscribe@example.com>',
|
|
31
|
+
* // 'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click'
|
|
32
|
+
* // }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
function createListUnsubscribeHeaders(url, mailto, oneClick) {
|
|
36
|
+
if (!url || url.trim().length === 0) {
|
|
37
|
+
throw new errors_1.HeaderError('Unsubscribe URL is required', {
|
|
38
|
+
code: errors_1.HeaderErrorCode.INVALID_HEADER,
|
|
39
|
+
context: { url },
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Validate HTTPS
|
|
43
|
+
const trimmedUrl = url.trim();
|
|
44
|
+
if (!trimmedUrl.toLowerCase().startsWith('https://')) {
|
|
45
|
+
throw new errors_1.HeaderError('Unsubscribe URL must use HTTPS', {
|
|
46
|
+
code: errors_1.HeaderErrorCode.INVALID_HEADER,
|
|
47
|
+
context: { url: trimmedUrl },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// Build List-Unsubscribe value
|
|
51
|
+
const parts = [`<${trimmedUrl}>`];
|
|
52
|
+
if (mailto !== undefined && mailto.trim().length > 0) {
|
|
53
|
+
const trimmedMailto = mailto.trim();
|
|
54
|
+
// Add mailto: prefix if not already present
|
|
55
|
+
const mailtoUri = trimmedMailto.startsWith('mailto:')
|
|
56
|
+
? trimmedMailto
|
|
57
|
+
: `mailto:${trimmedMailto}`;
|
|
58
|
+
parts.push(`<${mailtoUri}>`);
|
|
59
|
+
}
|
|
60
|
+
const result = {
|
|
61
|
+
'List-Unsubscribe': parts.join(', '),
|
|
62
|
+
};
|
|
63
|
+
// RFC 8058 one-click unsubscribe
|
|
64
|
+
if (oneClick === true) {
|
|
65
|
+
result['List-Unsubscribe-Post'] = 'List-Unsubscribe=One-Click';
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=unsubscribe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsubscribe.js","sourceRoot":"","sources":["../src/unsubscribe.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;AA2BF,oEA0CC;AAnED,qCAAwD;AAExD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAgB,4BAA4B,CAC1C,GAAW,EACX,MAAe,EACf,QAAkB;IAElB,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,oBAAW,CAAC,6BAA6B,EAAE;YACnD,IAAI,EAAE,wBAAe,CAAC,cAAc;YACpC,OAAO,EAAE,EAAE,GAAG,EAAE;SACjB,CAAC,CAAC;IACL,CAAC;IAED,iBAAiB;IACjB,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,oBAAW,CAAC,gCAAgC,EAAE;YACtD,IAAI,EAAE,wBAAe,CAAC,cAAc;YACpC,OAAO,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE;SAC7B,CAAC,CAAC;IACL,CAAC;IAED,+BAA+B;IAC/B,MAAM,KAAK,GAAa,CAAC,IAAI,UAAU,GAAG,CAAC,CAAC;IAC5C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QACpC,4CAA4C;QAC5C,MAAM,SAAS,GAAG,aAAa,CAAC,UAAU,CAAC,SAAS,CAAC;YACnD,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,UAAU,aAAa,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,MAAM,GAA2B;QACrC,kBAAkB,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;KACrC,CAAC;IAEF,iCAAiC;IACjC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,CAAC,uBAAuB,CAAC,GAAG,4BAA4B,CAAC;IACjE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bernierllc/email-headers",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Email header construction and parsing utilities for threading, unsubscribe, and read receipts with zero external dependencies",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
5
12
|
"keywords": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
"email",
|
|
14
|
+
"headers",
|
|
15
|
+
"threading",
|
|
16
|
+
"unsubscribe",
|
|
17
|
+
"mdn",
|
|
18
|
+
"read-receipt",
|
|
19
|
+
"rfc-8058",
|
|
20
|
+
"rfc-3798",
|
|
21
|
+
"typescript"
|
|
22
|
+
],
|
|
23
|
+
"author": "Bernier LLC",
|
|
24
|
+
"license": "PROPRIETARY",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/bernier-llc/tools"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/jest": "^29.5.0",
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"jest": "^29.5.0",
|
|
33
|
+
"ts-jest": "^29.1.0",
|
|
34
|
+
"typescript": "^5.0.0"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public",
|
|
41
|
+
"registry": "https://registry.npmjs.org/"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc",
|
|
45
|
+
"test": "jest",
|
|
46
|
+
"test:run": "jest --watchAll=false --forceExit",
|
|
47
|
+
"test:coverage": "jest --coverage",
|
|
48
|
+
"lint": "eslint src --ext .ts",
|
|
49
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
50
|
+
"clean": "rm -rf dist",
|
|
51
|
+
"prebuild": "npm run clean"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/README.md
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# @bernierllc/email-headers
|
|
2
|
-
|
|
3
|
-
## ⚠️ IMPORTANT NOTICE ⚠️
|
|
4
|
-
|
|
5
|
-
**This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
|
|
6
|
-
|
|
7
|
-
This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
|
|
8
|
-
|
|
9
|
-
## Purpose
|
|
10
|
-
|
|
11
|
-
This package exists to:
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@bernierllc/email-headers`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
15
|
-
|
|
16
|
-
## What is OIDC Trusted Publishing?
|
|
17
|
-
|
|
18
|
-
OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
|
|
19
|
-
|
|
20
|
-
## Setup Instructions
|
|
21
|
-
|
|
22
|
-
To properly configure OIDC trusted publishing for this package:
|
|
23
|
-
|
|
24
|
-
1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
28
|
-
|
|
29
|
-
## DO NOT USE THIS PACKAGE
|
|
30
|
-
|
|
31
|
-
This package is a placeholder for OIDC configuration only. It:
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
36
|
-
|
|
37
|
-
## More Information
|
|
38
|
-
|
|
39
|
-
For more details about npm's trusted publishing feature, see:
|
|
40
|
-
- [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
**Maintained for OIDC setup purposes only**
|