{"version":3,"file":"CronjobController.mjs","sourceRoot":"","sources":["../../src/cronjob/CronjobController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  ControllerGetStateAction,\n  ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { PermissionControllerGetPermissionsAction } from '@metamask/permission-controller';\nimport {\n  getCronjobCaveatJobs,\n  SnapEndowments,\n} from '@metamask/snaps-rpc-methods';\nimport type { BackgroundEvent, SnapId } from '@metamask/snaps-sdk';\nimport type { TruncatedSnap } from '@metamask/snaps-utils';\nimport {\n  toCensoredISO8601String,\n  HandlerType,\n  logError,\n} from '@metamask/snaps-utils';\nimport { assert, Duration, inMilliseconds } from '@metamask/utils';\nimport { castDraft } from 'immer';\nimport { DateTime } from 'luxon';\nimport { nanoid } from 'nanoid';\n\nimport type { CronjobControllerMethodActions } from './CronjobController-method-action-types';\nimport { getCronjobSpecificationSchedule, getExecutionDate } from './utils';\nimport type {\n  SnapControllerHandleRequestAction,\n  SnapControllerSnapDisabledEvent,\n  SnapControllerSnapEnabledEvent,\n  SnapControllerSnapInstalledEvent,\n  SnapControllerSnapUninstalledEvent,\n  SnapControllerSnapUpdatedEvent,\n} from '../snaps';\nimport { METAMASK_ORIGIN } from '../snaps/constants';\nimport { Timer } from '../snaps/Timer';\n\nexport type CronjobControllerGetStateAction = ControllerGetStateAction<\n  typeof controllerName,\n  CronjobControllerState\n>;\n\nexport type CronjobControllerStateChangeEvent = ControllerStateChangeEvent<\n  typeof controllerName,\n  CronjobControllerState\n>;\n\nexport type CronjobControllerActions =\n  | CronjobControllerGetStateAction\n  | CronjobControllerMethodActions;\n\nexport type CronjobControllerEvents = CronjobControllerStateChangeEvent;\n\ntype AllowedActions =\n  | PermissionControllerGetPermissionsAction\n  | SnapControllerHandleRequestAction;\n\ntype AllowedEvents =\n  | SnapControllerSnapInstalledEvent\n  | SnapControllerSnapUninstalledEvent\n  | SnapControllerSnapUpdatedEvent\n  | SnapControllerSnapEnabledEvent\n  | SnapControllerSnapDisabledEvent;\n\nexport type CronjobControllerMessenger = Messenger<\n  typeof controllerName,\n  CronjobControllerActions | AllowedActions,\n  CronjobControllerEvents | AllowedEvents\n>;\n\nexport const DAILY_TIMEOUT = inMilliseconds(24, Duration.Hour);\n\nexport type CronjobControllerStateManager = {\n  set(state: CronjobControllerState): void;\n  getInitialState(): CronjobControllerState | undefined;\n};\n\nexport type CronjobControllerArgs = {\n  messenger: CronjobControllerMessenger;\n\n  /**\n   * Persisted state that will be used for rehydration.\n   */\n  state?: CronjobControllerState;\n\n  /**\n   * State manager for the controller.\n   *\n   * This is a temporary workaround to allow the controller to update the state\n   * often without persisting all of the client state to disk.\n   */\n  stateManager: CronjobControllerStateManager;\n};\n\n/**\n * Represents a background event that is scheduled to be executed by the\n * cronjob controller.\n */\nexport type InternalBackgroundEvent = BackgroundEvent & {\n  /**\n   * Whether the event is recurring.\n   */\n  recurring: boolean;\n\n  /**\n   * The cron expression or ISO 8601 duration string that defines the event's\n   * schedule.\n   */\n  schedule: string;\n};\n\n/**\n * A schedulable background event, which is a subset of the\n * {@link InternalBackgroundEvent} type, containing only the fields required to\n * schedule an event. Other fields will be populated by the cronjob controller\n * automatically.\n */\nexport type SchedulableBackgroundEvent = Omit<\n  InternalBackgroundEvent,\n  'scheduledAt' | 'date' | 'id'\n> & {\n  /**\n   * The optional ID of the event. If not provided, a new ID will be\n   * generated.\n   */\n  id?: string;\n};\n\nexport type CronjobControllerState = {\n  /**\n   * Background events and cronjobs that are scheduled to be executed.\n   */\n  events: Record<string, InternalBackgroundEvent>;\n};\n\nconst controllerName = 'CronjobController';\n\nconst MESSENGER_EXPOSED_METHODS = [\n  'init',\n  'schedule',\n  'cancel',\n  'get',\n] as const;\n\n/**\n * The cronjob controller is responsible for managing cronjobs and background\n * events for Snaps. It allows Snaps to schedule events that will be executed\n * at a later time.\n */\nexport class CronjobController extends BaseController<\n  typeof controllerName,\n  CronjobControllerState,\n  CronjobControllerMessenger\n> {\n  readonly #timers: Map<string, Timer>;\n\n  readonly #stateManager: CronjobControllerStateManager;\n\n  #dailyTimer: Timer = new Timer(DAILY_TIMEOUT);\n\n  constructor({ messenger, state, stateManager }: CronjobControllerArgs) {\n    super({\n      messenger,\n      metadata: {\n        events: {\n          includeInStateLogs: false,\n          persist: false,\n          includeInDebugSnapshot: false,\n          usedInUi: false,\n        },\n      },\n      name: controllerName,\n      state: {\n        events: {},\n        ...state,\n        ...stateManager.getInitialState(),\n      },\n    });\n\n    this.#timers = new Map();\n    this.#stateManager = stateManager;\n\n    this.messenger.subscribe(\n      'SnapController:snapInstalled',\n      this.#handleSnapInstalledEvent,\n    );\n\n    this.messenger.subscribe(\n      'SnapController:snapUninstalled',\n      this.#handleSnapUninstalledEvent,\n    );\n\n    this.messenger.subscribe(\n      'SnapController:snapEnabled',\n      this.#handleSnapEnabledEvent,\n    );\n\n    this.messenger.subscribe(\n      'SnapController:snapDisabled',\n      this.#handleSnapDisabledEvent,\n    );\n\n    this.messenger.subscribe(\n      'SnapController:snapUpdated',\n      this.#handleSnapUpdatedEvent,\n    );\n\n    this.messenger.registerMethodActionHandlers(\n      this,\n      MESSENGER_EXPOSED_METHODS,\n    );\n  }\n\n  /**\n   * Initialize the CronjobController.\n   *\n   * This starts the daily timer, clears out expired events\n   * and reschedules any remaining events.\n   */\n  init() {\n    this.#start();\n    this.#clear();\n    this.#reschedule();\n  }\n\n  /**\n   * Schedule a non-recurring background event.\n   *\n   * @param event - The event to schedule.\n   * @returns The ID of the scheduled event.\n   */\n  schedule(event: Omit<SchedulableBackgroundEvent, 'recurring'>) {\n    return this.#add({\n      ...event,\n      recurring: false,\n    });\n  }\n\n  /**\n   * Cancel an event.\n   *\n   * @param origin - The origin making the cancel call.\n   * @param id - The id of the event to cancel.\n   * @throws If the event does not exist.\n   */\n  cancel(origin: string, id: string) {\n    assert(\n      this.state.events[id],\n      `A background event with the id of \"${id}\" does not exist.`,\n    );\n\n    assert(\n      this.state.events[id].snapId === origin,\n      'Only the origin that scheduled this event can cancel it.',\n    );\n\n    this.#cancel(id);\n  }\n\n  /**\n   * Get a list of a Snap's background events.\n   *\n   * @param snapId - The id of the Snap to fetch background events for.\n   * @returns An array of background events.\n   */\n  get(snapId: SnapId): InternalBackgroundEvent[] {\n    return Object.values(this.state.events)\n      .filter(\n        (snapEvent) => snapEvent.snapId === snapId && !snapEvent.recurring,\n      )\n      .map((event) => ({\n        ...event,\n        date: toCensoredISO8601String(event.date),\n        scheduledAt: toCensoredISO8601String(event.scheduledAt),\n      }));\n  }\n\n  /**\n   * Register cronjobs for a given Snap by getting specification from the\n   * permission caveats. Once registered, each job will be scheduled.\n   *\n   * @param snapId - The snap ID to register jobs for.\n   */\n  register(snapId: SnapId) {\n    const jobs = this.#getSnapCronjobs(snapId);\n    jobs?.forEach((job) => this.#add(job));\n  }\n\n  /**\n   * Unregister all cronjobs and background events for a given Snap.\n   *\n   * @param snapId - ID of a snap.\n   */\n  unregister(snapId: SnapId) {\n    for (const [id, event] of Object.entries(this.state.events)) {\n      if (event.snapId === snapId) {\n        this.#cancel(id);\n      }\n    }\n  }\n\n  /**\n   * Run controller teardown process and unsubscribe from Snap events.\n   */\n  destroy() {\n    super.destroy();\n\n    this.messenger.unsubscribe(\n      'SnapController:snapInstalled',\n      this.#handleSnapInstalledEvent,\n    );\n\n    this.messenger.unsubscribe(\n      'SnapController:snapUninstalled',\n      this.#handleSnapUninstalledEvent,\n    );\n\n    this.messenger.unsubscribe(\n      'SnapController:snapEnabled',\n      this.#handleSnapEnabledEvent,\n    );\n\n    this.messenger.unsubscribe(\n      'SnapController:snapDisabled',\n      this.#handleSnapDisabledEvent,\n    );\n\n    this.messenger.unsubscribe(\n      'SnapController:snapUpdated',\n      this.#handleSnapUpdatedEvent,\n    );\n\n    // Cancel all timers and clear the map.\n    this.#timers.forEach((timer) => timer.cancel());\n    this.#timers.clear();\n\n    if (this.#dailyTimer.status === 'running') {\n      this.#dailyTimer.cancel();\n    }\n  }\n\n  /**\n   * Start the daily timer that will reschedule events every 24 hours.\n   */\n  #start() {\n    this.#dailyTimer = new Timer(DAILY_TIMEOUT);\n    this.#dailyTimer.start(() => {\n      this.#reschedule();\n      this.#start();\n    });\n  }\n\n  /**\n   * Add a cronjob or background event to the controller state and schedule it\n   * for execution.\n   *\n   * @param event - The event to schedule.\n   * @returns The ID of the scheduled event.\n   */\n  #add(event: SchedulableBackgroundEvent) {\n    const id = event.id ?? nanoid();\n    const internalEvent: InternalBackgroundEvent = {\n      ...event,\n      id,\n      date: getExecutionDate(event.schedule),\n      scheduledAt: new Date().toISOString(),\n    };\n\n    const { nextState } = this.update((state) => {\n      state.events[internalEvent.id] = castDraft(internalEvent);\n    });\n\n    this.#stateManager.set(nextState);\n\n    this.#schedule(internalEvent);\n    return id;\n  }\n\n  /**\n   * Get the next execution date for a given event and start a timer for it.\n   *\n   * @param event - The event to schedule.\n   * @param next - Whether to schedule to the next date, otherwise will\n   * schedule for existing date.\n   */\n  #schedule(event: InternalBackgroundEvent, next = true) {\n    if (!next) {\n      this.#startTimer(event);\n      return;\n    }\n\n    const date = getExecutionDate(event.schedule);\n    const { nextState } = this.update((state) => {\n      state.events[event.id].date = date;\n    });\n\n    this.#stateManager.set(nextState);\n\n    this.#startTimer({\n      ...event,\n      date,\n    });\n  }\n\n  /**\n   * Set up and start a timer for the given event.\n   *\n   * @param event - The event to schedule.\n   * @throws If the event is scheduled in the past.\n   */\n  #startTimer(event: InternalBackgroundEvent) {\n    const ms =\n      DateTime.fromISO(event.date, { setZone: true }).toMillis() - Date.now();\n\n    // We don't schedule this job yet as it is too far in the future.\n    if (ms > DAILY_TIMEOUT) {\n      return;\n    }\n\n    // When an event is supposed to be scheduled close to the current time\n    // we may end up needing to execute immediately instead.\n    if (ms <= 0) {\n      this.#execute(event);\n      return;\n    }\n\n    const timer = new Timer(ms);\n    timer.start(() => {\n      this.#execute(event);\n    });\n\n    this.#timers.set(event.id, timer);\n  }\n\n  /**\n   * Execute a background event. This method is called when the event's timer\n   * expires.\n   *\n   * If the event is not recurring, it will be removed from the state after\n   * execution. If it is recurring, it will be rescheduled.\n   *\n   * @param event - The event to execute.\n   */\n  #execute(event: InternalBackgroundEvent) {\n    this.messenger\n      .call('SnapController:handleRequest', {\n        snapId: event.snapId,\n        origin: METAMASK_ORIGIN,\n        handler: HandlerType.OnCronjob,\n        request: event.request,\n      })\n      .catch((error) => {\n        logError(\n          `An error occurred while executing an event for Snap \"${event.snapId}\":`,\n          error,\n        );\n      });\n\n    this.#timers.delete(event.id);\n\n    // Non-recurring events are removed from the state after execution, and\n    // recurring events are rescheduled.\n    if (!event.recurring) {\n      const { nextState } = this.update((state) => {\n        delete state.events[event.id];\n      });\n\n      this.#stateManager.set(nextState);\n\n      return;\n    }\n\n    this.#schedule(event);\n  }\n\n  /**\n   * Cancel a background event by its ID. Unlike {@link cancel}, this method\n   * does not check the origin of the event, so it can be used internally.\n   *\n   * @param id - The ID of the background event to cancel.\n   */\n  #cancel(id: string) {\n    const timer = this.#timers.get(id);\n    timer?.cancel();\n    this.#timers.delete(id);\n\n    const { nextState } = this.update((state) => {\n      delete state.events[id];\n    });\n\n    this.#stateManager.set(nextState);\n  }\n\n  /**\n   * Retrieve all cronjob specifications for a Snap.\n   *\n   * @param snapId - ID of a Snap.\n   * @returns Array of cronjob specifications.\n   */\n  #getSnapCronjobs(snapId: SnapId): SchedulableBackgroundEvent[] {\n    const permissions = this.messenger.call(\n      'PermissionController:getPermissions',\n      snapId,\n    );\n\n    const permission = permissions?.[SnapEndowments.Cronjob];\n    const definitions = getCronjobCaveatJobs(permission);\n\n    if (!definitions) {\n      return [];\n    }\n\n    return definitions.map((definition, idx) => {\n      return {\n        snapId,\n        id: `cronjob-${snapId}-${idx}`,\n        request: definition.request,\n        schedule: getCronjobSpecificationSchedule(definition),\n        recurring: true,\n      };\n    });\n  }\n\n  /**\n   * Handle events that should cause cron jobs to be registered.\n   *\n   * @param snap - Basic Snap information.\n   */\n  readonly #handleSnapInstalledEvent = (snap: TruncatedSnap) => {\n    // In case of local Snaps, they may already have cronjobs that should be cleared.\n    this.unregister(snap.id);\n    this.register(snap.id);\n  };\n\n  /**\n   * Handle the Snap enabled event. This checks if the Snap has any cronjobs or\n   * background events that need to be rescheduled.\n   *\n   * @param snap - Basic Snap information.\n   */\n  readonly #handleSnapEnabledEvent = (snap: TruncatedSnap) => {\n    const events = this.get(snap.id);\n    this.#reschedule(events);\n    this.register(snap.id);\n  };\n\n  /**\n   * Handle events that should cause cronjobs and background events to be\n   * unregistered.\n   *\n   * @param snap - Basic Snap information.\n   */\n  readonly #handleSnapUninstalledEvent = (snap: TruncatedSnap) => {\n    this.unregister(snap.id);\n  };\n\n  /**\n   * Handle events that should cause cronjobs and background events to be\n   * unregistered.\n   *\n   * @param snap - Basic Snap information.\n   */\n  readonly #handleSnapDisabledEvent = (snap: TruncatedSnap) => {\n    this.unregister(snap.id);\n  };\n\n  /**\n   * Handle cron jobs on 'snapUpdated' event.\n   *\n   * @param snap - Basic Snap information.\n   */\n  readonly #handleSnapUpdatedEvent = (snap: TruncatedSnap) => {\n    this.unregister(snap.id);\n    this.register(snap.id);\n  };\n\n  /**\n   * Reschedule events that are yet to be executed. This should be called on\n   * controller initialization and once every 24 hours to ensure that\n   * background events are scheduled correctly.\n   *\n   * @param events - An array of events to reschedule. Defaults to all events in\n   * the controller state.\n   */\n  #reschedule(events = Object.values(this.state.events)) {\n    const now = Date.now();\n\n    for (const event of events) {\n      if (this.#timers.has(event.id)) {\n        // If the timer for this event already exists, we don't need to\n        // reschedule it.\n        continue;\n      }\n\n      const eventDate = DateTime.fromISO(event.date, {\n        setZone: true,\n      })\n        .toUTC()\n        .toMillis();\n\n      // If the event is recurring and the date is in the past, execute it\n      // immediately.\n      if (event.recurring && eventDate <= now) {\n        this.#execute(event);\n        continue;\n      }\n\n      this.#schedule(event, false);\n    }\n  }\n\n  /**\n   * Clear non-recurring events that are past their scheduled time.\n   */\n  #clear() {\n    const now = Date.now();\n\n    for (const event of Object.values(this.state.events)) {\n      const eventDate = DateTime.fromISO(event.date, {\n        setZone: true,\n      })\n        .toUTC()\n        .toMillis();\n\n      if (!event.recurring && eventDate < now) {\n        this.#cancel(event.id);\n      }\n    }\n  }\n}\n"]}