-
Notifications
You must be signed in to change notification settings - Fork 822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds workbox.streams #1439
Adds workbox.streams #1439
Conversation
@@ -3,15 +3,17 @@ module.exports = async (swUrl) => { | |||
let error = await global.__workbox.webdriver.executeAsyncScript((swUrl, cb) => { | |||
const onStateChangePromise = (registration, desiredState) => { | |||
return new Promise((resolve, reject) => { | |||
if (registration.installing === null) { | |||
throw new Error('Regstration is not installing'); | |||
if (desiredState === 'activated' && registration.active) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made some tweaks to this logic because I've seen some flakiness with the latest Safari TP, where registration.installing
was null
and this function gave up.
I think it's less prone to race conditions with these changes, since if we have a registration.active
and the state we care about is 'activated'
, that should be fine.
@@ -18,7 +18,7 @@ | |||
// https://fetch.spec.whatwg.org/#headers-class | |||
class Headers { | |||
constructor(obj = {}) { | |||
this.obj = obj; | |||
this.obj = Object.assign({}, obj); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works around an issue that I encountered in the tests.
https://github.com/pinterest/service-workers/blob/master/packages/service-worker-mock/models/Headers.js looks like it's much more robust than it used to be, and perhaps we should migrate off of our own mock at some point in the future.
}; | ||
} | ||
|
||
export default { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm (still) not 100% sure what belongs in the _default.mjs
's named exports and what doesn't.
I think this leads to a decent public interface, and I can use workbox.streams.strategy()
, workbox.streams.isSupported()
, etc. with this setup. But please double-check my thinking.
|
||
import './_version.mjs'; | ||
|
||
export { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to my previous comment about uncertainty re: what should be in the _default.mjs
export, I'm not 100% sure here, either.
fullyStreamedReject = reject; | ||
}); | ||
|
||
if (process.env.NODE_ENV !== 'production') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you change this to a single group:
Concatenating 5 sources.
And instead of ...reading...
, Reached the end of that source
under each group, could you just add success or failure along with the final response.
While I understand the desire to log the requests as the state changes, it doesn't work with console groups as everything gets out of sync and ends up accidentally nesting and mixing - instead you need to capture the logs and then log everything at once.
@@ -0,0 +1,41 @@ | |||
const {expect} = require('chai'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating meaningful node
-based mocks for all the primitives needed by workbox.streams
wasn't feasible, so I'm relying on the integration tests to exercise most of the actual logic.
() => `<p>this was executed at ${Date.now()}</p>`, | ||
({event}) => httpBinStrategy.makeRequest({ | ||
event, | ||
request: 'https://httpbin.org/bytes/12', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't use a third party.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Core logic looks good. Largely minor comments on the log and demo.
event.waitUntil((async () => { | ||
const cache = await caches.open(CACHE_NAME); | ||
await Promise.all([ | ||
cache.put(START_CACHE_KEY, new Response('<html><head></head><body>')), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be preferable to make this a little more "fun".
Perhaps you could have an iframe that displays a HTML page indicating its not streamed and with SW installed it loads a HTML page with a header and footer with a party popper in the middle. This highlights a clear use case of header and footer and party popper highlights its working.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
* `'text/html'` will be used by default. | ||
* @return {workbox.routing.Route~handlerCallback} | ||
* | ||
* @memberof workbox.streams |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work if you run gulp docs?
I think you need to define the @module workbox.streams before you assign memberof's
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
concatenate, | ||
concatenateToResponse, | ||
isSupported, | ||
strategy, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure strategy
is the best name here. workbox.streams.join()
workbox.streams.run()
workbox.streams.merge()
would explain the intent a little easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
join()
and merge()
are effectively synonyms of the existing concatenate()
, and I feel like using those names would confuse things and not make it clear what's different about strategy()
.
The thinking behind strategy()
is that this is an interface that matches the interfaces exposed under workbox.strategies.*
. So if a developer is familiar with one of the existing strategy interfaces, they'd presumably get that workbox.streams.strategy()
is doing the same thing.
If you'd really prefer something other than strategy()
, I guess I'd be okay with runt()
, but handle()
would be my first alternative choice. Or we could stick with strategy()
😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this having the same API means it should have the same name unless it is behaving the same.
// TODO: This should be possible to do by constructing a ReadableStream, but | ||
// I can't get it to work. As a hack, construct a new Response, and use the | ||
// reader associated with its body. | ||
return new Response(source).body.getReader(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @jaffathecake in case he has any ideas
fullyStreamedReject = reject; | ||
}); | ||
|
||
if (process.env.NODE_ENV !== 'production') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you change this to a single group:
Concatenating 5 sources.
And instead of ...reading...
, Reached the end of that source
under each group, could you just add success or failure along with the final response.
While I understand the desire to log the requests as the state changes, it doesn't work with console groups as everything gets out of sync and ends up accidentally nesting and mixing - instead you need to capture the logs and then log everything at once.
logStartOfGroup = true; | ||
if (process.env.NODE_ENV !== 'production') { | ||
logger.log('Reached the end of that source.'); | ||
logger.groupEnd(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dynamic log groups like this will cause issues - we had with some of the previous strategies. Instead you need to stash the logs you want to make and then log everything together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the heads-up about nesting and groups. I've refactored that.
I don't have access to the new Response
at this point in the code, only to the ReadableStream
. That doesn't end up exposing any useful information when it's logged to the console.
I'm going to take advantage of the fact that the Response
will normally be logged by folks who use our Router
class, and consider that sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to get @philipwalton's +2
}, | ||
}); | ||
|
||
return {done, stream}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remember we had a discussion before about whether done
was really needed for waitUntil
or whether respondWith
was sufficient to keep the SW alive. Did we ever confirm that one way or another?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I confirmed with Jake that while the relevant specifications are pointing towards this no longer being required in the future, the current implementation in Chrome (and likely other browsers) does require explicitly calling waitUntil()
to guarantee the SW is kept alive until the stream is drained.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, cool. Thanks for confirming.
LGTM |
R: @addyosmani @gauntface
Fixes #621
I'll add some inline comments with anything that I'd imagine could be contentious/questionable to aid in the review.