@upyo/jmap 0.6.0-dev.351 → 0.6.0-dev.354

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/README.md CHANGED
@@ -163,7 +163,9 @@ factories. Declaring `encoding` reads the source once per successful send;
163
163
  omitting it adds an analysis pass before upload. Every reader must reproduce
164
164
  the same bytes. `7bit` requires ASCII, `8bit` asserts ASCII MIME headers with an
165
165
  8-bit body, and `utf8` permits internationalized headers. Automatic analysis
166
- conservatively selects `utf8` for any non-ASCII byte. No transcoding occurs.
166
+ conservatively selects `utf8` for any non-ASCII byte. With `8bit`, the caller
167
+ must ensure nested MIME headers are ASCII; Upyo checks only top-level headers.
168
+ Use `utf8` or omit `encoding` if unsure. No transcoding occurs.
167
169
 
168
170
  Sources must have CRLF line endings including the final CRLF, nonempty headers,
169
171
  no NUL, and no line longer than 998 bytes excluding CRLF. Uploads stream without
package/dist/index.cjs CHANGED
@@ -319,15 +319,15 @@ async function uploadRawMessage(config, uploadUrl, accountId, plan, signal) {
319
319
  body,
320
320
  duplex: "half",
321
321
  redirect: "error",
322
- signal: owned
322
+ signal: outerSignal
323
323
  };
324
324
  try {
325
325
  const response = await fetch(uploadUrl.replace("{accountId}", encodeURIComponent(accountId)), request);
326
- owned.throwIfAborted();
326
+ outerSignal.throwIfAborted();
327
+ if (!response.ok) throw new JmapApiError(`Raw upload failed: ${response.status}`, response.status, await response.text(), void 0, (0, __upyo_core.parseRetryAfter)(response.headers.get("Retry-After")));
327
328
  if (!eof) throw new JmapApiError("Raw upload responded before validated EOF.");
328
- if (!response.ok) throw new JmapApiError(`Raw upload failed: ${response.status}`, response.status, await response.text());
329
329
  const result = await response.json();
330
- owned.throwIfAborted();
330
+ outerSignal.throwIfAborted();
331
331
  if (!isRecord(result) || result.accountId !== accountId || typeof result.blobId !== "string" || !result.blobId || result.size !== size) throw new JmapApiError("Invalid raw upload response.");
332
332
  return {
333
333
  accountId,
@@ -338,7 +338,6 @@ async function uploadRawMessage(config, uploadUrl, accountId, plan, signal) {
338
338
  } catch (error) {
339
339
  outerSignal.throwIfAborted();
340
340
  if (hasSourceError) throw sourceError;
341
- owned.throwIfAborted();
342
341
  throw error;
343
342
  } finally {
344
343
  owned.removeEventListener("abort", abort);
@@ -355,11 +354,17 @@ const using = [
355
354
  "urn:ietf:params:jmap:mail",
356
355
  "urn:ietf:params:jmap:submission"
357
356
  ];
358
- function failure(message, stage, retryable, unknown = false) {
357
+ function failure(message, stage, retryable, unknown = false, error) {
359
358
  return (0, __upyo_core.createFailedReceipt)(message, {
360
359
  provider: "jmap",
361
360
  code: `jmap.raw_${stage}_${unknown ? "unknown" : "failed"}`,
362
- category: unknown ? "unknown" : retryable ? void 0 : "rejected",
361
+ category: unknown ? "unknown" : error?.statusCode !== void 0 || retryable ? void 0 : "rejected",
362
+ statusCode: error?.statusCode,
363
+ retryAfterMilliseconds: error?.retryAfterMilliseconds,
364
+ providerDetails: error ? {
365
+ responseBody: error.responseBody,
366
+ jmapErrorType: error.jmapErrorType
367
+ } : void 0,
363
368
  retryable,
364
369
  attempts: 1
365
370
  });
@@ -390,7 +395,9 @@ function errorDescription(error) {
390
395
  return `Raw JMAP operation rejected: ${String(error.type)}${typeof error.description === "string" ? `: ${error.description}` : ""}`;
391
396
  }
392
397
  function isRequestRejection(error) {
393
- if (!(error instanceof JmapApiError) || !error.responseBody || error.statusCode === void 0 || error.statusCode < 400 || error.statusCode > 599) return false;
398
+ if (!(error instanceof JmapApiError)) return false;
399
+ if (error.statusCode === 401 || error.statusCode === 403) return true;
400
+ if (!error.responseBody || error.statusCode === void 0 || error.statusCode < 400 || error.statusCode > 599) return false;
394
401
  try {
395
402
  const body = JSON.parse(error.responseBody);
396
403
  if (!isRecord(body) || body.status !== void 0 && body.status !== error.statusCode) return false;
@@ -488,7 +495,7 @@ async function deliverRawMessage(config, session, accountId, draftsMailboxId, id
488
495
  attempts: 1
489
496
  });
490
497
  const definite = isRequestRejection(error);
491
- return failure(message, stage, stage !== "submission" && !definite, stage === "submission" && !definite);
498
+ return failure(message, stage, stage !== "submission" && !definite, stage === "submission" && !definite, error instanceof JmapApiError ? error : void 0);
492
499
  }
493
500
  }
494
501
  /** Validates raw discovery responses before accepting delivery identifiers. @internal */
package/dist/index.js CHANGED
@@ -296,15 +296,15 @@ async function uploadRawMessage(config, uploadUrl, accountId, plan, signal) {
296
296
  body,
297
297
  duplex: "half",
298
298
  redirect: "error",
299
- signal: owned
299
+ signal: outerSignal
300
300
  };
301
301
  try {
302
302
  const response = await fetch(uploadUrl.replace("{accountId}", encodeURIComponent(accountId)), request);
303
- owned.throwIfAborted();
303
+ outerSignal.throwIfAborted();
304
+ if (!response.ok) throw new JmapApiError(`Raw upload failed: ${response.status}`, response.status, await response.text(), void 0, parseRetryAfter(response.headers.get("Retry-After")));
304
305
  if (!eof) throw new JmapApiError("Raw upload responded before validated EOF.");
305
- if (!response.ok) throw new JmapApiError(`Raw upload failed: ${response.status}`, response.status, await response.text());
306
306
  const result = await response.json();
307
- owned.throwIfAborted();
307
+ outerSignal.throwIfAborted();
308
308
  if (!isRecord(result) || result.accountId !== accountId || typeof result.blobId !== "string" || !result.blobId || result.size !== size) throw new JmapApiError("Invalid raw upload response.");
309
309
  return {
310
310
  accountId,
@@ -315,7 +315,6 @@ async function uploadRawMessage(config, uploadUrl, accountId, plan, signal) {
315
315
  } catch (error) {
316
316
  outerSignal.throwIfAborted();
317
317
  if (hasSourceError) throw sourceError;
318
- owned.throwIfAborted();
319
318
  throw error;
320
319
  } finally {
321
320
  owned.removeEventListener("abort", abort);
@@ -332,11 +331,17 @@ const using = [
332
331
  "urn:ietf:params:jmap:mail",
333
332
  "urn:ietf:params:jmap:submission"
334
333
  ];
335
- function failure(message, stage, retryable, unknown = false) {
334
+ function failure(message, stage, retryable, unknown = false, error) {
336
335
  return createFailedReceipt(message, {
337
336
  provider: "jmap",
338
337
  code: `jmap.raw_${stage}_${unknown ? "unknown" : "failed"}`,
339
- category: unknown ? "unknown" : retryable ? void 0 : "rejected",
338
+ category: unknown ? "unknown" : error?.statusCode !== void 0 || retryable ? void 0 : "rejected",
339
+ statusCode: error?.statusCode,
340
+ retryAfterMilliseconds: error?.retryAfterMilliseconds,
341
+ providerDetails: error ? {
342
+ responseBody: error.responseBody,
343
+ jmapErrorType: error.jmapErrorType
344
+ } : void 0,
340
345
  retryable,
341
346
  attempts: 1
342
347
  });
@@ -367,7 +372,9 @@ function errorDescription(error) {
367
372
  return `Raw JMAP operation rejected: ${String(error.type)}${typeof error.description === "string" ? `: ${error.description}` : ""}`;
368
373
  }
369
374
  function isRequestRejection(error) {
370
- if (!(error instanceof JmapApiError) || !error.responseBody || error.statusCode === void 0 || error.statusCode < 400 || error.statusCode > 599) return false;
375
+ if (!(error instanceof JmapApiError)) return false;
376
+ if (error.statusCode === 401 || error.statusCode === 403) return true;
377
+ if (!error.responseBody || error.statusCode === void 0 || error.statusCode < 400 || error.statusCode > 599) return false;
371
378
  try {
372
379
  const body = JSON.parse(error.responseBody);
373
380
  if (!isRecord(body) || body.status !== void 0 && body.status !== error.statusCode) return false;
@@ -465,7 +472,7 @@ async function deliverRawMessage(config, session, accountId, draftsMailboxId, id
465
472
  attempts: 1
466
473
  });
467
474
  const definite = isRequestRejection(error);
468
- return failure(message, stage, stage !== "submission" && !definite, stage === "submission" && !definite);
475
+ return failure(message, stage, stage !== "submission" && !definite, stage === "submission" && !definite, error instanceof JmapApiError ? error : void 0);
469
476
  }
470
477
  }
471
478
  /** Validates raw discovery responses before accepting delivery identifiers. @internal */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upyo/jmap",
3
- "version": "0.6.0-dev.351",
3
+ "version": "0.6.0-dev.354",
4
4
  "description": "JMAP transport for Upyo email library",
5
5
  "keywords": [
6
6
  "email",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "sideEffects": false,
55
55
  "peerDependencies": {
56
- "@upyo/core": "0.6.0-dev.351+b257f1a6"
56
+ "@upyo/core": "0.6.0-dev.354+3493c572"
57
57
  },
58
58
  "devDependencies": {
59
59
  "jmap-rfc-types": "^0.1.2",