@upyo/jmap 0.5.0-dev.136 → 0.5.0-dev.156
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/dist/index.cjs +103 -37
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +81 -37
- package/package.json +3 -8
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
const __upyo_core = __toESM(require("@upyo/core"));
|
|
1
25
|
|
|
2
26
|
//#region src/errors.ts
|
|
3
27
|
/**
|
|
@@ -8,12 +32,16 @@ var JmapApiError = class extends Error {
|
|
|
8
32
|
statusCode;
|
|
9
33
|
responseBody;
|
|
10
34
|
jmapErrorType;
|
|
11
|
-
|
|
35
|
+
retryAfterMilliseconds;
|
|
36
|
+
attempts;
|
|
37
|
+
constructor(message, statusCode, responseBody, jmapErrorType, retryAfterMilliseconds, attempts) {
|
|
12
38
|
super(message);
|
|
13
39
|
this.name = "JmapApiError";
|
|
14
40
|
this.statusCode = statusCode;
|
|
15
41
|
this.responseBody = responseBody;
|
|
16
42
|
this.jmapErrorType = jmapErrorType;
|
|
43
|
+
this.retryAfterMilliseconds = retryAfterMilliseconds;
|
|
44
|
+
this.attempts = attempts;
|
|
17
45
|
}
|
|
18
46
|
};
|
|
19
47
|
/**
|
|
@@ -144,7 +172,7 @@ var JmapHttpClient = class {
|
|
|
144
172
|
const response = await this.fetchWithAuth(this.config.sessionUrl, { method: "GET" }, signal);
|
|
145
173
|
if (!response.ok) {
|
|
146
174
|
const text = await response.text();
|
|
147
|
-
throw new JmapApiError(`Session fetch failed: ${response.status}`, response.status, text);
|
|
175
|
+
throw new JmapApiError(`Session fetch failed: ${response.status}`, response.status, text, void 0, (0, __upyo_core.parseRetryAfter)(response.headers.get("Retry-After")), 1);
|
|
148
176
|
}
|
|
149
177
|
return await response.json();
|
|
150
178
|
}
|
|
@@ -169,7 +197,7 @@ var JmapHttpClient = class {
|
|
|
169
197
|
}, signal);
|
|
170
198
|
if (!response.ok) {
|
|
171
199
|
const text = await response.text();
|
|
172
|
-
const error = new JmapApiError(`JMAP request failed: ${response.status}`, response.status, text);
|
|
200
|
+
const error = new JmapApiError(`JMAP request failed: ${response.status}`, response.status, text, void 0, (0, __upyo_core.parseRetryAfter)(response.headers.get("Retry-After")), attempt + 1);
|
|
173
201
|
if (response.status >= 400 && response.status < 500) throw error;
|
|
174
202
|
throw error;
|
|
175
203
|
}
|
|
@@ -443,6 +471,7 @@ const JMAP_CAPABILITIES = {
|
|
|
443
471
|
* @since 0.4.0
|
|
444
472
|
*/
|
|
445
473
|
var JmapTransport = class {
|
|
474
|
+
id = "jmap";
|
|
446
475
|
config;
|
|
447
476
|
httpClient;
|
|
448
477
|
cachedSession = null;
|
|
@@ -469,10 +498,11 @@ var JmapTransport = class {
|
|
|
469
498
|
const session = await this.getSession(signal);
|
|
470
499
|
signal?.throwIfAborted();
|
|
471
500
|
const accountId = this.config.accountId ?? findMailAccount(session);
|
|
472
|
-
if (!accountId) return {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
501
|
+
if (!accountId) return createJmapFailure("No mail-capable account found in JMAP session", void 0, {
|
|
502
|
+
category: "configuration",
|
|
503
|
+
code: "jmap.no_mail_account",
|
|
504
|
+
retryable: false
|
|
505
|
+
});
|
|
476
506
|
const draftsMailboxId = await this.getDraftsMailboxId(session, accountId, signal);
|
|
477
507
|
signal?.throwIfAborted();
|
|
478
508
|
const identityId = await this.getIdentityId(session, accountId, message.sender.address, signal);
|
|
@@ -507,18 +537,16 @@ var JmapTransport = class {
|
|
|
507
537
|
}, signal);
|
|
508
538
|
return this.parseResponse(response);
|
|
509
539
|
} catch (error) {
|
|
510
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
}
|
|
518
|
-
return
|
|
519
|
-
|
|
520
|
-
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
521
|
-
};
|
|
540
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
541
|
+
if (signal?.aborted) throw getAbortReason(signal, error);
|
|
542
|
+
return createJmapFailure(`Request aborted: ${error.message}`, error, {
|
|
543
|
+
category: "timeout",
|
|
544
|
+
code: "abort",
|
|
545
|
+
retryable: true
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
if (error instanceof JmapApiError) return createJmapFailure(error.message, error);
|
|
549
|
+
return createJmapFailure(error instanceof Error ? error.message : String(error), error);
|
|
522
550
|
}
|
|
523
551
|
}
|
|
524
552
|
/**
|
|
@@ -543,10 +571,11 @@ var JmapTransport = class {
|
|
|
543
571
|
processingStage = "account discovery";
|
|
544
572
|
const accountId = this.config.accountId ?? findMailAccount(session);
|
|
545
573
|
if (!accountId) {
|
|
546
|
-
for (let i = 0; i < messageArray.length; i++) yield {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
574
|
+
for (let i = 0; i < messageArray.length; i++) yield createJmapFailure("No mail-capable account found in JMAP session", void 0, {
|
|
575
|
+
category: "configuration",
|
|
576
|
+
code: "jmap.no_mail_account",
|
|
577
|
+
retryable: false
|
|
578
|
+
});
|
|
550
579
|
return;
|
|
551
580
|
}
|
|
552
581
|
processingStage = "mailbox discovery";
|
|
@@ -604,13 +633,13 @@ var JmapTransport = class {
|
|
|
604
633
|
}, signal);
|
|
605
634
|
for (let i = 0; i < messageArray.length; i++) yield this.parseBatchResponseForIndex(response, i);
|
|
606
635
|
} catch (error) {
|
|
636
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
637
|
+
if (signal?.aborted) throw getAbortReason(signal, error);
|
|
638
|
+
}
|
|
607
639
|
const baseMessage = error instanceof Error && error.name === "AbortError" ? `Request aborted: ${error.message}` : error instanceof JmapApiError ? error.message : error instanceof Error ? error.message : String(error);
|
|
608
640
|
let detailedMessage = `Failed during ${processingStage}: ${baseMessage}`;
|
|
609
641
|
if (processingStage === "attachment upload" && attachmentsUploadedCount > 0) detailedMessage += ` (${attachmentsUploadedCount}/${messageArray.length} messages had attachments uploaded before failure)`;
|
|
610
|
-
for (let i = 0; i < messageArray.length; i++) yield
|
|
611
|
-
successful: false,
|
|
612
|
-
errorMessages: [detailedMessage]
|
|
613
|
-
};
|
|
642
|
+
for (let i = 0; i < messageArray.length; i++) yield createJmapFailure(detailedMessage, error);
|
|
614
643
|
}
|
|
615
644
|
}
|
|
616
645
|
/**
|
|
@@ -768,14 +797,17 @@ var JmapTransport = class {
|
|
|
768
797
|
if (submissionResult.notCreated) for (const [key, error] of Object.entries(submissionResult.notCreated)) errors.push(`Email submission failed (${key}): ${error.type}${error.description ? ` - ${error.description}` : ""}`);
|
|
769
798
|
if (submissionResult.created?.submission) return {
|
|
770
799
|
successful: true,
|
|
771
|
-
messageId: submissionResult.created.submission.id
|
|
800
|
+
messageId: submissionResult.created.submission.id,
|
|
801
|
+
provider: "jmap"
|
|
772
802
|
};
|
|
773
803
|
}
|
|
774
804
|
if (errors.length === 0) errors.push("Unknown error: No submission result received");
|
|
775
|
-
return {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
805
|
+
return (0, __upyo_core.createFailedReceipt)(errors, {
|
|
806
|
+
provider: "jmap",
|
|
807
|
+
category: "rejected",
|
|
808
|
+
code: "jmap.submission_failed",
|
|
809
|
+
retryable: false
|
|
810
|
+
});
|
|
779
811
|
}
|
|
780
812
|
/**
|
|
781
813
|
* Parses the JMAP batch response to extract receipt for a specific index.
|
|
@@ -805,16 +837,50 @@ var JmapTransport = class {
|
|
|
805
837
|
}
|
|
806
838
|
if (submissionResult.created?.[subKey]) return {
|
|
807
839
|
successful: true,
|
|
808
|
-
messageId: submissionResult.created[subKey].id
|
|
840
|
+
messageId: submissionResult.created[subKey].id,
|
|
841
|
+
provider: "jmap"
|
|
809
842
|
};
|
|
810
843
|
}
|
|
811
844
|
if (errors.length === 0) errors.push("Unknown error: No submission result received");
|
|
812
|
-
return {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
845
|
+
return (0, __upyo_core.createFailedReceipt)(errors, {
|
|
846
|
+
provider: "jmap",
|
|
847
|
+
category: "rejected",
|
|
848
|
+
code: "jmap.submission_failed",
|
|
849
|
+
retryable: false
|
|
850
|
+
});
|
|
816
851
|
}
|
|
817
852
|
};
|
|
853
|
+
function createJmapFailure(message, error, override = {}) {
|
|
854
|
+
const attempts = getAttemptCount(error);
|
|
855
|
+
if (error instanceof JmapApiError) return (0, __upyo_core.createFailedReceipt)(message, {
|
|
856
|
+
provider: "jmap",
|
|
857
|
+
statusCode: error.statusCode,
|
|
858
|
+
retryAfterMilliseconds: error.retryAfterMilliseconds,
|
|
859
|
+
attempts,
|
|
860
|
+
providerDetails: {
|
|
861
|
+
responseBody: error.responseBody,
|
|
862
|
+
jmapErrorType: error.jmapErrorType
|
|
863
|
+
},
|
|
864
|
+
category: override.category,
|
|
865
|
+
code: override.code,
|
|
866
|
+
retryable: override.retryable
|
|
867
|
+
});
|
|
868
|
+
return (0, __upyo_core.createFailedReceipt)(message, {
|
|
869
|
+
provider: "jmap",
|
|
870
|
+
attempts,
|
|
871
|
+
category: override.category,
|
|
872
|
+
code: override.code,
|
|
873
|
+
retryable: override.retryable
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
function getAttemptCount(error) {
|
|
877
|
+
if (error instanceof JmapApiError) return error.attempts ?? 1;
|
|
878
|
+
if (typeof error === "object" && error !== null && "attempts" in error && typeof error.attempts === "number") return error.attempts;
|
|
879
|
+
return 1;
|
|
880
|
+
}
|
|
881
|
+
function getAbortReason(signal, fallback) {
|
|
882
|
+
return signal.reason ?? fallback;
|
|
883
|
+
}
|
|
818
884
|
|
|
819
885
|
//#endregion
|
|
820
886
|
exports.JMAP_ERROR_TYPES = JMAP_ERROR_TYPES;
|
package/dist/index.d.cts
CHANGED
|
@@ -103,7 +103,8 @@ declare function createJmapConfig(config: JmapConfig): ResolvedJmapConfig;
|
|
|
103
103
|
* JMAP transport for sending emails via JMAP protocol (RFC 8620/8621).
|
|
104
104
|
* @since 0.4.0
|
|
105
105
|
*/
|
|
106
|
-
declare class JmapTransport implements Transport {
|
|
106
|
+
declare class JmapTransport implements Transport<"jmap"> {
|
|
107
|
+
readonly id = "jmap";
|
|
107
108
|
readonly config: ResolvedJmapConfig;
|
|
108
109
|
private readonly httpClient;
|
|
109
110
|
private cachedSession;
|
|
@@ -120,7 +121,7 @@ declare class JmapTransport implements Transport {
|
|
|
120
121
|
* @returns A receipt indicating success or failure.
|
|
121
122
|
* @since 0.4.0
|
|
122
123
|
*/
|
|
123
|
-
send(message: Message, options?: TransportOptions): Promise<Receipt
|
|
124
|
+
send(message: Message, options?: TransportOptions): Promise<Receipt<"jmap">>;
|
|
124
125
|
/**
|
|
125
126
|
* Sends multiple messages in a single batched JMAP request.
|
|
126
127
|
* @param messages The messages to send.
|
|
@@ -128,7 +129,7 @@ declare class JmapTransport implements Transport {
|
|
|
128
129
|
* @yields Receipts for each message.
|
|
129
130
|
* @since 0.4.0
|
|
130
131
|
*/
|
|
131
|
-
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt
|
|
132
|
+
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt<"jmap">>;
|
|
132
133
|
/**
|
|
133
134
|
* Gets or refreshes the JMAP session.
|
|
134
135
|
* @param signal Optional abort signal.
|
|
@@ -207,7 +208,9 @@ declare class JmapApiError extends Error {
|
|
|
207
208
|
readonly statusCode?: number;
|
|
208
209
|
readonly responseBody?: string;
|
|
209
210
|
readonly jmapErrorType?: string;
|
|
210
|
-
|
|
211
|
+
readonly retryAfterMilliseconds?: number;
|
|
212
|
+
readonly attempts?: number;
|
|
213
|
+
constructor(message: string, statusCode?: number, responseBody?: string, jmapErrorType?: string, retryAfterMilliseconds?: number, attempts?: number);
|
|
211
214
|
}
|
|
212
215
|
/**
|
|
213
216
|
* JMAP-specific error types from RFC 8620.
|
package/dist/index.d.ts
CHANGED
|
@@ -103,7 +103,8 @@ declare function createJmapConfig(config: JmapConfig): ResolvedJmapConfig;
|
|
|
103
103
|
* JMAP transport for sending emails via JMAP protocol (RFC 8620/8621).
|
|
104
104
|
* @since 0.4.0
|
|
105
105
|
*/
|
|
106
|
-
declare class JmapTransport implements Transport {
|
|
106
|
+
declare class JmapTransport implements Transport<"jmap"> {
|
|
107
|
+
readonly id = "jmap";
|
|
107
108
|
readonly config: ResolvedJmapConfig;
|
|
108
109
|
private readonly httpClient;
|
|
109
110
|
private cachedSession;
|
|
@@ -120,7 +121,7 @@ declare class JmapTransport implements Transport {
|
|
|
120
121
|
* @returns A receipt indicating success or failure.
|
|
121
122
|
* @since 0.4.0
|
|
122
123
|
*/
|
|
123
|
-
send(message: Message, options?: TransportOptions): Promise<Receipt
|
|
124
|
+
send(message: Message, options?: TransportOptions): Promise<Receipt<"jmap">>;
|
|
124
125
|
/**
|
|
125
126
|
* Sends multiple messages in a single batched JMAP request.
|
|
126
127
|
* @param messages The messages to send.
|
|
@@ -128,7 +129,7 @@ declare class JmapTransport implements Transport {
|
|
|
128
129
|
* @yields Receipts for each message.
|
|
129
130
|
* @since 0.4.0
|
|
130
131
|
*/
|
|
131
|
-
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt
|
|
132
|
+
sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt<"jmap">>;
|
|
132
133
|
/**
|
|
133
134
|
* Gets or refreshes the JMAP session.
|
|
134
135
|
* @param signal Optional abort signal.
|
|
@@ -207,7 +208,9 @@ declare class JmapApiError extends Error {
|
|
|
207
208
|
readonly statusCode?: number;
|
|
208
209
|
readonly responseBody?: string;
|
|
209
210
|
readonly jmapErrorType?: string;
|
|
210
|
-
|
|
211
|
+
readonly retryAfterMilliseconds?: number;
|
|
212
|
+
readonly attempts?: number;
|
|
213
|
+
constructor(message: string, statusCode?: number, responseBody?: string, jmapErrorType?: string, retryAfterMilliseconds?: number, attempts?: number);
|
|
211
214
|
}
|
|
212
215
|
/**
|
|
213
216
|
* JMAP-specific error types from RFC 8620.
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createFailedReceipt, parseRetryAfter } from "@upyo/core";
|
|
2
|
+
|
|
1
3
|
//#region src/errors.ts
|
|
2
4
|
/**
|
|
3
5
|
* Error class for JMAP API errors.
|
|
@@ -7,12 +9,16 @@ var JmapApiError = class extends Error {
|
|
|
7
9
|
statusCode;
|
|
8
10
|
responseBody;
|
|
9
11
|
jmapErrorType;
|
|
10
|
-
|
|
12
|
+
retryAfterMilliseconds;
|
|
13
|
+
attempts;
|
|
14
|
+
constructor(message, statusCode, responseBody, jmapErrorType, retryAfterMilliseconds, attempts) {
|
|
11
15
|
super(message);
|
|
12
16
|
this.name = "JmapApiError";
|
|
13
17
|
this.statusCode = statusCode;
|
|
14
18
|
this.responseBody = responseBody;
|
|
15
19
|
this.jmapErrorType = jmapErrorType;
|
|
20
|
+
this.retryAfterMilliseconds = retryAfterMilliseconds;
|
|
21
|
+
this.attempts = attempts;
|
|
16
22
|
}
|
|
17
23
|
};
|
|
18
24
|
/**
|
|
@@ -143,7 +149,7 @@ var JmapHttpClient = class {
|
|
|
143
149
|
const response = await this.fetchWithAuth(this.config.sessionUrl, { method: "GET" }, signal);
|
|
144
150
|
if (!response.ok) {
|
|
145
151
|
const text = await response.text();
|
|
146
|
-
throw new JmapApiError(`Session fetch failed: ${response.status}`, response.status, text);
|
|
152
|
+
throw new JmapApiError(`Session fetch failed: ${response.status}`, response.status, text, void 0, parseRetryAfter(response.headers.get("Retry-After")), 1);
|
|
147
153
|
}
|
|
148
154
|
return await response.json();
|
|
149
155
|
}
|
|
@@ -168,7 +174,7 @@ var JmapHttpClient = class {
|
|
|
168
174
|
}, signal);
|
|
169
175
|
if (!response.ok) {
|
|
170
176
|
const text = await response.text();
|
|
171
|
-
const error = new JmapApiError(`JMAP request failed: ${response.status}`, response.status, text);
|
|
177
|
+
const error = new JmapApiError(`JMAP request failed: ${response.status}`, response.status, text, void 0, parseRetryAfter(response.headers.get("Retry-After")), attempt + 1);
|
|
172
178
|
if (response.status >= 400 && response.status < 500) throw error;
|
|
173
179
|
throw error;
|
|
174
180
|
}
|
|
@@ -442,6 +448,7 @@ const JMAP_CAPABILITIES = {
|
|
|
442
448
|
* @since 0.4.0
|
|
443
449
|
*/
|
|
444
450
|
var JmapTransport = class {
|
|
451
|
+
id = "jmap";
|
|
445
452
|
config;
|
|
446
453
|
httpClient;
|
|
447
454
|
cachedSession = null;
|
|
@@ -468,10 +475,11 @@ var JmapTransport = class {
|
|
|
468
475
|
const session = await this.getSession(signal);
|
|
469
476
|
signal?.throwIfAborted();
|
|
470
477
|
const accountId = this.config.accountId ?? findMailAccount(session);
|
|
471
|
-
if (!accountId) return {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
478
|
+
if (!accountId) return createJmapFailure("No mail-capable account found in JMAP session", void 0, {
|
|
479
|
+
category: "configuration",
|
|
480
|
+
code: "jmap.no_mail_account",
|
|
481
|
+
retryable: false
|
|
482
|
+
});
|
|
475
483
|
const draftsMailboxId = await this.getDraftsMailboxId(session, accountId, signal);
|
|
476
484
|
signal?.throwIfAborted();
|
|
477
485
|
const identityId = await this.getIdentityId(session, accountId, message.sender.address, signal);
|
|
@@ -506,18 +514,16 @@ var JmapTransport = class {
|
|
|
506
514
|
}, signal);
|
|
507
515
|
return this.parseResponse(response);
|
|
508
516
|
} catch (error) {
|
|
509
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
return
|
|
518
|
-
|
|
519
|
-
errorMessages: [error instanceof Error ? error.message : String(error)]
|
|
520
|
-
};
|
|
517
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
518
|
+
if (signal?.aborted) throw getAbortReason(signal, error);
|
|
519
|
+
return createJmapFailure(`Request aborted: ${error.message}`, error, {
|
|
520
|
+
category: "timeout",
|
|
521
|
+
code: "abort",
|
|
522
|
+
retryable: true
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
if (error instanceof JmapApiError) return createJmapFailure(error.message, error);
|
|
526
|
+
return createJmapFailure(error instanceof Error ? error.message : String(error), error);
|
|
521
527
|
}
|
|
522
528
|
}
|
|
523
529
|
/**
|
|
@@ -542,10 +548,11 @@ var JmapTransport = class {
|
|
|
542
548
|
processingStage = "account discovery";
|
|
543
549
|
const accountId = this.config.accountId ?? findMailAccount(session);
|
|
544
550
|
if (!accountId) {
|
|
545
|
-
for (let i = 0; i < messageArray.length; i++) yield {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
551
|
+
for (let i = 0; i < messageArray.length; i++) yield createJmapFailure("No mail-capable account found in JMAP session", void 0, {
|
|
552
|
+
category: "configuration",
|
|
553
|
+
code: "jmap.no_mail_account",
|
|
554
|
+
retryable: false
|
|
555
|
+
});
|
|
549
556
|
return;
|
|
550
557
|
}
|
|
551
558
|
processingStage = "mailbox discovery";
|
|
@@ -603,13 +610,13 @@ var JmapTransport = class {
|
|
|
603
610
|
}, signal);
|
|
604
611
|
for (let i = 0; i < messageArray.length; i++) yield this.parseBatchResponseForIndex(response, i);
|
|
605
612
|
} catch (error) {
|
|
613
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
614
|
+
if (signal?.aborted) throw getAbortReason(signal, error);
|
|
615
|
+
}
|
|
606
616
|
const baseMessage = error instanceof Error && error.name === "AbortError" ? `Request aborted: ${error.message}` : error instanceof JmapApiError ? error.message : error instanceof Error ? error.message : String(error);
|
|
607
617
|
let detailedMessage = `Failed during ${processingStage}: ${baseMessage}`;
|
|
608
618
|
if (processingStage === "attachment upload" && attachmentsUploadedCount > 0) detailedMessage += ` (${attachmentsUploadedCount}/${messageArray.length} messages had attachments uploaded before failure)`;
|
|
609
|
-
for (let i = 0; i < messageArray.length; i++) yield
|
|
610
|
-
successful: false,
|
|
611
|
-
errorMessages: [detailedMessage]
|
|
612
|
-
};
|
|
619
|
+
for (let i = 0; i < messageArray.length; i++) yield createJmapFailure(detailedMessage, error);
|
|
613
620
|
}
|
|
614
621
|
}
|
|
615
622
|
/**
|
|
@@ -767,14 +774,17 @@ var JmapTransport = class {
|
|
|
767
774
|
if (submissionResult.notCreated) for (const [key, error] of Object.entries(submissionResult.notCreated)) errors.push(`Email submission failed (${key}): ${error.type}${error.description ? ` - ${error.description}` : ""}`);
|
|
768
775
|
if (submissionResult.created?.submission) return {
|
|
769
776
|
successful: true,
|
|
770
|
-
messageId: submissionResult.created.submission.id
|
|
777
|
+
messageId: submissionResult.created.submission.id,
|
|
778
|
+
provider: "jmap"
|
|
771
779
|
};
|
|
772
780
|
}
|
|
773
781
|
if (errors.length === 0) errors.push("Unknown error: No submission result received");
|
|
774
|
-
return {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
782
|
+
return createFailedReceipt(errors, {
|
|
783
|
+
provider: "jmap",
|
|
784
|
+
category: "rejected",
|
|
785
|
+
code: "jmap.submission_failed",
|
|
786
|
+
retryable: false
|
|
787
|
+
});
|
|
778
788
|
}
|
|
779
789
|
/**
|
|
780
790
|
* Parses the JMAP batch response to extract receipt for a specific index.
|
|
@@ -804,16 +814,50 @@ var JmapTransport = class {
|
|
|
804
814
|
}
|
|
805
815
|
if (submissionResult.created?.[subKey]) return {
|
|
806
816
|
successful: true,
|
|
807
|
-
messageId: submissionResult.created[subKey].id
|
|
817
|
+
messageId: submissionResult.created[subKey].id,
|
|
818
|
+
provider: "jmap"
|
|
808
819
|
};
|
|
809
820
|
}
|
|
810
821
|
if (errors.length === 0) errors.push("Unknown error: No submission result received");
|
|
811
|
-
return {
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
822
|
+
return createFailedReceipt(errors, {
|
|
823
|
+
provider: "jmap",
|
|
824
|
+
category: "rejected",
|
|
825
|
+
code: "jmap.submission_failed",
|
|
826
|
+
retryable: false
|
|
827
|
+
});
|
|
815
828
|
}
|
|
816
829
|
};
|
|
830
|
+
function createJmapFailure(message, error, override = {}) {
|
|
831
|
+
const attempts = getAttemptCount(error);
|
|
832
|
+
if (error instanceof JmapApiError) return createFailedReceipt(message, {
|
|
833
|
+
provider: "jmap",
|
|
834
|
+
statusCode: error.statusCode,
|
|
835
|
+
retryAfterMilliseconds: error.retryAfterMilliseconds,
|
|
836
|
+
attempts,
|
|
837
|
+
providerDetails: {
|
|
838
|
+
responseBody: error.responseBody,
|
|
839
|
+
jmapErrorType: error.jmapErrorType
|
|
840
|
+
},
|
|
841
|
+
category: override.category,
|
|
842
|
+
code: override.code,
|
|
843
|
+
retryable: override.retryable
|
|
844
|
+
});
|
|
845
|
+
return createFailedReceipt(message, {
|
|
846
|
+
provider: "jmap",
|
|
847
|
+
attempts,
|
|
848
|
+
category: override.category,
|
|
849
|
+
code: override.code,
|
|
850
|
+
retryable: override.retryable
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
function getAttemptCount(error) {
|
|
854
|
+
if (error instanceof JmapApiError) return error.attempts ?? 1;
|
|
855
|
+
if (typeof error === "object" && error !== null && "attempts" in error && typeof error.attempts === "number") return error.attempts;
|
|
856
|
+
return 1;
|
|
857
|
+
}
|
|
858
|
+
function getAbortReason(signal, fallback) {
|
|
859
|
+
return signal.reason ?? fallback;
|
|
860
|
+
}
|
|
817
861
|
|
|
818
862
|
//#endregion
|
|
819
863
|
export { JMAP_ERROR_TYPES, JmapApiError, JmapTransport, createJmapConfig, isCapabilityError, uploadBlob };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upyo/jmap",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.156",
|
|
4
4
|
"description": "JMAP transport for Upyo email library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"email",
|
|
@@ -53,19 +53,14 @@
|
|
|
53
53
|
},
|
|
54
54
|
"sideEffects": false,
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@upyo/core": "0.5.0-dev.
|
|
56
|
+
"@upyo/core": "0.5.0-dev.156+edad9790"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@dotenvx/dotenvx": "^1.47.3",
|
|
60
59
|
"jmap-rfc-types": "^0.1.2",
|
|
61
60
|
"tsdown": "^0.12.7",
|
|
62
61
|
"typescript": "5.8.3"
|
|
63
62
|
},
|
|
64
63
|
"scripts": {
|
|
65
|
-
"
|
|
66
|
-
"prepublish": "tsdown",
|
|
67
|
-
"test": "tsdown && dotenvx run --ignore=MISSING_ENV_FILE -- node --experimental-transform-types --test",
|
|
68
|
-
"test:bun": "tsdown && bun test --timeout=30000 --env-file=.env",
|
|
69
|
-
"test:deno": "DENO_JOBS=1 deno test --allow-env --allow-net --env-file=.env"
|
|
64
|
+
"prepublish": "mise run --no-deps :build"
|
|
70
65
|
}
|
|
71
66
|
}
|