-
Notifications
You must be signed in to change notification settings - Fork 303
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
Implements globalThis.Cloudflare.compatibilityFlags API #2527
Conversation
affe1e3
to
5c03173
Compare
47d97aa
to
511ab4c
Compare
This PR should be ready for review. |
This comment was marked as resolved.
This comment was marked as resolved.
6ea42b8
to
8555fdf
Compare
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
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.
Looks good to me 🙂 (from just a js/ts point of view)
This is otherwise ready to land but going to hold off until @kentonv signs off. |
This looks great! Given there's some complexity around My understanding is that 99% of the time, you're just going to want to check if a particular flag And all the flags listed here will be present: https://developers.cloudflare.com/workers/configuration/compatibility-dates/, correct? And by default all the |
Not sure I completely follow the question so hopefully this answers it by way of example: The If we have a And yes, tor the most part, most users will generally only be checking for |
@jasnell sorry if it's a silly question... I was just wondering, would there be any benefit in also exposing the compat date? or is it pretty unnecessary since the compat date basically just dictates the compat flags? (so those are ultimately all the ones you need to know) |
I was debating the same thing. Short answer is I don't know. I'm not sure it's going to be that useful, certainly possible tho. I wouldn't do it in this PR tho. |
ok thanks, I was just curious 🙂👍 |
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 very supportive of this feature, but let's please not rush merging this in.
Could we please settle the following first:
- why do we care about CJS support? (in other words, why not
import.meta.cloudflare
) https://x.com/jasnell/status/1824114538272411825 - why capital
C
in "globalThis.Cloudflare"? As far as I can think of, it is customary to use lower-case letters in JS for variables like this one, for exampleglobalThis.navigator
orglobalThis.process
. - are we intentionally not exposing the
compatibility_date
? I'm unsure if we should, so I'm mainly wondering if the omission is intentional. - can you please document the plan for typescript support? without a design doc, it's not clear to me if this will just work or if we need to do anything special. I expect that we'll need to change RTT to support dynamic type generation that is currently in works by @andyjessop
- can we ensure that these flags use an API that enable tree-shaking of the code, I don't believe that's the case now due to
'on'
and'off'
values instead of usingboolean
type
|
These are good argument for capital C. Thanks @mhart |
There is no rush.
The vast majority of modules on npm are still commonjs and will remain so for an indefinite amount of time.
I would also note that I've received direct feedback from ecosystem module maintainers that
#2527 (comment) .... short answer is: I'm definitely open to adding it if there's a useful use case for it.
The type of
How so?
Can you explain a bit more why the current proposed API doesn't work for tree shaking? Or, more usefully, propose an alternative API that would work better for tree shaking? |
I think we should go back to boolean values. With the string on/off values, since it is unintuitive, people are likely to forget and write truthy checks instead, and then spend hours trying to figure out why workerd is ignoring them when they try to configure the flag to be off in their tests. I don't think the string values solve the problem with negative flags. People will still write I think there are really two ways forward on that:
I could be convinced either way.
I would argue against exposing it, otherwise people might check against the date instead of against the specific flag, which would work most of the time but would be incorrect. Also, at present we don't actually have the compat date available on the edge -- it is consumed by the config service and converted to a boolean list before deployment. |
What about something like this? type CompatibilityFlags = {
enabled: Set<string>;
disabled: Set<string>;
};
interface Cloudflare {
compatibilityFlag: CompatibilityFlags;
} Seems very clear and not easy to get wrong to me, plus with |
@dario-piotrowicz That still has the same backwards-compatibility problem. If I write |
PR updated with the changes:
|
Why? That means you can't check Not necessarily saying I disagree with the change but was wondering what the motivation is. Sorry if I missed it somewhere in the long thread. |
#2527 (comment) Bullet point 1 I'd rather not have an API that throws on a known property getter as that carries its own DX hurdles. |
Oh, but this isn't what I was asking for there. By "negative flags" I didn't mean flags whose value is false, I meant flags declared with My suggestion is that we do not include disable-flags in the API at all, and instead tell people to test for the enable-flag, so that treating undefined as false is correct. |
f7287af
to
402c352
Compare
Updated to include all enable flags. |
504a442
to
a68b472
Compare
Rebased on master to resolve merge conflicts after the formatting change. |
A simple built-in module and API for determining if a given compat flag is set. ``` const { compatibilityFlags } = globalThis.Cloudflare; console.log(compatibilityFlags['url_standard']); // 'on' or 'off' console.log(compatibilityFlags['url_original']); // 'on' or 'off' ```
a68b472
to
7543825
Compare
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.
The logic regarding experimental flags seems odd to me but not enough to hold this up further.
Ok, just waiting on a final review from @IgorMinar |
Exactly!!! I'm reviewing the lastest, but I'm so happy that this is where we ended up. Full review soon. |
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 like this version of flags api a lot! thank you @jasnell and all the reviewers for patiently iterating on this. I no longer have any concerns and I'm happy with this being merged.
I would still love to get an ok from the @andyjessop or someone (@penalosa, @petebacondarwin,
@lrapoport-cf ) from the wrangler team on the typescript support as I requested a while back.
I do, however, think the API is simple enough now and TS support should be non-controversial and unless they review quickly we don't need to wait for them and instead to a post-merge review.
export const compatFlagsTest = { | ||
test() { | ||
// The compatibility flags object should be sealed, frozen, and not extensible. | ||
throws(() => (compatibilityFlags.no_nodejs_compat_v2 = '...')); |
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.
now that we don't support disable flags, this test throws for a reason that is different than when the test was originally written, and is the same assertion as not_a_real_compat_flag
. I suggest you either add a comment to say that you are explicitly testing for disable flags which should work as non-existent flags, or remove this line altogether.
ok(compatibilityFlags['nodejs_compat_v2']); | ||
ok(compatibilityFlags['url_standard']); | ||
|
||
ok(!compatibilityFlags['no_nodejs_compat_v2']); |
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 assertion doesn't prove that the value is undefined. I suggest that you test using in
rather than by potentially coercing the return value via !
|
||
ok(!compatibilityFlags['no_nodejs_compat_v2']); | ||
ok(!compatibilityFlags['url_original']); | ||
strictEqual(compatibilityFlags['no_nodejs_compat_v2'], undefined); |
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.
oh, given this test, I'd remove all the ok(!
assertions above as they only test that javascript works as expected.
Alternatively, it might be valuable to test for a lack of presence of the property descriptor via in
or getOwnPropertyDescriptor
.
ok(!compatibilityFlags['no_nodejs_compat_v2']); | ||
ok(!compatibilityFlags['url_original']); | ||
strictEqual(compatibilityFlags['no_nodejs_compat_v2'], undefined); | ||
strictEqual(compatibilityFlags['url_original'], undefined); |
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.
the same as above
|
||
// Since we are not specifying the experimental flag, experimental flags should | ||
// not be included in the output. | ||
strictEqual(compatibilityFlags['durable_object_rename'], undefined); |
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 instead assert that the property descriptor is missing? it's a more solid assertion.
// Since we are not specifying the experimental flag, experimental flags should | ||
// not be included in the output. | ||
strictEqual(compatibilityFlags['durable_object_rename'], undefined); | ||
strictEqual('durable_object_rename' in compatibilityFlags, false); |
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.
ah.. you do, so I'd remove the first one as that only tests javascript's behavior and not our use of it.
strictEqual('durable_object_rename' in compatibilityFlags, false); | ||
|
||
// If a flag does not exist, the value will be undefined. | ||
strictEqual(compatibilityFlags['not-a-real-compat-flag'], undefined); |
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.
same as above, I'd remove
ok(keys.includes('url_standard')); | ||
ok(!keys.includes('url_original')); | ||
ok(!keys.includes('not-a-real-compat-flag')); | ||
}, |
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.
yeah. this is great!
A simple built-in API for determining if a given compat flag is set.
Internal discussion wiki: (sorry external folks, you won't be able to see this) https://wiki.cfdata.org/display/~jsnell/Exposing+active+compat+flags+to+workers