@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.
@@ -853,8 +853,8 @@ const handleResponse = (request, response, content, send = 'send') => {
853
853
  * @param response the express Response object
854
854
  * @param stream all chunks are piped to this stream to add additional style elements to each streamed chunk
855
855
  */
856
- const renderStream = (getContextHtml, jsx, response, stream) => {
857
- // Store timeout reference for cleanup
856
+ const renderStream = (getContextHtml, jsx, request, response, stream) => {
857
+ // Store timeout reference for cleanup on normal or abnormal termination
858
858
  let timeoutId = null;
859
859
  const disposeTimeout = () => {
860
860
  if (timeoutId) {
@@ -862,6 +862,22 @@ const renderStream = (getContextHtml, jsx, response, stream) => {
862
862
  timeoutId = null;
863
863
  }
864
864
  };
865
+
866
+ // Only used for abnormal termination
867
+ const abortCleanup = err => {
868
+ disposeTimeout();
869
+ stream.destroy(err instanceof Error ? err : undefined);
870
+ abort();
871
+ };
872
+
873
+ // Guard against client disconnect
874
+ request.on('close', () => abortCleanup());
875
+
876
+ // Guard against transform errors
877
+ stream.on('error', err => {
878
+ abortCleanup(err);
879
+ if (!response.headersSent) response.destroy(err);
880
+ });
865
881
  const {
866
882
  abort,
867
883
  pipe
@@ -870,8 +886,7 @@ const renderStream = (getContextHtml, jsx, response, stream) => {
870
886
  const html = getContextHtml(false);
871
887
  if (!html) {
872
888
  // this means we have finished with the response already
873
- disposeTimeout();
874
- abort();
889
+ abortCleanup();
875
890
  } else {
876
891
  const header = html.split('{{APP}}')[0];
877
892
  response.setHeader('content-type', 'text/html; charset=utf-8');
@@ -882,10 +897,10 @@ const renderStream = (getContextHtml, jsx, response, stream) => {
882
897
  onAllReady() {
883
898
  const footer = getContextHtml(true).split('{{APP}}')[1];
884
899
  stream.write(footer);
885
- disposeTimeout(); // Clean up timeout when stream completes
900
+ disposeTimeout(); // Clear the timeout, let stream end naturally
886
901
  },
887
902
  onShellError(error) {
888
- disposeTimeout(); // Clean up timeout on error
903
+ abortCleanup(error); // Abnormal - destroy everything
889
904
  response.statusCode = 500;
890
905
  response.setHeader('content-type', 'text/html; charset=utf-8');
891
906
  response.send('<h1>Something went wrong</h1>');
@@ -899,10 +914,10 @@ const renderStream = (getContextHtml, jsx, response, stream) => {
899
914
  // Abandon and switch to client rendering after 30s.
900
915
  // Try lowering this to see the client recover.
901
916
  timeoutId = setTimeout(() => {
902
- timeoutId = null; // Clear reference when timeout executes
903
- abort();
904
- }, 30 * 1000);
905
- stream === null || stream === void 0 || stream.pipe(response);
917
+ timeoutId = null;
918
+ abortCleanup();
919
+ }, 30_000);
920
+ stream.pipe(response);
906
921
  };
907
922
 
908
923
  /**
@@ -942,6 +957,23 @@ const styledComponentsStream = sheet => {
942
957
  this.push(styledCSS + renderedHtml);
943
958
  }
944
959
  callback();
960
+ },
961
+ destroy(err, callback) {
962
+ // Called on both stream.destroy() and natural end
963
+ // Stops the sheet intercepting styles & releases its references
964
+
965
+ // try/catch is required if sheet.seal() throws for any reason,
966
+ // callback(err) must still be called, as Node.js stream internals depend
967
+ // on it to complete teardown. Omitting it causes the stream to hang.
968
+ try {
969
+ sheet.seal();
970
+ } catch (sealErr) {
971
+ // Catch any errors from sealing the sheet, we MUST always call the
972
+ // callback to prevent hanging the stream
973
+
974
+ console.error('[styledComponentsStream] sheet.seal() failed - styles may leak:', sealErr);
975
+ }
976
+ callback(err);
945
977
  }
946
978
  });
947
979
  return readerWriter;
@@ -1566,7 +1598,7 @@ const webApp = (app, ReactApp, config) => {
1566
1598
  const responseHTML = getContextHtml(true, styleTags, html);
1567
1599
  responseHandler(request, response, responseHTML);
1568
1600
  } else {
1569
- renderStream(getContextHtml, styledJsx, response, styledComponentsStream(sheet));
1601
+ renderStream(getContextHtml, styledJsx, request, response, styledComponentsStream(sheet));
1570
1602
  }
1571
1603
  } catch (err) {
1572
1604
  console.info(err.message);