{"version":3,"file":"authenticated-user-storage.mjs","sourceRoot":"","sources":["../src/authenticated-user-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  DataServiceCacheUpdatedEvent,\n  DataServiceGranularCacheUpdatedEvent,\n  DataServiceInvalidateQueriesAction,\n} from '@metamask/base-data-service';\nimport { BaseDataService } from '@metamask/base-data-service';\nimport type { CreateServicePolicyOptions } from '@metamask/controller-utils';\nimport { HttpError } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\n\nimport type { AuthenticatedUserStorageServiceMethodActions } from './authenticated-user-storage-method-action-types';\nimport type { Environment } from './env';\nimport { getUserStorageApiUrl } from './env';\nimport type {\n  ClientType,\n  DelegationResponse,\n  DelegationSubmission,\n  NotificationPreferences,\n} from './types';\nimport {\n  assertDelegationResponseArray,\n  assertNotificationPreferences,\n} from './validators';\n\n// === GENERAL ===\n\n/**\n * The name of the {@link AuthenticatedUserStorageService} service, used to\n * namespace the service's actions and events.\n */\nexport const serviceName = 'AuthenticatedUserStorageService';\n\n/**\n * Builds the versioned API base URL for a given environment.\n *\n * @param environment - The target environment.\n * @returns The base URL including the `/api/v1` path segment.\n */\nexport function getAuthenticatedStorageUrl(environment: Environment): string {\n  return `${getUserStorageApiUrl(environment)}/api/v1`;\n}\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n  'listDelegations',\n  'createDelegation',\n  'revokeDelegation',\n  'getNotificationPreferences',\n  'putNotificationPreferences',\n] as const;\n\n/**\n * Invalidates cached queries for {@link AuthenticatedUserStorageService}.\n */\nexport type AuthenticatedUserStorageInvalidateQueriesAction =\n  DataServiceInvalidateQueriesAction<typeof serviceName>;\n\n/**\n * Actions that {@link AuthenticatedUserStorageService} exposes to other\n * consumers.\n */\nexport type AuthenticatedUserStorageActions =\n  | AuthenticatedUserStorageServiceMethodActions\n  | AuthenticatedUserStorageInvalidateQueriesAction;\n\n/**\n * Retrieves a bearer token from the `AuthenticationController`, logging in the\n * user if necessary.\n */\ntype AuthenticationControllerGetBearerTokenAction = {\n  type: 'AuthenticationController:getBearerToken';\n  handler: (entropySourceId?: string) => Promise<string>;\n};\n\n/**\n * Actions from other messengers that {@link AuthenticatedUserStorageService}\n * calls.\n */\ntype AllowedActions = AuthenticationControllerGetBearerTokenAction;\n\n/**\n * Published when {@link AuthenticatedUserStorageService}'s cache is updated.\n */\nexport type AuthenticatedUserStorageCacheUpdatedEvent =\n  DataServiceCacheUpdatedEvent<typeof serviceName>;\n\n/**\n * Published when a key within {@link AuthenticatedUserStorageService}'s cache\n * is updated.\n */\nexport type AuthenticatedUserStorageGranularCacheUpdatedEvent =\n  DataServiceGranularCacheUpdatedEvent<typeof serviceName>;\n\n/**\n * Events that {@link AuthenticatedUserStorageService} exposes to other\n * consumers.\n */\nexport type AuthenticatedUserStorageEvents =\n  | AuthenticatedUserStorageCacheUpdatedEvent\n  | AuthenticatedUserStorageGranularCacheUpdatedEvent;\n\n/**\n * Events from other messengers that\n * {@link AuthenticatedUserStorageService} subscribes to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * {@link AuthenticatedUserStorageService}.\n */\nexport type AuthenticatedUserStorageMessenger = Messenger<\n  typeof serviceName,\n  AuthenticatedUserStorageActions | AllowedActions,\n  AuthenticatedUserStorageEvents | AllowedEvents\n>;\n\n// === SERVICE ===\n\n/**\n * Data service wrapping authenticated user-storage API endpoints.\n *\n * Provides methods for managing delegations and notification preferences\n * for the authenticated user.\n */\nexport class AuthenticatedUserStorageService extends BaseDataService<\n  typeof serviceName,\n  AuthenticatedUserStorageMessenger\n> {\n  readonly #environment: Environment;\n\n  /**\n   * Constructs a new AuthenticatedUserStorageService.\n   *\n   * @param args - The constructor arguments.\n   * @param args.messenger - The messenger suited for this service.\n   * @param args.environment - The target environment (dev, uat, prod).\n   * @param args.policyOptions - Options to pass to `createServicePolicy`, which\n   * is used to wrap each request. See {@link CreateServicePolicyOptions}.\n   */\n  constructor({\n    messenger,\n    environment,\n    policyOptions,\n  }: {\n    messenger: AuthenticatedUserStorageMessenger;\n    environment: Environment;\n    policyOptions?: CreateServicePolicyOptions;\n  }) {\n    super({ name: serviceName, messenger, policyOptions });\n    this.#environment = environment;\n\n    this.messenger.registerMethodActionHandlers(\n      this,\n      MESSENGER_EXPOSED_METHODS,\n    );\n  }\n\n  /**\n   * Returns all delegation records belonging to the authenticated user.\n   *\n   * @returns An array of delegation records, or an empty array if none exist.\n   */\n  async listDelegations(): Promise<DelegationResponse[]> {\n    const url = `${getAuthenticatedStorageUrl(this.#environment)}/delegations`;\n\n    const data = await this.fetchQuery({\n      queryKey: [`${this.name}:listDelegations`],\n      queryFn: async () => {\n        const headers = await this.#getHeaders();\n        const response = await fetch(url, { headers });\n\n        if (!response.ok) {\n          throw new HttpError(\n            response.status,\n            `Failed to list delegations: ${response.status}`,\n          );\n        }\n\n        return response.json();\n      },\n    });\n\n    assertDelegationResponseArray(data);\n    return data;\n  }\n\n  /**\n   * Stores a signed delegation record for the authenticated user.\n   *\n   * @param submission - The signed delegation and its metadata.\n   * @param clientType - Optional client type header.\n   */\n  async createDelegation(\n    submission: DelegationSubmission,\n    clientType?: ClientType,\n  ): Promise<void> {\n    const url = `${getAuthenticatedStorageUrl(this.#environment)}/delegations`;\n\n    await this.fetchQuery({\n      queryKey: [\n        `${this.name}:createDelegation`,\n        submission.metadata.delegationHash,\n      ],\n      staleTime: 0,\n      queryFn: async () => {\n        const headers = await this.#getHeaders(clientType);\n        const response = await fetch(url, {\n          method: 'POST',\n          headers,\n          body: JSON.stringify(submission),\n        });\n\n        if (!response.ok) {\n          throw new HttpError(\n            response.status,\n            `Failed to create delegation: ${response.status}`,\n          );\n        }\n\n        return null;\n      },\n    });\n\n    await this.invalidateQueries({\n      queryKey: [`${this.name}:listDelegations`],\n    });\n  }\n\n  /**\n   * Revokes (deletes) a delegation record.\n   *\n   * @param delegationHash - The unique hash identifying the delegation.\n   */\n  async revokeDelegation(delegationHash: string): Promise<void> {\n    const url = `${getAuthenticatedStorageUrl(this.#environment)}/delegations/${encodeURIComponent(delegationHash)}`;\n\n    await this.fetchQuery({\n      queryKey: [`${this.name}:revokeDelegation`, delegationHash],\n      staleTime: 0,\n      queryFn: async () => {\n        const headers = await this.#getHeaders();\n        const response = await fetch(url, {\n          method: 'DELETE',\n          headers,\n        });\n\n        if (!response.ok) {\n          throw new HttpError(\n            response.status,\n            `Failed to revoke delegation: ${response.status}`,\n          );\n        }\n\n        return null;\n      },\n    });\n\n    await this.invalidateQueries({\n      queryKey: [`${this.name}:listDelegations`],\n    });\n  }\n\n  /**\n   * Returns the notification preferences for the authenticated user.\n   *\n   * @returns The notification preferences object, or `null` if none have been\n   * set (404).\n   */\n  async getNotificationPreferences(): Promise<NotificationPreferences | null> {\n    const url = `${getAuthenticatedStorageUrl(this.#environment)}/preferences/notifications`;\n\n    const data = await this.fetchQuery({\n      queryKey: [`${this.name}:getNotificationPreferences`],\n      queryFn: async () => {\n        const headers = await this.#getHeaders();\n        const response = await fetch(url, { headers });\n\n        if (response.status === 404) {\n          return null;\n        }\n\n        if (!response.ok) {\n          throw new HttpError(\n            response.status,\n            `Failed to get notification preferences: ${response.status}`,\n          );\n        }\n\n        return response.json();\n      },\n    });\n\n    if (data === null) {\n      return null;\n    }\n\n    assertNotificationPreferences(data);\n    return data;\n  }\n\n  /**\n   * Creates or updates the notification preferences for the authenticated user.\n   *\n   * @param prefs - The full notification preferences object.\n   * @param clientType - Optional client type header.\n   */\n  async putNotificationPreferences(\n    prefs: NotificationPreferences,\n    clientType?: ClientType,\n  ): Promise<void> {\n    const url = `${getAuthenticatedStorageUrl(this.#environment)}/preferences/notifications`;\n\n    await this.fetchQuery({\n      queryKey: [\n        `${this.name}:putNotificationPreferences`,\n        prefs as unknown as Json,\n      ],\n      staleTime: 0,\n      queryFn: async () => {\n        const headers = await this.#getHeaders(clientType);\n        const response = await fetch(url, {\n          method: 'PUT',\n          headers,\n          body: JSON.stringify(prefs),\n        });\n\n        if (!response.ok) {\n          throw new HttpError(\n            response.status,\n            `Failed to put notification preferences: ${response.status}`,\n          );\n        }\n\n        return null;\n      },\n    });\n\n    await this.invalidateQueries({\n      queryKey: [`${this.name}:getNotificationPreferences`],\n    });\n  }\n\n  async #getHeaders(clientType?: ClientType): Promise<Record<string, string>> {\n    const accessToken = await this.messenger.call(\n      'AuthenticationController:getBearerToken',\n    );\n    const headers: Record<string, string> = {\n      'Content-Type': 'application/json',\n      Authorization: `Bearer ${accessToken}`,\n    };\n    if (clientType) {\n      headers['X-Client-Type'] = clientType;\n    }\n    return headers;\n  }\n}\n"]}