{"push_id":2768309132,"size":1,"distinct_size":1,"ref":"refs/heads/master","head":"1b1050bfc3cf0c5f800d5ceba5f19a4052791886","before":"6e8417ed6e0f427a656d3bf4e6de197edddfe491","commits":[{"sha":"1b1050bfc3cf0c5f800d5ceba5f19a4052791886","author":{"name":"Michał Gołębiowski-Owczarek","email":"644fc9bb9554b22049aee092b10c2ddb3ab4b891@gmail.com"},"message":"fix: resolve Preboot race conditions (#83)\n\nCurrently Preboot suffers from various race conditions:\r\n\r\n1. It waits for the `document.body` to be available and then applies its logic.\r\nHowever, the application root may not be available at this point - see the issue\r\n#72.\r\n\r\n2. Even if the application root exists when Preboot starts listening for events\r\nthe server node may not be available in full yet. Since Preboot searches for\r\nnodes matching desired selectors inside of the existing `serverNode`, it may\r\nskip some of them if the server node hasn't loaded fully. This is especially\r\ncommon if the internet connection is slow.\r\n\r\n3. Preboot waits for `body` in a tight loop, checking every 10 milliseconds.\r\nThis starves the browser but may also be clamped by it to a larger delay\r\n(around 1 second); that's what happens in modern browsers if the tab where the\r\npage loads is inactive which is what happens if you open a link in a new tab in\r\nChrome or Firefox. This means Preboot may start listening for events after\r\nAngular reports the app is stable and the `PrebootComplete` event fires. This\r\nthen leaves the server node active, never transferring to the client one which\r\nmakes for a broken site.\r\n\r\nTo solve it, we're doing the following:\r\n\r\n1. Since we want to attach event listeners as soon as possible when the server\r\nnode starts being available (so that it can be shallow-cloned) we cannot wait\r\nfor it to be available in full. Therefore, we switched to delegated event\r\nhandlers on each of the app roots instead of directly on specific nodes.\r\n\r\n2. As we support multiple app roots, to not duplicate large inline preboot code,\r\nwe've split `getInlinePrebootCode` into two parts: function definitions to be\r\nput in `<head>` and the invocation separate for each app root put just after\r\nthe app root opening tag.\r\n\r\n3. To maintain children offset numbers (otherwise selectors won't match between\r\nthe client & the server) we've removed the in-app-root script immediately when\r\nit starts executing; this won't stop the execution. `document.currentScript` is\r\na good way to find the currently running script but it doesn't work in IE. A\r\nfallback behavior is applied to IE that leverages the fact that start scripts\r\nare invoked synchronously so the current script is the last one currently in\r\nthe DOM.\r\n\r\n4. We've removed `waitUntilReady` as there's no reliable way to wait; we'll\r\ninvoke the init code immediately instead.\r\n\r\n5. We've switched the overlay used for freeing the UI to attach to\r\n`document.documentElement` instead of `document.body` so that we don't have to\r\nwait for `body`.\r\n\r\n6. The mentioned overlay is now created for each app root separately to avoid\r\nrace conditions.\r\n\r\nFixes #82\r\nFixes #72\r\n\r\nBREAKING CHANGES:\r\n\r\n1. When used in a non-Angular app, the code needs to be updated to use\r\n`getInlineDefinition` and `getInlineInvocation`; the previously defined\r\n`getInlinePrebootCode` has been removed. The `getInlineDefinition` output\r\nneeds to be placed before any content user might interactive with, preferably\r\nin <head>. The `getInlineInvocation` output needs to be put just after the\r\nopening tag of each app root. Only `getInlineDefinition` needs to have options\r\npassed. An example expected (simplified) layout in EJS format:\r\n\r\n```html\r\n<html>\r\n  <head>\r\n    <script><%= getInlineDefinition(options) %></script>\r\n  </head>\r\n  <body>\r\n    <app1-root>\r\n      <script><%= getInlineInvocation() %></script>\r\n      <h2>App1 header</h2>\r\n      <div>content</div>\r\n    </app1-root>\r\n    <app2-root>\r\n      <script><%= getInlineInvocation() %></script>\r\n      <h2>App2 header</h2>\r\n      <span>content</span>\r\n    </app2-root>\r\n  </body>\r\n</html>\r\n```\r\n\r\n2. The refactor breaks working with frameworks that replace elements\r\nlike AngularJS with UI Router as it no longer uses serverSelector and\r\nclientSelector but expects its clientNode to remain in the DOM.","distinct":true,"url":"https://api.github.com/repos/angular/preboot/commits/1b1050bfc3cf0c5f800d5ceba5f19a4052791886"}]}
{"actor":{"display_login":"CaerusKaru"}}