send.js 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import {promisify} from 'node:util';
  2. import {
  3. validateIpcMethod,
  4. handleEpipeError,
  5. handleSerializationError,
  6. disconnect,
  7. } from './validation.js';
  8. import {startSendMessage, endSendMessage} from './outgoing.js';
  9. import {handleSendStrict, waitForStrictResponse} from './strict.js';
  10. // Like `[sub]process.send()` but promise-based.
  11. // We do not `await subprocess` during `.sendMessage()` nor `.getOneMessage()` since those methods are transient.
  12. // Users would still need to `await subprocess` after the method is done.
  13. // Also, this would prevent `unhandledRejection` event from being emitted, making it silent.
  14. export const sendMessage = ({anyProcess, channel, isSubprocess, ipc}, message, {strict = false} = {}) => {
  15. const methodName = 'sendMessage';
  16. validateIpcMethod({
  17. methodName,
  18. isSubprocess,
  19. ipc,
  20. isConnected: anyProcess.connected,
  21. });
  22. return sendMessageAsync({
  23. anyProcess,
  24. channel,
  25. methodName,
  26. isSubprocess,
  27. message,
  28. strict,
  29. });
  30. };
  31. const sendMessageAsync = async ({anyProcess, channel, methodName, isSubprocess, message, strict}) => {
  32. const wrappedMessage = handleSendStrict({
  33. anyProcess,
  34. channel,
  35. isSubprocess,
  36. message,
  37. strict,
  38. });
  39. const outgoingMessagesState = startSendMessage(anyProcess, wrappedMessage, strict);
  40. try {
  41. await sendOneMessage({
  42. anyProcess,
  43. methodName,
  44. isSubprocess,
  45. wrappedMessage,
  46. message,
  47. });
  48. } catch (error) {
  49. disconnect(anyProcess);
  50. throw error;
  51. } finally {
  52. endSendMessage(outgoingMessagesState);
  53. }
  54. };
  55. // Used internally by `cancelSignal`
  56. export const sendOneMessage = async ({anyProcess, methodName, isSubprocess, wrappedMessage, message}) => {
  57. const sendMethod = getSendMethod(anyProcess);
  58. try {
  59. await Promise.all([
  60. waitForStrictResponse(wrappedMessage, anyProcess, isSubprocess),
  61. sendMethod(wrappedMessage),
  62. ]);
  63. } catch (error) {
  64. handleEpipeError({error, methodName, isSubprocess});
  65. handleSerializationError({
  66. error,
  67. methodName,
  68. isSubprocess,
  69. message,
  70. });
  71. throw error;
  72. }
  73. };
  74. // [sub]process.send() promisified, memoized
  75. const getSendMethod = anyProcess => {
  76. if (PROCESS_SEND_METHODS.has(anyProcess)) {
  77. return PROCESS_SEND_METHODS.get(anyProcess);
  78. }
  79. const sendMethod = promisify(anyProcess.send.bind(anyProcess));
  80. PROCESS_SEND_METHODS.set(anyProcess, sendMethod);
  81. return sendMethod;
  82. };
  83. const PROCESS_SEND_METHODS = new WeakMap();