@addonova/browser 0.0.0-stage → 0.2.0

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.
Files changed (327) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +693 -2
  3. package/dist/adapters/chrome/index.d.ts +9 -0
  4. package/dist/adapters/chrome/index.d.ts.map +1 -0
  5. package/dist/adapters/chrome/index.js +12 -0
  6. package/dist/adapters/chrome/index.js.map +1 -0
  7. package/dist/adapters/chromium/index.d.ts +12 -0
  8. package/dist/adapters/chromium/index.d.ts.map +1 -0
  9. package/dist/adapters/chromium/index.js +12 -0
  10. package/dist/adapters/chromium/index.js.map +1 -0
  11. package/dist/adapters/edge/index.d.ts +11 -0
  12. package/dist/adapters/edge/index.d.ts.map +1 -0
  13. package/dist/adapters/edge/index.js +13 -0
  14. package/dist/adapters/edge/index.js.map +1 -0
  15. package/dist/adapters/firefox/index.d.ts +10 -0
  16. package/dist/adapters/firefox/index.d.ts.map +1 -0
  17. package/dist/adapters/firefox/index.js +12 -0
  18. package/dist/adapters/firefox/index.js.map +1 -0
  19. package/dist/adapters/shared/adapter.d.ts +13 -0
  20. package/dist/adapters/shared/adapter.d.ts.map +1 -0
  21. package/dist/adapters/shared/adapter.js +24 -0
  22. package/dist/adapters/shared/adapter.js.map +1 -0
  23. package/dist/adapters/shared/namespace.d.ts +4 -0
  24. package/dist/adapters/shared/namespace.d.ts.map +1 -0
  25. package/dist/adapters/shared/namespace.js +20 -0
  26. package/dist/adapters/shared/namespace.js.map +1 -0
  27. package/dist/adapters/types.d.ts +10 -0
  28. package/dist/adapters/types.d.ts.map +1 -0
  29. package/dist/adapters/types.js +2 -0
  30. package/dist/adapters/types.js.map +1 -0
  31. package/dist/api/action/index.d.ts +6 -0
  32. package/dist/api/action/index.d.ts.map +1 -0
  33. package/dist/api/action/index.js +56 -0
  34. package/dist/api/action/index.js.map +1 -0
  35. package/dist/api/alarms/index.d.ts +5 -0
  36. package/dist/api/alarms/index.d.ts.map +1 -0
  37. package/dist/api/alarms/index.js +34 -0
  38. package/dist/api/alarms/index.js.map +1 -0
  39. package/dist/api/bookmarks/index.d.ts +5 -0
  40. package/dist/api/bookmarks/index.d.ts.map +1 -0
  41. package/dist/api/bookmarks/index.js +39 -0
  42. package/dist/api/bookmarks/index.js.map +1 -0
  43. package/dist/api/browser.d.ts +3 -0
  44. package/dist/api/browser.d.ts.map +1 -0
  45. package/dist/api/browser.js +2 -0
  46. package/dist/api/browser.js.map +1 -0
  47. package/dist/api/commands/index.d.ts +5 -0
  48. package/dist/api/commands/index.d.ts.map +1 -0
  49. package/dist/api/commands/index.js +12 -0
  50. package/dist/api/commands/index.js.map +1 -0
  51. package/dist/api/context-menus/index.d.ts +5 -0
  52. package/dist/api/context-menus/index.d.ts.map +1 -0
  53. package/dist/api/context-menus/index.js +21 -0
  54. package/dist/api/context-menus/index.js.map +1 -0
  55. package/dist/api/cookies/index.d.ts +5 -0
  56. package/dist/api/cookies/index.d.ts.map +1 -0
  57. package/dist/api/cookies/index.js +21 -0
  58. package/dist/api/cookies/index.js.map +1 -0
  59. package/dist/api/downloads/index.d.ts +5 -0
  60. package/dist/api/downloads/index.d.ts.map +1 -0
  61. package/dist/api/downloads/index.js +20 -0
  62. package/dist/api/downloads/index.js.map +1 -0
  63. package/dist/api/helpers.d.ts +17 -0
  64. package/dist/api/helpers.d.ts.map +1 -0
  65. package/dist/api/helpers.js +100 -0
  66. package/dist/api/helpers.js.map +1 -0
  67. package/dist/api/i18n/index.d.ts +5 -0
  68. package/dist/api/i18n/index.d.ts.map +1 -0
  69. package/dist/api/i18n/index.js +17 -0
  70. package/dist/api/i18n/index.js.map +1 -0
  71. package/dist/api/lazy-adapter.d.ts +5 -0
  72. package/dist/api/lazy-adapter.d.ts.map +1 -0
  73. package/dist/api/lazy-adapter.js +37 -0
  74. package/dist/api/lazy-adapter.js.map +1 -0
  75. package/dist/api/notifications/index.d.ts +5 -0
  76. package/dist/api/notifications/index.d.ts.map +1 -0
  77. package/dist/api/notifications/index.js +27 -0
  78. package/dist/api/notifications/index.js.map +1 -0
  79. package/dist/api/permissions/index.d.ts +5 -0
  80. package/dist/api/permissions/index.d.ts.map +1 -0
  81. package/dist/api/permissions/index.js +24 -0
  82. package/dist/api/permissions/index.js.map +1 -0
  83. package/dist/api/runtime/index.d.ts +5 -0
  84. package/dist/api/runtime/index.d.ts.map +1 -0
  85. package/dist/api/runtime/index.js +38 -0
  86. package/dist/api/runtime/index.js.map +1 -0
  87. package/dist/api/scripting/index.d.ts +5 -0
  88. package/dist/api/scripting/index.d.ts.map +1 -0
  89. package/dist/api/scripting/index.js +15 -0
  90. package/dist/api/scripting/index.js.map +1 -0
  91. package/dist/api/sessions/index.d.ts +5 -0
  92. package/dist/api/sessions/index.d.ts.map +1 -0
  93. package/dist/api/sessions/index.js +19 -0
  94. package/dist/api/sessions/index.js.map +1 -0
  95. package/dist/api/singletons.d.ts +40 -0
  96. package/dist/api/singletons.d.ts.map +1 -0
  97. package/dist/api/singletons.js +60 -0
  98. package/dist/api/singletons.js.map +1 -0
  99. package/dist/api/storage/area.d.ts +4 -0
  100. package/dist/api/storage/area.d.ts.map +1 -0
  101. package/dist/api/storage/area.js +29 -0
  102. package/dist/api/storage/area.js.map +1 -0
  103. package/dist/api/storage/index.d.ts +7 -0
  104. package/dist/api/storage/index.d.ts.map +1 -0
  105. package/dist/api/storage/index.js +37 -0
  106. package/dist/api/storage/index.js.map +1 -0
  107. package/dist/api/storage/item.d.ts +4 -0
  108. package/dist/api/storage/item.d.ts.map +1 -0
  109. package/dist/api/storage/item.js +109 -0
  110. package/dist/api/storage/item.js.map +1 -0
  111. package/dist/api/tabs/index.d.ts +5 -0
  112. package/dist/api/tabs/index.d.ts.map +1 -0
  113. package/dist/api/tabs/index.js +49 -0
  114. package/dist/api/tabs/index.js.map +1 -0
  115. package/dist/api/web-navigation/index.d.ts +5 -0
  116. package/dist/api/web-navigation/index.d.ts.map +1 -0
  117. package/dist/api/web-navigation/index.js +30 -0
  118. package/dist/api/web-navigation/index.js.map +1 -0
  119. package/dist/api/web-request/index.d.ts +5 -0
  120. package/dist/api/web-request/index.d.ts.map +1 -0
  121. package/dist/api/web-request/index.js +27 -0
  122. package/dist/api/web-request/index.js.map +1 -0
  123. package/dist/api/windows/index.d.ts +5 -0
  124. package/dist/api/windows/index.d.ts.map +1 -0
  125. package/dist/api/windows/index.js +27 -0
  126. package/dist/api/windows/index.js.map +1 -0
  127. package/dist/browser/detect.d.ts +19 -0
  128. package/dist/browser/detect.d.ts.map +1 -0
  129. package/dist/browser/detect.js +89 -0
  130. package/dist/browser/detect.js.map +1 -0
  131. package/dist/browser/info.d.ts +17 -0
  132. package/dist/browser/info.d.ts.map +1 -0
  133. package/dist/browser/info.js +77 -0
  134. package/dist/browser/info.js.map +1 -0
  135. package/dist/browser/resolve.d.ts +9 -0
  136. package/dist/browser/resolve.d.ts.map +1 -0
  137. package/dist/browser/resolve.js +57 -0
  138. package/dist/browser/resolve.js.map +1 -0
  139. package/dist/browser/support.d.ts +8 -0
  140. package/dist/browser/support.d.ts.map +1 -0
  141. package/dist/browser/support.js +18 -0
  142. package/dist/browser/support.js.map +1 -0
  143. package/dist/build/target.d.ts +5 -0
  144. package/dist/build/target.d.ts.map +1 -0
  145. package/dist/build/target.js +21 -0
  146. package/dist/build/target.js.map +1 -0
  147. package/dist/content/context.d.ts +24 -0
  148. package/dist/content/context.d.ts.map +1 -0
  149. package/dist/content/context.js +157 -0
  150. package/dist/content/context.js.map +1 -0
  151. package/dist/content/index.d.ts +7 -0
  152. package/dist/content/index.d.ts.map +1 -0
  153. package/dist/content/index.js +4 -0
  154. package/dist/content/index.js.map +1 -0
  155. package/dist/content/inject-root.d.ts +2 -0
  156. package/dist/content/inject-root.d.ts.map +1 -0
  157. package/dist/content/inject-root.js +4 -0
  158. package/dist/content/inject-root.js.map +1 -0
  159. package/dist/content/inject-script.d.ts +16 -0
  160. package/dist/content/inject-script.d.ts.map +1 -0
  161. package/dist/content/inject-script.js +39 -0
  162. package/dist/content/inject-script.js.map +1 -0
  163. package/dist/content/ui.d.ts +50 -0
  164. package/dist/content/ui.d.ts.map +1 -0
  165. package/dist/content/ui.js +331 -0
  166. package/dist/content/ui.js.map +1 -0
  167. package/dist/entries/chrome.d.ts +52 -0
  168. package/dist/entries/chrome.d.ts.map +1 -0
  169. package/dist/entries/chrome.js +101 -0
  170. package/dist/entries/chrome.js.map +1 -0
  171. package/dist/entries/chromium.d.ts +52 -0
  172. package/dist/entries/chromium.d.ts.map +1 -0
  173. package/dist/entries/chromium.js +102 -0
  174. package/dist/entries/chromium.js.map +1 -0
  175. package/dist/entries/edge.d.ts +52 -0
  176. package/dist/entries/edge.d.ts.map +1 -0
  177. package/dist/entries/edge.js +101 -0
  178. package/dist/entries/edge.js.map +1 -0
  179. package/dist/entries/firefox.d.ts +52 -0
  180. package/dist/entries/firefox.d.ts.map +1 -0
  181. package/dist/entries/firefox.js +100 -0
  182. package/dist/entries/firefox.js.map +1 -0
  183. package/dist/entries/info.d.ts +5 -0
  184. package/dist/entries/info.d.ts.map +1 -0
  185. package/dist/entries/info.js +21 -0
  186. package/dist/entries/info.js.map +1 -0
  187. package/dist/entries/shared-exports.d.ts +20 -0
  188. package/dist/entries/shared-exports.d.ts.map +1 -0
  189. package/dist/entries/shared-exports.js +10 -0
  190. package/dist/entries/shared-exports.js.map +1 -0
  191. package/dist/entries/support.d.ts +6 -0
  192. package/dist/entries/support.d.ts.map +1 -0
  193. package/dist/entries/support.js +16 -0
  194. package/dist/entries/support.js.map +1 -0
  195. package/dist/entries/utilities.d.ts +6 -0
  196. package/dist/entries/utilities.d.ts.map +1 -0
  197. package/dist/entries/utilities.js +7 -0
  198. package/dist/entries/utilities.js.map +1 -0
  199. package/dist/i18n/index.d.ts +16 -0
  200. package/dist/i18n/index.d.ts.map +1 -0
  201. package/dist/i18n/index.js +52 -0
  202. package/dist/i18n/index.js.map +1 -0
  203. package/dist/index.d.ts +30 -0
  204. package/dist/index.d.ts.map +1 -0
  205. package/dist/index.js +17 -0
  206. package/dist/index.js.map +1 -0
  207. package/dist/messaging/index.d.ts +22 -0
  208. package/dist/messaging/index.d.ts.map +1 -0
  209. package/dist/messaging/index.js +100 -0
  210. package/dist/messaging/index.js.map +1 -0
  211. package/dist/messaging/proxy.d.ts +17 -0
  212. package/dist/messaging/proxy.d.ts.map +1 -0
  213. package/dist/messaging/proxy.js +61 -0
  214. package/dist/messaging/proxy.js.map +1 -0
  215. package/dist/messaging/root.d.ts +4 -0
  216. package/dist/messaging/root.d.ts.map +1 -0
  217. package/dist/messaging/root.js +9 -0
  218. package/dist/messaging/root.js.map +1 -0
  219. package/dist/shared/capabilities.d.ts +25 -0
  220. package/dist/shared/capabilities.d.ts.map +1 -0
  221. package/dist/shared/capabilities.js +41 -0
  222. package/dist/shared/capabilities.js.map +1 -0
  223. package/dist/shared/errors.d.ts +18 -0
  224. package/dist/shared/errors.d.ts.map +1 -0
  225. package/dist/shared/errors.js +32 -0
  226. package/dist/shared/errors.js.map +1 -0
  227. package/dist/shared/events.d.ts +11 -0
  228. package/dist/shared/events.d.ts.map +1 -0
  229. package/dist/shared/events.js +19 -0
  230. package/dist/shared/events.js.map +1 -0
  231. package/dist/shared/objects.d.ts +4 -0
  232. package/dist/shared/objects.d.ts.map +1 -0
  233. package/dist/shared/objects.js +14 -0
  234. package/dist/shared/objects.js.map +1 -0
  235. package/dist/shared/promises.d.ts +11 -0
  236. package/dist/shared/promises.d.ts.map +1 -0
  237. package/dist/shared/promises.js +70 -0
  238. package/dist/shared/promises.js.map +1 -0
  239. package/dist/testing/fake-browser.d.ts +184 -0
  240. package/dist/testing/fake-browser.d.ts.map +1 -0
  241. package/dist/testing/fake-browser.js +994 -0
  242. package/dist/testing/fake-browser.js.map +1 -0
  243. package/dist/testing/index.d.ts +3 -0
  244. package/dist/testing/index.d.ts.map +1 -0
  245. package/dist/testing/index.js +2 -0
  246. package/dist/testing/index.js.map +1 -0
  247. package/dist/testing/setup.d.ts +2 -0
  248. package/dist/testing/setup.d.ts.map +1 -0
  249. package/dist/testing/setup.js +6 -0
  250. package/dist/testing/setup.js.map +1 -0
  251. package/dist/types/action.d.ts +35 -0
  252. package/dist/types/action.d.ts.map +1 -0
  253. package/dist/types/action.js +2 -0
  254. package/dist/types/action.js.map +1 -0
  255. package/dist/types/alarms.d.ts +21 -0
  256. package/dist/types/alarms.d.ts.map +1 -0
  257. package/dist/types/alarms.js +2 -0
  258. package/dist/types/alarms.js.map +1 -0
  259. package/dist/types/bookmarks.d.ts +57 -0
  260. package/dist/types/bookmarks.d.ts.map +1 -0
  261. package/dist/types/bookmarks.js +2 -0
  262. package/dist/types/bookmarks.js.map +1 -0
  263. package/dist/types/commands.d.ts +11 -0
  264. package/dist/types/commands.d.ts.map +1 -0
  265. package/dist/types/commands.js +2 -0
  266. package/dist/types/commands.js.map +1 -0
  267. package/dist/types/context-menus.d.ts +48 -0
  268. package/dist/types/context-menus.d.ts.map +1 -0
  269. package/dist/types/context-menus.js +2 -0
  270. package/dist/types/context-menus.js.map +1 -0
  271. package/dist/types/cookies.d.ts +54 -0
  272. package/dist/types/cookies.d.ts.map +1 -0
  273. package/dist/types/cookies.js +2 -0
  274. package/dist/types/cookies.js.map +1 -0
  275. package/dist/types/downloads.d.ts +43 -0
  276. package/dist/types/downloads.d.ts.map +1 -0
  277. package/dist/types/downloads.js +2 -0
  278. package/dist/types/downloads.js.map +1 -0
  279. package/dist/types/i18n.d.ts +14 -0
  280. package/dist/types/i18n.d.ts.map +1 -0
  281. package/dist/types/i18n.js +2 -0
  282. package/dist/types/i18n.js.map +1 -0
  283. package/dist/types/index.d.ts +19 -0
  284. package/dist/types/index.d.ts.map +1 -0
  285. package/dist/types/index.js +2 -0
  286. package/dist/types/index.js.map +1 -0
  287. package/dist/types/notifications.d.ts +35 -0
  288. package/dist/types/notifications.d.ts.map +1 -0
  289. package/dist/types/notifications.js +2 -0
  290. package/dist/types/notifications.js.map +1 -0
  291. package/dist/types/permissions.d.ts +14 -0
  292. package/dist/types/permissions.d.ts.map +1 -0
  293. package/dist/types/permissions.js +2 -0
  294. package/dist/types/permissions.js.map +1 -0
  295. package/dist/types/runtime.d.ts +58 -0
  296. package/dist/types/runtime.d.ts.map +1 -0
  297. package/dist/types/runtime.js +2 -0
  298. package/dist/types/runtime.js.map +1 -0
  299. package/dist/types/scripting.d.ts +30 -0
  300. package/dist/types/scripting.d.ts.map +1 -0
  301. package/dist/types/scripting.js +2 -0
  302. package/dist/types/scripting.js.map +1 -0
  303. package/dist/types/sessions.d.ts +28 -0
  304. package/dist/types/sessions.d.ts.map +1 -0
  305. package/dist/types/sessions.js +2 -0
  306. package/dist/types/sessions.js.map +1 -0
  307. package/dist/types/storage.d.ts +51 -0
  308. package/dist/types/storage.d.ts.map +1 -0
  309. package/dist/types/storage.js +2 -0
  310. package/dist/types/storage.js.map +1 -0
  311. package/dist/types/tabs.d.ts +96 -0
  312. package/dist/types/tabs.d.ts.map +1 -0
  313. package/dist/types/tabs.js +2 -0
  314. package/dist/types/tabs.js.map +1 -0
  315. package/dist/types/web-navigation.d.ts +28 -0
  316. package/dist/types/web-navigation.d.ts.map +1 -0
  317. package/dist/types/web-navigation.js +2 -0
  318. package/dist/types/web-navigation.js.map +1 -0
  319. package/dist/types/web-request.d.ts +20 -0
  320. package/dist/types/web-request.d.ts.map +1 -0
  321. package/dist/types/web-request.js +2 -0
  322. package/dist/types/web-request.js.map +1 -0
  323. package/dist/types/windows.d.ts +52 -0
  324. package/dist/types/windows.d.ts.map +1 -0
  325. package/dist/types/windows.js +2 -0
  326. package/dist/types/windows.js.map +1 -0
  327. package/package.json +97 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CodeHemu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,694 @@
1
- # Temporary Holding Version
1
+ [![Addonova Browser Social Banner](https://raw.githubusercontent.com/addonova/.github/refs/heads/main/profile/image/addonova_browser_banner.png)](https://github.com/addonova)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <div align="center">
4
+ <p>
5
+ <a href="https://www.npmjs.com/package/@addonova/browser"><img src="https://img.shields.io/npm/v/@addonova/browser?labelColor=%23153c26&color=%23ccff00" alt="Version"></a>
6
+ <a href="https://nodejs.org/en/about/previous-releases"><img src="https://img.shields.io/node/v/@addonova/browser?labelColor=%23153c26&color=%23ccff00" alt="node compatibility"></a>
7
+ <a href="https://raw.githubusercontent.com/addonova/browser/refs/heads/main/LICENSE"><img src="https://img.shields.io/github/license/addonova/browser?labelColor=%23153c26&color=%23ccff00" alt="License"></a>
8
+ <a href="https://github.com/addonova/browser/issues"><img src="https://img.shields.io/github/issues/addonova/browser?labelColor=%23153c26&color=%23ccff00" alt="Issues"></a>
9
+ <a href="https://github.com/addonova/browser/graphs/contributors"><img src="https://img.shields.io/github/contributors/addonova/browser?labelColor=%23153c26&color=%23ccff00" alt="Contributors"></a>
10
+ <a href="https://github.com/sponsors/code-hemu"><img src="https://img.shields.io/badge/Sponsor-GitHub-red?labelColor=%23153c26&color=%23ccff00" alt="Sponsor"></a>
11
+ </p>
12
+
13
+ **Unified API's for cross-browser extensions.**
14
+
15
+ </div>
16
+
17
+ Browser extension APIs are similar but not identical. `@addonova/browser`
18
+ normalizes the differences that can be normalized safely:
19
+
20
+ - one Promise-based method shape for asynchronous APIs;
21
+ - unchanged native events with `addListener`, `removeListener`, and
22
+ `hasListener`;
23
+ - typed errors for missing APIs, missing permissions, and native failures;
24
+ - support for `chrome`, `chromium`, `edge`, and `firefox` targets;
25
+ - browser-specific entry points for build-time code elimination.
26
+
27
+ The package does not generate manifests, inject content scripts, grant
28
+ permissions, or emulate an API the browser does not provide. See the verified
29
+ [compatibility matrix](docs/compatibility-matrix.md) for the exact support
30
+ surface.
31
+
32
+ ## Installation
33
+
34
+ ```sh
35
+ npm install @addonova/browser
36
+ ```
37
+
38
+ Requirements:
39
+
40
+ - ESM bundler or extension runtime with ES module support.
41
+ - Node.js `>=20` for development and the Guchho-based test tooling.
42
+ - No runtime dependency on Addonova Build.
43
+
44
+ Package entries:
45
+
46
+ | Import | Purpose |
47
+ | --- | --- |
48
+ | `@addonova/browser` | Runtime browser detection; default fallback. |
49
+ | `@addonova/browser/chrome` | Fixed Chrome target. |
50
+ | `@addonova/browser/chromium` | Fixed Chromium target. |
51
+ | `@addonova/browser/edge` | Fixed Edge target. |
52
+ | `@addonova/browser/firefox` | Fixed Firefox target. |
53
+
54
+ ## Quick start
55
+
56
+ ```ts
57
+ import { action, storage, tabs } from '@addonova/browser';
58
+
59
+ export async function updateBadge(): Promise<void> {
60
+ const [tab] = await tabs.query({ active: true, currentWindow: true });
61
+
62
+ if (tab?.id === undefined) {
63
+ return;
64
+ }
65
+
66
+ const { enabled = false } = await storage.local.get('enabled');
67
+
68
+ await tabs.sendMessage(tab.id, {
69
+ type: 'set-enabled',
70
+ enabled,
71
+ });
72
+ await action.setBadgeText({ text: enabled ? 'ON' : '' });
73
+ }
74
+ ```
75
+
76
+ Register listeners at the top level of a service worker or event page:
77
+
78
+ ```ts
79
+ import { runtime } from '@addonova/browser';
80
+
81
+ runtime.onInstalled.addListener((details) => {
82
+ console.log('Installed/updated:', details.reason);
83
+ });
84
+ ```
85
+
86
+ ## Runtime utilities
87
+
88
+ ### Typed storage
89
+
90
+ Use `storage.defineItem()` to keep a value typed, provide a fallback or lazy
91
+ initializer, and run versioned migrations:
92
+
93
+ ```ts
94
+ import { storage } from '@addonova/browser';
95
+
96
+ interface Settings {
97
+ theme: 'light' | 'dark';
98
+ compact: boolean;
99
+ }
100
+
101
+ const settings = storage.defineItem<Settings>('settings', {
102
+ area: 'local',
103
+ fallback: { theme: 'light', compact: false },
104
+ version: 2,
105
+ migrations: {
106
+ 2(value) {
107
+ return { compact: false, ...(value as Partial<Settings>) };
108
+ },
109
+ },
110
+ });
111
+
112
+ const current = await settings.getValue();
113
+ await settings.setValue({ ...current, theme: 'dark' });
114
+
115
+ const stop = settings.watch((newValue, oldValue) => {
116
+ console.log('settings changed', oldValue, newValue);
117
+ });
118
+ stop();
119
+ ```
120
+
121
+ `getMeta()` and `setMeta()` expose the stored item version. `removeValue()`
122
+ deletes the value; the next read falls back or initializes again.
123
+
124
+ ### Type-safe messaging
125
+
126
+ Define a message protocol once and share it between the sender and receiver:
127
+
128
+ ```ts
129
+ import { defineExtensionMessaging } from '@addonova/browser';
130
+
131
+ interface Protocol {
132
+ ping(data: { at: number }): { at: number };
133
+ add(a: number, b: number): number;
134
+ }
135
+
136
+ const messaging = defineExtensionMessaging<Protocol>();
137
+
138
+ // Background or any context with a listener.
139
+ messaging.onMessage('ping', ({ data, sender }) => {
140
+ console.log('ping from', sender.id, data.at);
141
+
142
+ return { at: Date.now() };
143
+ });
144
+
145
+ // Any extension context.
146
+ const response = await messaging.sendMessage('ping', { at: Date.now() });
147
+ ```
148
+
149
+ Errors thrown by a handler are serialized and rethrown on the sender. Use
150
+ `defineProxyService()` when the service has many methods:
151
+
152
+ ```ts
153
+ import { defineProxyService } from '@addonova/browser';
154
+
155
+ const [registerCounter, getCounter] = defineProxyService(
156
+ 'counter',
157
+ () => {
158
+ let value = 0;
159
+
160
+ return {
161
+ add(amount: number) {
162
+ value += amount;
163
+ return value;
164
+ },
165
+ read() {
166
+ return value;
167
+ },
168
+ };
169
+ },
170
+ );
171
+
172
+ registerCounter();
173
+ const counter = getCounter();
174
+
175
+ await counter.add(2);
176
+ await counter.read();
177
+ registerCounter.dispose();
178
+ ```
179
+
180
+ ### Content scripts
181
+
182
+ `createContentScriptContext()` tracks timers, animation frames, and listeners
183
+ and invalidates them together when the content script is unloaded:
184
+
185
+ ```ts
186
+ import {
187
+ createContentScriptContext,
188
+ injectScript,
189
+ } from '@addonova/browser';
190
+
191
+ const ctx = createContentScriptContext();
192
+
193
+ ctx.setTimeout(() => {
194
+ console.log('still valid:', ctx.isValid);
195
+ }, 1000);
196
+
197
+ ctx.addEventListener(document, 'click', () => {
198
+ console.log('clicked');
199
+ });
200
+
201
+ ctx.onInvalidated(() => {
202
+ console.log('content script invalidated');
203
+ });
204
+
205
+ await injectScript('/injected.js', { allFrames: false });
206
+ ```
207
+
208
+ Add `validateRuntime: false` when a test environment does not expose
209
+ `runtime.id`.
210
+
211
+ ### Content-script UI
212
+
213
+ Mount UI into the page, a shadow root, or an iframe:
214
+
215
+ ```ts
216
+ import {
217
+ createIntegratedUi,
218
+ createShadowRootUi,
219
+ } from '@addonova/browser';
220
+
221
+ const inline = createIntegratedUi(ctx, {
222
+ anchor: '#app',
223
+ append: 'last',
224
+ onMount(container) {
225
+ const button = document.createElement('button');
226
+ button.textContent = 'Addonova';
227
+ container.append(button);
228
+
229
+ return button;
230
+ },
231
+ });
232
+
233
+ await inline.mount();
234
+ inline.remove();
235
+
236
+ const panel = createShadowRootUi(ctx, {
237
+ anchor: 'body',
238
+ position: 'overlay',
239
+ alignment: 'bottom-right',
240
+ css: ':host { all: initial; } .panel { font: 14px sans-serif; }',
241
+ onMount(container) {
242
+ const element = document.createElement('div');
243
+ element.className = 'panel';
244
+ element.textContent = 'Shadow UI';
245
+ container.append(element);
246
+
247
+ return element;
248
+ },
249
+ });
250
+
251
+ await panel.mount();
252
+ panel.autoMount();
253
+ ```
254
+
255
+ UI helpers support `anchor` selectors or elements, `append` modes
256
+ (`first`, `last`, `before`, `after`, `replace`), `position`
257
+ (`inline`, `overlay`, `modal`), `alignment`, `zIndex`, and `onRemove`.
258
+ `autoMount()` mounts when the anchor appears, remounts when it is removed, and
259
+ stops on context invalidation.
260
+
261
+ ### i18n
262
+
263
+ ```ts
264
+ import { createI18nHelper, formatMessage, t } from '@addonova/browser';
265
+
266
+ const i18nHelper = createI18nHelper({ fallbackToKey: true });
267
+
268
+ i18nHelper.locale;
269
+ i18nHelper.t('greeting', 'Ada');
270
+ i18nHelper.raw('greeting', 'Ada');
271
+
272
+ t('items_selected', ['3']);
273
+ formatMessage('Selected $1 item(s)', '3');
274
+ ```
275
+
276
+ `t()` falls back to the message name when the browser returns an empty string.
277
+
278
+ ## Testing
279
+
280
+ ### Fake browser
281
+
282
+ `@addonova/browser/testing` provides an in-memory implementation of the runtime
283
+ API. It records storage, tabs, permissions, notifications, alarms, and events,
284
+ and routes `runtime.sendMessage()` through `onMessage` listeners.
285
+
286
+ ```ts
287
+ import {
288
+ createFakeBrowser,
289
+ installFakeBrowser,
290
+ resetFakeBrowser,
291
+ uninstallFakeBrowser,
292
+ } from '@addonova/browser/testing';
293
+
294
+ const fake = installFakeBrowser({ target: 'chrome' });
295
+
296
+ await fake.storage.local.set({ enabled: true });
297
+ fake.runtime.onMessage.addListener((message, _sender, sendResponse) => {
298
+ sendResponse({ ok: true });
299
+ return true;
300
+ });
301
+
302
+ resetFakeBrowser();
303
+ uninstallFakeBrowser();
304
+ ```
305
+
306
+ Use `createFakeBrowser()` for isolated instances and `fakeBrowser` for the
307
+ shared default. `reset()` clears state while keeping listeners.
308
+
309
+ ### Guchho Test setup
310
+
311
+ Register the setup file so the fake browser is installed before each test:
312
+
313
+ ```ts
314
+ // guchho.test.config.ts
315
+ import { defineConfig } from '@guchho/test';
316
+
317
+ export default defineConfig({
318
+ setupFiles: ['@addonova/browser/testing/setup'],
319
+ });
320
+ ```
321
+
322
+ The setup entry installs the fake browser before the suite, resets it before
323
+ each test, and uninstalls it afterwards. `@guchho/test` remains an optional peer
324
+ dependency.
325
+
326
+ ### End-to-end tests
327
+
328
+ See [`docs/playwright-e2e.md`](docs/playwright-e2e.md) for a Playwright guide
329
+ that loads the built extension into a persistent Chromium context.
330
+
331
+ ## Browser compatibility
332
+
333
+ The verified matrix is in
334
+ [`docs/compatibility-matrix.md`](docs/compatibility-matrix.md). The short
335
+ version:
336
+
337
+ - Chrome, Chromium, and Edge share one Chromium adapter. Current stable
338
+ releases target Manifest V3; legacy MV2 is outside current support scope.
339
+ - Edge legacy MV2 is still in Microsoft's deprecation transition and is
340
+ conditional.
341
+ - Firefox supports both MV2 and MV3. Firefox MV3 uses `background.scripts`
342
+ event pages, not a service worker.
343
+ - Firefox 102+ exposes `scripting` in MV2; Chromium MV2 does not.
344
+ - `tabs.executeScript` and blocking `webRequest` APIs are intentionally not
345
+ exposed. Use `scripting` and observational `webRequest` events instead.
346
+ - Firefox does not support `notifications.update()` or
347
+ `notifications.onButtonClicked`.
348
+
349
+ Run `supports()` before using an optional API when an extension must work across
350
+ older browser versions.
351
+
352
+ ## API reference
353
+
354
+ The root export exposes a `browser` object plus named namespaces:
355
+
356
+ ```ts
357
+ import {
358
+ action,
359
+ alarms,
360
+ bookmarks,
361
+ browser,
362
+ browserInfo,
363
+ commands,
364
+ contextMenus,
365
+ cookies,
366
+ downloads,
367
+ getAvailability,
368
+ getBrowserInfo,
369
+ i18n,
370
+ isBrowserSupported,
371
+ notifications,
372
+ permissions,
373
+ runtime,
374
+ scripting,
375
+ sessions,
376
+ storage,
377
+ supports,
378
+ tabs,
379
+ webNavigation,
380
+ webRequest,
381
+ windows,
382
+ BrowserApiError,
383
+ } from '@addonova/browser';
384
+ ```
385
+
386
+ | Namespace | Representative methods |
387
+ | --- | --- |
388
+ | `runtime` | `id`, `getManifest`, `getURL`, `sendMessage`, `connect`, events |
389
+ | `storage` | `local`, `sync`, `session`, `managed`, `isAreaAvailable` |
390
+ | `tabs` | `query`, `get`, `create`, `update`, `remove`, `reload`, `sendMessage` |
391
+ | `windows` | `getCurrent`, `create`, `update`, `remove`, `getAll` |
392
+ | `action` | badge text/color, title, icon, `openPopup`, `onClicked` |
393
+ | `alarms` | `create`, `get`, `getAll`, `clear`, `clearAll`, `onAlarm` |
394
+ | `commands` | `getAll`, `onCommand` |
395
+ | `contextMenus` | `create`, `update`, `remove`, `removeAll`, `onClicked` |
396
+ | `notifications` | `create`, `update`, `clear`, `getAll`, events |
397
+ | `permissions` | `contains`, `request`, `remove`, `getAll`, events |
398
+ | `scripting` | `executeScript`, `insertCSS`, `removeCSS` |
399
+ | `bookmarks` | `getTree`, `search`, CRUD, events |
400
+ | `cookies` | `get`, `getAll`, `set`, `remove`, `onChanged` |
401
+ | `downloads` | `download`, `cancel`, `search`, `onChanged` |
402
+ | `sessions` | `getRecentlyClosed`, `restore`, `onChanged` |
403
+ | `webNavigation` | navigation event passthrough |
404
+ | `webRequest` | observational request event passthrough |
405
+ | `i18n` | `getMessage`, `getUILanguage`, `detectLanguage` |
406
+
407
+ Shared exports:
408
+
409
+ - `browserInfo`: lazily resolved browser metadata.
410
+ - `getBrowserInfo()`: resolves the browser name, version, engine, and manifest
411
+ version.
412
+ - `supports('namespace.method')`: boolean feature check.
413
+ - `getAvailability('namespace.method')`: typed availability result with a
414
+ reason.
415
+ - `isBrowserSupported()`: whether an extension runtime is available.
416
+ - `BrowserApiError`: normalized error class with a `code`.
417
+ - `callbackToPromise`, `promisifyMethod`, and event helpers for advanced
418
+ integrations.
419
+
420
+ ## TypeScript examples
421
+
422
+ All public types are exported from the package root.
423
+
424
+ ```ts
425
+ import {
426
+ BrowserApiError,
427
+ runtime,
428
+ storage,
429
+ type StorageChange,
430
+ } from '@addonova/browser';
431
+
432
+ interface Settings {
433
+ enabled: boolean;
434
+ theme: 'light' | 'dark';
435
+ }
436
+
437
+ export async function readSettings(): Promise<Settings> {
438
+ const values = await storage.local.get(['enabled', 'theme']);
439
+
440
+ return {
441
+ enabled: values.enabled === true,
442
+ theme: values.theme === 'dark' ? 'dark' : 'light',
443
+ };
444
+ }
445
+
446
+ storage.onChanged.addListener(
447
+ (changes: Record<string, StorageChange>, areaName) => {
448
+ if (areaName !== 'local' || changes.enabled === undefined) {
449
+ return;
450
+ }
451
+
452
+ console.log('enabled changed:', changes.enabled.newValue);
453
+ },
454
+ );
455
+
456
+ export async function sendHello(): Promise<void> {
457
+ try {
458
+ await runtime.sendMessage({ type: 'hello' });
459
+ } catch (error) {
460
+ if (error instanceof BrowserApiError) {
461
+ console.error(error.code, error.api, error.message);
462
+ }
463
+ }
464
+ }
465
+ ```
466
+
467
+ ## Browser detection
468
+
469
+ ```ts
470
+ import { browserInfo, getBrowserInfo } from '@addonova/browser';
471
+
472
+ console.log(browserInfo.name); // 'chrome' | 'chromium' | 'edge' | 'firefox' | 'unknown'
473
+ console.log(browserInfo.engine); // 'chromium' | 'gecko' | 'unknown'
474
+
475
+ const info = getBrowserInfo();
476
+ console.log(info);
477
+ ```
478
+
479
+ Detection is feature-first: it checks the extension runtime and API shape before
480
+ parsing the user agent. Outside an extension, the package returns an unknown
481
+ environment instead of throwing at import time.
482
+
483
+ ## 8. Feature detection
484
+
485
+ ```ts
486
+ import { getAvailability, storage, supports } from '@addonova/browser';
487
+
488
+ if (supports('scripting.executeScript')) {
489
+ // The native API is present.
490
+ }
491
+
492
+ const availability = getAvailability('storage.session');
493
+ console.log(availability.available, availability.reason);
494
+
495
+ if (storage.isAreaAvailable('session')) {
496
+ await storage.session.set({ draft: 'saved for this browser session' });
497
+ }
498
+ ```
499
+
500
+ `supports()` is a fast capability check. `getAvailability()` explains why a
501
+ capability is unavailable. Accessing a missing method or event throws
502
+ `BrowserApiError` with `BROWSER_UNSUPPORTED_API`.
503
+
504
+ ## 9. Browser-specific adapters
505
+
506
+ Use the root import for runtime detection, or a fixed subpath when the target is
507
+ known at build time:
508
+
509
+ ```ts
510
+ import { tabs } from '@addonova/browser/chrome';
511
+ ```
512
+
513
+ Chrome, Chromium, and Edge share the Chromium adapter implementation. Firefox
514
+ uses a separate Gecko adapter with native Promises. All entries expose the same
515
+ public surface. Application code should normally import the root; the build
516
+ integration chooses the subpath.
517
+
518
+ ## Browser-specific tree shaking
519
+
520
+ Use static imports so a bundler can remove unused APIs:
521
+
522
+ ```ts
523
+ import { tabs } from '@addonova/browser';
524
+
525
+ const [tab] = await tabs.query({ active: true, currentWindow: true });
526
+ ```
527
+
528
+ Aliasing the root to a browser subpath also removes unrelated adapters:
529
+
530
+ ```js
531
+ // Vite-style alias
532
+ resolve: {
533
+ alias: {
534
+ '@addonova/browser': '@addonova/browser/chrome',
535
+ },
536
+ }
537
+ ```
538
+
539
+ The repository verifies real generated bundles with Guchho in
540
+ `tests/build/tree-shaking.test.ts`. A Chrome build retains Chrome plus shared
541
+ Chromium code, a Firefox build retains Firefox code, and an Edge build retains
542
+ Edge plus shared Chromium code. Importing only `tabs` removes unrelated API
543
+ modules. Dynamic access through the `browser` object intentionally retains every
544
+ API. See [`docs/build-integration.md`](docs/build-integration.md).
545
+
546
+ ## Build-time API selection
547
+
548
+ The optional compile-time definition `__ADDONOVA_BROWSER__` accepts `'chrome'`,
549
+ `'chromium'`, `'firefox'`, `'edge'`, or `'auto'`:
550
+
551
+ ```js
552
+ define: {
553
+ __ADDONOVA_BROWSER__: '"chrome"',
554
+ }
555
+ ```
556
+
557
+ It makes target-dependent runtime behavior deterministic. It does not replace
558
+ the subpath alias for code elimination. Invalid or missing values fall back to
559
+ `'auto'`.
560
+
561
+ ## Runtime resolution
562
+
563
+ The default root import resolves the adapter on first use and memoizes it:
564
+
565
+ 1. Read the build-time target, if present.
566
+ 2. Detect the extension runtime and browser API shape.
567
+ 3. Select Chrome, Chromium, Edge, or Firefox.
568
+ 4. Fall back to an unknown environment that reports explicit unsupported errors.
569
+
570
+ Runtime resolution always remains available. It does not require Addonova Build,
571
+ a define, or an alias. Fixed subpaths skip detection and use their target
572
+ provider directly.
573
+
574
+ ## Manifest compatibility
575
+
576
+ The library does not create or edit `manifest.json`.
577
+
578
+ Manifest V3:
579
+
580
+ ```json
581
+ {
582
+ "manifest_version": 3,
583
+ "background": { "service_worker": "background.js", "type": "module" },
584
+ "action": { "default_popup": "popup.html" },
585
+ "permissions": ["storage", "tabs"],
586
+ "host_permissions": ["https://example.com/*"]
587
+ }
588
+ ```
589
+
590
+ For Firefox MV3, use an event page instead of a service worker:
591
+
592
+ ```json
593
+ {
594
+ "manifest_version": 3,
595
+ "background": { "scripts": ["background.js"] },
596
+ "action": { "default_popup": "popup.html" },
597
+ "browser_specific_settings": {
598
+ "gecko": { "id": "example@addonova.dev" }
599
+ }
600
+ }
601
+ ```
602
+
603
+ Manifest V2 for Firefox uses `browser_action` or `page_action`, host
604
+ permissions inside `permissions`, and background scripts or a background page.
605
+ Chromium legacy MV2 is outside current support scope; Edge MV2 is conditional.
606
+ See the [compatibility matrix](docs/compatibility-matrix.md) for background
607
+ shapes, `webRequestBlocking`, `scripting`, and host-permission differences.
608
+
609
+ Register event listeners synchronously at the top level of a service worker or
610
+ event page. Do not wait for asynchronous initialization before registering
611
+ listeners.
612
+
613
+ ## Error handling
614
+
615
+ ```ts
616
+ import { BrowserApiError, runtime } from '@addonova/browser';
617
+
618
+ try {
619
+ await runtime.sendMessage({ type: 'ping' });
620
+ } catch (error) {
621
+ if (error instanceof BrowserApiError) {
622
+ switch (error.code) {
623
+ case 'BROWSER_UNSUPPORTED_API':
624
+ case 'BROWSER_UNSUPPORTED_ENVIRONMENT':
625
+ case 'BROWSER_PERMISSION_DENIED':
626
+ case 'BROWSER_API_FAILED':
627
+ console.error(error.code, error.api, error.message);
628
+ break;
629
+ }
630
+ }
631
+ }
632
+ ```
633
+
634
+ Error codes:
635
+
636
+ | Code | Meaning |
637
+ | --- | --- |
638
+ | `BROWSER_UNSUPPORTED_API` | Namespace, method, or event is unavailable. |
639
+ | `BROWSER_UNSUPPORTED_ENVIRONMENT` | No extension runtime was detected. |
640
+ | `BROWSER_PERMISSION_DENIED` | The browser reported a permission failure. |
641
+ | `BROWSER_API_FAILED` | A native API failed for another reason. |
642
+
643
+ No module fakes success, silently no-ops, or falls back to an unrequested API.
644
+
645
+ ## Permissions
646
+
647
+ Permissions are never requested or granted automatically. Declare static
648
+ permissions in the manifest and request optional permissions from a permitted
649
+ user gesture:
650
+
651
+ ```ts
652
+ import { permissions } from '@addonova/browser';
653
+
654
+ const granted = await permissions.request({
655
+ permissions: ['bookmarks'],
656
+ });
657
+
658
+ if (!granted) {
659
+ console.warn('Bookmarks permission was not granted.');
660
+ }
661
+ ```
662
+
663
+ Module permission mapping:
664
+
665
+ | Module | Requirement |
666
+ | --- | --- |
667
+ | `storage` | `storage` |
668
+ | `tabs` | `tabs` for sensitive fields; host permissions for page access |
669
+ | `alarms` | `alarms` |
670
+ | `contextMenus` | `contextMenus` |
671
+ | `notifications` | `notifications` |
672
+ | `bookmarks` | `bookmarks` |
673
+ | `cookies` | `cookies` plus host permissions |
674
+ | `downloads` | `downloads` |
675
+ | `sessions` | `sessions` |
676
+ | `webNavigation` | `webNavigation` |
677
+ | `webRequest` | `webRequest` plus host permissions |
678
+ | `scripting` | `scripting` plus host permissions or `activeTab` |
679
+ | `runtime`, `action`, `commands`, `i18n` | No extra runtime permission |
680
+
681
+ Static host permissions are granted at install time subject to browser policy.
682
+ `permissions.request()` asks the user; it can return `false` or reject. See
683
+ [`docs/api-compatibility.md`](docs/api-compatibility.md) and the
684
+ [compatibility matrix](docs/compatibility-matrix.md).
685
+
686
+ Packaging and manual browser smoke-test steps are in
687
+ [`docs/integration-testing.md`](docs/integration-testing.md).
688
+
689
+ Do not publish to npm without explicit authorization. Security issues should be
690
+ reported according to [`SECURITY`](https://github.com/addonova/.github/blob/main/.github/SECURITY.md).
691
+
692
+ ## License
693
+
694
+ Addonova is licensed under **MIT**. Copyright © [CodeHemu](https://github.com/code-hemu).