@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.
- package/LICENSE +21 -0
- package/README.md +693 -2
- package/dist/adapters/chrome/index.d.ts +9 -0
- package/dist/adapters/chrome/index.d.ts.map +1 -0
- package/dist/adapters/chrome/index.js +12 -0
- package/dist/adapters/chrome/index.js.map +1 -0
- package/dist/adapters/chromium/index.d.ts +12 -0
- package/dist/adapters/chromium/index.d.ts.map +1 -0
- package/dist/adapters/chromium/index.js +12 -0
- package/dist/adapters/chromium/index.js.map +1 -0
- package/dist/adapters/edge/index.d.ts +11 -0
- package/dist/adapters/edge/index.d.ts.map +1 -0
- package/dist/adapters/edge/index.js +13 -0
- package/dist/adapters/edge/index.js.map +1 -0
- package/dist/adapters/firefox/index.d.ts +10 -0
- package/dist/adapters/firefox/index.d.ts.map +1 -0
- package/dist/adapters/firefox/index.js +12 -0
- package/dist/adapters/firefox/index.js.map +1 -0
- package/dist/adapters/shared/adapter.d.ts +13 -0
- package/dist/adapters/shared/adapter.d.ts.map +1 -0
- package/dist/adapters/shared/adapter.js +24 -0
- package/dist/adapters/shared/adapter.js.map +1 -0
- package/dist/adapters/shared/namespace.d.ts +4 -0
- package/dist/adapters/shared/namespace.d.ts.map +1 -0
- package/dist/adapters/shared/namespace.js +20 -0
- package/dist/adapters/shared/namespace.js.map +1 -0
- package/dist/adapters/types.d.ts +10 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/api/action/index.d.ts +6 -0
- package/dist/api/action/index.d.ts.map +1 -0
- package/dist/api/action/index.js +56 -0
- package/dist/api/action/index.js.map +1 -0
- package/dist/api/alarms/index.d.ts +5 -0
- package/dist/api/alarms/index.d.ts.map +1 -0
- package/dist/api/alarms/index.js +34 -0
- package/dist/api/alarms/index.js.map +1 -0
- package/dist/api/bookmarks/index.d.ts +5 -0
- package/dist/api/bookmarks/index.d.ts.map +1 -0
- package/dist/api/bookmarks/index.js +39 -0
- package/dist/api/bookmarks/index.js.map +1 -0
- package/dist/api/browser.d.ts +3 -0
- package/dist/api/browser.d.ts.map +1 -0
- package/dist/api/browser.js +2 -0
- package/dist/api/browser.js.map +1 -0
- package/dist/api/commands/index.d.ts +5 -0
- package/dist/api/commands/index.d.ts.map +1 -0
- package/dist/api/commands/index.js +12 -0
- package/dist/api/commands/index.js.map +1 -0
- package/dist/api/context-menus/index.d.ts +5 -0
- package/dist/api/context-menus/index.d.ts.map +1 -0
- package/dist/api/context-menus/index.js +21 -0
- package/dist/api/context-menus/index.js.map +1 -0
- package/dist/api/cookies/index.d.ts +5 -0
- package/dist/api/cookies/index.d.ts.map +1 -0
- package/dist/api/cookies/index.js +21 -0
- package/dist/api/cookies/index.js.map +1 -0
- package/dist/api/downloads/index.d.ts +5 -0
- package/dist/api/downloads/index.d.ts.map +1 -0
- package/dist/api/downloads/index.js +20 -0
- package/dist/api/downloads/index.js.map +1 -0
- package/dist/api/helpers.d.ts +17 -0
- package/dist/api/helpers.d.ts.map +1 -0
- package/dist/api/helpers.js +100 -0
- package/dist/api/helpers.js.map +1 -0
- package/dist/api/i18n/index.d.ts +5 -0
- package/dist/api/i18n/index.d.ts.map +1 -0
- package/dist/api/i18n/index.js +17 -0
- package/dist/api/i18n/index.js.map +1 -0
- package/dist/api/lazy-adapter.d.ts +5 -0
- package/dist/api/lazy-adapter.d.ts.map +1 -0
- package/dist/api/lazy-adapter.js +37 -0
- package/dist/api/lazy-adapter.js.map +1 -0
- package/dist/api/notifications/index.d.ts +5 -0
- package/dist/api/notifications/index.d.ts.map +1 -0
- package/dist/api/notifications/index.js +27 -0
- package/dist/api/notifications/index.js.map +1 -0
- package/dist/api/permissions/index.d.ts +5 -0
- package/dist/api/permissions/index.d.ts.map +1 -0
- package/dist/api/permissions/index.js +24 -0
- package/dist/api/permissions/index.js.map +1 -0
- package/dist/api/runtime/index.d.ts +5 -0
- package/dist/api/runtime/index.d.ts.map +1 -0
- package/dist/api/runtime/index.js +38 -0
- package/dist/api/runtime/index.js.map +1 -0
- package/dist/api/scripting/index.d.ts +5 -0
- package/dist/api/scripting/index.d.ts.map +1 -0
- package/dist/api/scripting/index.js +15 -0
- package/dist/api/scripting/index.js.map +1 -0
- package/dist/api/sessions/index.d.ts +5 -0
- package/dist/api/sessions/index.d.ts.map +1 -0
- package/dist/api/sessions/index.js +19 -0
- package/dist/api/sessions/index.js.map +1 -0
- package/dist/api/singletons.d.ts +40 -0
- package/dist/api/singletons.d.ts.map +1 -0
- package/dist/api/singletons.js +60 -0
- package/dist/api/singletons.js.map +1 -0
- package/dist/api/storage/area.d.ts +4 -0
- package/dist/api/storage/area.d.ts.map +1 -0
- package/dist/api/storage/area.js +29 -0
- package/dist/api/storage/area.js.map +1 -0
- package/dist/api/storage/index.d.ts +7 -0
- package/dist/api/storage/index.d.ts.map +1 -0
- package/dist/api/storage/index.js +37 -0
- package/dist/api/storage/index.js.map +1 -0
- package/dist/api/storage/item.d.ts +4 -0
- package/dist/api/storage/item.d.ts.map +1 -0
- package/dist/api/storage/item.js +109 -0
- package/dist/api/storage/item.js.map +1 -0
- package/dist/api/tabs/index.d.ts +5 -0
- package/dist/api/tabs/index.d.ts.map +1 -0
- package/dist/api/tabs/index.js +49 -0
- package/dist/api/tabs/index.js.map +1 -0
- package/dist/api/web-navigation/index.d.ts +5 -0
- package/dist/api/web-navigation/index.d.ts.map +1 -0
- package/dist/api/web-navigation/index.js +30 -0
- package/dist/api/web-navigation/index.js.map +1 -0
- package/dist/api/web-request/index.d.ts +5 -0
- package/dist/api/web-request/index.d.ts.map +1 -0
- package/dist/api/web-request/index.js +27 -0
- package/dist/api/web-request/index.js.map +1 -0
- package/dist/api/windows/index.d.ts +5 -0
- package/dist/api/windows/index.d.ts.map +1 -0
- package/dist/api/windows/index.js +27 -0
- package/dist/api/windows/index.js.map +1 -0
- package/dist/browser/detect.d.ts +19 -0
- package/dist/browser/detect.d.ts.map +1 -0
- package/dist/browser/detect.js +89 -0
- package/dist/browser/detect.js.map +1 -0
- package/dist/browser/info.d.ts +17 -0
- package/dist/browser/info.d.ts.map +1 -0
- package/dist/browser/info.js +77 -0
- package/dist/browser/info.js.map +1 -0
- package/dist/browser/resolve.d.ts +9 -0
- package/dist/browser/resolve.d.ts.map +1 -0
- package/dist/browser/resolve.js +57 -0
- package/dist/browser/resolve.js.map +1 -0
- package/dist/browser/support.d.ts +8 -0
- package/dist/browser/support.d.ts.map +1 -0
- package/dist/browser/support.js +18 -0
- package/dist/browser/support.js.map +1 -0
- package/dist/build/target.d.ts +5 -0
- package/dist/build/target.d.ts.map +1 -0
- package/dist/build/target.js +21 -0
- package/dist/build/target.js.map +1 -0
- package/dist/content/context.d.ts +24 -0
- package/dist/content/context.d.ts.map +1 -0
- package/dist/content/context.js +157 -0
- package/dist/content/context.js.map +1 -0
- package/dist/content/index.d.ts +7 -0
- package/dist/content/index.d.ts.map +1 -0
- package/dist/content/index.js +4 -0
- package/dist/content/index.js.map +1 -0
- package/dist/content/inject-root.d.ts +2 -0
- package/dist/content/inject-root.d.ts.map +1 -0
- package/dist/content/inject-root.js +4 -0
- package/dist/content/inject-root.js.map +1 -0
- package/dist/content/inject-script.d.ts +16 -0
- package/dist/content/inject-script.d.ts.map +1 -0
- package/dist/content/inject-script.js +39 -0
- package/dist/content/inject-script.js.map +1 -0
- package/dist/content/ui.d.ts +50 -0
- package/dist/content/ui.d.ts.map +1 -0
- package/dist/content/ui.js +331 -0
- package/dist/content/ui.js.map +1 -0
- package/dist/entries/chrome.d.ts +52 -0
- package/dist/entries/chrome.d.ts.map +1 -0
- package/dist/entries/chrome.js +101 -0
- package/dist/entries/chrome.js.map +1 -0
- package/dist/entries/chromium.d.ts +52 -0
- package/dist/entries/chromium.d.ts.map +1 -0
- package/dist/entries/chromium.js +102 -0
- package/dist/entries/chromium.js.map +1 -0
- package/dist/entries/edge.d.ts +52 -0
- package/dist/entries/edge.d.ts.map +1 -0
- package/dist/entries/edge.js +101 -0
- package/dist/entries/edge.js.map +1 -0
- package/dist/entries/firefox.d.ts +52 -0
- package/dist/entries/firefox.d.ts.map +1 -0
- package/dist/entries/firefox.js +100 -0
- package/dist/entries/firefox.js.map +1 -0
- package/dist/entries/info.d.ts +5 -0
- package/dist/entries/info.d.ts.map +1 -0
- package/dist/entries/info.js +21 -0
- package/dist/entries/info.js.map +1 -0
- package/dist/entries/shared-exports.d.ts +20 -0
- package/dist/entries/shared-exports.d.ts.map +1 -0
- package/dist/entries/shared-exports.js +10 -0
- package/dist/entries/shared-exports.js.map +1 -0
- package/dist/entries/support.d.ts +6 -0
- package/dist/entries/support.d.ts.map +1 -0
- package/dist/entries/support.js +16 -0
- package/dist/entries/support.js.map +1 -0
- package/dist/entries/utilities.d.ts +6 -0
- package/dist/entries/utilities.d.ts.map +1 -0
- package/dist/entries/utilities.js +7 -0
- package/dist/entries/utilities.js.map +1 -0
- package/dist/i18n/index.d.ts +16 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +52 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/messaging/index.d.ts +22 -0
- package/dist/messaging/index.d.ts.map +1 -0
- package/dist/messaging/index.js +100 -0
- package/dist/messaging/index.js.map +1 -0
- package/dist/messaging/proxy.d.ts +17 -0
- package/dist/messaging/proxy.d.ts.map +1 -0
- package/dist/messaging/proxy.js +61 -0
- package/dist/messaging/proxy.js.map +1 -0
- package/dist/messaging/root.d.ts +4 -0
- package/dist/messaging/root.d.ts.map +1 -0
- package/dist/messaging/root.js +9 -0
- package/dist/messaging/root.js.map +1 -0
- package/dist/shared/capabilities.d.ts +25 -0
- package/dist/shared/capabilities.d.ts.map +1 -0
- package/dist/shared/capabilities.js +41 -0
- package/dist/shared/capabilities.js.map +1 -0
- package/dist/shared/errors.d.ts +18 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +32 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/events.d.ts +11 -0
- package/dist/shared/events.d.ts.map +1 -0
- package/dist/shared/events.js +19 -0
- package/dist/shared/events.js.map +1 -0
- package/dist/shared/objects.d.ts +4 -0
- package/dist/shared/objects.d.ts.map +1 -0
- package/dist/shared/objects.js +14 -0
- package/dist/shared/objects.js.map +1 -0
- package/dist/shared/promises.d.ts +11 -0
- package/dist/shared/promises.d.ts.map +1 -0
- package/dist/shared/promises.js +70 -0
- package/dist/shared/promises.js.map +1 -0
- package/dist/testing/fake-browser.d.ts +184 -0
- package/dist/testing/fake-browser.d.ts.map +1 -0
- package/dist/testing/fake-browser.js +994 -0
- package/dist/testing/fake-browser.js.map +1 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +2 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/setup.d.ts +2 -0
- package/dist/testing/setup.d.ts.map +1 -0
- package/dist/testing/setup.js +6 -0
- package/dist/testing/setup.js.map +1 -0
- package/dist/types/action.d.ts +35 -0
- package/dist/types/action.d.ts.map +1 -0
- package/dist/types/action.js +2 -0
- package/dist/types/action.js.map +1 -0
- package/dist/types/alarms.d.ts +21 -0
- package/dist/types/alarms.d.ts.map +1 -0
- package/dist/types/alarms.js +2 -0
- package/dist/types/alarms.js.map +1 -0
- package/dist/types/bookmarks.d.ts +57 -0
- package/dist/types/bookmarks.d.ts.map +1 -0
- package/dist/types/bookmarks.js +2 -0
- package/dist/types/bookmarks.js.map +1 -0
- package/dist/types/commands.d.ts +11 -0
- package/dist/types/commands.d.ts.map +1 -0
- package/dist/types/commands.js +2 -0
- package/dist/types/commands.js.map +1 -0
- package/dist/types/context-menus.d.ts +48 -0
- package/dist/types/context-menus.d.ts.map +1 -0
- package/dist/types/context-menus.js +2 -0
- package/dist/types/context-menus.js.map +1 -0
- package/dist/types/cookies.d.ts +54 -0
- package/dist/types/cookies.d.ts.map +1 -0
- package/dist/types/cookies.js +2 -0
- package/dist/types/cookies.js.map +1 -0
- package/dist/types/downloads.d.ts +43 -0
- package/dist/types/downloads.d.ts.map +1 -0
- package/dist/types/downloads.js +2 -0
- package/dist/types/downloads.js.map +1 -0
- package/dist/types/i18n.d.ts +14 -0
- package/dist/types/i18n.d.ts.map +1 -0
- package/dist/types/i18n.js +2 -0
- package/dist/types/i18n.js.map +1 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/notifications.d.ts +35 -0
- package/dist/types/notifications.d.ts.map +1 -0
- package/dist/types/notifications.js +2 -0
- package/dist/types/notifications.js.map +1 -0
- package/dist/types/permissions.d.ts +14 -0
- package/dist/types/permissions.d.ts.map +1 -0
- package/dist/types/permissions.js +2 -0
- package/dist/types/permissions.js.map +1 -0
- package/dist/types/runtime.d.ts +58 -0
- package/dist/types/runtime.d.ts.map +1 -0
- package/dist/types/runtime.js +2 -0
- package/dist/types/runtime.js.map +1 -0
- package/dist/types/scripting.d.ts +30 -0
- package/dist/types/scripting.d.ts.map +1 -0
- package/dist/types/scripting.js +2 -0
- package/dist/types/scripting.js.map +1 -0
- package/dist/types/sessions.d.ts +28 -0
- package/dist/types/sessions.d.ts.map +1 -0
- package/dist/types/sessions.js +2 -0
- package/dist/types/sessions.js.map +1 -0
- package/dist/types/storage.d.ts +51 -0
- package/dist/types/storage.d.ts.map +1 -0
- package/dist/types/storage.js +2 -0
- package/dist/types/storage.js.map +1 -0
- package/dist/types/tabs.d.ts +96 -0
- package/dist/types/tabs.d.ts.map +1 -0
- package/dist/types/tabs.js +2 -0
- package/dist/types/tabs.js.map +1 -0
- package/dist/types/web-navigation.d.ts +28 -0
- package/dist/types/web-navigation.d.ts.map +1 -0
- package/dist/types/web-navigation.js +2 -0
- package/dist/types/web-navigation.js.map +1 -0
- package/dist/types/web-request.d.ts +20 -0
- package/dist/types/web-request.d.ts.map +1 -0
- package/dist/types/web-request.js +2 -0
- package/dist/types/web-request.js.map +1 -0
- package/dist/types/windows.d.ts +52 -0
- package/dist/types/windows.d.ts.map +1 -0
- package/dist/types/windows.js +2 -0
- package/dist/types/windows.js.map +1 -0
- 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
|
-
|
|
1
|
+
[](https://github.com/addonova)
|
|
2
2
|
|
|
3
|
-
|
|
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).
|