{"version":3,"file":"AnalyticsDataRegulationController.mjs","sourceRoot":"","sources":["../src/AnalyticsDataRegulationController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  ControllerGetStateAction,\n  ControllerStateChangeEvent,\n  StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\n\nimport type { AnalyticsDataRegulationControllerMethodActions } from './AnalyticsDataRegulationController-method-action-types';\nimport type { AnalyticsDataRegulationServiceActions } from './AnalyticsDataRegulationService';\nimport { projectLogger as log } from './logger';\nimport { DATA_DELETE_RESPONSE_STATUSES, DATA_DELETE_STATUSES } from './types';\nimport type { DeleteRegulationResponse, DeleteRegulationStatus } from './types';\n\n// === GENERAL ===\n\n/**\n * The name of the {@link AnalyticsDataRegulationController}, used to namespace the\n * controller's actions and events and to namespace the controller's state data\n * when composed with other controllers.\n */\nexport const controllerName = 'AnalyticsDataRegulationController';\n\n// === STATE ===\n\n/**\n * Describes the shape of the state object for {@link AnalyticsDataRegulationController}.\n */\nexport type AnalyticsDataRegulationControllerState = {\n  /**\n   * Indicates if data has been recorded since the last deletion request.\n   */\n  hasCollectedDataSinceDeletionRequest: boolean;\n\n  /**\n   * Segment's data deletion regulation ID.\n   * The ID returned by the Segment delete API which allows checking the status of the deletion request.\n   */\n  deleteRegulationId?: string;\n\n  /**\n   * Segment's data deletion regulation creation timestamp.\n   * The timestamp (in milliseconds since epoch) when the deletion request was created.\n   */\n  deleteRegulationTimestamp?: number;\n};\n\n/**\n * Returns default values for AnalyticsDataRegulationController state.\n *\n * @returns Default state\n */\nexport function getDefaultAnalyticsDataRegulationControllerState(): AnalyticsDataRegulationControllerState {\n  return {\n    hasCollectedDataSinceDeletionRequest: false,\n  };\n}\n\n/**\n * The metadata for each property in {@link AnalyticsDataRegulationControllerState}.\n */\nconst analyticsDataRegulationControllerMetadata = {\n  hasCollectedDataSinceDeletionRequest: {\n    includeInStateLogs: true,\n    persist: true,\n    includeInDebugSnapshot: true,\n    usedInUi: true,\n  },\n  deleteRegulationId: {\n    includeInStateLogs: true,\n    persist: true,\n    includeInDebugSnapshot: true,\n    usedInUi: true,\n  },\n  deleteRegulationTimestamp: {\n    includeInStateLogs: true,\n    persist: true,\n    includeInDebugSnapshot: true,\n    usedInUi: true,\n  },\n} satisfies StateMetadata<AnalyticsDataRegulationControllerState>;\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n  'createDataDeletionTask',\n  'checkDataDeleteStatus',\n  'updateDataRecordingFlag',\n] as const;\n\n/**\n * Returns the state of the {@link AnalyticsDataRegulationController}.\n */\nexport type AnalyticsDataRegulationControllerGetStateAction =\n  ControllerGetStateAction<\n    typeof controllerName,\n    AnalyticsDataRegulationControllerState\n  >;\n\n/**\n * Actions that {@link AnalyticsDataRegulationControllerMessenger} exposes to other consumers.\n */\nexport type AnalyticsDataRegulationControllerActions =\n  | AnalyticsDataRegulationControllerGetStateAction\n  | AnalyticsDataRegulationControllerMethodActions;\n\n/**\n * Actions from other messengers that {@link AnalyticsDataRegulationControllerMessenger} calls.\n */\ntype AllowedActions = AnalyticsDataRegulationServiceActions;\n\n/**\n * Event emitted when a data deletion task is created.\n */\nexport type DataDeletionTaskCreatedEvent = {\n  type: `${typeof controllerName}:dataDeletionTaskCreated`;\n  payload: [DeleteRegulationResponse];\n};\n\n/**\n * Event emitted when the data recording flag is updated.\n */\nexport type DataRecordingFlagUpdatedEvent = {\n  type: `${typeof controllerName}:dataRecordingFlagUpdated`;\n  payload: [boolean];\n};\n\n/**\n * Event emitted when the state of the {@link AnalyticsDataRegulationController} changes.\n */\nexport type AnalyticsDataRegulationControllerStateChangeEvent =\n  ControllerStateChangeEvent<\n    typeof controllerName,\n    AnalyticsDataRegulationControllerState\n  >;\n\n/**\n * Events that {@link AnalyticsDataRegulationControllerMessenger} exposes to other consumers.\n */\nexport type AnalyticsDataRegulationControllerEvents =\n  | AnalyticsDataRegulationControllerStateChangeEvent\n  | DataDeletionTaskCreatedEvent\n  | DataRecordingFlagUpdatedEvent;\n\n/**\n * Events from other messengers that {@link AnalyticsDataRegulationControllerMessenger} subscribes to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link AnalyticsDataRegulationController}.\n */\nexport type AnalyticsDataRegulationControllerMessenger = Messenger<\n  typeof controllerName,\n  AnalyticsDataRegulationControllerActions | AllowedActions,\n  AnalyticsDataRegulationControllerEvents | AllowedEvents\n>;\n\n// === CONTROLLER DEFINITION ===\n\n/**\n * The options that AnalyticsDataRegulationController takes.\n */\nexport type AnalyticsDataRegulationControllerOptions = {\n  /**\n   * Initial controller state.\n   */\n  state?: Partial<AnalyticsDataRegulationControllerState>;\n  /**\n   * Messenger used to communicate with BaseController and other controllers.\n   */\n  messenger: AnalyticsDataRegulationControllerMessenger;\n  /**\n   * Analytics ID used for data deletion requests.\n   */\n  analyticsId: string;\n};\n\n/**\n * The AnalyticsDataRegulationController manages analytics privacy and GDPR/CCPA data deletion functionality.\n * It communicates with Segment's Regulations API via a proxy to create and monitor data deletion requests.\n *\n * This controller follows the MetaMask controller pattern and integrates with the\n * messenger system to allow other controllers and components to manage data deletion tasks.\n */\nexport class AnalyticsDataRegulationController extends BaseController<\n  typeof controllerName,\n  AnalyticsDataRegulationControllerState,\n  AnalyticsDataRegulationControllerMessenger\n> {\n  /**\n   * Analytics ID used for data deletion requests.\n   */\n  readonly #analyticsId: string;\n\n  /**\n   * Constructs an AnalyticsDataRegulationController instance.\n   *\n   * @param options - Controller options\n   * @param options.state - Initial controller state. Use `getDefaultAnalyticsDataRegulationControllerState()` for defaults.\n   * @param options.messenger - Messenger used to communicate with BaseController\n   * @param options.analyticsId - Analytics ID used for data deletion requests\n   */\n  constructor({\n    state = {},\n    messenger,\n    analyticsId,\n  }: AnalyticsDataRegulationControllerOptions) {\n    const initialState: AnalyticsDataRegulationControllerState = {\n      ...getDefaultAnalyticsDataRegulationControllerState(),\n      ...state,\n    };\n\n    super({\n      name: controllerName,\n      metadata: analyticsDataRegulationControllerMetadata,\n      state: initialState,\n      messenger,\n    });\n\n    this.#analyticsId = analyticsId;\n\n    this.messenger.registerMethodActionHandlers(\n      this,\n      MESSENGER_EXPOSED_METHODS,\n    );\n\n    log('AnalyticsDataRegulationController initialized', {\n      hasCollectedDataSinceDeletionRequest:\n        this.state.hasCollectedDataSinceDeletionRequest,\n      hasDeleteRegulationId: Boolean(this.state.deleteRegulationId),\n      deleteRegulationTimestamp: this.state.deleteRegulationTimestamp,\n    });\n  }\n\n  /**\n   * Creates a new delete regulation for the user.\n   * This is necessary to respect the GDPR and CCPA regulations.\n   *\n   * @returns Promise containing the status of the request with regulateId\n   * @throws Error if analytics ID is missing or if the service call fails\n   */\n  async createDataDeletionTask(): Promise<{\n    status: typeof DATA_DELETE_RESPONSE_STATUSES.Success;\n    regulateId: string;\n  }> {\n    if (!this.#analyticsId || this.#analyticsId.trim() === '') {\n      const error = new Error(\n        'Analytics ID not found. You need to provide a valid analytics ID when initializing the AnalyticsDataRegulationController.',\n      );\n      log('Analytics Deletion Task Error', error);\n      throw error;\n    }\n\n    const response = await this.messenger.call(\n      'AnalyticsDataRegulationService:createDataDeletionTask',\n      this.#analyticsId,\n    );\n\n    const deletionTimestamp = Date.now();\n    // Service validates and throws on all errors, so if we reach here, the response\n    // is guaranteed to be a success response with regulateId present\n    this.update((state) => {\n      state.deleteRegulationId = response.regulateId;\n      state.deleteRegulationTimestamp = deletionTimestamp;\n      state.hasCollectedDataSinceDeletionRequest = false;\n    });\n\n    this.messenger.publish(\n      `${controllerName}:dataDeletionTaskCreated`,\n      response,\n    );\n\n    return response;\n  }\n\n  /**\n   * Check the latest delete regulation status.\n   *\n   * @returns Promise containing the timestamp, delete status and collected data flag\n   */\n  async checkDataDeleteStatus(): Promise<DeleteRegulationStatus> {\n    // Capture all state values before async call to ensure consistency\n    // in case createDataDeletionTask() completes concurrently\n    const { deleteRegulationId } = this.state;\n    const { deleteRegulationTimestamp } = this.state;\n    const { hasCollectedDataSinceDeletionRequest } = this.state;\n\n    const status: DeleteRegulationStatus = {\n      deletionRequestTimestamp: deleteRegulationTimestamp,\n      dataDeletionRequestStatus: DATA_DELETE_STATUSES.Unknown,\n      hasCollectedDataSinceDeletionRequest,\n    };\n\n    if (!deleteRegulationId) {\n      return status;\n    }\n\n    // Service validates and throws on all errors, so if we reach here, the response\n    // is guaranteed to be a success response with dataDeleteStatus present\n    const dataDeletionTaskStatus = await this.messenger.call(\n      'AnalyticsDataRegulationService:checkDataDeleteStatus',\n      deleteRegulationId,\n    );\n\n    status.dataDeletionRequestStatus = dataDeletionTaskStatus.dataDeleteStatus;\n\n    return status;\n  }\n\n  /**\n   * Update the data recording flag if needed.\n   * This method should be called after tracking events to ensure\n   * the data recording flag is properly updated for data deletion workflows.\n   *\n   * The flag can only be set to `true` (indicating data has been collected).\n   * It cannot be explicitly set to `false` - it is only reset to `false` when\n   * a new deletion task is created via `createDataDeletionTask`.\n   *\n   */\n  updateDataRecordingFlag(): void {\n    if (!this.state.hasCollectedDataSinceDeletionRequest) {\n      this.update((state) => {\n        state.hasCollectedDataSinceDeletionRequest = true;\n      });\n\n      this.messenger.publish(\n        `${controllerName}:dataRecordingFlagUpdated`,\n        true,\n      );\n    }\n  }\n}\n"]}