-
Notifications
You must be signed in to change notification settings - Fork 129
Expand file tree
/
Copy pathcompatibility.js
More file actions
160 lines (143 loc) · 6.24 KB
/
compatibility.js
File metadata and controls
160 lines (143 loc) · 6.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
function versionToInt(version) {
// convert v2 to 2
return parseInt(version.replace('v', ''));
}
/**
* Remove features from `config` that have reached their end of life.
* This function will also remove the `eol` key from features when it
* matches the current version of `config`.
*
* @param {object} config - the config object to remove eol features from
* @param {int} version - the version of the config object
*/
export function removeEolFeatures(config, version) {
for (const feature of Object.keys(config.features)) {
const eol = config.features[feature].eol;
if (!eol) {
continue;
}
const eolInt = versionToInt(eol);
if (eolInt < version) {
// This feature's support ends in a previous config so remove it from platformConfig
delete config.features[feature];
} else if (eolInt >= version) {
// Remove the eol key if it matches the current version
delete config.features[feature].eol;
}
}
}
/**
* The compat functions are used to convert a config object to a previous version.
* Each previous version of the config should have an entry which modifies the config
* to be compatible with that version.
*
* Compat function format:
* param {object} config - the config object to convert. This config should be one version higher than the target version.
* return {object} - The converted config object
*/
export const compatFunctions = {
v1: (config) => {
// Breaking changes: minSupportedVersion key in features
const v1Config = JSON.parse(JSON.stringify(config));
for (const feature of Object.keys(v1Config.features)) {
if (v1Config.features[feature].minSupportedVersion) {
delete v1Config.features[feature].minSupportedVersion;
v1Config.features[feature].state = 'disabled';
}
}
return v1Config;
},
v2: (config) => {
// Breaking changes: rollout key in sub-features
const v2Config = JSON.parse(JSON.stringify(config));
for (const feature of Object.keys(v2Config.features)) {
const subFeatures = v2Config.features[feature].features;
if (subFeatures) {
for (const subFeature of Object.keys(subFeatures)) {
if (subFeatures[subFeature].rollout) {
delete subFeatures[subFeature].rollout;
v2Config.features[feature].features[subFeature].state = 'disabled';
}
}
}
}
return v2Config;
},
v3: (config, unmodifiedConfig, platform) => {
// Breaking changes: none, reasons stripped starting in v4
const v3Config = JSON.parse(JSON.stringify(config));
/**
* This function will take reasons from the source array and assign them to the target array.
*
* @param {Array} target - the array to assign reasons to
* @param {Array} source - the array to pull reasons from
*/
function assignReasons(target, source) {
target = target.map((exception, i) => {
const reason = source[i]?.reason || '';
return Object.assign(exception, { reason });
});
}
// Replace reasons
for (const key of Object.keys(unmodifiedConfig.features)) {
v3Config.features[key].exceptions = v3Config.features[key].exceptions || [];
assignReasons(v3Config.features[key].exceptions, unmodifiedConfig?.features[key]?.exceptions);
if (key === 'trackerAllowlist') {
for (const domain of Object.keys(unmodifiedConfig.features[key].settings.allowlistedTrackers)) {
const rules = v3Config.features[key].settings.allowlistedTrackers[domain].rules;
assignReasons(rules, unmodifiedConfig?.features[key]?.settings?.allowlistedTrackers[domain]?.rules);
}
}
if (key === 'customUserAgent') {
if (unmodifiedConfig.features[key].settings.omitApplicationSites) {
assignReasons(
v3Config.features[key].settings.omitApplicationSites,
unmodifiedConfig.features[key].settings.omitApplicationSites,
);
}
if (unmodifiedConfig.features[key].settings.omitVersionSites) {
assignReasons(
v3Config.features[key].settings.omitVersionSites,
unmodifiedConfig.features[key].settings.omitVersionSites,
);
}
}
}
// Change "internal" feature + sub-feature state to "disabled" for the
// v3 Windows config. Older versions of the Windows browser cannot parse
// configurations that use the "internal" state.
if (platform === 'windows') {
for (const feature of Object.values(v3Config.features)) {
if (feature.state === 'internal') {
feature.state = 'disabled';
}
if (feature.features) {
for (const subFeature of Object.values(feature.features)) {
if (subFeature.state === 'internal') {
subFeature.state = 'disabled';
}
}
}
}
}
return v3Config;
},
v4: (config, unmodifiedConfig, platform) => {
// Breaking changes: added preview state for features and sub-features.
const v4Config = JSON.parse(JSON.stringify(config));
// [Windows] Invalid states for features and sub-features will be automatically set to "disabled" from Windows Release v0.118.0
for (const feature of Object.values(v4Config.features)) {
if (feature.state === 'preview') {
feature.state = 'disabled';
}
if (feature.features) {
for (const subFeature of Object.values(feature.features)) {
if (subFeature.state === 'preview') {
subFeature.state = 'disabled';
}
}
}
}
return v4Config;
},
};