Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,21 @@ Explore the API guides for available helpers, examples, and browser-specific det
- Firefox — partial support via compatible helpers (e.g., `sidebarAction`, `runtime.getBrowserInfo`)
- Apple Safari — limited WebExtensions support; many Chromium-specific APIs are not available, so some helpers won’t work.

All `isAvailable…()` helpers accept an optional **native method name**. A namespace can exist while an individual
method is missing, especially in Safari:

```ts
import {getBadgeTextColor, isAvailableAction} from "@addon-core/browser";

if (isAvailableAction("getBadgeTextColor")) {
const color = await getBadgeTextColor();
}
```

The check is synchronous and does not invoke the method. It checks callable presence; permissions, valid arguments
and successful execution still depend on the browser. Calls without an argument retain their namespace check.
See [Safari compatibility and migration notes](docs/safari.md) for native differences and changed result types.

## TypeScript compatibility

The package depends on `@types/chrome@^0.3.5`, which owns the global `browser` alias
Expand Down
9 changes: 6 additions & 3 deletions codegen/availability/generate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function generateAvailability(specs) {
}

const exportName = `isAvailable${alias ?? namespace[0].toUpperCase() + namespace.slice(1)}`;
const {imports, expression} = render({namespace});
const {imports, expression, methodType} = render({namespace});

return {
namespace,
Expand All @@ -37,11 +37,14 @@ export function generateAvailability(specs) {
source: [
"// Edit codegen/availability/apis.mjs and run npm run generate.",
"",
'import type {ApiMethodName} from "../../internal/api-method";',
...imports,
"",
`export const ${exportName} = (): boolean => {`,
`export const ${exportName} = (method?: ApiMethodName<${methodType}>): boolean => {`,
" try {",
` return Boolean(${expression});`,
` const api = ${expression};`,
"",
' return method === undefined ? Boolean(api) : api != null && typeof Reflect.get(api, method) === "function";',
" } catch {",
" return false;",
" }",
Expand Down
1 change: 1 addition & 0 deletions codegen/availability/templates/action.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export function renderActionAvailability() {
return {
imports: ['import {action} from "./api";'],
expression: "action()",
methodType: "typeof chrome.action | typeof chrome.browserAction",
};
}
1 change: 1 addition & 0 deletions codegen/availability/templates/basic.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export function renderBasicAvailability({namespace}) {
return {
imports: ['import {browser} from "../browser";'],
expression: `browser().${namespace}`,
methodType: `typeof chrome.${namespace}`,
};
}
1 change: 1 addition & 0 deletions codegen/availability/templates/sidebar.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export function renderSidebarAvailability() {
return {
imports: ['import {sidebarAction, sidePanel} from "./api";'],
expression: "sidePanel() || sidebarAction()",
methodType: "NonNullable<ReturnType<typeof sidePanel> | ReturnType<typeof sidebarAction>>",
};
}
9 changes: 7 additions & 2 deletions docs/action.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,18 @@ A unified, promise-based wrapper around the Chrome Action API. It transparently
### isAvailableAction

```ts
isAvailableAction(): boolean
isAvailableAction(method?: ApiMethodName<typeof chrome.action | typeof chrome.browserAction>): boolean
```

The optional `method` is a native function name, for example `isAvailableAction("isEnabled")`.
Import the public `ApiMethodName` type from `@addon-core/browser` for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.

Checks the same API selected by action methods and events: `action` for Manifest V3 and `browserAction` for Manifest V2. Does not fall back to the other namespace if the selected one is missing.
Returns `false` if the namespace or WebExtension environment is absent, or if accessing it throws.
The check is synchronous, does not log, and reads the current API on every call without caching.
It checks namespace presence only; it does not guarantee individual methods, permissions, or a successful operation.
Without an argument, it checks namespace presence only. Pass a native method name to also check that it is callable.
Neither form verifies permissions, argument support, or a successful operation. See [method availability](safari.md).

```ts
import {isAvailableAction} from "@addon-core/browser";
Expand Down
25 changes: 21 additions & 4 deletions docs/alarms.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,18 @@ A promise-based wrapper for the Chrome `alarms` API.
### isAvailableAlarms

```ts
isAvailableAlarms(): boolean
isAvailableAlarms(method?: ApiMethodName<typeof chrome.alarms>): boolean
```

The optional `method` is a native function name, for example `isAvailableAlarms("clear")`.
Import the public `ApiMethodName` type from `@addon-core/browser` for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.

Returns `true` when the `alarms` namespace is present on the API selected by `browser()` in the current context.
Returns `false` if the namespace or WebExtension environment is absent, or if accessing it throws.
The check is synchronous, does not log, and reads the current API on every call without caching.
It checks namespace presence only; it does not guarantee individual methods, permissions, or a successful operation.
Without an argument, it checks namespace presence only. Pass a native method name to also check that it is callable.
Neither form verifies permissions, argument support, or a successful operation. See [method availability](safari.md).

```ts
import {isAvailableAlarms} from "@addon-core/browser";
Expand All @@ -49,7 +54,11 @@ if (isAvailableAlarms()) {
clearAlarm(name: string): Promise<boolean>
```

Clears the alarm with the specified name, returning `true` if an existing alarm was found and cleared.
Clears the alarm with the specified name and preserves a native boolean when provided. WebKit first reads whether
the alarm exists; older implementations that report no removal result use this earlier observation. That fallback
is not atomic: another context or alarm firing can change the state between the read and deletion.
Chromium and Firefox perform no pre-read. An unexpected missing/non-boolean result outside the legacy WebKit fallback
rejects the Promise rather than silently reporting `false`. Read and deletion errors also reject.

<a name="clearAllAlarm"></a>

Expand All @@ -59,7 +68,13 @@ Clears the alarm with the specified name, returning `true` if an existing alarm
clearAllAlarm(): Promise<boolean>
```

Clears all alarms, returning `true` if any alarms were found and cleared.
Clears all alarms and returns whether the collection read immediately before deletion was nonempty. Every browser
uses `getAll` followed by `clearAll`; native removal results are ignored. This gives one boolean contract despite
Chromium returning `true` for an empty collection and Firefox 159/legacy WebKit reporting no result.
The read and deletion are not atomic: another context or alarm firing may change the collection between them.
Read and deletion errors reject instead of returning the earlier observation.

This changes the previous native-result behavior: an empty Chromium collection now returns `false`.

<a name="createAlarm"></a>

Expand All @@ -70,6 +85,8 @@ createAlarm(name: string, info: chrome.alarms.AlarmCreateInfo): Promise<void>
```

Creates a new alarm or updates an existing one with the given name and scheduling options.
The wrapper uses WebKit's supported two-argument call, handling both its older synchronous return and newer Promise.
Native synchronous errors and Promise rejections reject the public `Promise<void>`.

<a name="createAlarmIfNotExists"></a>

Expand Down
9 changes: 7 additions & 2 deletions docs/audio.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@ A promise-based wrapper for the Chrome `audio` API.
### isAvailableAudio

```ts
isAvailableAudio(): boolean
isAvailableAudio(method?: ApiMethodName<typeof chrome.audio>): boolean
```

The optional `method` is a native function name, for example `isAvailableAudio("getDevices")`.
Import the public `ApiMethodName` type from `@addon-core/browser` for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.

Returns `true` when the `audio` namespace is present on the API selected by `browser()` in the current context.
Returns `false` if the namespace or WebExtension environment is absent, or if accessing it throws.
The check is synchronous, does not log, and reads the current API on every call without caching.
It checks namespace presence only; it does not guarantee individual methods, permissions, or a successful operation.
Without an argument, it checks namespace presence only. Pass a native method name to also check that it is callable.
Neither form verifies permissions, argument support, or a successful operation. See [method availability](safari.md).

```ts
import {isAvailableAudio} from "@addon-core/browser";
Expand Down
6 changes: 5 additions & 1 deletion docs/bookmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,13 @@ All methods reject on native errors instead of converting failures into empty ar
### isAvailableBookmarks

```ts
isAvailableBookmarks(): boolean
isAvailableBookmarks(method?: ApiMethodName<typeof chrome.bookmarks>): boolean
```

The optional `method` is a native function name, for example `isAvailableBookmarks("getTree")`.
Import the public `ApiMethodName` type from `@addon-core/browser` for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.

Checks the current API selected by `browser()` on every call. Returns `false` if the namespace or
extension environment is missing, or access throws. It does not log or guarantee that a later operation succeeds.

Expand Down
9 changes: 7 additions & 2 deletions docs/browsing-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@ A promise-based wrapper for the Chrome `browsingData` API.
### isAvailableBrowsingData

```ts
isAvailableBrowsingData(): boolean
isAvailableBrowsingData(method?: ApiMethodName<typeof chrome.browsingData>): boolean
```

The optional `method` is a native function name, for example `isAvailableBrowsingData("remove")`.
Import the public `ApiMethodName` type from `@addon-core/browser` for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.

Returns `true` when the `browsingData` namespace is present on the API selected by `browser()` in the current context.
Returns `false` if the namespace or WebExtension environment is absent, or if accessing it throws.
The check is synchronous, does not log, and reads the current API on every call without caching.
It checks namespace presence only; it does not guarantee individual methods, permissions, or a successful operation.
Without an argument, it checks namespace presence only. Pass a native method name to also check that it is callable.
Neither form verifies permissions, argument support, or a successful operation. See [method availability](safari.md).

```ts
import {isAvailableBrowsingData} from "@addon-core/browser";
Expand Down
9 changes: 7 additions & 2 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,18 @@ A promise-based wrapper for the Chrome `commands` API.
### isAvailableCommands

```ts
isAvailableCommands(): boolean
isAvailableCommands(method?: ApiMethodName<typeof chrome.commands>): boolean
```

The optional `method` is a native function name, for example `isAvailableCommands("getAll")`.
Import the public `ApiMethodName` type from `@addon-core/browser` for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.

Returns `true` when the `commands` namespace is present on the API selected by `browser()` in the current context.
Returns `false` if the namespace or WebExtension environment is absent, or if accessing it throws.
The check is synchronous, does not log, and reads the current API on every call without caching.
It checks namespace presence only; it does not guarantee individual methods, permissions, or a successful operation.
Without an argument, it checks namespace presence only. Pass a native method name to also check that it is callable.
Neither form verifies permissions, argument support, or a successful operation. See [method availability](safari.md).

```ts
import {isAvailableCommands} from "@addon-core/browser";
Expand Down
9 changes: 7 additions & 2 deletions docs/context-menus.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ A promise-based wrapper for the Chrome `contextMenus` API.
### isAvailableContextMenus

```ts
isAvailableContextMenus(): boolean
isAvailableContextMenus(method?: ApiMethodName<typeof chrome.contextMenus>): boolean
```

The optional `method` is a native function name, for example `isAvailableContextMenus("create")`.
Import the public `ApiMethodName` type from `@addon-core/browser` for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.

Returns `true` when the `contextMenus` namespace is present on the API selected by `browser()` in the current context.
Returns `false` if the namespace or WebExtension environment is absent, or if accessing it throws.
The check is synchronous, does not log, and reads the current API on every call without caching.
It checks namespace presence only; it does not guarantee individual methods, permissions, or a successful operation.
Without an argument, it checks namespace presence only. Pass a native method name to also check that it is callable.
Neither form verifies permissions, argument support, or a successful operation. See [method availability](safari.md).

```ts
import {isAvailableContextMenus} from "@addon-core/browser";
Expand Down
15 changes: 11 additions & 4 deletions docs/cookies.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,18 @@ A promise-based wrapper for the Chrome `cookies` API.
### isAvailableCookies

```ts
isAvailableCookies(): boolean
isAvailableCookies(method?: ApiMethodName<typeof chrome.cookies>): boolean
```

The optional `method` is a native function name, for example `isAvailableCookies("get")`.
Import the public `ApiMethodName` type from `@addon-core/browser` for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.

Returns `true` when the `cookies` namespace is present on the API selected by `browser()` in the current context.
Returns `false` if the namespace or WebExtension environment is absent, or if accessing it throws.
The check is synchronous, does not log, and reads the current API on every call without caching.
It checks namespace presence only; it does not guarantee individual methods, permissions, or a successful operation.
Without an argument, it checks namespace presence only. Pass a native method name to also check that it is callable.
Neither form verifies permissions, argument support, or a successful operation. See [method availability](safari.md).

```ts
import {isAvailableCookies} from "@addon-core/browser";
Expand Down Expand Up @@ -84,10 +89,12 @@ Retrieves the partition key for the cookie associated with the given frame (Mani
### removeCookie

```
removeCookie(details: chrome.cookies.CookieDetails): Promise<chrome.cookies.CookieDetails>
removeCookie(details: chrome.cookies.CookieDetails): Promise<chrome.cookies.CookieDetails | null>
```

Removes the cookie matching the specified details, returning the details of the removed cookie.
Removes the cookie matching the specified details. Preserves returned native details and normalizes an absent result
(`null` or `undefined`) to `null`. Chromium can return details even when the cookie was already absent; this result
does not establish that a cookie existed. See the [migration notes](safari.md#breaking-change-migration).

<a name="setCookie"></a>

Expand Down
6 changes: 5 additions & 1 deletion docs/declarative-net-request.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,13 @@ for rule matching and limits rather than assuming identical behavior across brow
### isAvailableDnr

```ts
isAvailableDnr(): boolean
isAvailableDnr(method?: ApiMethodName<typeof chrome.declarativeNetRequest>): boolean
```

The optional `method` is a native function name, for example `isAvailableDnr("getDynamicRules")`.
Import the public `ApiMethodName` type from `@addon-core/browser` for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.

Checks the current native namespace on each call. Returns `false` if the environment or namespace
is missing or access throws. It does not log, probe native operations or cache results.

Expand Down
9 changes: 7 additions & 2 deletions docs/document-scan.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ A promise-based wrapper for the Chrome `documentScan` API.
### isAvailableDocumentScan

```ts
isAvailableDocumentScan(): boolean
isAvailableDocumentScan(method?: ApiMethodName<typeof chrome.documentScan>): boolean
```

The optional `method` is a native function name, for example `isAvailableDocumentScan("scan")`.
Import the public `ApiMethodName` type from `@addon-core/browser` for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.

Returns `true` when the `documentScan` namespace is present on the API selected by `browser()` in the current context.
Returns `false` if the namespace or WebExtension environment is absent, or if accessing it throws.
The check is synchronous, does not log, and reads the current API on every call without caching.
It checks namespace presence only; it does not guarantee individual methods, permissions, or a successful operation.
Without an argument, it checks namespace presence only. Pass a native method name to also check that it is callable.
Neither form verifies permissions, argument support, or a successful operation. See [method availability](safari.md).

```ts
import {isAvailableDocumentScan} from "@addon-core/browser";
Expand Down
9 changes: 7 additions & 2 deletions docs/downloads.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,18 @@ A promise-based wrapper for the Chrome `downloads` API.
### isAvailableDownloads

```ts
isAvailableDownloads(): boolean
isAvailableDownloads(method?: ApiMethodName<typeof chrome.downloads>): boolean
```

The optional `method` is a native function name, for example `isAvailableDownloads("search")`.
Import the public `ApiMethodName` type from `@addon-core/browser` for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.

Returns `true` when the `downloads` namespace is present on the API selected by `browser()` in the current context.
Returns `false` if the namespace or WebExtension environment is absent, or if accessing it throws.
The check is synchronous, does not log, and reads the current API on every call without caching.
It checks namespace presence only; it does not guarantee individual methods, permissions, or a successful operation.
Without an argument, it checks namespace presence only. Pass a native method name to also check that it is callable.
Neither form verifies permissions, argument support, or a successful operation. See [method availability](safari.md).

```ts
import {isAvailableDownloads} from "@addon-core/browser";
Expand Down
Loading