@vllnt/convex-email 0.1.0-canary.63aca6b

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 (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +171 -0
  3. package/dist/client/index.d.ts +174 -0
  4. package/dist/client/index.d.ts.map +1 -0
  5. package/dist/client/index.js +151 -0
  6. package/dist/client/index.js.map +1 -0
  7. package/dist/client/types.d.ts +84 -0
  8. package/dist/client/types.d.ts.map +1 -0
  9. package/dist/client/types.js +3 -0
  10. package/dist/client/types.js.map +1 -0
  11. package/dist/component/_generated/api.d.ts +40 -0
  12. package/dist/component/_generated/api.d.ts.map +1 -0
  13. package/dist/component/_generated/api.js +31 -0
  14. package/dist/component/_generated/api.js.map +1 -0
  15. package/dist/component/_generated/component.d.ts +110 -0
  16. package/dist/component/_generated/component.d.ts.map +1 -0
  17. package/dist/component/_generated/component.js +11 -0
  18. package/dist/component/_generated/component.js.map +1 -0
  19. package/dist/component/_generated/dataModel.d.ts +46 -0
  20. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  21. package/dist/component/_generated/dataModel.js +11 -0
  22. package/dist/component/_generated/dataModel.js.map +1 -0
  23. package/dist/component/_generated/server.d.ts +121 -0
  24. package/dist/component/_generated/server.d.ts.map +1 -0
  25. package/dist/component/_generated/server.js +78 -0
  26. package/dist/component/_generated/server.js.map +1 -0
  27. package/dist/component/convex.config.d.ts +3 -0
  28. package/dist/component/convex.config.d.ts.map +1 -0
  29. package/dist/component/convex.config.js +7 -0
  30. package/dist/component/convex.config.js.map +1 -0
  31. package/dist/component/crons.d.ts +16 -0
  32. package/dist/component/crons.d.ts.map +1 -0
  33. package/dist/component/crons.js +19 -0
  34. package/dist/component/crons.js.map +1 -0
  35. package/dist/component/mutations.d.ts +95 -0
  36. package/dist/component/mutations.d.ts.map +1 -0
  37. package/dist/component/mutations.js +243 -0
  38. package/dist/component/mutations.js.map +1 -0
  39. package/dist/component/queries.d.ts +59 -0
  40. package/dist/component/queries.d.ts.map +1 -0
  41. package/dist/component/queries.js +61 -0
  42. package/dist/component/queries.js.map +1 -0
  43. package/dist/component/schema.d.ts +54 -0
  44. package/dist/component/schema.d.ts.map +1 -0
  45. package/dist/component/schema.js +40 -0
  46. package/dist/component/schema.js.map +1 -0
  47. package/dist/component/validators.d.ts +54 -0
  48. package/dist/component/validators.d.ts.map +1 -0
  49. package/dist/component/validators.js +40 -0
  50. package/dist/component/validators.js.map +1 -0
  51. package/dist/jmap/index.d.ts +22 -0
  52. package/dist/jmap/index.d.ts.map +1 -0
  53. package/dist/jmap/index.js +21 -0
  54. package/dist/jmap/index.js.map +1 -0
  55. package/dist/jmap/send.d.ts +125 -0
  56. package/dist/jmap/send.d.ts.map +1 -0
  57. package/dist/jmap/send.js +418 -0
  58. package/dist/jmap/send.js.map +1 -0
  59. package/dist/jmap/types.d.ts +107 -0
  60. package/dist/jmap/types.d.ts.map +1 -0
  61. package/dist/jmap/types.js +16 -0
  62. package/dist/jmap/types.js.map +1 -0
  63. package/dist/shared.d.ts +32 -0
  64. package/dist/shared.d.ts.map +1 -0
  65. package/dist/shared.js +33 -0
  66. package/dist/shared.js.map +1 -0
  67. package/dist/smtp/index.d.ts +22 -0
  68. package/dist/smtp/index.d.ts.map +1 -0
  69. package/dist/smtp/index.js +21 -0
  70. package/dist/smtp/index.js.map +1 -0
  71. package/dist/smtp/send.d.ts +51 -0
  72. package/dist/smtp/send.d.ts.map +1 -0
  73. package/dist/smtp/send.js +124 -0
  74. package/dist/smtp/send.js.map +1 -0
  75. package/dist/smtp/transport.d.ts +43 -0
  76. package/dist/smtp/transport.d.ts.map +1 -0
  77. package/dist/smtp/transport.js +55 -0
  78. package/dist/smtp/transport.js.map +1 -0
  79. package/dist/smtp/types.d.ts +122 -0
  80. package/dist/smtp/types.d.ts.map +1 -0
  81. package/dist/smtp/types.js +9 -0
  82. package/dist/smtp/types.js.map +1 -0
  83. package/package.json +118 -0
  84. package/src/client/index.ts +312 -0
  85. package/src/client/types.ts +90 -0
  86. package/src/component/_generated/api.ts +56 -0
  87. package/src/component/_generated/component.ts +134 -0
  88. package/src/component/_generated/dataModel.ts +60 -0
  89. package/src/component/_generated/server.ts +156 -0
  90. package/src/component/convex.config.ts +9 -0
  91. package/src/component/crons.ts +23 -0
  92. package/src/component/mutations.ts +262 -0
  93. package/src/component/queries.ts +70 -0
  94. package/src/component/schema.ts +40 -0
  95. package/src/component/validators.ts +47 -0
  96. package/src/jmap/index.ts +39 -0
  97. package/src/jmap/send.test.ts +565 -0
  98. package/src/jmap/send.ts +502 -0
  99. package/src/jmap/types.ts +117 -0
  100. package/src/shared.ts +41 -0
  101. package/src/smtp/index.ts +30 -0
  102. package/src/smtp/send.test.ts +240 -0
  103. package/src/smtp/send.ts +154 -0
  104. package/src/smtp/transport.ts +58 -0
  105. package/src/smtp/types.ts +124 -0
  106. package/src/test.ts +12 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/jmap/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Pure, injectable generic-JMAP send logic. Everything here is runtime-neutral
3
+ * and network-free: it drives an injected {@link JmapFetch} (the host's runtime
4
+ * `fetch`), so a fake gives full coverage with no socket. There is no Node-only
5
+ * piece — JMAP is plain HTTP — so the whole adapter is covered at 100%.
6
+ *
7
+ * Sending is the JMAP two-call batch (RFC 8621): `Email/set` creates the message
8
+ * in a mailbox, then `EmailSubmission/set` submits it. {@link discoverJmapSession}
9
+ * resolves the account / identity / mailbox ids from a JMAP session so a host need
10
+ * not hand-wire them.
11
+ */
12
+ import type { JmapConfig, JmapDiscoverOptions, JmapFetch, JmapMessage, JmapSendResult, JmapSender } from "./types.js";
13
+ /**
14
+ * Validate a host-supplied {@link JmapConfig}: every id must be a non-empty
15
+ * string. Throws a plain `Error` on an invalid config — the host surfaces it.
16
+ * Pure: no I/O.
17
+ *
18
+ * @param config - The resolved JMAP connection config.
19
+ * @returns The same config, unchanged.
20
+ */
21
+ export declare function validateJmapConfig(config: JmapConfig): JmapConfig;
22
+ /** The JMAP `Email` create object plus the submission envelope for one message. */
23
+ interface EmailCreate {
24
+ /** The JMAP `Email/set` create object. */
25
+ email: Record<string, unknown>;
26
+ /** The `EmailSubmission` envelope (`mailFrom` + `rcptTo`). */
27
+ envelope: {
28
+ mailFrom: {
29
+ email: string;
30
+ };
31
+ rcptTo: Array<{
32
+ email: string;
33
+ }>;
34
+ };
35
+ }
36
+ /**
37
+ * Build the JMAP `Email` create object and the submission envelope from a
38
+ * {@link JmapMessage}: resolve `from` (message → `config.from`), require one of
39
+ * `text`/`html`, and reject CR/LF in `to`/`from`/`replyTo`/`subject`/`headers`.
40
+ * Pure.
41
+ *
42
+ * @param message - The outbound message.
43
+ * @param config - The resolved config (for the `from` default + the mailbox).
44
+ * @returns The `Email` create object and the submission envelope.
45
+ */
46
+ export declare function buildEmailCreate(message: JmapMessage, config: Pick<JmapConfig, "from" | "mailboxId">): EmailCreate;
47
+ /**
48
+ * Build the full JMAP request body for sending one {@link JmapMessage}: an
49
+ * `Email/set` create (creation id `draft`) followed by an `EmailSubmission/set`
50
+ * that back-references it (`#draft`). Pure.
51
+ *
52
+ * @param message - The message to send.
53
+ * @param config - The resolved JMAP config.
54
+ * @returns The JMAP request body to POST to the endpoint.
55
+ */
56
+ export declare function buildSubmitRequest(message: JmapMessage, config: JmapConfig): {
57
+ using: string[];
58
+ methodCalls: unknown[];
59
+ };
60
+ /**
61
+ * Parse a JMAP send response into a {@link JmapSendResult}: reject method-level
62
+ * errors and `notCreated` entries, then read the created `Email` id and the
63
+ * `EmailSubmission` id. Throws on any failure (the host catches it and calls
64
+ * `markFailed`). Pure.
65
+ *
66
+ * @param json - The parsed JMAP response body.
67
+ * @returns The created email id and submission id (the latter as `messageId`).
68
+ */
69
+ export declare function parseSubmitResponse(json: unknown): JmapSendResult;
70
+ /**
71
+ * Send one {@link JmapMessage} through an injected {@link JmapFetch} and return a
72
+ * normalized {@link JmapSendResult}. Validates the config, builds the two-call
73
+ * JMAP batch, POSTs it, and parses the result. Throws on an HTTP or JMAP error
74
+ * (the host catches it and calls `markFailed`). Pure: pass the host's runtime
75
+ * `fetch` in production, or a fake in a unit test.
76
+ *
77
+ * @param fetchFn - The injected `fetch` (the host's runtime `fetch`, or a fake).
78
+ * @param message - The message to send.
79
+ * @param config - The resolved JMAP config.
80
+ * @returns The normalized send result — store `messageId` as the queue `providerId`.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * // host Convex action (no "use node" — fetch runs in V8):
85
+ * const { messageId } = await sendViaJmap((u, i) => fetch(u, i), { to, html }, config);
86
+ * await email.markSent(ctx, id, { providerId: messageId });
87
+ * ```
88
+ */
89
+ export declare function sendViaJmap(fetchFn: JmapFetch, message: JmapMessage, config: JmapConfig): Promise<JmapSendResult>;
90
+ /**
91
+ * Resolve a {@link JmapConfig} from a JMAP session: fetch the session resource
92
+ * for the `apiUrl` + primary mail account, then `Identity/get` for the sending
93
+ * identity and `Mailbox/get` for the Sent (or Drafts) mailbox. Pure: drives the
94
+ * injected {@link JmapFetch}. The host runs this once and passes the result to
95
+ * {@link createJmapSender} / {@link sendViaJmap}.
96
+ *
97
+ * @param fetchFn - The injected `fetch`.
98
+ * @param opts - The session URL, bearer token, and optional preferred `from`.
99
+ * @returns A resolved {@link JmapConfig} ready to send with.
100
+ */
101
+ export declare function discoverJmapSession(fetchFn: JmapFetch, opts: JmapDiscoverOptions): Promise<JmapConfig>;
102
+ /**
103
+ * Build a bound {@link JmapSender} over a resolved config and an injected
104
+ * `fetch`: a function that sends one message and returns the normalized result.
105
+ * Validates the config eagerly. This is the one call a host wires into its flush
106
+ * action (a plain Convex action — no `"use node"`).
107
+ *
108
+ * @param config - The resolved JMAP config (e.g. from {@link discoverJmapSession}).
109
+ * @param fetchFn - The host's runtime `fetch`.
110
+ * @returns A sender that dispatches one {@link JmapMessage} via the configured server.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * const config = await discoverJmapSession((u, i) => fetch(u, i), {
115
+ * sessionUrl: "https://mail.example.com/.well-known/jmap",
116
+ * token: process.env.JMAP_TOKEN!,
117
+ * from: "no-reply@app.com",
118
+ * });
119
+ * const send = createJmapSender(config, (u, i) => fetch(u, i));
120
+ * const { messageId } = await send({ to, subject, html });
121
+ * ```
122
+ */
123
+ export declare function createJmapSender(config: JmapConfig, fetchFn: JmapFetch): JmapSender;
124
+ export {};
125
+ //# sourceMappingURL=send.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../src/jmap/send.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,mBAAmB,EACnB,SAAS,EACT,WAAW,EACX,cAAc,EACd,UAAU,EACX,MAAM,YAAY,CAAC;AA8BpB;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAejE;AAED,mFAAmF;AACnF,UAAU,WAAW;IACnB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,8DAA8D;IAC9D,QAAQ,EAAE;QACR,QAAQ,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5B,MAAM,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAClC,CAAC;CACH;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAAC,GAC7C,WAAW,CAiFb;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,GACjB;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,OAAO,EAAE,CAAA;CAAE,CAsB7C;AA4CD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAkBjE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,cAAc,CAAC,CAgBzB;AAyED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,SAAS,EAClB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,UAAU,CAAC,CA0DrB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,SAAS,GACjB,UAAU,CAGZ"}
@@ -0,0 +1,418 @@
1
+ /**
2
+ * Pure, injectable generic-JMAP send logic. Everything here is runtime-neutral
3
+ * and network-free: it drives an injected {@link JmapFetch} (the host's runtime
4
+ * `fetch`), so a fake gives full coverage with no socket. There is no Node-only
5
+ * piece — JMAP is plain HTTP — so the whole adapter is covered at 100%.
6
+ *
7
+ * Sending is the JMAP two-call batch (RFC 8621): `Email/set` creates the message
8
+ * in a mailbox, then `EmailSubmission/set` submits it. {@link discoverJmapSession}
9
+ * resolves the account / identity / mailbox ids from a JMAP session so a host need
10
+ * not hand-wire them.
11
+ */
12
+ /** A control character (CR/LF) that must never reach a header value. */
13
+ const CRLF = /[\r\n]/;
14
+ /** The JMAP capability URNs the adapter uses. */
15
+ const CAP_CORE = "urn:ietf:params:jmap:core";
16
+ const CAP_MAIL = "urn:ietf:params:jmap:mail";
17
+ const CAP_SUBMISSION = "urn:ietf:params:jmap:submission";
18
+ /** Narrow an unknown value to a plain object without a type assertion. */
19
+ function isRecord(value) {
20
+ return typeof value === "object" && value !== null;
21
+ }
22
+ /** Guard a single header-bound value against CR/LF injection. */
23
+ function assertNoCrlf(label, value) {
24
+ if (CRLF.test(value)) {
25
+ throw new Error(`jmap message: \`${label}\` must not contain CR or LF`);
26
+ }
27
+ }
28
+ /** Split a comma-separated address list into trimmed, non-empty addresses. */
29
+ function splitAddresses(raw) {
30
+ return raw
31
+ .split(",")
32
+ .map((a) => a.trim())
33
+ .filter((a) => a.length > 0);
34
+ }
35
+ /**
36
+ * Validate a host-supplied {@link JmapConfig}: every id must be a non-empty
37
+ * string. Throws a plain `Error` on an invalid config — the host surfaces it.
38
+ * Pure: no I/O.
39
+ *
40
+ * @param config - The resolved JMAP connection config.
41
+ * @returns The same config, unchanged.
42
+ */
43
+ export function validateJmapConfig(config) {
44
+ const keys = [
45
+ "endpoint",
46
+ "token",
47
+ "accountId",
48
+ "identityId",
49
+ "mailboxId",
50
+ ];
51
+ for (const key of keys) {
52
+ const value = config[key];
53
+ if (typeof value !== "string" || value.trim() === "") {
54
+ throw new Error(`jmap config: \`${key}\` must be a non-empty string`);
55
+ }
56
+ }
57
+ return config;
58
+ }
59
+ /**
60
+ * Build the JMAP `Email` create object and the submission envelope from a
61
+ * {@link JmapMessage}: resolve `from` (message → `config.from`), require one of
62
+ * `text`/`html`, and reject CR/LF in `to`/`from`/`replyTo`/`subject`/`headers`.
63
+ * Pure.
64
+ *
65
+ * @param message - The outbound message.
66
+ * @param config - The resolved config (for the `from` default + the mailbox).
67
+ * @returns The `Email` create object and the submission envelope.
68
+ */
69
+ export function buildEmailCreate(message, config) {
70
+ if (typeof message.to !== "string" || message.to.trim() === "") {
71
+ throw new Error("jmap message: `to` must be a non-empty string");
72
+ }
73
+ const from = message.from ?? config.from;
74
+ if (from === undefined || from.trim() === "") {
75
+ throw new Error("jmap message: `from` is required (pass `message.from` or `config.from`)");
76
+ }
77
+ if (message.text === undefined && message.html === undefined) {
78
+ throw new Error("jmap message: one of `text` or `html` is required");
79
+ }
80
+ const recipients = splitAddresses(message.to);
81
+ if (recipients.length === 0) {
82
+ throw new Error("jmap message: `to` must contain at least one address");
83
+ }
84
+ assertNoCrlf("from", from);
85
+ for (const rcpt of recipients) {
86
+ assertNoCrlf("to", rcpt);
87
+ }
88
+ if (message.replyTo !== undefined) {
89
+ assertNoCrlf("replyTo", message.replyTo);
90
+ }
91
+ if (message.subject !== undefined) {
92
+ assertNoCrlf("subject", message.subject);
93
+ }
94
+ let body;
95
+ if (message.text !== undefined && message.html !== undefined) {
96
+ body = {
97
+ bodyStructure: {
98
+ type: "multipart/alternative",
99
+ subParts: [
100
+ { type: "text/plain", partId: "text" },
101
+ { type: "text/html", partId: "html" },
102
+ ],
103
+ },
104
+ bodyValues: {
105
+ text: { value: message.text },
106
+ html: { value: message.html },
107
+ },
108
+ };
109
+ }
110
+ else if (message.html !== undefined) {
111
+ body = {
112
+ bodyStructure: { type: "text/html", partId: "html" },
113
+ bodyValues: { html: { value: message.html } },
114
+ };
115
+ }
116
+ else {
117
+ body = {
118
+ bodyStructure: { type: "text/plain", partId: "text" },
119
+ bodyValues: { text: { value: message.text } },
120
+ };
121
+ }
122
+ const email = {
123
+ mailboxIds: { [config.mailboxId]: true },
124
+ keywords: { $seen: true },
125
+ from: [{ email: from }],
126
+ to: recipients.map((email) => ({ email })),
127
+ ...(message.subject !== undefined ? { subject: message.subject } : {}),
128
+ ...(message.replyTo !== undefined
129
+ ? { replyTo: [{ email: message.replyTo }] }
130
+ : {}),
131
+ ...body,
132
+ };
133
+ if (message.headers !== undefined) {
134
+ for (const [key, value] of Object.entries(message.headers)) {
135
+ assertNoCrlf(`headers.${key}`, key);
136
+ assertNoCrlf(`headers.${key}`, value);
137
+ email[`header:${key}:asText`] = value;
138
+ }
139
+ }
140
+ return {
141
+ email,
142
+ envelope: {
143
+ mailFrom: { email: from },
144
+ rcptTo: recipients.map((email) => ({ email })),
145
+ },
146
+ };
147
+ }
148
+ /**
149
+ * Build the full JMAP request body for sending one {@link JmapMessage}: an
150
+ * `Email/set` create (creation id `draft`) followed by an `EmailSubmission/set`
151
+ * that back-references it (`#draft`). Pure.
152
+ *
153
+ * @param message - The message to send.
154
+ * @param config - The resolved JMAP config.
155
+ * @returns The JMAP request body to POST to the endpoint.
156
+ */
157
+ export function buildSubmitRequest(message, config) {
158
+ const { email, envelope } = buildEmailCreate(message, config);
159
+ return {
160
+ using: [CAP_CORE, CAP_MAIL, CAP_SUBMISSION],
161
+ methodCalls: [
162
+ ["Email/set", { accountId: config.accountId, create: { draft: email } }, "0"],
163
+ [
164
+ "EmailSubmission/set",
165
+ {
166
+ accountId: config.accountId,
167
+ create: {
168
+ sub: {
169
+ emailId: "#draft",
170
+ identityId: config.identityId,
171
+ envelope,
172
+ },
173
+ },
174
+ },
175
+ "1",
176
+ ],
177
+ ],
178
+ };
179
+ }
180
+ /** Find the arguments of the first method response with the given name. */
181
+ function findInvocation(responses, name) {
182
+ for (const inv of responses) {
183
+ if (Array.isArray(inv) && inv[0] === name && isRecord(inv[1])) {
184
+ return inv[1];
185
+ }
186
+ }
187
+ return null;
188
+ }
189
+ /** Describe a JMAP `SetError` (its `type`, or `"unknown"`). */
190
+ function describeSetError(value) {
191
+ return isRecord(value) && typeof value.type === "string"
192
+ ? value.type
193
+ : "unknown";
194
+ }
195
+ /** Read a created object's id from a `Foo/set` response, throwing on `notCreated`. */
196
+ function readCreatedId(args, createId, label) {
197
+ const notCreated = args.notCreated;
198
+ if (isRecord(notCreated) && createId in notCreated) {
199
+ throw new Error(`jmap: ${label} not created (${describeSetError(notCreated[createId])})`);
200
+ }
201
+ const created = args.created;
202
+ if (isRecord(created)) {
203
+ const entry = created[createId];
204
+ if (isRecord(entry) && typeof entry.id === "string") {
205
+ return entry.id;
206
+ }
207
+ }
208
+ throw new Error(`jmap: ${label} not created (no id in response)`);
209
+ }
210
+ /**
211
+ * Parse a JMAP send response into a {@link JmapSendResult}: reject method-level
212
+ * errors and `notCreated` entries, then read the created `Email` id and the
213
+ * `EmailSubmission` id. Throws on any failure (the host catches it and calls
214
+ * `markFailed`). Pure.
215
+ *
216
+ * @param json - The parsed JMAP response body.
217
+ * @returns The created email id and submission id (the latter as `messageId`).
218
+ */
219
+ export function parseSubmitResponse(json) {
220
+ if (!isRecord(json) || !Array.isArray(json.methodResponses)) {
221
+ throw new Error("jmap: malformed response (no methodResponses)");
222
+ }
223
+ const responses = json.methodResponses;
224
+ for (const inv of responses) {
225
+ if (Array.isArray(inv) && inv[0] === "error") {
226
+ throw new Error(`jmap: method error (${describeSetError(inv[1])})`);
227
+ }
228
+ }
229
+ const emailArgs = findInvocation(responses, "Email/set");
230
+ const subArgs = findInvocation(responses, "EmailSubmission/set");
231
+ if (emailArgs === null || subArgs === null) {
232
+ throw new Error("jmap: response missing Email/set or EmailSubmission/set");
233
+ }
234
+ const emailId = readCreatedId(emailArgs, "draft", "Email");
235
+ const messageId = readCreatedId(subArgs, "sub", "EmailSubmission");
236
+ return { messageId, emailId };
237
+ }
238
+ /**
239
+ * Send one {@link JmapMessage} through an injected {@link JmapFetch} and return a
240
+ * normalized {@link JmapSendResult}. Validates the config, builds the two-call
241
+ * JMAP batch, POSTs it, and parses the result. Throws on an HTTP or JMAP error
242
+ * (the host catches it and calls `markFailed`). Pure: pass the host's runtime
243
+ * `fetch` in production, or a fake in a unit test.
244
+ *
245
+ * @param fetchFn - The injected `fetch` (the host's runtime `fetch`, or a fake).
246
+ * @param message - The message to send.
247
+ * @param config - The resolved JMAP config.
248
+ * @returns The normalized send result — store `messageId` as the queue `providerId`.
249
+ *
250
+ * @example
251
+ * ```ts
252
+ * // host Convex action (no "use node" — fetch runs in V8):
253
+ * const { messageId } = await sendViaJmap((u, i) => fetch(u, i), { to, html }, config);
254
+ * await email.markSent(ctx, id, { providerId: messageId });
255
+ * ```
256
+ */
257
+ export async function sendViaJmap(fetchFn, message, config) {
258
+ validateJmapConfig(config);
259
+ const body = buildSubmitRequest(message, config);
260
+ const res = await fetchFn(config.endpoint, {
261
+ method: "POST",
262
+ headers: {
263
+ Authorization: `Bearer ${config.token}`,
264
+ "Content-Type": "application/json",
265
+ Accept: "application/json",
266
+ },
267
+ body: JSON.stringify(body),
268
+ });
269
+ if (!res.ok) {
270
+ throw new Error(`jmap: request failed (HTTP ${res.status})`);
271
+ }
272
+ return parseSubmitResponse(await res.json());
273
+ }
274
+ /** POST a JMAP request and return its `methodResponses`, throwing on HTTP/shape errors. */
275
+ async function jmapCall(fetchFn, endpoint, token, request) {
276
+ const res = await fetchFn(endpoint, {
277
+ method: "POST",
278
+ headers: {
279
+ Authorization: `Bearer ${token}`,
280
+ "Content-Type": "application/json",
281
+ Accept: "application/json",
282
+ },
283
+ body: JSON.stringify(request),
284
+ });
285
+ if (!res.ok) {
286
+ throw new Error(`jmap: request failed (HTTP ${res.status})`);
287
+ }
288
+ const json = await res.json();
289
+ if (!isRecord(json) || !Array.isArray(json.methodResponses)) {
290
+ throw new Error("jmap: malformed response (no methodResponses)");
291
+ }
292
+ return json.methodResponses;
293
+ }
294
+ /** Pick the sending identity (by `from`, else the first) from an `Identity/get` list. */
295
+ function pickIdentity(list, from) {
296
+ const valid = [];
297
+ for (const item of list) {
298
+ if (isRecord(item) &&
299
+ typeof item.id === "string" &&
300
+ typeof item.email === "string") {
301
+ valid.push({ id: item.id, email: item.email });
302
+ }
303
+ }
304
+ const lower = from?.toLowerCase();
305
+ const matched = lower !== undefined
306
+ ? valid.find((i) => i.email.toLowerCase() === lower)
307
+ : undefined;
308
+ return matched ?? valid[0] ?? null;
309
+ }
310
+ /** Pick the mailbox to file the sent copy in: role `sent`, else `drafts`, else first. */
311
+ function pickMailboxId(list) {
312
+ const valid = [];
313
+ for (const item of list) {
314
+ if (isRecord(item) && typeof item.id === "string") {
315
+ valid.push({
316
+ id: item.id,
317
+ role: typeof item.role === "string" ? item.role : null,
318
+ });
319
+ }
320
+ }
321
+ const sent = valid.find((m) => m.role === "sent");
322
+ if (sent !== undefined) {
323
+ return sent.id;
324
+ }
325
+ const drafts = valid.find((m) => m.role === "drafts");
326
+ if (drafts !== undefined) {
327
+ return drafts.id;
328
+ }
329
+ return valid[0]?.id ?? null;
330
+ }
331
+ /**
332
+ * Resolve a {@link JmapConfig} from a JMAP session: fetch the session resource
333
+ * for the `apiUrl` + primary mail account, then `Identity/get` for the sending
334
+ * identity and `Mailbox/get` for the Sent (or Drafts) mailbox. Pure: drives the
335
+ * injected {@link JmapFetch}. The host runs this once and passes the result to
336
+ * {@link createJmapSender} / {@link sendViaJmap}.
337
+ *
338
+ * @param fetchFn - The injected `fetch`.
339
+ * @param opts - The session URL, bearer token, and optional preferred `from`.
340
+ * @returns A resolved {@link JmapConfig} ready to send with.
341
+ */
342
+ export async function discoverJmapSession(fetchFn, opts) {
343
+ if (typeof opts.sessionUrl !== "string" || opts.sessionUrl.trim() === "") {
344
+ throw new Error("jmap discover: `sessionUrl` must be a non-empty string");
345
+ }
346
+ if (typeof opts.token !== "string" || opts.token.trim() === "") {
347
+ throw new Error("jmap discover: `token` must be a non-empty string");
348
+ }
349
+ const sres = await fetchFn(opts.sessionUrl, {
350
+ method: "GET",
351
+ headers: { Authorization: `Bearer ${opts.token}`, Accept: "application/json" },
352
+ });
353
+ if (!sres.ok) {
354
+ throw new Error(`jmap discover: session request failed (HTTP ${sres.status})`);
355
+ }
356
+ const session = await sres.json();
357
+ if (!isRecord(session) || typeof session.apiUrl !== "string") {
358
+ throw new Error("jmap discover: session has no apiUrl");
359
+ }
360
+ const accounts = session.primaryAccounts;
361
+ const accountId = isRecord(accounts) ? accounts[CAP_MAIL] : undefined;
362
+ if (typeof accountId !== "string") {
363
+ throw new Error("jmap discover: no primary mail account");
364
+ }
365
+ const endpoint = session.apiUrl;
366
+ const idResponses = await jmapCall(fetchFn, endpoint, opts.token, {
367
+ using: [CAP_CORE, CAP_SUBMISSION],
368
+ methodCalls: [["Identity/get", { accountId, ids: null }, "0"]],
369
+ });
370
+ const idArgs = findInvocation(idResponses, "Identity/get");
371
+ const identity = pickIdentity(idArgs !== null && Array.isArray(idArgs.list) ? idArgs.list : [], opts.from);
372
+ if (identity === null) {
373
+ throw new Error("jmap discover: no sending identity found");
374
+ }
375
+ const mbResponses = await jmapCall(fetchFn, endpoint, opts.token, {
376
+ using: [CAP_CORE, CAP_MAIL],
377
+ methodCalls: [["Mailbox/get", { accountId, ids: null }, "0"]],
378
+ });
379
+ const mbArgs = findInvocation(mbResponses, "Mailbox/get");
380
+ const mailboxId = pickMailboxId(mbArgs !== null && Array.isArray(mbArgs.list) ? mbArgs.list : []);
381
+ if (mailboxId === null) {
382
+ throw new Error("jmap discover: no sent or drafts mailbox found");
383
+ }
384
+ return {
385
+ endpoint,
386
+ token: opts.token,
387
+ accountId,
388
+ identityId: identity.id,
389
+ mailboxId,
390
+ from: opts.from ?? identity.email,
391
+ };
392
+ }
393
+ /**
394
+ * Build a bound {@link JmapSender} over a resolved config and an injected
395
+ * `fetch`: a function that sends one message and returns the normalized result.
396
+ * Validates the config eagerly. This is the one call a host wires into its flush
397
+ * action (a plain Convex action — no `"use node"`).
398
+ *
399
+ * @param config - The resolved JMAP config (e.g. from {@link discoverJmapSession}).
400
+ * @param fetchFn - The host's runtime `fetch`.
401
+ * @returns A sender that dispatches one {@link JmapMessage} via the configured server.
402
+ *
403
+ * @example
404
+ * ```ts
405
+ * const config = await discoverJmapSession((u, i) => fetch(u, i), {
406
+ * sessionUrl: "https://mail.example.com/.well-known/jmap",
407
+ * token: process.env.JMAP_TOKEN!,
408
+ * from: "no-reply@app.com",
409
+ * });
410
+ * const send = createJmapSender(config, (u, i) => fetch(u, i));
411
+ * const { messageId } = await send({ to, subject, html });
412
+ * ```
413
+ */
414
+ export function createJmapSender(config, fetchFn) {
415
+ validateJmapConfig(config);
416
+ return (message) => sendViaJmap(fetchFn, message, config);
417
+ }
418
+ //# sourceMappingURL=send.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/jmap/send.ts"],"names":[],"mappings":"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"}