@benclmnt/postmock 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +251 -0
  3. package/dist/seeds/conformance/00-core.d.ts +4 -0
  4. package/dist/seeds/conformance/00-core.js +12 -0
  5. package/dist/seeds/conformance/20-bounces.d.ts +3 -0
  6. package/dist/seeds/conformance/20-bounces.js +108 -0
  7. package/dist/seeds/conformance/40-messages.d.ts +3 -0
  8. package/dist/seeds/conformance/40-messages.js +144 -0
  9. package/dist/seeds/conformance/50-server-color.d.ts +8 -0
  10. package/dist/seeds/conformance/50-server-color.js +13 -0
  11. package/dist/seeds/conformance/70-account.d.ts +9 -0
  12. package/dist/seeds/conformance/70-account.js +29 -0
  13. package/dist/seeds/conformance/80-templates.d.ts +4 -0
  14. package/dist/seeds/conformance/80-templates.js +23 -0
  15. package/dist/seeds/conformance.d.ts +2 -0
  16. package/dist/seeds/conformance.js +3 -0
  17. package/dist/seeds/empty.d.ts +4 -0
  18. package/dist/seeds/empty.js +3 -0
  19. package/dist/seeds/lib/conformance.d.ts +16 -0
  20. package/dist/seeds/lib/conformance.js +18 -0
  21. package/dist/seeds/lib/history.d.ts +27 -0
  22. package/dist/seeds/lib/history.js +129 -0
  23. package/dist/seeds/lib/read-server.d.ts +10 -0
  24. package/dist/seeds/lib/read-server.js +10 -0
  25. package/dist/seeds/lib/template-server.d.ts +13 -0
  26. package/dist/seeds/lib/template-server.js +13 -0
  27. package/dist/src/api/account/authentication.d.ts +39 -0
  28. package/dist/src/api/account/authentication.js +97 -0
  29. package/dist/src/api/account/domains.d.ts +59 -0
  30. package/dist/src/api/account/domains.js +78 -0
  31. package/dist/src/api/account/paging.d.ts +12 -0
  32. package/dist/src/api/account/paging.js +35 -0
  33. package/dist/src/api/account/push.d.ts +14 -0
  34. package/dist/src/api/account/push.js +84 -0
  35. package/dist/src/api/account/routes.d.ts +1 -0
  36. package/dist/src/api/account/routes.js +288 -0
  37. package/dist/src/api/account/senders.d.ts +54 -0
  38. package/dist/src/api/account/senders.js +87 -0
  39. package/dist/src/api/account/servers.d.ts +40 -0
  40. package/dist/src/api/account/servers.js +148 -0
  41. package/dist/src/api/bounces/json.d.ts +25 -0
  42. package/dist/src/api/bounces/json.js +27 -0
  43. package/dist/src/api/bounces/routes.d.ts +1 -0
  44. package/dist/src/api/bounces/routes.js +127 -0
  45. package/dist/src/api/bulk/bulk.d.ts +43 -0
  46. package/dist/src/api/bulk/bulk.js +81 -0
  47. package/dist/src/api/bulk/routes.d.ts +1 -0
  48. package/dist/src/api/bulk/routes.js +252 -0
  49. package/dist/src/api/data-removals/routes.d.ts +1 -0
  50. package/dist/src/api/data-removals/routes.js +59 -0
  51. package/dist/src/api/email/json.d.ts +11 -0
  52. package/dist/src/api/email/json.js +33 -0
  53. package/dist/src/api/email/routes.d.ts +1 -0
  54. package/dist/src/api/email/routes.js +64 -0
  55. package/dist/src/api/inbound/routes.d.ts +1 -0
  56. package/dist/src/api/inbound/routes.js +40 -0
  57. package/dist/src/api/index.d.ts +1 -0
  58. package/dist/src/api/index.js +3 -0
  59. package/dist/src/api/message-streams/routes.d.ts +1 -0
  60. package/dist/src/api/message-streams/routes.js +211 -0
  61. package/dist/src/api/message-streams/streams.d.ts +28 -0
  62. package/dist/src/api/message-streams/streams.js +50 -0
  63. package/dist/src/api/messages/json.d.ts +149 -0
  64. package/dist/src/api/messages/json.js +114 -0
  65. package/dist/src/api/messages/query.d.ts +19 -0
  66. package/dist/src/api/messages/query.js +64 -0
  67. package/dist/src/api/messages/routes.d.ts +1 -0
  68. package/dist/src/api/messages/routes.js +203 -0
  69. package/dist/src/api/server/json.d.ts +26 -0
  70. package/dist/src/api/server/json.js +25 -0
  71. package/dist/src/api/server/routes.d.ts +1 -0
  72. package/dist/src/api/server/routes.js +17 -0
  73. package/dist/src/api/stats/aggregate.d.ts +58 -0
  74. package/dist/src/api/stats/aggregate.js +66 -0
  75. package/dist/src/api/stats/routes.d.ts +1 -0
  76. package/dist/src/api/stats/routes.js +72 -0
  77. package/dist/src/api/suppressions/routes.d.ts +1 -0
  78. package/dist/src/api/suppressions/routes.js +98 -0
  79. package/dist/src/api/templates/content.d.ts +21 -0
  80. package/dist/src/api/templates/content.js +39 -0
  81. package/dist/src/api/templates/routes.d.ts +1 -0
  82. package/dist/src/api/templates/routes.js +164 -0
  83. package/dist/src/api/templates/send.d.ts +13 -0
  84. package/dist/src/api/templates/send.js +105 -0
  85. package/dist/src/api/templates/templates.d.ts +100 -0
  86. package/dist/src/api/templates/templates.js +218 -0
  87. package/dist/src/api/templates/validate.d.ts +15 -0
  88. package/dist/src/api/templates/validate.js +77 -0
  89. package/dist/src/api/triggers/routes.d.ts +1 -0
  90. package/dist/src/api/triggers/routes.js +62 -0
  91. package/dist/src/api/webhooks/routes.d.ts +14 -0
  92. package/dist/src/api/webhooks/routes.js +192 -0
  93. package/dist/src/config.d.ts +17 -0
  94. package/dist/src/config.js +95 -0
  95. package/dist/src/control/app.d.ts +5 -0
  96. package/dist/src/control/app.js +42 -0
  97. package/dist/src/control/endpoints/account.d.ts +1 -0
  98. package/dist/src/control/endpoints/account.js +68 -0
  99. package/dist/src/control/endpoints/bulk.d.ts +1 -0
  100. package/dist/src/control/endpoints/bulk.js +29 -0
  101. package/dist/src/control/endpoints/core.d.ts +1 -0
  102. package/dist/src/control/endpoints/core.js +104 -0
  103. package/dist/src/control/endpoints/inbound.d.ts +1 -0
  104. package/dist/src/control/endpoints/inbound.js +61 -0
  105. package/dist/src/control/endpoints/recipients.d.ts +1 -0
  106. package/dist/src/control/endpoints/recipients.js +136 -0
  107. package/dist/src/control/endpoints/smtp.d.ts +1 -0
  108. package/dist/src/control/endpoints/smtp.js +74 -0
  109. package/dist/src/control/endpoints/tracking.d.ts +1 -0
  110. package/dist/src/control/endpoints/tracking.js +95 -0
  111. package/dist/src/control/endpoints/webhooks.d.ts +1 -0
  112. package/dist/src/control/endpoints/webhooks.js +36 -0
  113. package/dist/src/control/index.d.ts +1 -0
  114. package/dist/src/control/index.js +3 -0
  115. package/dist/src/control/registry.d.ts +25 -0
  116. package/dist/src/control/registry.js +19 -0
  117. package/dist/src/control/seed.d.ts +11 -0
  118. package/dist/src/control/seed.js +32 -0
  119. package/dist/src/control/seeding.d.ts +10 -0
  120. package/dist/src/control/seeding.js +27 -0
  121. package/dist/src/discover.d.ts +16 -0
  122. package/dist/src/discover.js +49 -0
  123. package/dist/src/errors.d.ts +49 -0
  124. package/dist/src/errors.js +701 -0
  125. package/dist/src/events.d.ts +49 -0
  126. package/dist/src/events.js +19 -0
  127. package/dist/src/http/app.d.ts +8 -0
  128. package/dist/src/http/app.js +51 -0
  129. package/dist/src/http/auth.d.ts +7 -0
  130. package/dist/src/http/auth.js +32 -0
  131. package/dist/src/http/faults.d.ts +8 -0
  132. package/dist/src/http/faults.js +33 -0
  133. package/dist/src/http/normalize.d.ts +57 -0
  134. package/dist/src/http/normalize.js +225 -0
  135. package/dist/src/http/respond.d.ts +23 -0
  136. package/dist/src/http/respond.js +37 -0
  137. package/dist/src/http/routes.d.ts +60 -0
  138. package/dist/src/http/routes.js +64 -0
  139. package/dist/src/inbound/deliver.d.ts +9 -0
  140. package/dist/src/inbound/deliver.js +34 -0
  141. package/dist/src/inbound/parse.d.ts +21 -0
  142. package/dist/src/inbound/parse.js +71 -0
  143. package/dist/src/inbound/receive.d.ts +17 -0
  144. package/dist/src/inbound/receive.js +89 -0
  145. package/dist/src/main.d.ts +2 -0
  146. package/dist/src/main.js +14 -0
  147. package/dist/src/mime/compose.d.ts +33 -0
  148. package/dist/src/mime/compose.js +58 -0
  149. package/dist/src/pipeline/addresses.d.ts +6 -0
  150. package/dist/src/pipeline/addresses.js +38 -0
  151. package/dist/src/pipeline/inactive.d.ts +8 -0
  152. package/dist/src/pipeline/inactive.js +20 -0
  153. package/dist/src/pipeline/submit.d.ts +138 -0
  154. package/dist/src/pipeline/submit.js +415 -0
  155. package/dist/src/plugins/message-events.d.ts +7 -0
  156. package/dist/src/plugins/message-events.js +76 -0
  157. package/dist/src/plugins/smtp.d.ts +15 -0
  158. package/dist/src/plugins/smtp.js +42 -0
  159. package/dist/src/plugins/stats.d.ts +7 -0
  160. package/dist/src/plugins/stats.js +78 -0
  161. package/dist/src/plugins/webhooks.d.ts +4 -0
  162. package/dist/src/plugins/webhooks.js +80 -0
  163. package/dist/src/plugins.d.ts +20 -0
  164. package/dist/src/plugins.js +9 -0
  165. package/dist/src/recipients/test-bounces.d.ts +15 -0
  166. package/dist/src/recipients/test-bounces.js +31 -0
  167. package/dist/src/recipients/transitions.d.ts +57 -0
  168. package/dist/src/recipients/transitions.js +279 -0
  169. package/dist/src/render/mustachio.d.ts +60 -0
  170. package/dist/src/render/mustachio.js +265 -0
  171. package/dist/src/runtime.d.ts +12 -0
  172. package/dist/src/runtime.js +11 -0
  173. package/dist/src/server.d.ts +25 -0
  174. package/dist/src/server.js +49 -0
  175. package/dist/src/smtp/auth.d.ts +35 -0
  176. package/dist/src/smtp/auth.js +66 -0
  177. package/dist/src/smtp/listener.d.ts +16 -0
  178. package/dist/src/smtp/listener.js +121 -0
  179. package/dist/src/smtp/mime.d.ts +36 -0
  180. package/dist/src/smtp/mime.js +181 -0
  181. package/dist/src/smtp/receive.d.ts +21 -0
  182. package/dist/src/smtp/receive.js +89 -0
  183. package/dist/src/smtp/reply.d.ts +14 -0
  184. package/dist/src/smtp/reply.js +24 -0
  185. package/dist/src/state/clock.d.ts +35 -0
  186. package/dist/src/state/clock.js +107 -0
  187. package/dist/src/state/ids.d.ts +6 -0
  188. package/dist/src/state/ids.js +7 -0
  189. package/dist/src/state/servers.d.ts +27 -0
  190. package/dist/src/state/servers.js +101 -0
  191. package/dist/src/state/store.d.ts +57 -0
  192. package/dist/src/state/store.js +98 -0
  193. package/dist/src/state/suppressions.d.ts +9 -0
  194. package/dist/src/state/suppressions.js +8 -0
  195. package/dist/src/state/types.d.ts +530 -0
  196. package/dist/src/state/types.js +41 -0
  197. package/dist/src/time.d.ts +11 -0
  198. package/dist/src/time.js +48 -0
  199. package/dist/src/tracking.d.ts +53 -0
  200. package/dist/src/tracking.js +150 -0
  201. package/dist/src/webhooks/deliver.d.ts +60 -0
  202. package/dist/src/webhooks/deliver.js +172 -0
  203. package/dist/src/webhooks/egress.d.ts +19 -0
  204. package/dist/src/webhooks/egress.js +40 -0
  205. package/dist/src/webhooks/outbound.d.ts +26 -0
  206. package/dist/src/webhooks/outbound.js +47 -0
  207. package/dist/src/webhooks/payloads.d.ts +111 -0
  208. package/dist/src/webhooks/payloads.js +128 -0
  209. package/package.json +55 -0
@@ -0,0 +1,47 @@
1
+ import { deliver, OUTBOUND } from "./deliver.js";
2
+ /**
3
+ * Sends an event to every verified `/webhooks` row of its stream with the trigger on, then to the
4
+ * server hook URL (docs/05 §1.5, §5). Server hook URLs apply to every stream (INFERRED, Q10).
5
+ */
6
+ export async function emitOutbound(runtime, event) {
7
+ const server = runtime.store.state.servers.get(event.serverId);
8
+ if (server === undefined)
9
+ return;
10
+ const hooks = [];
11
+ for (const webhook of runtime.store.state.webhooks.values()) {
12
+ if (webhook.ServerID === server.ID &&
13
+ webhook.MessageStream === event.messageStream &&
14
+ webhook.Status === "verified" &&
15
+ webhook.Triggers[event.trigger].Enabled) {
16
+ hooks.push({
17
+ hook: { kind: "webhook", webhook },
18
+ target: {
19
+ serverId: server.ID,
20
+ hook: { kind: "webhook", webhookId: webhook.ID, trigger: event.trigger },
21
+ url: webhook.Url,
22
+ httpAuth: webhook.HttpAuth,
23
+ headers: webhook.HttpHeaders,
24
+ },
25
+ });
26
+ }
27
+ }
28
+ const field = event.serverHookField;
29
+ if (field !== null && server[field] !== "") {
30
+ hooks.push({
31
+ hook: { kind: "server", server },
32
+ target: {
33
+ serverId: server.ID,
34
+ hook: { kind: "server", field },
35
+ url: server[field],
36
+ httpAuth: null,
37
+ headers: [],
38
+ },
39
+ });
40
+ }
41
+ for (const { hook, target } of hooks) {
42
+ const payload = event.payload(hook);
43
+ if (payload !== null) {
44
+ await deliver(runtime, { target, recordType: event.recordType, payload, policy: OUTBOUND });
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,111 @@
1
+ import type { EventMap } from "../events.ts";
2
+ import { type Bounce, type ClickEvent, type InboundMessage, type OpenEvent, type SubscriptionChange } from "../state/types.ts";
3
+ /** docs/05 §2.1, §2.2, §2.8. `Content` only when the hook asks for it. */
4
+ export declare function bouncePayload(recordType: "Bounce" | "SpamComplaint", bounce: Bounce, includeContent: boolean): {
5
+ RecordType: "Bounce" | "SpamComplaint";
6
+ MessageStream: string;
7
+ ID: number;
8
+ Type: "AddressChange" | "AutoResponder" | "BadEmailAddress" | "Blocked" | "ChallengeVerification" | "DMARCPolicy" | "DnsError" | "HardBounce" | "InboundError" | "ManuallyDeactivated" | "OpenRelayTest" | "SMTPApiError" | "SoftBounce" | "SpamComplaint" | "SpamNotification" | "Subscribe" | "TemplateRenderingFailed" | "Transient" | "Unconfirmed" | "Unknown" | "Unsubscribe" | "VirusNotification";
9
+ TypeCode: 1 | 2 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096 | 8192 | 16384 | 100000 | 100001 | 100002 | 100003 | 100006 | 100007 | 100008 | 100009 | 100010;
10
+ Name: "Address change" | "Auto responder" | "DMARC Policy" | "DNS error" | "Hard bounce" | "ISP block" | "Invalid email address" | "Manually deactivated" | "Message delayed/Undeliverable" | "Open relay test" | "Processing failed" | "Registration not confirmed" | "SMTP API error" | "Soft bounce" | "Spam challenge verification" | "Spam complaint" | "Spam notification" | "Subscribe request" | "Template rendering failed" | "Unknown" | "Unsubscribe request" | "Virus notification";
11
+ Tag: string;
12
+ MessageID: string;
13
+ Metadata: Record<string, string>;
14
+ ServerID: number;
15
+ Description: string;
16
+ Details: string;
17
+ Email: string;
18
+ From: string | null;
19
+ BouncedAt: string;
20
+ DumpAvailable: boolean;
21
+ Inactive: boolean;
22
+ CanActivate: boolean;
23
+ Subject: string;
24
+ Content?: string;
25
+ };
26
+ /** docs/05 §2.3: one event per recipient. */
27
+ export declare function deliveryPayload({ message, recipient, deliveredAt, details, }: EventMap["delivered"]): {
28
+ RecordType: string;
29
+ MessageStream: string;
30
+ ServerID: number;
31
+ MessageID: string;
32
+ Recipient: string;
33
+ Tag: string;
34
+ DeliveredAt: string;
35
+ Details: string;
36
+ Metadata: Record<string, string>;
37
+ };
38
+ /** docs/05 §2.4. No `ServerID`. */
39
+ export declare function openPayload(open: OpenEvent): {
40
+ Client?: import("../state/types.ts").ClientInfo;
41
+ OS?: import("../state/types.ts").ClientInfo;
42
+ Platform?: "Desktop" | "Mobile" | "Unknown" | "WebMail";
43
+ UserAgent: string;
44
+ RecordType: string;
45
+ MessageStream: string;
46
+ FirstOpen: boolean;
47
+ ReadSeconds: number;
48
+ Geo?: import("../state/types.ts").Geo;
49
+ MessageID: string;
50
+ Metadata: Record<string, string>;
51
+ ReceivedAt: string;
52
+ Tag: string;
53
+ Recipient: string;
54
+ };
55
+ /** docs/05 §2.5. No `ServerID`. */
56
+ export declare function clickPayload(click: ClickEvent): {
57
+ Client?: import("../state/types.ts").ClientInfo;
58
+ OS?: import("../state/types.ts").ClientInfo;
59
+ Platform?: "Desktop" | "Mobile" | "Unknown" | "WebMail";
60
+ UserAgent: string;
61
+ RecordType: string;
62
+ MessageStream: string;
63
+ ClickLocation: "HTML" | "Text";
64
+ OriginalLink: string;
65
+ Geo?: import("../state/types.ts").Geo;
66
+ MessageID: string;
67
+ Metadata: Record<string, string>;
68
+ ReceivedAt: string;
69
+ Tag: string;
70
+ Recipient: string;
71
+ };
72
+ /** docs/05 §2.6. `MessageID`, `SuppressionReason` and `Tag` stay null where the change has none. */
73
+ export declare function subscriptionChangePayload(change: SubscriptionChange): {
74
+ RecordType: string;
75
+ MessageID: string | null;
76
+ ServerID: number;
77
+ MessageStream: string;
78
+ ChangedAt: string;
79
+ Recipient: string;
80
+ Origin: import("../state/types.ts").SuppressionOrigin;
81
+ SuppressSending: boolean;
82
+ SuppressionReason: import("../state/types.ts").SuppressionReason | null;
83
+ Tag: string | null;
84
+ Metadata: Record<string, string>;
85
+ };
86
+ /** docs/05 §2.7, §4.2. No `RecordType`. `RawEmail` only with the server's `RawEmailEnabled`. */
87
+ export declare function inboundPayload(message: InboundMessage, rawEmailEnabled: boolean): {
88
+ FromName: string;
89
+ MessageStream: string;
90
+ From: string;
91
+ FromFull: import("../state/types.ts").InboundAddress;
92
+ To: string;
93
+ ToFull: import("../state/types.ts").InboundAddress[];
94
+ Cc: string;
95
+ CcFull: import("../state/types.ts").InboundAddress[];
96
+ Bcc: string;
97
+ BccFull: import("../state/types.ts").InboundAddress[];
98
+ OriginalRecipient: string;
99
+ Subject: string;
100
+ MessageID: string;
101
+ ReplyTo: string;
102
+ MailboxHash: string;
103
+ Date: string;
104
+ TextBody: string;
105
+ HtmlBody: string;
106
+ StrippedTextReply: string;
107
+ Tag: string;
108
+ Headers: import("../state/types.ts").Header[];
109
+ Attachments: import("../state/types.ts").InboundAttachment[];
110
+ RawEmail?: string;
111
+ };
@@ -0,0 +1,128 @@
1
+ import { BOUNCE_TYPES, } from "../state/types.js";
2
+ import { formatTimestamp } from "../time.js";
3
+ // Webhook bodies per RecordType, keys in doc order (docs/05 §2). Dates use the `Z` form of the doc
4
+ // examples. A send without a tag gives `Tag: ""`, as inbound does (INFERRED).
5
+ const at = (instant) => formatTimestamp(instant, "utc");
6
+ const tag = (value) => value ?? "";
7
+ /** docs/05 §2.1, §2.2, §2.8. `Content` only when the hook asks for it. */
8
+ export function bouncePayload(recordType, bounce, includeContent) {
9
+ return {
10
+ RecordType: recordType,
11
+ MessageStream: bounce.MessageStream,
12
+ ID: bounce.ID,
13
+ Type: bounce.Type,
14
+ TypeCode: BOUNCE_TYPES[bounce.Type].TypeCode,
15
+ Name: BOUNCE_TYPES[bounce.Type].Name,
16
+ Tag: tag(bounce.Tag),
17
+ MessageID: bounce.MessageID,
18
+ Metadata: bounce.Metadata,
19
+ ServerID: bounce.ServerID,
20
+ Description: bounce.Description,
21
+ Details: bounce.Details,
22
+ Email: bounce.Email,
23
+ From: bounce.From,
24
+ BouncedAt: at(bounce.BouncedAt),
25
+ DumpAvailable: bounce.Content !== "",
26
+ Inactive: bounce.Inactive,
27
+ CanActivate: bounce.CanActivate,
28
+ Subject: bounce.Subject,
29
+ ...(includeContent && { Content: bounce.Content }),
30
+ };
31
+ }
32
+ /** docs/05 §2.3: one event per recipient. */
33
+ export function deliveryPayload({ message, recipient, deliveredAt, details, }) {
34
+ return {
35
+ RecordType: "Delivery",
36
+ MessageStream: message.MessageStream,
37
+ ServerID: message.ServerID,
38
+ MessageID: message.MessageID,
39
+ Recipient: recipient,
40
+ Tag: tag(message.Tag),
41
+ DeliveredAt: at(deliveredAt),
42
+ Details: details,
43
+ Metadata: message.Metadata,
44
+ };
45
+ }
46
+ /** Client, OS, Platform and Geo are left out when unknown (docs/05 §2.4). */
47
+ const reader = (e) => ({
48
+ ...(e.Client !== null && { Client: e.Client }),
49
+ ...(e.OS !== null && { OS: e.OS }),
50
+ ...(e.Platform !== null && { Platform: e.Platform }),
51
+ UserAgent: e.UserAgent,
52
+ });
53
+ /** docs/05 §2.4. No `ServerID`. */
54
+ export function openPayload(open) {
55
+ return {
56
+ RecordType: "Open",
57
+ MessageStream: open.MessageStream,
58
+ FirstOpen: open.FirstOpen,
59
+ ...reader(open),
60
+ ReadSeconds: open.ReadSeconds,
61
+ ...(open.Geo !== null && { Geo: open.Geo }),
62
+ MessageID: open.MessageID,
63
+ Metadata: open.Metadata,
64
+ ReceivedAt: at(open.ReceivedAt),
65
+ Tag: tag(open.Tag),
66
+ Recipient: open.Recipient,
67
+ };
68
+ }
69
+ /** docs/05 §2.5. No `ServerID`. */
70
+ export function clickPayload(click) {
71
+ return {
72
+ RecordType: "Click",
73
+ MessageStream: click.MessageStream,
74
+ ClickLocation: click.ClickLocation,
75
+ ...reader(click),
76
+ OriginalLink: click.OriginalLink,
77
+ ...(click.Geo !== null && { Geo: click.Geo }),
78
+ MessageID: click.MessageID,
79
+ Metadata: click.Metadata,
80
+ ReceivedAt: at(click.ReceivedAt),
81
+ Tag: tag(click.Tag),
82
+ Recipient: click.Recipient,
83
+ };
84
+ }
85
+ /** docs/05 §2.6. `MessageID`, `SuppressionReason` and `Tag` stay null where the change has none. */
86
+ export function subscriptionChangePayload(change) {
87
+ return {
88
+ RecordType: "SubscriptionChange",
89
+ MessageID: change.MessageID,
90
+ ServerID: change.ServerID,
91
+ MessageStream: change.MessageStream,
92
+ ChangedAt: at(change.ChangedAt),
93
+ Recipient: change.Recipient,
94
+ Origin: change.Origin,
95
+ SuppressSending: change.SuppressSending,
96
+ SuppressionReason: change.SuppressionReason,
97
+ Tag: change.Tag,
98
+ Metadata: change.Metadata,
99
+ };
100
+ }
101
+ /** docs/05 §2.7, §4.2. No `RecordType`. `RawEmail` only with the server's `RawEmailEnabled`. */
102
+ export function inboundPayload(message, rawEmailEnabled) {
103
+ return {
104
+ FromName: message.FromName,
105
+ MessageStream: message.MessageStream,
106
+ From: message.From,
107
+ FromFull: message.FromFull,
108
+ To: message.To,
109
+ ToFull: message.ToFull,
110
+ Cc: message.Cc,
111
+ CcFull: message.CcFull,
112
+ Bcc: message.Bcc,
113
+ BccFull: message.BccFull,
114
+ OriginalRecipient: message.OriginalRecipient,
115
+ Subject: message.Subject,
116
+ MessageID: message.MessageID,
117
+ ReplyTo: message.ReplyTo,
118
+ MailboxHash: message.MailboxHash,
119
+ Date: message.Date,
120
+ TextBody: message.TextBody,
121
+ HtmlBody: message.HtmlBody,
122
+ StrippedTextReply: message.StrippedTextReply,
123
+ Tag: message.Tag,
124
+ Headers: message.Headers,
125
+ Attachments: message.Attachments,
126
+ ...(rawEmailEnabled && { RawEmail: message.rawEmail }),
127
+ };
128
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@benclmnt/postmock",
3
+ "version": "0.0.1",
4
+ "description": "A mock Postmark server for testing code that sends email through Postmark",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "bin": {
11
+ "postmock": "dist/src/main.js"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/src/server.d.ts",
16
+ "default": "./dist/src/server.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "engines": {
23
+ "node": ">=24"
24
+ },
25
+ "packageManager": "pnpm@11.22.0",
26
+ "scripts": {
27
+ "start": "node src/main.ts",
28
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
29
+ "prepack": "npm run build",
30
+ "check": "biome check . && tsc --noEmit && vitest run",
31
+ "test": "vitest run",
32
+ "conformance": "node conformance/run.ts",
33
+ "conformance:check": "node conformance/check.ts",
34
+ "compat-table": "node tools/compat-table.ts"
35
+ },
36
+ "dependencies": {
37
+ "@hono/node-server": "^2.1.1",
38
+ "hono": "^4.13.8",
39
+ "libmime": "5.4.4",
40
+ "mailparser": "^3.9.28",
41
+ "smtp-server": "^3.19.13",
42
+ "zod": "^4.6.5"
43
+ },
44
+ "devDependencies": {
45
+ "@biomejs/biome": "^2.5.13",
46
+ "@types/libmime": "^5.3.0",
47
+ "@types/mailparser": "^3.4.6",
48
+ "@types/node": "^24.13.4",
49
+ "@types/nodemailer": "^8.0.1",
50
+ "@types/smtp-server": "^3.5.13",
51
+ "nodemailer": "^9.1.1",
52
+ "typescript": "^7.0.2",
53
+ "vitest": "^5.0.1"
54
+ }
55
+ }