@zengenti/contensis-react-base 4.0.0-beta.59 → 4.0.0-beta.60

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.
@@ -835,8 +835,8 @@ const handleResponse = (request, response, content, send = 'send') => {
835
835
  * @param response the express Response object
836
836
  * @param stream all chunks are piped to this stream to add additional style elements to each streamed chunk
837
837
  */
838
- const renderStream = (getContextHtml, jsx, response, stream) => {
839
- // Store timeout reference for cleanup
838
+ const renderStream = (getContextHtml, jsx, request, response, stream) => {
839
+ // Store timeout reference for cleanup on normal or abnormal termination
840
840
  let timeoutId = null;
841
841
  const disposeTimeout = () => {
842
842
  if (timeoutId) {
@@ -844,6 +844,22 @@ const renderStream = (getContextHtml, jsx, response, stream) => {
844
844
  timeoutId = null;
845
845
  }
846
846
  };
847
+
848
+ // Only used for abnormal termination
849
+ const abortCleanup = err => {
850
+ disposeTimeout();
851
+ stream.destroy(err instanceof Error ? err : undefined);
852
+ abort();
853
+ };
854
+
855
+ // Guard against client disconnect
856
+ request.on('close', () => abortCleanup());
857
+
858
+ // Guard against transform errors
859
+ stream.on('error', err => {
860
+ abortCleanup(err);
861
+ if (!response.headersSent) response.destroy(err);
862
+ });
847
863
  const {
848
864
  abort,
849
865
  pipe
@@ -852,8 +868,7 @@ const renderStream = (getContextHtml, jsx, response, stream) => {
852
868
  const html = getContextHtml(false);
853
869
  if (!html) {
854
870
  // this means we have finished with the response already
855
- disposeTimeout();
856
- abort();
871
+ abortCleanup();
857
872
  } else {
858
873
  const header = html.split('{{APP}}')[0];
859
874
  response.setHeader('content-type', 'text/html; charset=utf-8');
@@ -864,10 +879,10 @@ const renderStream = (getContextHtml, jsx, response, stream) => {
864
879
  onAllReady() {
865
880
  const footer = getContextHtml(true).split('{{APP}}')[1];
866
881
  stream.write(footer);
867
- disposeTimeout(); // Clean up timeout when stream completes
882
+ disposeTimeout(); // Clear the timeout, let stream end naturally
868
883
  },
869
884
  onShellError(error) {
870
- disposeTimeout(); // Clean up timeout on error
885
+ abortCleanup(error); // Abnormal - destroy everything
871
886
  response.statusCode = 500;
872
887
  response.setHeader('content-type', 'text/html; charset=utf-8');
873
888
  response.send('<h1>Something went wrong</h1>');
@@ -881,10 +896,10 @@ const renderStream = (getContextHtml, jsx, response, stream) => {
881
896
  // Abandon and switch to client rendering after 30s.
882
897
  // Try lowering this to see the client recover.
883
898
  timeoutId = setTimeout(() => {
884
- timeoutId = null; // Clear reference when timeout executes
885
- abort();
886
- }, 30 * 1000);
887
- stream === null || stream === void 0 || stream.pipe(response);
899
+ timeoutId = null;
900
+ abortCleanup();
901
+ }, 30_000);
902
+ stream.pipe(response);
888
903
  };
889
904
 
890
905
  /**
@@ -924,6 +939,23 @@ const styledComponentsStream = sheet => {
924
939
  this.push(styledCSS + renderedHtml);
925
940
  }
926
941
  callback();
942
+ },
943
+ destroy(err, callback) {
944
+ // Called on both stream.destroy() and natural end
945
+ // Stops the sheet intercepting styles & releases its references
946
+
947
+ // try/catch is required if sheet.seal() throws for any reason,
948
+ // callback(err) must still be called, as Node.js stream internals depend
949
+ // on it to complete teardown. Omitting it causes the stream to hang.
950
+ try {
951
+ sheet.seal();
952
+ } catch (sealErr) {
953
+ // Catch any errors from sealing the sheet, we MUST always call the
954
+ // callback to prevent hanging the stream
955
+
956
+ console.error('[styledComponentsStream] sheet.seal() failed - styles may leak:', sealErr);
957
+ }
958
+ callback(err);
927
959
  }
928
960
  });
929
961
  return readerWriter;
@@ -1548,7 +1580,7 @@ const webApp = (app, ReactApp, config) => {
1548
1580
  const responseHTML = getContextHtml(true, styleTags, html);
1549
1581
  responseHandler(request, response, responseHTML);
1550
1582
  } else {
1551
- renderStream(getContextHtml, styledJsx, response, styledComponentsStream(sheet));
1583
+ renderStream(getContextHtml, styledJsx, request, response, styledComponentsStream(sheet));
1552
1584
  }
1553
1585
  } catch (err) {
1554
1586
  console.info(err.message);