@@ -21,16 +21,186 @@ import { extensionConstructMessagingConfig } from './sendmessage-transport.js';
21
21
* @property {string[] } [enabledFeatures]
22
22
*/
23
23
24
- export default class ContentFeature {
24
+ class ConfigParser {
25
25
/** @type {import('./utils.js').RemoteConfig | undefined } */
26
26
#bundledConfig;
27
+
28
+ /** @type {import('./content-scope-features.js').LoadArgs } */
29
+ #loadArgs;
30
+
31
+ /** @type {Record<string, unknown> | undefined } */
32
+ #featureSettings;
33
+
34
+ /** @type {any } */
35
+ name ;
36
+
27
37
/** @type {object | undefined } */
28
38
#trackerLookup;
39
+
29
40
/** @type {boolean | undefined } */
30
41
#documentOriginIsTracker;
31
- /** @type {Record<string, unknown> | undefined } */
32
- // eslint-disable-next-line no-unused-private-class-members
33
- #bundledfeatureSettings;
42
+
43
+ /**
44
+ * @param {any } name
45
+ */
46
+ constructor ( name ) {
47
+ this . name = name ;
48
+ }
49
+
50
+ /**
51
+ * @param {import('./content-scope-features.js').LoadArgs } loadArgs
52
+ */
53
+ initConfig ( loadArgs ) {
54
+ const { bundledConfig, site, platform } = loadArgs ;
55
+ this . #bundledConfig = bundledConfig ;
56
+ // If we have a bundled config, treat it as a regular config
57
+ // This will be overriden by the remote config if it is available
58
+ if ( this . #bundledConfig) {
59
+ const enabledFeatures = computeEnabledFeatures ( bundledConfig , site . domain , platform . version ) ;
60
+ this . #featureSettings = parseFeatureSettings ( bundledConfig , enabledFeatures ) ;
61
+ }
62
+ this . #trackerLookup = loadArgs . trackerLookup ;
63
+ this . #documentOriginIsTracker = loadArgs . documentOriginIsTracker ;
64
+ }
65
+
66
+ /**
67
+ * Given a config key, interpret the value as a list of domain overrides, and return the elements that match the current page
68
+ * Consider using patchSettings instead as per `getFeatureSetting`.
69
+ * @param {string } featureKeyName
70
+ * @return {any[] }
71
+ * @protected
72
+ */
73
+ matchDomainFeatureSetting ( featureKeyName ) {
74
+ const domain = this . #loadArgs?. site . domain ;
75
+ if ( ! domain ) return [ ] ;
76
+ const domains = this . _getFeatureSettings ( ) ?. [ featureKeyName ] || [ ] ;
77
+ return domains . filter ( ( rule ) => {
78
+ if ( Array . isArray ( rule . domain ) ) {
79
+ return rule . domain . some ( ( domainRule ) => {
80
+ return matchHostname ( domain , domainRule ) ;
81
+ } ) ;
82
+ }
83
+ return matchHostname ( domain , rule . domain ) ;
84
+ } ) ;
85
+ }
86
+
87
+ /**
88
+ * Return the settings object for a feature
89
+ * @param {string } [featureName] - The name of the feature to get the settings for; defaults to the name of the feature
90
+ * @returns {any }
91
+ */
92
+ _getFeatureSettings ( featureName ) {
93
+ const camelFeatureName = featureName ?? camelcase ( this . name ) ;
94
+ return this . #featureSettings?. [ camelFeatureName ] ;
95
+ }
96
+
97
+ /**
98
+ * For simple boolean settings, return true if the setting is 'enabled'
99
+ * For objects, verify the 'state' field is 'enabled'.
100
+ * This allows for future forwards compatibility with more complex settings if required.
101
+ * For example:
102
+ * ```json
103
+ * {
104
+ * "toggle": "enabled"
105
+ * }
106
+ * ```
107
+ * Could become later (without breaking changes):
108
+ * ```json
109
+ * {
110
+ * "toggle": {
111
+ * "state": "enabled",
112
+ * "someOtherKey": 1
113
+ * }
114
+ * }
115
+ * ```
116
+ * This also supports domain overrides as per `getFeatureSetting`.
117
+ * @param {string } featureKeyName
118
+ * @param {string } [featureName]
119
+ * @returns {boolean }
120
+ */
121
+ getFeatureSettingEnabled ( featureKeyName , featureName ) {
122
+ const result = this . getFeatureSetting ( featureKeyName , featureName ) ;
123
+ if ( typeof result === 'object' ) {
124
+ return result . state === 'enabled' ;
125
+ }
126
+ return result === 'enabled' ;
127
+ }
128
+
129
+ /**
130
+ * Return a specific setting from the feature settings
131
+ * If the "settings" key within the config has a "domains" key, it will be used to override the settings.
132
+ * This uses JSONPatch to apply the patches to settings before getting the setting value.
133
+ * For example.com getFeatureSettings('val') will return 1:
134
+ * ```json
135
+ * {
136
+ * "settings": {
137
+ * "domains": [
138
+ * {
139
+ * "domain": "example.com",
140
+ * "patchSettings": [
141
+ * { "op": "replace", "path": "/val", "value": 1 }
142
+ * ]
143
+ * }
144
+ * ]
145
+ * }
146
+ * }
147
+ * ```
148
+ * "domain" can either be a string or an array of strings.
149
+
150
+ * For boolean states you should consider using getFeatureSettingEnabled.
151
+ * @param {string } featureKeyName
152
+ * @param {string } [featureName]
153
+ * @returns {any }
154
+ */
155
+ getFeatureSetting ( featureKeyName , featureName ) {
156
+ let result = this . _getFeatureSettings ( featureName ) ;
157
+ if ( featureKeyName === 'domains' ) {
158
+ throw new Error ( 'domains is a reserved feature setting key name' ) ;
159
+ }
160
+ const domainMatch = [ ...this . matchDomainFeatureSetting ( 'domains' ) ] . sort ( ( a , b ) => {
161
+ return a . domain . length - b . domain . length ;
162
+ } ) ;
163
+ for ( const match of domainMatch ) {
164
+ if ( match . patchSettings === undefined ) {
165
+ continue ;
166
+ }
167
+ try {
168
+ result = immutableJSONPatch ( result , match . patchSettings ) ;
169
+ } catch ( e ) {
170
+ console . error ( 'Error applying patch settings' , e ) ;
171
+ }
172
+ }
173
+ return result ?. [ featureKeyName ] ;
174
+ }
175
+
176
+ /**
177
+ * @returns {object }
178
+ **/
179
+ get trackerLookup ( ) {
180
+ return this . #trackerLookup || { } ;
181
+ }
182
+
183
+ /**
184
+ * @returns {import('./utils.js').RemoteConfig | undefined }
185
+ **/
186
+ get bundledConfig ( ) {
187
+ return this . #bundledConfig;
188
+ }
189
+
190
+ /**
191
+ * @returns {boolean }
192
+ */
193
+ get documentOriginIsTracker ( ) {
194
+ return ! ! this . #documentOriginIsTracker;
195
+ }
196
+
197
+ set documentOriginIsTracker ( value ) {
198
+ this . #documentOriginIsTracker = value ;
199
+ }
200
+ }
201
+
202
+ export default class ContentFeature extends ConfigParser {
203
+ /** @type {import('./utils.js').RemoteConfig | undefined } */
34
204
/** @type {import('../../messaging').Messaging } */
35
205
// eslint-disable-next-line no-unused-private-class-members
36
206
#messaging;
@@ -41,7 +211,7 @@ export default class ContentFeature {
41
211
#args;
42
212
43
213
constructor ( featureName ) {
44
- this . name = featureName ;
214
+ super ( featureName ) ;
45
215
this . #args = null ;
46
216
this . monitor = new PerformanceMonitor ( ) ;
47
217
}
@@ -77,27 +247,6 @@ export default class ContentFeature {
77
247
return this . #args?. assets ;
78
248
}
79
249
80
- /**
81
- * @returns {boolean }
82
- */
83
- get documentOriginIsTracker ( ) {
84
- return ! ! this . #documentOriginIsTracker;
85
- }
86
-
87
- /**
88
- * @returns {object }
89
- **/
90
- get trackerLookup ( ) {
91
- return this . #trackerLookup || { } ;
92
- }
93
-
94
- /**
95
- * @returns {import('./utils.js').RemoteConfig | undefined }
96
- **/
97
- get bundledConfig ( ) {
98
- return this . #bundledConfig;
99
- }
100
-
101
250
/**
102
251
* @deprecated as we should make this internal to the class and not used externally
103
252
* @return {MessagingContext }
@@ -144,116 +293,6 @@ export default class ContentFeature {
144
293
return processAttr ( configSetting , defaultValue ) ;
145
294
}
146
295
147
- /**
148
- * Return a specific setting from the feature settings
149
- * If the "settings" key within the config has a "domains" key, it will be used to override the settings.
150
- * This uses JSONPatch to apply the patches to settings before getting the setting value.
151
- * For example.com getFeatureSettings('val') will return 1:
152
- * ```json
153
- * {
154
- * "settings": {
155
- * "domains": [
156
- * {
157
- * "domain": "example.com",
158
- * "patchSettings": [
159
- * { "op": "replace", "path": "/val", "value": 1 }
160
- * ]
161
- * }
162
- * ]
163
- * }
164
- * }
165
- * ```
166
- * "domain" can either be a string or an array of strings.
167
-
168
- * For boolean states you should consider using getFeatureSettingEnabled.
169
- * @param {string } featureKeyName
170
- * @param {string } [featureName]
171
- * @returns {any }
172
- */
173
- getFeatureSetting ( featureKeyName , featureName ) {
174
- let result = this . _getFeatureSettings ( featureName ) ;
175
- if ( featureKeyName === 'domains' ) {
176
- throw new Error ( 'domains is a reserved feature setting key name' ) ;
177
- }
178
- const domainMatch = [ ...this . matchDomainFeatureSetting ( 'domains' ) ] . sort ( ( a , b ) => {
179
- return a . domain . length - b . domain . length ;
180
- } ) ;
181
- for ( const match of domainMatch ) {
182
- if ( match . patchSettings === undefined ) {
183
- continue ;
184
- }
185
- try {
186
- result = immutableJSONPatch ( result , match . patchSettings ) ;
187
- } catch ( e ) {
188
- console . error ( 'Error applying patch settings' , e ) ;
189
- }
190
- }
191
- return result ?. [ featureKeyName ] ;
192
- }
193
-
194
- /**
195
- * Return the settings object for a feature
196
- * @param {string } [featureName] - The name of the feature to get the settings for; defaults to the name of the feature
197
- * @returns {any }
198
- */
199
- _getFeatureSettings ( featureName ) {
200
- const camelFeatureName = featureName || camelcase ( this . name ) ;
201
- return this . #args?. featureSettings ?. [ camelFeatureName ] ;
202
- }
203
-
204
- /**
205
- * For simple boolean settings, return true if the setting is 'enabled'
206
- * For objects, verify the 'state' field is 'enabled'.
207
- * This allows for future forwards compatibility with more complex settings if required.
208
- * For example:
209
- * ```json
210
- * {
211
- * "toggle": "enabled"
212
- * }
213
- * ```
214
- * Could become later (without breaking changes):
215
- * ```json
216
- * {
217
- * "toggle": {
218
- * "state": "enabled",
219
- * "someOtherKey": 1
220
- * }
221
- * }
222
- * ```
223
- * This also supports domain overrides as per `getFeatureSetting`.
224
- * @param {string } featureKeyName
225
- * @param {string } [featureName]
226
- * @returns {boolean }
227
- */
228
- getFeatureSettingEnabled ( featureKeyName , featureName ) {
229
- const result = this . getFeatureSetting ( featureKeyName , featureName ) ;
230
- if ( typeof result === 'object' ) {
231
- return result . state === 'enabled' ;
232
- }
233
- return result === 'enabled' ;
234
- }
235
-
236
- /**
237
- * Given a config key, interpret the value as a list of domain overrides, and return the elements that match the current page
238
- * Consider using patchSettings instead as per `getFeatureSetting`.
239
- * @param {string } featureKeyName
240
- * @return {any[] }
241
- * @private
242
- */
243
- matchDomainFeatureSetting ( featureKeyName ) {
244
- const domain = this . #args?. site . domain ;
245
- if ( ! domain ) return [ ] ;
246
- const domains = this . _getFeatureSettings ( ) ?. [ featureKeyName ] || [ ] ;
247
- return domains . filter ( ( rule ) => {
248
- if ( Array . isArray ( rule . domain ) ) {
249
- return rule . domain . some ( ( domainRule ) => {
250
- return matchHostname ( domain , domainRule ) ;
251
- } ) ;
252
- }
253
- return matchHostname ( domain , rule . domain ) ;
254
- } ) ;
255
- }
256
-
257
296
init ( args ) { }
258
297
259
298
callInit ( args ) {
@@ -313,15 +352,7 @@ export default class ContentFeature {
313
352
const mark = this . monitor . mark ( this . name + 'CallLoad' ) ;
314
353
this . #args = args ;
315
354
this . platform = args . platform ;
316
- this . #bundledConfig = args . bundledConfig ;
317
- // If we have a bundled config, treat it as a regular config
318
- // This will be overriden by the remote config if it is available
319
- if ( this . #bundledConfig && this . #args) {
320
- const enabledFeatures = computeEnabledFeatures ( args . bundledConfig , args . site . domain , this . platform . version ) ;
321
- this . #args. featureSettings = parseFeatureSettings ( args . bundledConfig , enabledFeatures ) ;
322
- }
323
- this . #trackerLookup = args . trackerLookup ;
324
- this . #documentOriginIsTracker = args . documentOriginIsTracker ;
355
+ this . initConfig ( args ) ;
325
356
this . load ( args ) ;
326
357
mark . end ( ) ;
327
358
}
0 commit comments