@mavogel/awscdk-rootmail 0.1.14 → 0.1.16

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 (168) hide show
  1. package/.jsii +10 -10
  2. package/lib/rootmail.js +1 -1
  3. package/lib/ses-receive.js +1 -1
  4. package/node_modules/@aws-sdk/checksums/package.json +3 -3
  5. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-cjs/index.js +23 -1
  6. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-es/models/CloudWatchLogsServiceException.js +1 -0
  7. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-es/models/errors.js +21 -0
  8. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/models/CloudWatchLogsServiceException.d.ts +1 -0
  9. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/models/errors.d.ts +21 -0
  10. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/ts3.4/models/CloudWatchLogsServiceException.d.ts +1 -0
  11. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/ts3.4/models/errors.d.ts +21 -0
  12. package/node_modules/@aws-sdk/client-cloudwatch-logs/package.json +4 -4
  13. package/node_modules/@aws-sdk/client-route-53/dist-cjs/index.js +71 -1
  14. package/node_modules/@aws-sdk/client-route-53/dist-es/models/Route53ServiceException.js +1 -0
  15. package/node_modules/@aws-sdk/client-route-53/dist-es/models/errors.js +69 -0
  16. package/node_modules/@aws-sdk/client-route-53/dist-types/models/Route53ServiceException.d.ts +1 -0
  17. package/node_modules/@aws-sdk/client-route-53/dist-types/models/errors.d.ts +69 -0
  18. package/node_modules/@aws-sdk/client-route-53/dist-types/ts3.4/models/Route53ServiceException.d.ts +1 -0
  19. package/node_modules/@aws-sdk/client-route-53/dist-types/ts3.4/models/errors.d.ts +69 -0
  20. package/node_modules/@aws-sdk/client-route-53/package.json +4 -4
  21. package/node_modules/@aws-sdk/client-s3/dist-cjs/index.js +23 -1
  22. package/node_modules/@aws-sdk/client-s3/dist-es/models/S3ServiceException.js +1 -0
  23. package/node_modules/@aws-sdk/client-s3/dist-es/models/errors.js +21 -0
  24. package/node_modules/@aws-sdk/client-s3/dist-types/models/S3ServiceException.d.ts +1 -0
  25. package/node_modules/@aws-sdk/client-s3/dist-types/models/errors.d.ts +21 -0
  26. package/node_modules/@aws-sdk/client-s3/dist-types/ts3.4/models/S3ServiceException.d.ts +1 -0
  27. package/node_modules/@aws-sdk/client-s3/dist-types/ts3.4/models/errors.d.ts +21 -0
  28. package/node_modules/@aws-sdk/client-s3/package.json +8 -8
  29. package/node_modules/@aws-sdk/client-ses/dist-cjs/index.js +36 -1
  30. package/node_modules/@aws-sdk/client-ses/dist-es/models/SESServiceException.js +1 -0
  31. package/node_modules/@aws-sdk/client-ses/dist-es/models/errors.js +34 -0
  32. package/node_modules/@aws-sdk/client-ses/dist-types/models/SESServiceException.d.ts +1 -0
  33. package/node_modules/@aws-sdk/client-ses/dist-types/models/errors.d.ts +34 -0
  34. package/node_modules/@aws-sdk/client-ses/dist-types/ts3.4/models/SESServiceException.d.ts +1 -0
  35. package/node_modules/@aws-sdk/client-ses/dist-types/ts3.4/models/errors.d.ts +34 -0
  36. package/node_modules/@aws-sdk/client-ses/package.json +4 -4
  37. package/node_modules/@aws-sdk/client-ssm/dist-cjs/index.js +142 -1
  38. package/node_modules/@aws-sdk/client-ssm/dist-es/models/SSMServiceException.js +1 -0
  39. package/node_modules/@aws-sdk/client-ssm/dist-es/models/errors.js +140 -0
  40. package/node_modules/@aws-sdk/client-ssm/dist-types/models/SSMServiceException.d.ts +1 -0
  41. package/node_modules/@aws-sdk/client-ssm/dist-types/models/errors.d.ts +140 -0
  42. package/node_modules/@aws-sdk/client-ssm/dist-types/ts3.4/models/SSMServiceException.d.ts +1 -0
  43. package/node_modules/@aws-sdk/client-ssm/dist-types/ts3.4/models/errors.d.ts +140 -0
  44. package/node_modules/@aws-sdk/client-ssm/package.json +4 -4
  45. package/node_modules/@aws-sdk/core/package.json +3 -3
  46. package/node_modules/@aws-sdk/credential-provider-env/package.json +3 -3
  47. package/node_modules/@aws-sdk/credential-provider-http/package.json +3 -3
  48. package/node_modules/@aws-sdk/credential-provider-ini/package.json +10 -10
  49. package/node_modules/@aws-sdk/credential-provider-login/package.json +4 -4
  50. package/node_modules/@aws-sdk/credential-provider-node/package.json +8 -8
  51. package/node_modules/@aws-sdk/credential-provider-process/package.json +3 -3
  52. package/node_modules/@aws-sdk/credential-provider-sso/package.json +5 -5
  53. package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +4 -4
  54. package/node_modules/@aws-sdk/middleware-sdk-s3/package.json +4 -4
  55. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +11 -1
  56. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +6 -1
  57. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +6 -1
  58. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +13 -1
  59. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +9 -1
  60. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/models/CognitoIdentityServiceException.js +1 -0
  61. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/models/errors.js +9 -0
  62. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/models/SigninServiceException.js +1 -0
  63. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/models/errors.js +4 -0
  64. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/models/SSOServiceException.js +1 -0
  65. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/models/errors.js +4 -0
  66. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/models/SSOOIDCServiceException.js +1 -0
  67. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/models/errors.js +11 -0
  68. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/models/STSServiceException.js +1 -0
  69. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/models/errors.js +7 -0
  70. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/cognito-identity/models/CognitoIdentityServiceException.d.ts +1 -0
  71. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/cognito-identity/models/errors.d.ts +9 -0
  72. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/signin/models/SigninServiceException.d.ts +1 -0
  73. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/signin/models/errors.d.ts +4 -0
  74. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso/models/SSOServiceException.d.ts +1 -0
  75. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso/models/errors.d.ts +4 -0
  76. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso-oidc/models/SSOOIDCServiceException.d.ts +1 -0
  77. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso-oidc/models/errors.d.ts +11 -0
  78. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sts/models/STSServiceException.d.ts +1 -0
  79. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sts/models/errors.d.ts +7 -0
  80. package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/cognito-identity/models/CognitoIdentityServiceException.d.ts +1 -0
  81. package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/cognito-identity/models/errors.d.ts +9 -0
  82. package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/signin/models/SigninServiceException.d.ts +1 -0
  83. package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/signin/models/errors.d.ts +4 -0
  84. package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso/models/SSOServiceException.d.ts +1 -0
  85. package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso/models/errors.d.ts +4 -0
  86. package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso-oidc/models/SSOOIDCServiceException.d.ts +1 -0
  87. package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso-oidc/models/errors.d.ts +11 -0
  88. package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sts/models/STSServiceException.d.ts +1 -0
  89. package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sts/models/errors.d.ts +7 -0
  90. package/node_modules/@aws-sdk/nested-clients/package.json +4 -4
  91. package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +2 -2
  92. package/node_modules/@aws-sdk/token-providers/package.json +4 -4
  93. package/node_modules/@smithy/core/dist-cjs/submodules/client/index.js +33 -11
  94. package/node_modules/@smithy/core/dist-cjs/submodules/endpoints/index.browser.js +2 -1
  95. package/node_modules/@smithy/core/dist-cjs/submodules/endpoints/index.js +2 -1
  96. package/node_modules/@smithy/core/dist-cjs/submodules/schema/index.js +3 -0
  97. package/node_modules/@smithy/core/dist-es/submodules/client/smithy-client/exceptions.js +33 -11
  98. package/node_modules/@smithy/core/dist-es/submodules/endpoints/util-endpoints/cache/EndpointCache.js +2 -1
  99. package/node_modules/@smithy/core/dist-es/submodules/schema/TypeRegistry.js +3 -0
  100. package/node_modules/@smithy/core/dist-types/submodules/client/smithy-client/exceptions.d.ts +1 -0
  101. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/client/smithy-client/exceptions.d.ts +1 -0
  102. package/node_modules/@smithy/core/package.json +1 -1
  103. package/node_modules/@smithy/signature-v4/dist-cjs/index.js +4 -1
  104. package/node_modules/@smithy/signature-v4/dist-es/getCanonicalHeaders.js +4 -1
  105. package/node_modules/@smithy/signature-v4/package.json +3 -3
  106. package/node_modules/mailparser/.release-please-manifest.json +1 -1
  107. package/node_modules/mailparser/CHANGELOG.md +7 -0
  108. package/node_modules/mailparser/package.json +2 -2
  109. package/node_modules/nodemailer/CHANGELOG.md +27 -0
  110. package/node_modules/nodemailer/dist/cjs/base64/index.d.ts +3 -0
  111. package/node_modules/nodemailer/dist/cjs/base64/index.js +80 -41
  112. package/node_modules/nodemailer/dist/cjs/dkim/index.js +43 -29
  113. package/node_modules/nodemailer/dist/cjs/dkim/message-parser.js +2 -2
  114. package/node_modules/nodemailer/dist/cjs/errors.d.ts +17 -0
  115. package/node_modules/nodemailer/dist/cjs/errors.js +12 -1
  116. package/node_modules/nodemailer/dist/cjs/fetch/index.js +23 -3
  117. package/node_modules/nodemailer/dist/cjs/mailer/index.js +23 -7
  118. package/node_modules/nodemailer/dist/cjs/mailer/mail-message.d.ts +5 -0
  119. package/node_modules/nodemailer/dist/cjs/mailer/mail-message.js +20 -0
  120. package/node_modules/nodemailer/dist/cjs/mime-node/index.d.ts +1 -1
  121. package/node_modules/nodemailer/dist/cjs/mime-node/index.js +165 -105
  122. package/node_modules/nodemailer/dist/cjs/nodemailer.js +12 -9
  123. package/node_modules/nodemailer/dist/cjs/package-info.d.ts +1 -1
  124. package/node_modules/nodemailer/dist/cjs/package-info.js +1 -1
  125. package/node_modules/nodemailer/dist/cjs/qp/index.d.ts +2 -0
  126. package/node_modules/nodemailer/dist/cjs/qp/index.js +103 -33
  127. package/node_modules/nodemailer/dist/cjs/sendmail-transport/index.js +54 -14
  128. package/node_modules/nodemailer/dist/cjs/shared/index.d.ts +32 -1
  129. package/node_modules/nodemailer/dist/cjs/shared/index.js +212 -115
  130. package/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.d.ts +3 -1
  131. package/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.js +88 -68
  132. package/node_modules/nodemailer/dist/cjs/smtp-connection/index.d.ts +7 -1
  133. package/node_modules/nodemailer/dist/cjs/smtp-connection/index.js +348 -136
  134. package/node_modules/nodemailer/dist/cjs/smtp-pool/index.d.ts +3 -0
  135. package/node_modules/nodemailer/dist/cjs/smtp-pool/index.js +7 -1
  136. package/node_modules/nodemailer/dist/cjs/smtp-pool/pool-resource.js +106 -17
  137. package/node_modules/nodemailer/dist/cjs/smtp-transport/index.js +8 -2
  138. package/node_modules/nodemailer/dist/esm/base64/index.d.ts +3 -0
  139. package/node_modules/nodemailer/dist/esm/base64/index.js +80 -41
  140. package/node_modules/nodemailer/dist/esm/dkim/index.js +43 -29
  141. package/node_modules/nodemailer/dist/esm/dkim/message-parser.js +2 -2
  142. package/node_modules/nodemailer/dist/esm/errors.d.ts +17 -0
  143. package/node_modules/nodemailer/dist/esm/errors.js +10 -0
  144. package/node_modules/nodemailer/dist/esm/fetch/index.js +23 -3
  145. package/node_modules/nodemailer/dist/esm/mailer/index.js +23 -7
  146. package/node_modules/nodemailer/dist/esm/mailer/mail-message.d.ts +5 -0
  147. package/node_modules/nodemailer/dist/esm/mailer/mail-message.js +20 -0
  148. package/node_modules/nodemailer/dist/esm/mime-node/index.d.ts +1 -1
  149. package/node_modules/nodemailer/dist/esm/mime-node/index.js +166 -106
  150. package/node_modules/nodemailer/dist/esm/nodemailer.js +12 -9
  151. package/node_modules/nodemailer/dist/esm/package-info.d.ts +1 -1
  152. package/node_modules/nodemailer/dist/esm/package-info.js +1 -1
  153. package/node_modules/nodemailer/dist/esm/qp/index.d.ts +2 -0
  154. package/node_modules/nodemailer/dist/esm/qp/index.js +103 -33
  155. package/node_modules/nodemailer/dist/esm/sendmail-transport/index.js +54 -14
  156. package/node_modules/nodemailer/dist/esm/shared/index.d.ts +32 -1
  157. package/node_modules/nodemailer/dist/esm/shared/index.js +207 -115
  158. package/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.d.ts +3 -1
  159. package/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.js +88 -68
  160. package/node_modules/nodemailer/dist/esm/smtp-connection/index.d.ts +7 -1
  161. package/node_modules/nodemailer/dist/esm/smtp-connection/index.js +348 -136
  162. package/node_modules/nodemailer/dist/esm/smtp-pool/index.d.ts +3 -0
  163. package/node_modules/nodemailer/dist/esm/smtp-pool/index.js +7 -1
  164. package/node_modules/nodemailer/dist/esm/smtp-pool/pool-resource.js +106 -17
  165. package/node_modules/nodemailer/dist/esm/smtp-transport/index.js +8 -2
  166. package/node_modules/nodemailer/package.json +1 -1
  167. package/node_modules/uuid/package.json +17 -17
  168. package/package.json +9 -9
@@ -71,6 +71,14 @@ const PLAIN_ADDRESS = /^[^\s"(),:;<>@[\\\]]+@[^\s"(),:;<>@[\\\]]+$/;
71
71
  // unroutable garbage into mail for a domain the sender never named. None of these
72
72
  // characters are legal in a domain, so keep them away from the mapper.
73
73
  const URL_PARSER_UNSAFE = /[/\\?#%\x00-\x20\x7F]/;
74
+ // pipeline() needs a callback to not throw, the errors it sees reach the last stream anyway
75
+ const PIPELINE_NOOP = () => false;
76
+ // The error a node streaming into an output that was destroyed stops with
77
+ function abortedError() {
78
+ const err = new Error('Message stream was closed before the message was generated');
79
+ err.code = errors.ESTREAM;
80
+ return err;
81
+ }
74
82
  /**
75
83
  * Encodes a domain the way browsers, the WHATWG URL Standard and DNS facing resolvers do,
76
84
  * which is with UTS-46 mapping applied before the Punycode step.
@@ -90,9 +98,14 @@ const URL_PARSER_UNSAFE = /[/\\?#%\x00-\x20\x7F]/;
90
98
  */
91
99
  function normalizeDomain(domain, toUnicode) {
92
100
  // domainToASCII and domainToUnicode landed in Node 7, the bundled codec covers Node 6
93
- const mapper = toUnicode ? node_url_1.default.domainToUnicode : node_url_1.default.domainToASCII;
94
- if (typeof mapper === 'function' && !URL_PARSER_UNSAFE.test(domain)) {
95
- const mapped = mapper(domain);
101
+ if (typeof node_url_1.default.domainToASCII === 'function' &&
102
+ typeof node_url_1.default.domainToUnicode === 'function' &&
103
+ !URL_PARSER_UNSAFE.test(domain)) {
104
+ // The U-label form is decoded from the A-label form rather than from the input:
105
+ // Deno's domainToASCII returns an empty string for a label it cannot decode, while
106
+ // its domainToUnicode returns the label with U+FFFD in place of the bad part
107
+ const ascii = node_url_1.default.domainToASCII(domain);
108
+ const mapped = ascii && toUnicode ? node_url_1.default.domainToUnicode(ascii) : ascii;
96
109
  if (mapped) {
97
110
  return mapped;
98
111
  }
@@ -444,11 +457,7 @@ class MimeNode {
444
457
  if (typeof this.content.pipe === 'function') {
445
458
  // pre-stream handler. might be triggered if a stream is set as content
446
459
  // and 'error' fires before anything is done with this stream
447
- this._contentErrorHandler = err => {
448
- this.content.removeListener('error', this._contentErrorHandler);
449
- this.content = err;
450
- };
451
- this.content.once('error', this._contentErrorHandler);
460
+ shared.recordStreamErrors(this.content);
452
461
  }
453
462
  else if (typeof this.content === 'string') {
454
463
  this._isPlainText = mimeFuncs.isPlainText(this.content);
@@ -660,40 +669,45 @@ class MimeNode {
660
669
  createReadStream(options) {
661
670
  options = options || {};
662
671
  const stream = new node_stream_1.PassThrough(options);
663
- let outputStream = stream;
664
- let transform;
665
672
  this.stream(stream, options, err => {
666
673
  if (err) {
667
- outputStream.emit('error', err);
674
+ stream.destroy(err);
668
675
  return;
669
676
  }
670
677
  stream.end();
671
678
  });
672
- for (let i = 0, len = this._transforms.length; i < len; i++) {
673
- transform =
674
- typeof this._transforms[i] === 'function' ? this._transforms[i]() : this._transforms[i];
675
- outputStream.once('error', err => {
676
- transform.emit('error', err);
677
- });
678
- outputStream = outputStream.pipe(transform);
679
+ // the content streams of the nodes the message did not get to are not going to be read
680
+ stream.once('close', () => {
681
+ if (!stream.writableFinished) {
682
+ this._destroyContentStreams();
683
+ }
684
+ });
685
+ // The stages are joined with pipeline, which destroys all of them when any one fails
686
+ // or is destroyed. An error anywhere reaches the returned stream exactly once, and a
687
+ // consumer that destroys the returned stream stops the tree from reading its sources
688
+ const stages = [stream];
689
+ for (const transform of this._transforms) {
690
+ stages.push(typeof transform === 'function' ? transform() : transform);
679
691
  }
680
692
  // ensure terminating newline after possible user transforms
681
- transform = new last_newline_js_1.default();
682
- outputStream.once('error', err => {
683
- transform.emit('error', err);
684
- });
685
- outputStream = outputStream.pipe(transform);
686
- // dkim and stuff
687
- for (let i = 0, len = this._processFuncs.length; i < len; i++) {
688
- transform = this._processFuncs[i];
689
- outputStream = transform(outputStream);
693
+ stages.push(new last_newline_js_1.default());
694
+ let outputStream = (0, node_stream_1.pipeline)(stages, PIPELINE_NOOP);
695
+ // dkim and stuff. A process function reads its input itself and reports the errors of
696
+ // that input on its output, so only a consumer abort has to be carried back upstream
697
+ for (const processFunc of this._processFuncs) {
698
+ const input = outputStream;
699
+ outputStream = processFunc(input);
700
+ if (outputStream !== input) {
701
+ (0, node_stream_1.finished)(outputStream, err => {
702
+ if (err) {
703
+ input.destroy();
704
+ }
705
+ });
706
+ }
690
707
  }
691
708
  if (this.newline) {
692
709
  const winbreak = ['win', 'windows', 'dos', '\r\n'].includes(this.newline.toString().toLowerCase());
693
- const newlineTransform = winbreak ? new le_windows_js_1.default() : new le_unix_js_1.default();
694
- const stream = outputStream.pipe(newlineTransform);
695
- outputStream.on('error', err => stream.emit('error', err));
696
- return stream;
710
+ outputStream = (0, node_stream_1.pipeline)(outputStream, winbreak ? new le_windows_js_1.default() : new le_unix_js_1.default(), PIPELINE_NOOP);
697
711
  }
698
712
  return outputStream;
699
713
  }
@@ -719,8 +733,10 @@ class MimeNode {
719
733
  }
720
734
  stream(outputStream, options, done) {
721
735
  const transferEncoding = this.getTransferEncoding();
722
- let contentStream;
723
- let localStream;
736
+ // the streams this node is reading from. A consumer that goes away mid-message destroys
737
+ // the output, and these are released with it instead of staying paused with a file or a
738
+ // socket open behind them
739
+ let activeStreams = [];
724
740
  // protect actual callback against multiple triggering
725
741
  let returned = false;
726
742
  const callback = (err) => {
@@ -728,13 +744,39 @@ class MimeNode {
728
744
  return;
729
745
  }
730
746
  returned = true;
747
+ outputStream.removeListener('close', onOutputClose);
731
748
  done(err);
732
749
  };
750
+ function onOutputClose() {
751
+ for (const stream of activeStreams) {
752
+ stream.destroy();
753
+ }
754
+ return callback(abortedError());
755
+ }
756
+ // reads the streams into the output. The listener is only attached while the node reads
757
+ // something, a deeply nested tree would otherwise stack one per level on the output
758
+ const readInto = (...streams) => {
759
+ if (!activeStreams.length) {
760
+ outputStream.once('close', onOutputClose);
761
+ }
762
+ activeStreams = streams;
763
+ };
764
+ // stops here if the output was destroyed in the meantime
765
+ const aborted = () => {
766
+ if (outputStream.destroyed) {
767
+ callback(abortedError());
768
+ return true;
769
+ }
770
+ return false;
771
+ };
733
772
  // for multipart nodes, push child nodes
734
773
  // for content nodes end the stream
735
774
  const finalize = () => {
736
775
  let childId = 0;
737
776
  const processChildNode = () => {
777
+ if (aborted()) {
778
+ return;
779
+ }
738
780
  if (childId >= this.childNodes.length) {
739
781
  outputStream.write('\r\n--' + this.boundary + '--\r\n');
740
782
  return callback();
@@ -757,89 +799,80 @@ class MimeNode {
757
799
  };
758
800
  // pushes node content
759
801
  const sendContent = () => {
760
- if (this.content) {
761
- if (Object.prototype.toString.call(this.content) === '[object Error]') {
762
- // content is already errored
763
- return callback(this.content);
802
+ if (aborted()) {
803
+ return;
804
+ }
805
+ if (!this.content) {
806
+ return setImmediate(finalize);
807
+ }
808
+ const createStream = () => {
809
+ if (aborted()) {
810
+ return;
764
811
  }
765
- if (typeof this.content.pipe === 'function') {
766
- this.content.removeListener('error', this._contentErrorHandler);
767
- this._contentErrorHandler = err => callback(err);
768
- this.content.once('error', this._contentErrorHandler);
812
+ const contentError = this._takeStreamContent(this.content);
813
+ if (contentError) {
814
+ return callback(contentError);
769
815
  }
770
- const createStream = () => {
771
- if (['quoted-printable', 'base64'].includes(transferEncoding)) {
772
- contentStream = new (transferEncoding === 'base64' ? base64 : qp).Encoder(options);
773
- contentStream.pipe(outputStream, {
774
- end: false
775
- });
776
- contentStream.once('end', finalize);
777
- contentStream.once('error', err => callback(err));
778
- localStream = this._getStream(this.content);
779
- localStream.pipe(contentStream);
780
- }
781
- else {
782
- // anything that is not QP or Base54 passes as-is
783
- localStream = this._getStream(this.content);
784
- localStream.pipe(outputStream, {
785
- end: false
786
- });
787
- localStream.once('end', finalize);
788
- }
789
- localStream.once('error', err => callback(err));
790
- };
791
- if (this.content._resolve) {
792
- const chunks = [];
793
- let chunklen = 0;
794
- let returned = false;
795
- const sourceStream = this._getStream(this.content);
796
- sourceStream.on('error', err => {
797
- if (returned) {
798
- return;
799
- }
800
- returned = true;
801
- callback(err);
802
- });
803
- sourceStream.on('readable', () => {
804
- let chunk;
805
- while ((chunk = sourceStream.read()) !== null) {
806
- chunks.push(chunk);
807
- chunklen += chunk.length;
808
- }
809
- });
810
- sourceStream.on('end', () => {
811
- if (returned) {
812
- return;
813
- }
814
- returned = true;
815
- this.content._resolve = false;
816
- this.content._resolvedValue = Buffer.concat(chunks, chunklen);
817
- setImmediate(createStream);
816
+ const localStream = this._getStream(this.content);
817
+ if (['quoted-printable', 'base64'].includes(transferEncoding)) {
818
+ const contentStream = transferEncoding === 'base64'
819
+ ? new base64.Encoder(options)
820
+ : // outside of text a lone CR or LF is data, encoding it keeps it from being
821
+ // turned into a line break by a newline transform or the receiving side
822
+ new qp.Encoder(Object.assign({}, options, { binary: !/^text\//i.test(this.contentType || '') }));
823
+ readInto(localStream, contentStream);
824
+ contentStream.pipe(outputStream, {
825
+ end: false
818
826
  });
827
+ // pipeline reports errors and tears both streams down. It calls back once the
828
+ // encoder took all of its input, the encoded output may still be waiting to
829
+ // be read, so the node is only done once the encoder's readable side ended
830
+ (0, node_stream_1.pipeline)(localStream, contentStream, err => err && callback(err));
831
+ (0, node_stream_1.finished)(contentStream, { writable: false }, err => (err ? callback(err) : finalize()));
819
832
  }
820
833
  else {
821
- setImmediate(createStream);
834
+ // anything that is not QP or Base54 passes as-is
835
+ readInto(localStream);
836
+ localStream.pipe(outputStream, {
837
+ end: false
838
+ });
839
+ (0, node_stream_1.finished)(localStream, { writable: false }, err => (err ? callback(err) : finalize()));
822
840
  }
823
- return;
841
+ };
842
+ if (this.content._resolve) {
843
+ const sourceStream = this._getStream(this.content);
844
+ readInto(sourceStream);
845
+ shared.resolveStream(sourceStream, (err, value) => {
846
+ if (err) {
847
+ return callback(err);
848
+ }
849
+ if (returned) {
850
+ return;
851
+ }
852
+ this.content._resolve = false;
853
+ this.content._resolvedValue = value;
854
+ setImmediate(createStream);
855
+ });
856
+ }
857
+ else {
858
+ setImmediate(createStream);
824
859
  }
825
- return setImmediate(finalize);
826
860
  };
827
861
  if (this._raw) {
828
862
  setImmediate(() => {
829
- if (Object.prototype.toString.call(this._raw) === '[object Error]') {
830
- // content is already errored
831
- return callback(this._raw);
863
+ if (aborted()) {
864
+ return;
832
865
  }
833
- // remove default error handler (if set)
834
- if (typeof this._raw.pipe === 'function') {
835
- this._raw.removeListener('error', this._contentErrorHandler);
866
+ const rawError = this._takeStreamContent(this._raw);
867
+ if (rawError) {
868
+ return callback(rawError);
836
869
  }
837
870
  const raw = this._getStream(this._raw);
871
+ readInto(raw);
838
872
  raw.pipe(outputStream, {
839
873
  end: false
840
874
  });
841
- raw.on('error', err => outputStream.emit('error', err));
842
- raw.on('end', finalize);
875
+ (0, node_stream_1.finished)(raw, { writable: false }, err => (err ? callback(err) : finalize()));
843
876
  });
844
877
  }
845
878
  else {
@@ -955,11 +988,7 @@ class MimeNode {
955
988
  if (this._raw && typeof this._raw.pipe === 'function') {
956
989
  // pre-stream handler. might be triggered if a stream is set as content
957
990
  // and 'error' fires before anything is done with this stream
958
- this._contentErrorHandler = err => {
959
- this._raw.removeListener('error', this._contentErrorHandler);
960
- this._raw = err;
961
- };
962
- this._raw.once('error', this._contentErrorHandler);
991
+ shared.recordStreamErrors(this._raw);
963
992
  }
964
993
  return this;
965
994
  }
@@ -985,6 +1014,37 @@ class MimeNode {
985
1014
  }
986
1015
  return false;
987
1016
  }
1017
+ /**
1018
+ * Destroys the content streams of this node and of every node below it
1019
+ *
1020
+ * @internal
1021
+ */
1022
+ _destroyContentStreams() {
1023
+ shared.destroyStream(this.content);
1024
+ shared.destroyStream(this._raw);
1025
+ for (const child of this.childNodes) {
1026
+ child._destroyContentStreams();
1027
+ }
1028
+ }
1029
+ /**
1030
+ * Checks that a content value can still be read before the node streams it. A stream is
1031
+ * refused once it errored, ended or was destroyed: piping it would either never finish or
1032
+ * produce an empty body without any error
1033
+ *
1034
+ * @param content Node content or raw value
1035
+ * @returns The error to stop with, or null when the content can be read
1036
+ * @internal
1037
+ */
1038
+ _takeStreamContent(content) {
1039
+ if (Object.prototype.toString.call(content) === '[object Error]') {
1040
+ return content;
1041
+ }
1042
+ if (!content || typeof content.pipe !== 'function' || content._resolvedValue) {
1043
+ // the value of a resolved stream is read from the buffered copy
1044
+ return null;
1045
+ }
1046
+ return shared.unreadableStreamError(content);
1047
+ }
988
1048
  /**
989
1049
  * Detects and returns handle to a stream related with the content.
990
1050
  *
@@ -50,10 +50,12 @@ const index_js_7 = __importDefault(require("./ses-transport/index.js"));
50
50
  const errors = __importStar(require("./errors.js"));
51
51
  const index_js_8 = __importDefault(require("./fetch/index.js"));
52
52
  const packageData = __importStar(require("./package-info.js"));
53
- const ETHEREAL_API = (process.env.ETHEREAL_API || 'https://api.nodemailer.com').replace(/\/+$/, '');
54
- const ETHEREAL_WEB = (process.env.ETHEREAL_WEB || 'https://ethereal.email').replace(/\/+$/, '');
55
- const ETHEREAL_API_KEY = (process.env.ETHEREAL_API_KEY || '').replace(/\s*/g, '') || null;
56
- const ETHEREAL_CACHE = ['true', 'yes', 'y', '1'].includes((process.env.ETHEREAL_CACHE || 'yes').toString().trim().toLowerCase());
53
+ // Read on use rather than at load, so that importing the module does not touch the
54
+ // environment (Deno refuses that without --allow-env, even for apps that never use Ethereal)
55
+ const etherealApi = () => (process.env.ETHEREAL_API || 'https://api.nodemailer.com').replace(/\/+$/, '');
56
+ const etherealWeb = () => (process.env.ETHEREAL_WEB || 'https://ethereal.email').replace(/\/+$/, '');
57
+ const etherealApiKey = () => (process.env.ETHEREAL_API_KEY || '').replace(/\s*/g, '') || null;
58
+ const etherealCache = () => ['true', 'yes', 'y', '1'].includes((process.env.ETHEREAL_CACHE || 'yes').toString().trim().toLowerCase());
57
59
  let testAccount = false;
58
60
  function createTransport(transporter, defaults) {
59
61
  let options;
@@ -114,11 +116,11 @@ function createTestAccount(apiUrl, callback) {
114
116
  });
115
117
  }
116
118
  const done = callback;
117
- if (ETHEREAL_CACHE && testAccount) {
119
+ if (etherealCache() && testAccount) {
118
120
  setImmediate(() => done(null, testAccount));
119
121
  return promise;
120
122
  }
121
- apiUrl = apiUrl || ETHEREAL_API;
123
+ apiUrl = apiUrl || etherealApi();
122
124
  const chunks = [];
123
125
  let chunklen = 0;
124
126
  const requestHeaders = {};
@@ -126,8 +128,9 @@ function createTestAccount(apiUrl, callback) {
126
128
  requestor: packageData.name,
127
129
  version: packageData.version
128
130
  };
129
- if (ETHEREAL_API_KEY) {
130
- requestHeaders.Authorization = 'Bearer ' + ETHEREAL_API_KEY;
131
+ const apiKey = etherealApiKey();
132
+ if (apiKey) {
133
+ requestHeaders.Authorization = 'Bearer ' + apiKey;
131
134
  }
132
135
  const fetchOptions = {
133
136
  contentType: 'application/json',
@@ -195,7 +198,7 @@ function getTestMessageUrl(info) {
195
198
  }
196
199
  }
197
200
  if (infoProps.has('STATUS') && infoProps.has('MSGID')) {
198
- return ((testAccount && testAccount.web) || ETHEREAL_WEB) + '/message/' + infoProps.get('MSGID');
201
+ return ((testAccount && testAccount.web) || etherealWeb()) + '/message/' + infoProps.get('MSGID');
199
202
  }
200
203
  return false;
201
204
  }
@@ -1,3 +1,3 @@
1
1
  export declare const name = "nodemailer";
2
- export declare const version = "10.0.16";
2
+ export declare const version = "10.1.0";
3
3
  export declare const homepage = "https://nodemailer.com/";
@@ -3,5 +3,5 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.homepage = exports.version = exports.name = void 0;
5
5
  exports.name = 'nodemailer';
6
- exports.version = '10.0.16';
6
+ exports.version = '10.1.0';
7
7
  exports.homepage = 'https://nodemailer.com/';
@@ -14,6 +14,8 @@ export declare function wrap(str: string, lineLength?: number): string;
14
14
  export interface QPEncoderOptions {
15
15
  /** Maximum length for lines, set to false to disable wrapping */
16
16
  lineLength?: number | false | undefined;
17
+ /** The input is binary data: a CR or LF that is not part of a CRLF pair is encoded */
18
+ binary?: boolean | undefined;
17
19
  }
18
20
  /** The name @types/nodemailer used for QPEncoderOptions */
19
21
  export type EncoderOptions = QPEncoderOptions;
@@ -21,23 +21,52 @@ const QP_RANGES = [
21
21
  [0x20, 0x3c], // <SP>!"#$%&'()*+,-./0123456789:;
22
22
  [0x3e, 0x7e] // >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}
23
23
  ];
24
+ // 1 for every byte value that is written as is, see QP_RANGES
25
+ const QP_LITERAL = new Uint8Array(256);
26
+ for (let i = 0; i < 256; i++) {
27
+ QP_LITERAL[i] = checkRanges(i, QP_RANGES) ? 1 : 0;
28
+ }
29
+ const HEX_DIGITS = Buffer.from('0123456789ABCDEF', 'latin1');
30
+ const isWhitespace = (c) => c === 0x20 || c === 0x09;
24
31
  function encode(buffer) {
25
- if (typeof buffer === 'string') {
26
- buffer = Buffer.from(buffer, 'utf-8');
27
- }
28
- let result = '';
29
- let ord;
30
- for (let i = 0, len = buffer.length; i < len; i++) {
31
- ord = buffer[i];
32
+ return encodeBytes(typeof buffer === 'string' ? Buffer.from(buffer, 'utf-8') : buffer);
33
+ }
34
+ /**
35
+ * Encodes bytes that may be followed by more input
36
+ *
37
+ * @param buffer Bytes to encode
38
+ * @param [next] The byte that follows the buffer, whitespace before it is kept literal unless it
39
+ * is a line break. Without it the buffer ends the input and its trailing whitespace is encoded
40
+ * @param [binary] Keep only CRLF pairs literal. A lone CR or LF is data, not a line break, and
41
+ * anything that rewrites line endings on the way would change it
42
+ * @param [previous] The byte before the buffer, for a buffer that starts with LF
43
+ * @returns Quoted-Printable encoded string
44
+ */
45
+ function encodeBytes(buffer, next, binary, previous) {
46
+ const len = buffer.length;
47
+ // every byte takes three characters at most
48
+ const output = Buffer.allocUnsafe(len * 3);
49
+ let pos = 0;
50
+ for (let i = 0; i < len; i++) {
51
+ const ord = buffer[i];
52
+ const following = i + 1 < len ? buffer[i + 1] : next;
53
+ const lineBreakByte = binary && (ord === 0x0d || ord === 0x0a)
54
+ ? ord === 0x0d
55
+ ? following === 0x0a
56
+ : (i > 0 ? buffer[i - 1] : previous) === 0x0d
57
+ : true;
32
58
  // if the char is in allowed range, then keep as is, unless it is a WS in the end of a line
33
- if (checkRanges(ord, QP_RANGES) &&
34
- !((ord === 0x20 || ord === 0x09) && (i === len - 1 || buffer[i + 1] === 0x0a || buffer[i + 1] === 0x0d))) {
35
- result += String.fromCharCode(ord);
59
+ if (QP_LITERAL[ord] &&
60
+ lineBreakByte &&
61
+ !(isWhitespace(ord) && (following === undefined || following === 0x0a || following === 0x0d))) {
62
+ output[pos++] = ord;
36
63
  continue;
37
64
  }
38
- result += '=' + (ord < 0x10 ? '0' : '') + ord.toString(16).toUpperCase();
65
+ output[pos++] = 0x3d; // =
66
+ output[pos++] = HEX_DIGITS[Math.floor(ord / 16)];
67
+ output[pos++] = HEX_DIGITS[ord % 16];
39
68
  }
40
- return result;
69
+ return output.toString('latin1', 0, pos);
41
70
  }
42
71
  /**
43
72
  * Adds soft line breaks to a Quoted-Printable string
@@ -148,6 +177,10 @@ function checkRanges(nr, ranges) {
148
177
  }
149
178
  return false;
150
179
  }
180
+ // Input is encoded and wrapped this many bytes at a time. wrap() works on strings, and a large
181
+ // chunk handed over at once, such as a whole Buffer attachment, would be held several times over
182
+ // as intermediate strings
183
+ const ENCODE_SLICE_SIZE = 64 * 1024;
151
184
  /**
152
185
  * Creates a transform stream for encoding data to Quoted-Printable encoding
153
186
  *
@@ -163,45 +196,82 @@ class Encoder extends node_stream_1.Transform {
163
196
  this.options.lineLength = this.options.lineLength || 76;
164
197
  }
165
198
  this._curLine = '';
199
+ this._remainingBytes = false;
200
+ this._lastByte = undefined;
166
201
  this.inputBytes = 0;
167
202
  this.outputBytes = 0;
168
203
  }
169
204
  /** @internal */
170
205
  _transform(chunk, encoding, done) {
171
- let qp;
172
- if (encoding !== 'buffer') {
173
- chunk = Buffer.from(chunk, encoding);
174
- }
175
- if (!chunk || !chunk.length) {
206
+ let buf = encoding !== 'buffer' ? Buffer.from(chunk, encoding) : chunk;
207
+ if (!buf || !buf.length) {
176
208
  return done();
177
209
  }
178
- this.inputBytes += chunk.length;
179
- if (this.options.lineLength) {
180
- qp = this._curLine + encode(chunk);
181
- qp = wrap(qp, this.options.lineLength);
182
- qp = qp.replace(/(^|\n)([^\n]*)$/, (match, lineBreak, lastLine) => {
183
- this._curLine = lastLine;
184
- return lineBreak;
185
- });
186
- if (qp) {
187
- this.outputBytes += qp.length;
188
- this.push(qp);
189
- }
210
+ this.inputBytes += buf.length;
211
+ if (this._remainingBytes) {
212
+ buf = Buffer.concat([this._remainingBytes, buf], this._remainingBytes.length + buf.length);
213
+ this._remainingBytes = false;
190
214
  }
191
- else {
192
- qp = encode(chunk);
193
- this.outputBytes += qp.length;
194
- this.push(qp, 'ascii');
215
+ // Whitespace is encoded when it ends a line, and the end of the input counts as one. Hold
216
+ // back the whitespace a chunk ends with until it is known what follows it, so the output
217
+ // does not depend on where the input was split
218
+ let end = buf.length;
219
+ // a binary CR can only be written once it is known whether LF follows
220
+ while (end > 0 && (isWhitespace(buf[end - 1]) || (this.options.binary && buf[end - 1] === 0x0d))) {
221
+ end--;
222
+ }
223
+ if (buf.length - end <= ENCODE_SLICE_SIZE) {
224
+ this._remainingBytes = end < buf.length ? Buffer.from(buf.subarray(end)) : false;
225
+ buf = buf.subarray(0, end);
195
226
  }
227
+ this._encodeSlices(buf);
196
228
  done();
197
229
  }
198
230
  /** @internal */
199
231
  _flush(done) {
232
+ if (this._remainingBytes) {
233
+ this._encodeSlices(this._remainingBytes);
234
+ this._remainingBytes = false;
235
+ }
200
236
  if (this._curLine) {
201
237
  this.outputBytes += this._curLine.length;
202
238
  this.push(this._curLine, 'ascii');
239
+ this._curLine = '';
203
240
  }
204
241
  done();
205
242
  }
243
+ /**
244
+ * Encodes the input in slices of ENCODE_SLICE_SIZE bytes. Each slice is told the byte that
245
+ * follows it, so a slice that ends in whitespace is encoded as if it was not split
246
+ *
247
+ * @internal
248
+ */
249
+ _encodeSlices(buf) {
250
+ for (let start = 0; start < buf.length; start += ENCODE_SLICE_SIZE) {
251
+ const end = Math.min(start + ENCODE_SLICE_SIZE, buf.length);
252
+ this._encodeSlice(buf.subarray(start, end), end < buf.length ? buf[end] : undefined);
253
+ this._lastByte = buf[end - 1];
254
+ }
255
+ }
256
+ /** @internal */
257
+ _encodeSlice(buf, next) {
258
+ let qp;
259
+ if (this.options.lineLength) {
260
+ qp = wrap(this._curLine + encodeBytes(buf, next, this.options.binary, this._lastByte), this.options.lineLength);
261
+ // the last line is kept until it is known whether it needs a soft break
262
+ const lastLF = qp.lastIndexOf('\n');
263
+ this._curLine = qp.substring(lastLF + 1);
264
+ qp = qp.substring(0, lastLF + 1);
265
+ if (qp) {
266
+ this.outputBytes += qp.length;
267
+ this.push(qp, 'ascii');
268
+ }
269
+ }
270
+ else {
271
+ qp = encodeBytes(buf, next, this.options.binary, this._lastByte);
272
+ this.outputBytes += qp.length;
273
+ this.push(qp, 'ascii');
274
+ }
275
+ }
206
276
  }
207
277
  exports.Encoder = Encoder;