Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@wxt-dev/alarms Proposal #1539

Open
Timeraa opened this issue Mar 28, 2025 · 3 comments
Open

@wxt-dev/alarms Proposal #1539

Timeraa opened this issue Mar 28, 2025 · 3 comments
Assignees

Comments

@Timeraa
Copy link
Member

Timeraa commented Mar 28, 2025

@wxt-dev/alarms API Proposal

Overview

This proposal outlines a simple wrapper for Chrome’s native browser.alarms API, available as @wxt-dev/alarms. The goal is to provide an asynchronous, promise-based interface that aligns with our existing API design patterns. The new design centers on a single function, defineAlarm, which returns an object with helper methods to manage an alarm instance.

Objectives

  • Consistency: Provide a uniform, promise-based API similar to our other modules.
  • Simplicity: Encapsulate alarm management within a defined object.
  • Extensibility: Lay the groundwork for future enhancements, such as snapshotting and restoration of alarms.

Proposed API Design

1. defineAlarm

This function defines an alarm, automatically initializing and registering it if it does not already exist. It returns an object with methods to retrieve, reschedule, clear, and watch the alarm. By handling the registration of the alarm listener internally, this design eliminates the need for manually adding a listener via browser.alarms.onAlarm.addListener for each alarm, thereby reducing boilerplate and potential errors.

/**
 * Defines an alarm with a specified name and configuration.
 * Returns an object with helper methods to manage the alarm instance.
 *
 * @param {string} name - The name of the alarm.
 * @param {browser.alarms.AlarmCreateInfo} options - The scheduling options for the alarm.
 * @returns {DefinedAlarm} An object representing the defined alarm.
 */
function defineAlarm(name: string, options: browser.alarms.AlarmCreateInfo): DefinedAlarm;

The returned DefinedAlarm object has the following interface:

interface DefinedAlarm {
  /**
   * Retrieves the current alarm configuration.
   *
   * @returns {Promise<browser.alarms.Alarm | null>} A promise that resolves to the alarm object, or null if not found.
   */
  getAlarm(): Promise<browser.alarms.Alarm | null>;

  /**
   * Reschedules the alarm with new scheduling options.
   *
   * @param {browser.alarms.AlarmCreateInfo} options - The new scheduling options.
   * @returns {Promise<void>} A promise that resolves once the alarm is rescheduled.
   */
  reschedule(options: browser.alarms.AlarmCreateInfo): Promise<void>;

  /**
   * Clears the alarm. Note: This only temporarily disables the alarm until the extension is reloaded or `defineAlarm` is run again.
   *
   * @returns {Promise<boolean>} A promise that resolves to true if the alarm was cleared successfully.
   */
  clear(): Promise<boolean>;

  /**
   * Registers a callback to listen for alarm triggers.
   *
   * @param {(alarm: browser.alarms.Alarm) => void} callback - The callback function to invoke when the alarm triggers.
   * @returns {() => void} A function to unwatch the alarm.
   */
  watch(callback: (alarm: browser.alarms.Alarm) => void): () => void;
}

Example Usage

import { alarms } from '@wxt-dev/alarms';

// Define a new alarm and store it in a variable
const dailyReminder = alarms.defineAlarm('dailyReminder', { delayInMinutes: 1440, periodInMinutes: 1440 });

// Retrieve the alarm details when needed
const alarmDetails = await dailyReminder.getAlarm();
console.log('Alarm details:', alarmDetails);

// Reschedule the alarm if necessary
await dailyReminder.reschedule({ delayInMinutes: 1440, periodInMinutes: 1440 });

// Set up a listener for the alarm trigger
const unwatch = dailyReminder.watch((triggeredAlarm) => {
  console.log('Alarm triggered:', triggeredAlarm);
});

// Clear the alarm when needed (this temporarily disables the alarm until the extension is reloaded or `defineAlarm` is run again)
await dailyReminder.clear();

// Remove the watch listener if it's no longer required
unwatch();

Alternative Scheduling Options

The API is designed with flexibility in mind. In addition to using browser.alarms for scheduling, it can be extended to support alternative scheduling mechanisms such as cron-based scheduling or even setInterval for simpler use cases. Developers may configure the scheduling method via options, enabling a more versatile approach to alarm management.

Why is this API Beneficial?

Using the traditional approach, you would need to manually add an event listener for each alarm, as shown below:

browser.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === 'dailyReminder') {
    // Handle the alarm trigger
    console.log('Alarm triggered:', alarm);
  }
});

This approach requires you to check the alarm name each time an alarm is triggered. With our proposed API, this boilerplate code is encapsulated within the defineAlarm method. The API automatically registers a listener for the specified alarm and provides a dedicated watch method, simplifying your code to just:

const unwatch = dailyReminder.watch((triggeredAlarm) => {
  console.log('Alarm triggered:', triggeredAlarm);
});

This not only improves code clarity and maintainability but also reduces the risk of errors from manual listener management.

Conclusion

This proposal outlines a straightforward and useful interface for working with Chrome alarms in an asynchronous and consistent manner. By returning an object from defineAlarm, developers can manage a specific alarm instance with its own methods, improving code clarity and encapsulation. This approach streamlines alarm management and reduces boilerplate code, making it easier to integrate and maintain alarm functionality within the extension.

@Tzyito
Copy link

Tzyito commented Mar 28, 2025

That's awesome, i'm looking forward to it!

@aklinker1
Copy link
Collaborator

Nice, I like it! Couple of questions:

  • Should create be named update or reschedule? Since defineAlarm already "creates" the alarm?
  • I always find I need CRON support, is that something we should consider now while designing the API or build it into the initial version?
  • What other alarm based packages are there, what problems do they solve, what can we learn from them, and how does this new package improve them? Also, we should consider the APIs of other popular, non extension, job scheduling packages.
    • @webext-core/job-scheduler - How does splitting the alarm definition and the on trigger callback into two calls provide more value than providing both together in a single object?
    • cron also makes you define the on trigger listener at the same time as the config for when it should fire.

@Timeraa
Copy link
Member Author

Timeraa commented Mar 28, 2025

Nice, I like it! Couple of questions:

* Should `create` be named `update` or `reschedule`? Since `defineAlarm` already "creates" the alarm?

* I always find I need CRON support, is that something we should consider now while designing the API or build it into the initial version?

* What other alarm based packages are there, what problems do they solve, what can we learn from them, and how does this new package improve them? Also, we should consider the APIs of other popular, non extension, job scheduling packages.
  
  * [`@webext-core/job-scheduler`](https://webext-core.aklinker1.io/job-scheduler/installation/) - How does splitting the alarm definition and the on trigger callback into two calls provide more value than providing both together in a single object?
  * [`cron`](https://www.npmjs.com/package/cron) also makes you define the on trigger listener at the same time as the config for when it should fire.
  1. I think reschedule sounds a bit better yeah since that's effectively what you'd be doing.
  2. I was actually thinking that it might be useful to support cron etc. in the options so I'm open to incorporate that on the implementation so far it'd be alarms, cron, plain setInterval
  3. I haven't done any research in that regard just thought that an official solution would be good as either a built-in or package

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants