@vacantthinker/firefox-addon-framework-easy 2026.605.843 → 2026.605.958

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,15 +26,15 @@ export class BaseORM { }
26
26
  ### 📄 File: `src/browserDownload.js`
27
27
  ```javascript
28
28
  export async function browserDownloadByDownlink(
29
- { }
29
+ { }
30
30
 
31
31
  ```
32
32
 
33
33
  ### 📄 File: `src/browserNotification.js`
34
34
  ```javascript
35
35
  export async function browserNotificationCreate(
36
- content,
37
- title = browserRuntimeManifestName(),
36
+ content,
37
+ title = browserRuntimeManifestName(),
38
38
  ) { }
39
39
 
40
40
  ```
@@ -44,7 +44,7 @@ export async function browserNotificationCreate(
44
44
  export function browserRuntimeReload() { }
45
45
 
46
46
  export async function browserRuntimeSetUninstallURL(
47
- url = '',
47
+ url = '',
48
48
  ) { }
49
49
 
50
50
  export function browserRuntimeOnUpdateAvailable(doWhat = null) { }
@@ -62,13 +62,13 @@ export function browserRuntimeManifestName() { }
62
62
  ### 📄 File: `src/browserRuntimeOnMessageCommon.js`
63
63
  ```javascript
64
64
  export function browserRuntimeOnMessageCommon(
65
- act,
66
- message,
67
- sendResponse
65
+ act,
66
+ message,
67
+ sendResponse
68
68
  ) { }
69
69
 
70
70
  export function browserRuntimeOnMessageMerge(
71
- message, sender
71
+ message, sender
72
72
  ) { }
73
73
 
74
74
  ```
@@ -96,8 +96,8 @@ export class DomainORM extends BaseORM { }
96
96
  ### 📄 File: `src/generate.js`
97
97
  ```javascript
98
98
  export function generateHtmlByUserSettings(
99
- userSettings,
100
- radioItemClickCallback,
99
+ userSettings,
100
+ radioItemClickCallback,
101
101
  ) { }
102
102
 
103
103
  export function generateMkvScriptForSystemWindows({ }
@@ -171,8 +171,8 @@ export async function serviceDownloadByDownlink(message) { }
171
171
  ### 📄 File: `src/serviceFetch.js`
172
172
  ```javascript
173
173
  export async function servicePostJson(
174
- serverUrl,
175
- message,
174
+ serverUrl,
175
+ message,
176
176
  ) { }
177
177
 
178
178
  export async function serviceSendDataToLocalAria2(message) { }
@@ -202,7 +202,7 @@ export function serviceRemoveIllegalWord(value) { }
202
202
  ### 📄 File: `src/serviceOpJavascript.js`
203
203
  ```javascript
204
204
  export async function serviceTakeScreenshot(
205
- { }
205
+ { }
206
206
 
207
207
  export async function serviceElementPicker(message) { }
208
208
 
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "@vacantthinker/firefox-addon-framework-easy",
3
- "version": "2026.0605.0843",
3
+ "version": "2026.0605.0958",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
7
7
  "access": "public",
8
8
  "provenance": true
9
9
  },
10
- "scripts": {},
10
+ "scripts": {
11
+ "install_package": "npm install"
12
+ },
11
13
  "author": "VacantThinker",
12
14
  "license": "AGPL-3.0-only",
13
15
  "repository": {
@@ -20,5 +22,23 @@
20
22
  },
21
23
  "dependencies": {
22
24
  "@types/firefox-webext-browser": "^143.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "prettier": "3.8.3",
28
+ "prettier-plugin-jsdoc": "1.8.1"
29
+ },
30
+ "prettier": {
31
+ "semi": true,
32
+ "singleQuote": true,
33
+ "tabWidth": 2,
34
+ "useTabs": false,
35
+ "printWidth": 80,
36
+ "plugins": [
37
+ "prettier-plugin-jsdoc"
38
+ ],
39
+ "jsdocVerticalizeParams": true,
40
+ "jsdocLineWrappingStyle": "balance",
41
+ "jsdocPreferToKeepShortOnOneLine": false,
42
+ "jsdocKeepUnpublished": false
23
43
  }
24
44
  }
package/src/BaseORM.js CHANGED
@@ -19,7 +19,7 @@ export class BaseORM {
19
19
  constructor(prefix, id, defaultValue = {}) {
20
20
  if (new.target === BaseORM) {
21
21
  throw new TypeError(
22
- 'Cannot construct BaseORM instances directly (Abstract Class).');
22
+ 'Cannot construct BaseORM instances directly (Abstract Class).');
23
23
  }
24
24
  if (!prefix || !id) {
25
25
  throw new Error('Both prefix and id must be specified.');
@@ -6,10 +6,10 @@
6
6
  * @returns {Promise<void>}
7
7
  */
8
8
  export async function browserDownloadByDownlink(
9
- {
10
- downlink,
11
- filename = null,
12
- }) {
9
+ {
10
+ downlink,
11
+ filename = null,
12
+ }) {
13
13
 
14
14
  let url = downlink;
15
15
  let options = {url};
@@ -7,8 +7,8 @@ import {browserRuntimeManifestName} from './browserRuntime.js';
7
7
  * @returns {Promise<string>}
8
8
  */
9
9
  export async function browserNotificationCreate(
10
- content,
11
- title = browserRuntimeManifestName(),
10
+ content,
11
+ title = browserRuntimeManifestName(),
12
12
  ) {
13
13
 
14
14
  const tag = 'browserNotificationCreate';
@@ -7,7 +7,7 @@ export function browserRuntimeReload() {
7
7
  * @param url{string}
8
8
  */
9
9
  export async function browserRuntimeSetUninstallURL(
10
- url = '',
10
+ url = '',
11
11
  ) {
12
12
  await browser.runtime.setUninstallURL(url);
13
13
  }
@@ -7,7 +7,7 @@ import {browserNotificationCreate} from './browserNotification.js';
7
7
  * offer common act <=> function, eg: actRemoveTab, actLog
8
8
  *
9
9
  * @param act{
10
- * 'actLog'
10
+ * |'actLog'
11
11
  * |'actRequestTabIdTabUrl'
12
12
  * |'actNotification'
13
13
  * |'actRemoveTab'
@@ -18,9 +18,9 @@ import {browserNotificationCreate} from './browserNotification.js';
18
18
  * @param sendResponse
19
19
  */
20
20
  export function browserRuntimeOnMessageCommon(
21
- act,
22
- message,
23
- sendResponse
21
+ act,
22
+ message,
23
+ sendResponse
24
24
  ) {
25
25
  switch (act) {
26
26
  case 'actLog':
@@ -50,7 +50,7 @@ export function browserRuntimeOnMessageCommon(
50
50
  * @param message
51
51
  * @param sender
52
52
  * @returns {
53
- * 'actLog'
53
+ * |'actLog'
54
54
  * |'actRequestTabIdTabUrl'
55
55
  * |'actNotification'
56
56
  * |'actRemoveTab'
@@ -59,7 +59,7 @@ export function browserRuntimeOnMessageCommon(
59
59
  * }
60
60
  */
61
61
  export function browserRuntimeOnMessageMerge(
62
- message, sender
62
+ message, sender
63
63
  ) {
64
64
  let keyAct = 'act';
65
65
  let act = message[keyAct];
package/src/browserTab.js CHANGED
@@ -10,13 +10,13 @@ export async function browserTabSendMessage(tabId, message) {
10
10
  export function browserTabWaitReloadThenSendMessageToContentJs(message) {
11
11
  let tabId = message.tabId;
12
12
  browser.tabs.onUpdated.addListener(
13
- async function lis(tabId, changeInfo) {
14
- if (changeInfo.status === 'complete') {
15
- browser.tabs.onUpdated.removeListener(lis);
16
- await browserTabSendMessage(tabId, message);
17
- }
13
+ async function lis(tabId, changeInfo) {
14
+ if (changeInfo.status === 'complete') {
15
+ browser.tabs.onUpdated.removeListener(lis);
16
+ await browserTabSendMessage(tabId, message);
18
17
  }
19
- , {tabId, properties: ['status']});
18
+ }
19
+ , {tabId, properties: ['status']});
20
20
  }
21
21
 
22
22
  /**
@@ -41,14 +41,14 @@ export async function browserTabCreateToDownload(message) {
41
41
 
42
42
  let {tabId} = await tabOpCreateNear(properties);
43
43
  browser.tabs.onUpdated.addListener(
44
- async function lis(tabId, changeInfo) {
45
- if (changeInfo.status === 'complete') {
46
- browser.tabs.onUpdated.removeListener(lis);
47
- // todo code here
48
- await tabOpRemove(tabId);
49
- }
44
+ async function lis(tabId, changeInfo) {
45
+ if (changeInfo.status === 'complete') {
46
+ browser.tabs.onUpdated.removeListener(lis);
47
+ // todo code here
48
+ await tabOpRemove(tabId);
50
49
  }
51
- , {tabId, properties: ['status']});
50
+ }
51
+ , {tabId, properties: ['status']});
52
52
  }
53
53
 
54
54
  /**
@@ -73,15 +73,15 @@ export async function browserTabCreateNearSendMessageToContentJs(message) {
73
73
 
74
74
  let {tabId} = await tabOpCreateNear(properties);
75
75
  browser.tabs.onUpdated.addListener(
76
- async function lis(tabId, changeInfo) {
77
- if (changeInfo.status === 'complete') {
78
- browser.tabs.onUpdated.removeListener(lis);
79
- // todo code here
80
- await browserTabSendMessage(
81
- tabId, Object.assign({}, message, {tabId}));
82
- }
76
+ async function lis(tabId, changeInfo) {
77
+ if (changeInfo.status === 'complete') {
78
+ browser.tabs.onUpdated.removeListener(lis);
79
+ // todo code here
80
+ await browserTabSendMessage(
81
+ tabId, Object.assign({}, message, {tabId}));
83
82
  }
84
- , {tabId, properties: ['status']});
83
+ }
84
+ , {tabId, properties: ['status']});
85
85
  }
86
86
 
87
87
  /**
@@ -91,13 +91,13 @@ export async function browserTabCreateNearSendMessageToContentJs(message) {
91
91
  */
92
92
  export function browserTabWaitReloadThenRemoveIt({tabId}) {
93
93
  browser.tabs.onUpdated.addListener(
94
- async function lis(tabId, changeInfo) {
95
- if (changeInfo.status === 'complete') {
96
- browser.tabs.onUpdated.removeListener(lis);
97
- // todo code here
98
- await tabOpRemove(tabId);
99
- }
94
+ async function lis(tabId, changeInfo) {
95
+ if (changeInfo.status === 'complete') {
96
+ browser.tabs.onUpdated.removeListener(lis);
97
+ // todo code here
98
+ await tabOpRemove(tabId);
100
99
  }
101
- , {tabId, properties: ['status']});
100
+ }
101
+ , {tabId, properties: ['status']});
102
102
 
103
103
  }
package/src/generate.js CHANGED
@@ -8,8 +8,8 @@ import {stoOpGet, stoOpSet} from './opStorage.js';
8
8
  * @returns {HTMLFieldSetElement[]}
9
9
  */
10
10
  export function generateHtmlByUserSettings(
11
- userSettings,
12
- radioItemClickCallback,
11
+ userSettings,
12
+ radioItemClickCallback,
13
13
  ) {
14
14
  // Keeps track of all generated fieldsets by their storageKey
15
15
  const elementsMap = {};
@@ -56,7 +56,7 @@ export function generateHtmlByUserSettings(
56
56
 
57
57
  eleInput.addEventListener('change', async () => {
58
58
  const optionsCurrent = await stoOpGet(storageKey) ||
59
- storageValue.selected || [];
59
+ storageValue.selected || [];
60
60
  const set = new Set(Array.from(optionsCurrent));
61
61
 
62
62
  if (eleInput.checked) {
@@ -76,8 +76,8 @@ export function generateHtmlByUserSettings(
76
76
  else if (type === 'radio') {
77
77
  stoOpGet(storageKey).then((v) => {
78
78
  const currentSelected = (v !== undefined && v !== null) ?
79
- v :
80
- storageValue.selected;
79
+ v :
80
+ storageValue.selected;
81
81
  if (option === currentSelected) {
82
82
  eleInput.checked = true;
83
83
  }
@@ -86,7 +86,7 @@ export function generateHtmlByUserSettings(
86
86
  triggerVisibility(storageKey, currentSelected);
87
87
  });
88
88
 
89
- eleLabel.onclick = function() {
89
+ eleLabel.onclick = function () {
90
90
  stoOpSet(storageKey, option).then(() => {
91
91
  if (typeof radioItemClickCallback === 'function') {
92
92
  radioItemClickCallback(storageKey, option);
@@ -111,8 +111,8 @@ export function generateHtmlByUserSettings(
111
111
  stoOpGet(storageKey).then((v) => {
112
112
  // Fallback to default schema configuration if no value is stored yet
113
113
  let currentStatus = (v !== undefined && v !== null) ?
114
- (v === true || v === 'true') :
115
- storageValue.selected;
114
+ (v === true || v === 'true') :
115
+ storageValue.selected;
116
116
  eleButton.textContent = String(currentStatus);
117
117
 
118
118
  // Initial visibility evaluation
@@ -139,8 +139,8 @@ export function generateHtmlByUserSettings(
139
139
 
140
140
  stoOpGet(storageKey).then((v) => {
141
141
  const currentVal = (v !== undefined && v !== null) ?
142
- v :
143
- storageValue.selected;
142
+ v :
143
+ storageValue.selected;
144
144
  eleInput.value = currentVal;
145
145
 
146
146
  // Initial visibility evaluation
@@ -162,7 +162,7 @@ export function generateHtmlByUserSettings(
162
162
  }
163
163
 
164
164
  // --- CONDITION 4: SPAN / READ-ONLY TEXT ---
165
- else if (type === 'span' ) {
165
+ else if (type === 'span') {
166
166
  const eleSpan = document.createElement('span');
167
167
  // Optional: Add a class for styling read-only text differently
168
168
  // eleSpan.className = 'read-only-text';
@@ -170,8 +170,8 @@ export function generateHtmlByUserSettings(
170
170
  stoOpGet(storageKey).then((v) => {
171
171
  // Fallback to default schema configuration if no value is stored yet
172
172
  const currentVal = (v !== undefined && v !== null) ?
173
- v :
174
- storageValue.selected;
173
+ v :
174
+ storageValue.selected;
175
175
 
176
176
  // Render as plain text
177
177
  eleSpan.textContent = String(currentVal);
package/src/opTab.js CHANGED
@@ -120,7 +120,7 @@ export async function tabOpReload(tabId) {
120
120
  * @returns {Promise<void>}
121
121
  */
122
122
  export async function tabOpRemove(tabId) {
123
- await browser.tabs.remove(tabId);
123
+ await browser.tabs.remove(tabId);
124
124
  }
125
125
 
126
126
  /**
@@ -5,8 +5,8 @@
5
5
  * @returns {Promise<Response>}
6
6
  */
7
7
  export async function servicePostJson(
8
- serverUrl,
9
- message,
8
+ serverUrl,
9
+ message,
10
10
  ) {
11
11
 
12
12
  let body = JSON.stringify(message);
@@ -1,8 +1,5 @@
1
1
  import {browserRuntimePlatformInfo} from './browserRuntime.js';
2
- import {
3
- generateMkvScriptForSystemFedora,
4
- generateMkvScriptForSystemWindows,
5
- } from './generate.js';
2
+ import {generateMkvScriptForSystemFedora, generateMkvScriptForSystemWindows,} from './generate.js';
6
3
 
7
4
  /**
8
5
  *
@@ -22,23 +19,23 @@ export async function serviceCopyContentToClipboard(data) {
22
19
  export function serviceSaveContentToLocal(content, filename, ext = 'txt') {
23
20
  const eleBtn = document.createElement('button');
24
21
  eleBtn.addEventListener(
25
- 'click',
26
- function() {
27
- const eleA = document.createElement('a');
22
+ 'click',
23
+ function () {
24
+ const eleA = document.createElement('a');
28
25
 
29
- const extObj = {
30
- txt: 'text/plain',
31
- json: 'application/json',
32
- };
33
- const type = extObj[ext];
26
+ const extObj = {
27
+ txt: 'text/plain',
28
+ json: 'application/json',
29
+ };
30
+ const type = extObj[ext];
34
31
 
35
- const file = new Blob([content], {type});
36
- eleA.href = URL.createObjectURL(file);
37
- eleA.download = [filename, ext].join('.');
38
- eleA.click();
39
- URL.revokeObjectURL(eleA.href);
40
- },
41
- false,
32
+ const file = new Blob([content], {type});
33
+ eleA.href = URL.createObjectURL(file);
34
+ eleA.download = [filename, ext].join('.');
35
+ eleA.click();
36
+ URL.revokeObjectURL(eleA.href);
37
+ },
38
+ false,
42
39
  );
43
40
  eleBtn.click();
44
41
  // eleBtn.previousElementSibling
@@ -65,31 +62,19 @@ export async function serviceGenerateMkvToolNixScript({vid, title}) {
65
62
  }
66
63
 
67
64
  /**
68
- * 终极清理函数:过滤所有中英文标点、全角符号、以及非法/不可见字符。
69
- * (100% 预防下载报错和复制粘贴导致的乱码)
70
- * @param {string} value - 原始视频标题
71
- * @returns {string} - 干净、安全的纯文本/字母数字文件名
65
+ * @param {string} value -
66
+ * @returns {string} -
72
67
  */
73
68
  export function serviceRemoveIllegalWord(value) {
74
69
  if (!value) return '';
75
70
 
76
- // 1. 获取第一行并去除两端空格
77
71
  let name = value.trim().split(/\r?\n/).shift();
78
72
 
79
- // 2. 使用 Unicode 属性移除所有危险字符:
80
- // \p{P} = 所有标点符号
81
- // \p{S} = 所有符号(Emoji、数学符号、货币符号)
82
- // \p{C} = 所有控制/格式化/代理字符(完美修复不可见的 U+202A/U+202C 导致崩溃的 Bug!)
83
73
  name = name.replace(/[\p{P}\p{S}\p{C}]/gu, ' ');
84
74
 
85
- // 3. Firefox WebExtension 严格拦截的特殊字符。
86
- // (大部分已经被 \p{P} 和 \p{S} 处理,但显式移除可以确保彻底杜绝边缘报错)
87
75
  name = name.replace(/[~"#%&*:<>?/\\{|}]/g, ' ');
88
76
 
89
- // 4. 将连续的空格(包括全角/Unicode空格)合并为一个标准空格
90
77
  name = name.replace(/[\s\u3000]+/g, ' ').trim();
91
78
 
92
- // 5. Firefox 下载 API 会因为文件名以点(.)或连字符(-)开头/结尾而报错
93
- // 再次修剪以确保绝对安全
94
79
  return name.replace(/^[-.]+|[-.]+$/g, '');
95
80
  }
@@ -20,24 +20,24 @@ import {browserNotificationCreate} from './browserNotification.js';
20
20
  * @returns {Promise<void>}
21
21
  */
22
22
  export async function serviceTakeScreenshot(
23
- {
24
- tabId,
25
- filename,
26
- rect,
27
- }) {
23
+ {
24
+ tabId,
25
+ filename,
26
+ rect,
27
+ }) {
28
28
 
29
29
  const tag = 'actTakeScreenshot()';
30
30
  let dataURI = await browser.tabs.captureTab(tabId, {
31
31
  rect: rect,
32
32
  });
33
33
  let assign = Object.assign(
34
- {},
35
- {dataURI, filename},
34
+ {},
35
+ {dataURI, filename},
36
36
  );
37
37
  await browser.scripting.executeScript({
38
38
  target: {tabId},
39
39
  args: [assign],
40
- func: function(message) {
40
+ func: function (message) {
41
41
  if (message) {
42
42
  let {dataURI, filename} = message;
43
43
 
@@ -70,7 +70,7 @@ export async function serviceElementPicker(message) {
70
70
  await browser.scripting.executeScript({
71
71
  target: {tabId},
72
72
  args: [message],
73
- func: async function(message) {
73
+ func: async function (message) {
74
74
  if (!message) return;
75
75
  console.log('picker.js initialized', message);
76
76
 
@@ -137,9 +137,9 @@ export async function serviceElementPicker(message) {
137
137
  overlay.style.setProperty('top', `${clientRect.top}px`, 'important');
138
138
  overlay.style.setProperty('left', `${clientRect.left}px`, 'important');
139
139
  overlay.style.setProperty('width', `${clientRect.width}px`,
140
- 'important');
140
+ 'important');
141
141
  overlay.style.setProperty('height', `${clientRect.height}px`,
142
- 'important');
142
+ 'important');
143
143
 
144
144
  // Change mouse cursor to indicate picking mode
145
145
  document.body.style.setProperty('cursor', 'crosshair', 'important');
@@ -165,13 +165,13 @@ export async function serviceElementPicker(message) {
165
165
 
166
166
  // Assuming 'target' is your clicked element (e.g., from e.target)
167
167
  let messageTakeScreenshot = Object.assign(
168
- {}, // Start with a fresh, empty object
169
- message, // Put the original message first so it doesn't overwrite your new data
170
- {rect},
171
- {
172
- // The guaranteed unique CSS path (e.g., "div#wrap > ul > li:nth-of-type(2)")
173
- uniqueSelector: getUniqueSelector(target),
174
- },
168
+ {}, // Start with a fresh, empty object
169
+ message, // Put the original message first so it doesn't overwrite your new data
170
+ {rect},
171
+ {
172
+ // The guaranteed unique CSS path (e.g., "div#wrap > ul > li:nth-of-type(2)")
173
+ uniqueSelector: getUniqueSelector(target),
174
+ },
175
175
  );
176
176
 
177
177
  await browser.runtime.sendMessage(messageTakeScreenshot);
@@ -225,9 +225,9 @@ export async function serviceGetFullPageRectData(message) {
225
225
  x, y, width, height,
226
226
  };
227
227
  browser.runtime.sendMessage(Object.assign(
228
- {},
229
- message,
230
- {rect},
228
+ {},
229
+ message,
230
+ {rect},
231
231
  ));
232
232
  // todo end if (message)
233
233
  },
@@ -275,7 +275,7 @@ export async function serviceFindAllMagnetLink(message) {
275
275
  // --- Type 2: Find inside raw text (for <div>, <span>, <td>, etc.) ---
276
276
  // We target elements that don't have children to avoid grabbing huge parent container blocks
277
277
  const allElements = document.querySelectorAll(
278
- 'div, span, td, p, a, button');
278
+ 'div, span, td, p, a, button');
279
279
  allElements.forEach(el => {
280
280
  if (el.children.length === 0) { // Deepest element
281
281
  const text = el.textContent.trim();
@@ -293,11 +293,11 @@ export async function serviceFindAllMagnetLink(message) {
293
293
  }
294
294
 
295
295
  await browser.runtime.sendMessage(Object.assign(
296
- {},
297
- message,
298
- {
299
- data: findAllMagnetLinks(),
300
- },
296
+ {},
297
+ message,
298
+ {
299
+ data: findAllMagnetLinks(),
300
+ },
301
301
  ));
302
302
 
303
303
  // todo end if(message)
@@ -31,9 +31,9 @@ export async function serviceUpdataALLTextNodeColor(message) {
31
31
  */
32
32
  function nativeTreeWalkerFindALLElementHasNodeText() {
33
33
  const walker = document.createTreeWalker(
34
- document.body,
35
- NodeFilter.SHOW_TEXT,
36
- null,
34
+ document.body,
35
+ NodeFilter.SHOW_TEXT,
36
+ null,
37
37
  );
38
38
 
39
39
  let node;
@@ -96,9 +96,9 @@ export async function serviceUpdataALLNodeBackgroundColor(message) {
96
96
  */
97
97
  function nativeTreeWalker() {
98
98
  const walker = document.createTreeWalker(
99
- document.body,
100
- NodeFilter.SHOW_ELEMENT,
101
- null,
99
+ document.body,
100
+ NodeFilter.SHOW_ELEMENT,
101
+ null,
102
102
  );
103
103
 
104
104
  let node;
@@ -7,15 +7,15 @@ import {stoOpGet, stoOpSet} from './opStorage.js';
7
7
  */
8
8
  export async function serviceInitUserSettings(userSettings) {
9
9
  const initPromises = Object.entries(userSettings)
10
- .map(async ([key, setting]) => {
11
- const oldValue = await stoOpGet(key);
12
-
13
- // FIX: Check strictly for null or undefined.
14
- // This allows `false` and `0` to be recognized as valid saved values.
15
- if (oldValue === null || oldValue === undefined) {
16
- await stoOpSet(key, setting.selected);
17
- }
18
- });
10
+ .map(async ([key, setting]) => {
11
+ const oldValue = await stoOpGet(key);
12
+
13
+ // FIX: Check strictly for null or undefined.
14
+ // This allows `false` and `0` to be recognized as valid saved values.
15
+ if (oldValue === null || oldValue === undefined) {
16
+ await stoOpSet(key, setting.selected);
17
+ }
18
+ });
19
19
 
20
20
  await Promise.all(initPromises);
21
21
  }