@metamask-previews/profile-controller 1.2.1-preview-0439f4dd7 → 1.2.1-preview-9a6250503
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/ProfileService.d.ts.map +1 -1
- package/dist/ProfileService.js +37 -2
- package/dist/ProfileService.js.map +1 -1
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Preserve the Profile API's error message in the `HttpError` thrown by `ProfileService` requests, so clients can surface the server-provided reason. When a non-2xx response body contains a `{ code, message }` payload, the error message is now `<operation error>: <status>: <API message>`; it falls back to the previous `<operation error>: <status>` when the body is absent, unreadable, or has no `message`.
|
|
13
|
+
|
|
10
14
|
## [1.2.1]
|
|
11
15
|
|
|
12
16
|
### Added
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProfileService.d.ts","sourceRoot":"","sources":["../src/ProfileService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC5B,oCAAoC,EACpC,kCAAkC,EACnC,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"ProfileService.d.ts","sourceRoot":"","sources":["../src/ProfileService.ts"],"names":[],"mappings":"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"}
|
package/dist/ProfileService.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseDataService } from '@metamask/base-data-service';
|
|
2
2
|
import { HttpError } from '@metamask/controller-utils';
|
|
3
|
-
import { array, boolean, enums, intersection, nullable, optional, sensitive, string, type as structType, } from '@metamask/superstruct';
|
|
3
|
+
import { array, boolean, enums, intersection, nullable, optional, sensitive, string, type as structType, validate, } from '@metamask/superstruct';
|
|
4
4
|
import { CaipAccountIdStruct } from '@metamask/utils';
|
|
5
5
|
export const serviceName = 'ProfileService';
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
@@ -23,6 +23,15 @@ export const ProfileServiceErrorMessage = {
|
|
|
23
23
|
// Superstruct schemas
|
|
24
24
|
// ---------------------------------------------------------------------------
|
|
25
25
|
const TradingPrivacyStruct = enums(['public', 'private']);
|
|
26
|
+
/**
|
|
27
|
+
* The body the Profile API returns for a non-2xx response. The `message` is
|
|
28
|
+
* surfaced to clients so they can react to the specific failure; `code` is
|
|
29
|
+
* optional because not every error includes one.
|
|
30
|
+
*/
|
|
31
|
+
const ApiErrorResponseStruct = structType({
|
|
32
|
+
code: optional(string()),
|
|
33
|
+
message: string(),
|
|
34
|
+
});
|
|
26
35
|
const ProfileApiResponseStruct = structType({
|
|
27
36
|
profile_id: string(),
|
|
28
37
|
username: string(),
|
|
@@ -138,6 +147,29 @@ export class ProfileService extends BaseDataService {
|
|
|
138
147
|
const token = await this.messenger.call('AuthenticationController:getBearerToken');
|
|
139
148
|
return { Authorization: `Bearer ${token}` };
|
|
140
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Reads the error message from an unsuccessful API response body.
|
|
152
|
+
*
|
|
153
|
+
* The Profile API returns errors as `{ code, message }`. If the body is
|
|
154
|
+
* missing, is not JSON, or does not contain a message, this returns
|
|
155
|
+
* `undefined` so the caller can fall back to a generic message.
|
|
156
|
+
*
|
|
157
|
+
* @param response - The unsuccessful response.
|
|
158
|
+
* @returns The API-provided error message, or `undefined` if unavailable.
|
|
159
|
+
*/
|
|
160
|
+
async #readErrorMessage(response) {
|
|
161
|
+
try {
|
|
162
|
+
const body = await response.json();
|
|
163
|
+
const [error, result] = validate(body, ApiErrorResponseStruct);
|
|
164
|
+
if (!error) {
|
|
165
|
+
return result.message;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// The body is absent or not valid JSON; fall back to a generic message.
|
|
170
|
+
}
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
141
173
|
async #fetch(endpoint, { method = 'GET', error, json, searchParams, }) {
|
|
142
174
|
const authHeaders = await this.#getAuthHeaders();
|
|
143
175
|
const url = new URL(`${this.#v1Url}/${endpoint}`);
|
|
@@ -155,7 +187,10 @@ export class ProfileService extends BaseDataService {
|
|
|
155
187
|
...(json === undefined ? {} : { body: JSON.stringify(json) }),
|
|
156
188
|
});
|
|
157
189
|
if (!response.ok) {
|
|
158
|
-
|
|
190
|
+
const apiMessage = await this.#readErrorMessage(response);
|
|
191
|
+
throw new HttpError(response.status, apiMessage
|
|
192
|
+
? `${error}: ${response.status}: ${apiMessage}`
|
|
193
|
+
: `${error}: ${response.status}`);
|
|
159
194
|
}
|
|
160
195
|
if (method === 'DELETE') {
|
|
161
196
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProfileService.js","sourceRoot":"","sources":["../src/ProfileService.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseDataService } from '@metamask/base-data-service';\nimport type {\n CreateServicePolicyOptions,\n DataServiceCacheUpdatedEvent,\n DataServiceGranularCacheUpdatedEvent,\n DataServiceInvalidateQueriesAction,\n} from '@metamask/base-data-service';\nimport { HttpError } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type { AuthenticationController } from '@metamask/profile-sync-controller';\nimport {\n array,\n boolean,\n enums,\n intersection,\n nullable,\n optional,\n sensitive,\n string,\n type as structType,\n} from '@metamask/superstruct';\nimport type { Infer } from '@metamask/superstruct';\nimport type { Json } from '@metamask/utils';\nimport { CaipAccountIdStruct } from '@metamask/utils';\n\nimport type { ProfileServiceMethodActions } from './ProfileService-method-action-types.js';\n\nexport const serviceName = 'ProfileService';\n\n// ---------------------------------------------------------------------------\n// Error messages\n// ---------------------------------------------------------------------------\n\n/** Human-readable error messages for each ProfileService operation. */\nexport const ProfileServiceErrorMessage = {\n GET_PROFILE_FAILED: 'ProfileService: failed to fetch profile',\n CREATE_PROFILE_FAILED: 'ProfileService: failed to create profile',\n REPLACE_PROFILE_FAILED: 'ProfileService: failed to replace profile',\n UPDATE_PROFILE_FAILED: 'ProfileService: failed to update profile',\n DELETE_PROFILE_FAILED: 'ProfileService: failed to delete profile',\n CHECK_USERNAME_AVAILABILITY_FAILED:\n 'ProfileService: failed to check username availability',\n GET_X_AUTH_URL_FAILED: 'ProfileService: failed to get X authentication URL',\n CONNECT_X_FAILED: 'ProfileService: failed to connect X account',\n GET_X_ACCOUNT_FAILED: 'ProfileService: failed to get X account',\n DISCONNECT_X_FAILED: 'ProfileService: failed to disconnect X account',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Superstruct schemas\n// ---------------------------------------------------------------------------\n\nconst TradingPrivacyStruct = enums(['public', 'private'] as const);\n\nconst ProfileApiResponseStruct = structType({\n profile_id: string(),\n username: string(),\n display_name: string(),\n bio: nullable(string()),\n linked_addresses: array(CaipAccountIdStruct),\n avatar_url: string(),\n trading_privacy: TradingPrivacyStruct,\n connected_to_x: boolean(),\n created_at: string(),\n updated_at: string(),\n});\n\nconst UsernameAvailabilityErrorStruct = structType({\n code: string(),\n message: string(),\n});\n\nconst UsernameAvailabilityResponseStruct = structType({\n username: string(),\n available: boolean(),\n valid: boolean(),\n normalized: string(),\n errors: array(UsernameAvailabilityErrorStruct),\n});\n\nconst XAuthUrlResponseStruct = structType({\n url: sensitive(string()),\n state: sensitive(string()),\n});\n\nconst ConnectXResponseStruct = structType({\n x_user_id: string(),\n x_profile_url: string(),\n username: string(),\n display_name: string(),\n avatar_url: string(),\n created_at: string(),\n updated_at: string(),\n});\n\nconst CreateProfileResponseStruct = intersection([\n ProfileApiResponseStruct,\n structType({ x_profile: optional(ConnectXResponseStruct) }),\n]);\n\nconst ConnectXParamsStruct = structType({\n code: sensitive(string()),\n state: sensitive(string()),\n});\n\nconst CreateProfileParamsStruct = structType({\n profile_id: string(),\n username: string(),\n display_name: string(),\n bio: optional(nullable(string())),\n linked_addresses: array(CaipAccountIdStruct),\n avatar_url: optional(string()),\n trading_privacy: TradingPrivacyStruct,\n});\n\nconst ReplaceProfileParamsStruct = structType({\n username: string(),\n display_name: string(),\n bio: optional(nullable(string())),\n linked_addresses: array(CaipAccountIdStruct),\n avatar_url: optional(string()),\n trading_privacy: TradingPrivacyStruct,\n});\n\nconst UpdateProfileParamsStruct = structType({\n username: optional(string()),\n display_name: optional(string()),\n bio: optional(nullable(string())),\n linked_addresses: optional(array(CaipAccountIdStruct)),\n avatar_url: optional(string()),\n trading_privacy: optional(TradingPrivacyStruct),\n});\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'getProfile',\n 'createProfile',\n 'replaceProfile',\n 'updateProfile',\n 'deleteProfile',\n 'checkUsernameAvailability',\n 'getXAuthUrl',\n 'connectX',\n 'getXAccount',\n 'disconnectX',\n] as const;\n\n/** The shape of a profile returned by the MetaMask Profile API. */\nexport type ProfileApiResponse = Infer<typeof ProfileApiResponseStruct>;\n\n/** The response shape for createProfile — includes an optional linked X profile if the user had already connected X. */\nexport type CreateProfileResponse = Infer<typeof CreateProfileResponseStruct>;\n\n/** The response shape for a username availability check. */\nexport type UsernameAvailabilityResponse = Infer<\n typeof UsernameAvailabilityResponseStruct\n>;\n\n/** The response shape returned when connecting or fetching an X account. */\nexport type ConnectXResponse = Infer<typeof ConnectXResponseStruct>;\n\n/** The response shape returned when requesting the X OAuth authentication URL. */\nexport type XAuthUrlResponse = Infer<typeof XAuthUrlResponseStruct>;\n\n/** Alias for {@link ConnectXResponse} returned when fetching the linked X account. */\nexport type XAccountResponse = ConnectXResponse;\n\n/** Parameters for creating a new MetaMask profile. */\nexport type CreateProfileParams = Infer<typeof CreateProfileParamsStruct>;\n\n/** Parameters for fully replacing an existing MetaMask profile (PUT). */\nexport type ReplaceProfileParams = Infer<typeof ReplaceProfileParamsStruct>;\n\n/** Parameters for partially updating an existing MetaMask profile (PATCH). */\nexport type UpdateProfileParams = Infer<typeof UpdateProfileParamsStruct>;\n\n/** Parameters for completing the X OAuth PKCE flow. */\nexport type ConnectXParams = Infer<typeof ConnectXParamsStruct>;\n\n// ---------------------------------------------------------------------------\n// Messenger types\n// ---------------------------------------------------------------------------\n\n/** Union of all actions exposed by ProfileService, including cache invalidation. */\nexport type ProfileServiceActions =\n | ProfileServiceMethodActions\n | DataServiceInvalidateQueriesAction<typeof serviceName>;\n\n/** Event emitted when the ProfileService query cache is updated. */\nexport type ProfileServiceCacheUpdatedEvent = DataServiceCacheUpdatedEvent<\n typeof serviceName\n>;\n\n/** Event emitted with per-query granularity when the ProfileService cache is updated. */\nexport type ProfileServiceGranularCacheUpdatedEvent =\n DataServiceGranularCacheUpdatedEvent<typeof serviceName>;\n\n/** Union of all events emitted by ProfileService. */\nexport type ProfileServiceEvents =\n | ProfileServiceCacheUpdatedEvent\n | ProfileServiceGranularCacheUpdatedEvent;\n\ntype AllowedActions =\n AuthenticationController.AuthenticationControllerGetBearerTokenAction;\n\ntype AllowedEvents = never;\n\n/** Messenger type for ProfileService, scoped to its actions and events. */\nexport type ProfileServiceMessenger = Messenger<\n typeof serviceName,\n ProfileServiceActions | AllowedActions,\n ProfileServiceEvents | AllowedEvents\n>;\n\n// ---------------------------------------------------------------------------\n// Service\n// ---------------------------------------------------------------------------\n\n/** Communicates with the MetaMask Profile API and exposes all operations via the messenger. */\nexport class ProfileService extends BaseDataService<\n typeof serviceName,\n ProfileServiceMessenger\n> {\n readonly #baseUrl: string;\n\n get #v1Url(): string {\n return `${this.#baseUrl}/v1`;\n }\n\n /**\n * Creates a new ProfileService instance.\n *\n * @param options - Constructor options.\n * @param options.messenger - The messenger scoped to ProfileService.\n * @param options.baseUrl - Base URL for the MetaMask Profile API.\n * @param options.policyOptions - Optional service policy configuration.\n */\n constructor({\n messenger,\n baseUrl,\n policyOptions,\n }: {\n messenger: ProfileServiceMessenger;\n baseUrl: string;\n policyOptions?: CreateServicePolicyOptions;\n }) {\n super({ name: serviceName, messenger, policyOptions });\n this.#baseUrl = baseUrl;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Gets the authentication headers for the request.\n *\n * @returns The authentication headers.\n */\n async #getAuthHeaders(): Promise<Record<string, string>> {\n const token = await this.messenger.call(\n 'AuthenticationController:getBearerToken',\n );\n return { Authorization: `Bearer ${token}` };\n }\n\n /**\n * Executes an authenticated HTTP request and returns the parsed JSON response.\n *\n * @param endpoint - The path relative to the v1 base URL.\n * @param options - Request options.\n * @param options.method - The HTTP method. Defaults to 'GET'.\n * @param options.error - The error message to use if the response is not OK.\n * @param options.json - Optional JSON body. If provided, sets Content-Type and serializes as body.\n * @returns The parsed JSON response.\n * @throws {HttpError} If the response is not a 2xx status code.\n */\n async #fetch(\n endpoint: string,\n options: { method: 'DELETE'; error: string },\n ): Promise<null>;\n\n async #fetch<ResponseType extends Json>(\n endpoint: string,\n options: {\n method?: string;\n error: string;\n json?: unknown;\n searchParams?: Record<string, string>;\n },\n ): Promise<ResponseType>;\n\n async #fetch<ResponseType extends Json>(\n endpoint: string,\n {\n method = 'GET',\n error,\n json,\n searchParams,\n }: {\n method?: string;\n error: string;\n json?: unknown;\n searchParams?: Record<string, string>;\n },\n ): Promise<ResponseType | null> {\n const authHeaders = await this.#getAuthHeaders();\n const url = new URL(`${this.#v1Url}/${endpoint}`);\n if (searchParams) {\n for (const [key, value] of Object.entries(searchParams)) {\n url.searchParams.append(key, value);\n }\n }\n const response = await fetch(url.toString(), {\n ...(method === 'GET' ? {} : { method }),\n headers: {\n ...authHeaders,\n ...(json === undefined ? {} : { 'Content-Type': 'application/json' }),\n },\n ...(json === undefined ? {} : { body: JSON.stringify(json) }),\n });\n if (!response.ok) {\n throw new HttpError(response.status, `${error}: ${response.status}`);\n }\n if (method === 'DELETE') {\n return null;\n }\n return (await response.json()) as ResponseType;\n }\n\n /**\n * Fetches a profile by its identifier.\n *\n * @param profileId - The profile identifier to fetch.\n * @returns The profile data from the API.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response does not match the expected shape.\n */\n async getProfile(profileId: string): Promise<ProfileApiResponse> {\n return this.fetchQuery({\n queryKey: [`${this.name}:getProfile`, profileId],\n responseStruct: ProfileApiResponseStruct,\n queryFn: async () =>\n this.#fetch<ProfileApiResponse>(\n `profiles/${encodeURIComponent(profileId)}`,\n {\n error: ProfileServiceErrorMessage.GET_PROFILE_FAILED,\n },\n ),\n });\n }\n\n /**\n * Creates a new MetaMask profile.\n *\n * @param params - The profile creation parameters.\n * @returns The created profile data.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response do not match the expected shape.\n */\n async createProfile(\n params: CreateProfileParams,\n ): Promise<CreateProfileResponse> {\n return this.executeMutation({\n mutationKey: [`${this.name}:createProfile`],\n responseStruct: CreateProfileResponseStruct,\n mutationFn: async () =>\n this.#fetch<CreateProfileResponse>('profiles', {\n method: 'POST',\n error: ProfileServiceErrorMessage.CREATE_PROFILE_FAILED,\n json: params,\n }),\n });\n }\n\n /**\n * Fully replaces an existing profile (PUT).\n *\n * @param profileId - The identifier of the profile to replace.\n * @param params - The replacement profile data.\n * @returns The updated profile data.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response do not match the expected shape.\n */\n async replaceProfile(\n profileId: string,\n params: ReplaceProfileParams,\n ): Promise<ProfileApiResponse> {\n return this.executeMutation({\n mutationKey: [`${this.name}:replaceProfile`, profileId],\n responseStruct: ProfileApiResponseStruct,\n mutationFn: async () =>\n this.#fetch<ProfileApiResponse>(\n `profiles/${encodeURIComponent(profileId)}`,\n {\n method: 'PUT',\n error: ProfileServiceErrorMessage.REPLACE_PROFILE_FAILED,\n json: params,\n },\n ),\n });\n }\n\n /**\n * Partially updates an existing profile (PATCH).\n *\n * @param profileId - The identifier of the profile to update.\n * @param params - The fields to update.\n * @returns The updated profile data.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response do not match the expected shape.\n */\n async updateProfile(\n profileId: string,\n params: UpdateProfileParams,\n ): Promise<ProfileApiResponse> {\n return this.executeMutation({\n mutationKey: [`${this.name}:updateProfile`, profileId],\n responseStruct: ProfileApiResponseStruct,\n mutationFn: async () =>\n this.#fetch<ProfileApiResponse>(\n `profiles/${encodeURIComponent(profileId)}`,\n {\n method: 'PATCH',\n error: ProfileServiceErrorMessage.UPDATE_PROFILE_FAILED,\n json: params,\n },\n ),\n });\n }\n\n /**\n * Deletes a profile by its identifier.\n *\n * @param profileId - The identifier of the profile to delete.\n * @returns The result of the mutation.\n * @throws {HttpError} If the API returns a non-2xx response.\n */\n async deleteProfile(profileId: string): Promise<void> {\n return this.executeMutation({\n mutationKey: [`${this.name}:deleteProfile`, profileId],\n mutationFn: async () =>\n this.#fetch(`profiles/${encodeURIComponent(profileId)}`, {\n method: 'DELETE',\n error: ProfileServiceErrorMessage.DELETE_PROFILE_FAILED,\n }),\n });\n }\n\n /**\n * Checks whether a username is available.\n *\n * @param username - The username to check.\n * @returns Availability details including validity and normalized form.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response does not match the expected shape.\n */\n async checkUsernameAvailability(\n username: string,\n ): Promise<UsernameAvailabilityResponse> {\n return this.fetchQuery({\n queryKey: [`${this.name}:checkUsernameAvailability`, username],\n staleTime: 0,\n responseStruct: UsernameAvailabilityResponseStruct,\n queryFn: async () =>\n this.#fetch<UsernameAvailabilityResponse>(\n 'profiles/username/availability',\n {\n error:\n ProfileServiceErrorMessage.CHECK_USERNAME_AVAILABILITY_FAILED,\n searchParams: { username },\n },\n ),\n });\n }\n\n /**\n * Fetches the X OAuth PKCE authorization URL and its associated state parameter.\n * The profile is resolved server-side from the verified bearer token.\n *\n * @returns An object containing the authorization URL and the state token.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response does not match the expected shape.\n */\n async getXAuthUrl(): Promise<XAuthUrlResponse> {\n return this.executeMutation({\n mutationKey: [`${this.name}:getXAuthUrl`],\n responseStruct: XAuthUrlResponseStruct,\n mutationFn: async () =>\n this.#fetch<XAuthUrlResponse>('profiles/x/authentication-url', {\n error: ProfileServiceErrorMessage.GET_X_AUTH_URL_FAILED,\n }),\n });\n }\n\n /**\n * Completes the X OAuth PKCE flow and links the X account to the profile.\n * The profile is resolved server-side from the verified bearer token.\n *\n * @param params - The OAuth callback code and state from the X redirect.\n * @returns The linked X account data.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response does not match the expected shape.\n */\n async connectX(params: ConnectXParams): Promise<ConnectXResponse> {\n const response = await this.executeMutation({\n mutationKey: [`${this.name}:connectX`],\n responseStruct: ConnectXResponseStruct,\n mutationFn: async () =>\n this.#fetch<ConnectXResponse>('profiles/x/connect', {\n method: 'POST',\n error: ProfileServiceErrorMessage.CONNECT_X_FAILED,\n json: params,\n }),\n });\n // The response does not identify the profile, so drop every cached\n // profile: `connected_to_x` is now out of date.\n await this.invalidateQueries({ queryKey: [`${this.name}:getProfile`] });\n return response;\n }\n\n /**\n * Fetches the X account currently linked to the authenticated profile.\n * The profile is resolved server-side from the verified bearer token.\n *\n * @returns The linked X account data.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response does not match the expected shape.\n */\n async getXAccount(): Promise<XAccountResponse> {\n return this.fetchQuery({\n queryKey: [`${this.name}:getXAccount`],\n staleTime: 0,\n responseStruct: ConnectXResponseStruct,\n queryFn: async () =>\n this.#fetch<ConnectXResponse>('profiles/x/account', {\n error: ProfileServiceErrorMessage.GET_X_ACCOUNT_FAILED,\n }),\n });\n }\n\n /**\n * Disconnects the X account linked to the authenticated profile.\n * Identity is resolved server-side from the verified bearer token, so the\n * request targets the shared account endpoint rather than a profile-scoped\n * path.\n * A `404` response is treated as success (no X account is linked), making\n * the operation idempotent.\n *\n * @param profileId - The ID of the profile whose cached data should be\n * invalidated after the disconnection. The API resolves the linked X account\n * from the bearer token, not from this value.\n * @returns The result of the mutation.\n * @throws {HttpError} If the API returns a non-2xx response other than `404`.\n */\n async disconnectX(profileId: string): Promise<void> {\n await this.executeMutation({\n mutationKey: [`${this.name}:disconnectX`, profileId],\n mutationFn: async () => {\n try {\n return await this.#fetch('profiles/x/account', {\n method: 'DELETE',\n error: ProfileServiceErrorMessage.DISCONNECT_X_FAILED,\n });\n } catch (error) {\n if (error instanceof HttpError && error.httpStatus === 404) {\n return null;\n }\n throw error;\n }\n },\n });\n // The cached profile no longer reflects `connected_to_x`.\n await this.invalidateQueries({\n queryKey: [`${this.name}:getProfile`, profileId],\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ProfileService.js","sourceRoot":"","sources":["../src/ProfileService.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseDataService } from '@metamask/base-data-service';\nimport type {\n CreateServicePolicyOptions,\n DataServiceCacheUpdatedEvent,\n DataServiceGranularCacheUpdatedEvent,\n DataServiceInvalidateQueriesAction,\n} from '@metamask/base-data-service';\nimport { HttpError } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type { AuthenticationController } from '@metamask/profile-sync-controller';\nimport {\n array,\n boolean,\n enums,\n intersection,\n nullable,\n optional,\n sensitive,\n string,\n type as structType,\n validate,\n} from '@metamask/superstruct';\nimport type { Infer } from '@metamask/superstruct';\nimport type { Json } from '@metamask/utils';\nimport { CaipAccountIdStruct } from '@metamask/utils';\n\nimport type { ProfileServiceMethodActions } from './ProfileService-method-action-types.js';\n\nexport const serviceName = 'ProfileService';\n\n// ---------------------------------------------------------------------------\n// Error messages\n// ---------------------------------------------------------------------------\n\n/** Human-readable error messages for each ProfileService operation. */\nexport const ProfileServiceErrorMessage = {\n GET_PROFILE_FAILED: 'ProfileService: failed to fetch profile',\n CREATE_PROFILE_FAILED: 'ProfileService: failed to create profile',\n REPLACE_PROFILE_FAILED: 'ProfileService: failed to replace profile',\n UPDATE_PROFILE_FAILED: 'ProfileService: failed to update profile',\n DELETE_PROFILE_FAILED: 'ProfileService: failed to delete profile',\n CHECK_USERNAME_AVAILABILITY_FAILED:\n 'ProfileService: failed to check username availability',\n GET_X_AUTH_URL_FAILED: 'ProfileService: failed to get X authentication URL',\n CONNECT_X_FAILED: 'ProfileService: failed to connect X account',\n GET_X_ACCOUNT_FAILED: 'ProfileService: failed to get X account',\n DISCONNECT_X_FAILED: 'ProfileService: failed to disconnect X account',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Superstruct schemas\n// ---------------------------------------------------------------------------\n\nconst TradingPrivacyStruct = enums(['public', 'private'] as const);\n\n/**\n * The body the Profile API returns for a non-2xx response. The `message` is\n * surfaced to clients so they can react to the specific failure; `code` is\n * optional because not every error includes one.\n */\nconst ApiErrorResponseStruct = structType({\n code: optional(string()),\n message: string(),\n});\n\nconst ProfileApiResponseStruct = structType({\n profile_id: string(),\n username: string(),\n display_name: string(),\n bio: nullable(string()),\n linked_addresses: array(CaipAccountIdStruct),\n avatar_url: string(),\n trading_privacy: TradingPrivacyStruct,\n connected_to_x: boolean(),\n created_at: string(),\n updated_at: string(),\n});\n\nconst UsernameAvailabilityErrorStruct = structType({\n code: string(),\n message: string(),\n});\n\nconst UsernameAvailabilityResponseStruct = structType({\n username: string(),\n available: boolean(),\n valid: boolean(),\n normalized: string(),\n errors: array(UsernameAvailabilityErrorStruct),\n});\n\nconst XAuthUrlResponseStruct = structType({\n url: sensitive(string()),\n state: sensitive(string()),\n});\n\nconst ConnectXResponseStruct = structType({\n x_user_id: string(),\n x_profile_url: string(),\n username: string(),\n display_name: string(),\n avatar_url: string(),\n created_at: string(),\n updated_at: string(),\n});\n\nconst CreateProfileResponseStruct = intersection([\n ProfileApiResponseStruct,\n structType({ x_profile: optional(ConnectXResponseStruct) }),\n]);\n\nconst ConnectXParamsStruct = structType({\n code: sensitive(string()),\n state: sensitive(string()),\n});\n\nconst CreateProfileParamsStruct = structType({\n profile_id: string(),\n username: string(),\n display_name: string(),\n bio: optional(nullable(string())),\n linked_addresses: array(CaipAccountIdStruct),\n avatar_url: optional(string()),\n trading_privacy: TradingPrivacyStruct,\n});\n\nconst ReplaceProfileParamsStruct = structType({\n username: string(),\n display_name: string(),\n bio: optional(nullable(string())),\n linked_addresses: array(CaipAccountIdStruct),\n avatar_url: optional(string()),\n trading_privacy: TradingPrivacyStruct,\n});\n\nconst UpdateProfileParamsStruct = structType({\n username: optional(string()),\n display_name: optional(string()),\n bio: optional(nullable(string())),\n linked_addresses: optional(array(CaipAccountIdStruct)),\n avatar_url: optional(string()),\n trading_privacy: optional(TradingPrivacyStruct),\n});\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'getProfile',\n 'createProfile',\n 'replaceProfile',\n 'updateProfile',\n 'deleteProfile',\n 'checkUsernameAvailability',\n 'getXAuthUrl',\n 'connectX',\n 'getXAccount',\n 'disconnectX',\n] as const;\n\n/** The shape of a profile returned by the MetaMask Profile API. */\nexport type ProfileApiResponse = Infer<typeof ProfileApiResponseStruct>;\n\n/** The response shape for createProfile — includes an optional linked X profile if the user had already connected X. */\nexport type CreateProfileResponse = Infer<typeof CreateProfileResponseStruct>;\n\n/** The response shape for a username availability check. */\nexport type UsernameAvailabilityResponse = Infer<\n typeof UsernameAvailabilityResponseStruct\n>;\n\n/** The response shape returned when connecting or fetching an X account. */\nexport type ConnectXResponse = Infer<typeof ConnectXResponseStruct>;\n\n/** The response shape returned when requesting the X OAuth authentication URL. */\nexport type XAuthUrlResponse = Infer<typeof XAuthUrlResponseStruct>;\n\n/** Alias for {@link ConnectXResponse} returned when fetching the linked X account. */\nexport type XAccountResponse = ConnectXResponse;\n\n/** Parameters for creating a new MetaMask profile. */\nexport type CreateProfileParams = Infer<typeof CreateProfileParamsStruct>;\n\n/** Parameters for fully replacing an existing MetaMask profile (PUT). */\nexport type ReplaceProfileParams = Infer<typeof ReplaceProfileParamsStruct>;\n\n/** Parameters for partially updating an existing MetaMask profile (PATCH). */\nexport type UpdateProfileParams = Infer<typeof UpdateProfileParamsStruct>;\n\n/** Parameters for completing the X OAuth PKCE flow. */\nexport type ConnectXParams = Infer<typeof ConnectXParamsStruct>;\n\n// ---------------------------------------------------------------------------\n// Messenger types\n// ---------------------------------------------------------------------------\n\n/** Union of all actions exposed by ProfileService, including cache invalidation. */\nexport type ProfileServiceActions =\n | ProfileServiceMethodActions\n | DataServiceInvalidateQueriesAction<typeof serviceName>;\n\n/** Event emitted when the ProfileService query cache is updated. */\nexport type ProfileServiceCacheUpdatedEvent = DataServiceCacheUpdatedEvent<\n typeof serviceName\n>;\n\n/** Event emitted with per-query granularity when the ProfileService cache is updated. */\nexport type ProfileServiceGranularCacheUpdatedEvent =\n DataServiceGranularCacheUpdatedEvent<typeof serviceName>;\n\n/** Union of all events emitted by ProfileService. */\nexport type ProfileServiceEvents =\n | ProfileServiceCacheUpdatedEvent\n | ProfileServiceGranularCacheUpdatedEvent;\n\ntype AllowedActions =\n AuthenticationController.AuthenticationControllerGetBearerTokenAction;\n\ntype AllowedEvents = never;\n\n/** Messenger type for ProfileService, scoped to its actions and events. */\nexport type ProfileServiceMessenger = Messenger<\n typeof serviceName,\n ProfileServiceActions | AllowedActions,\n ProfileServiceEvents | AllowedEvents\n>;\n\n// ---------------------------------------------------------------------------\n// Service\n// ---------------------------------------------------------------------------\n\n/** Communicates with the MetaMask Profile API and exposes all operations via the messenger. */\nexport class ProfileService extends BaseDataService<\n typeof serviceName,\n ProfileServiceMessenger\n> {\n readonly #baseUrl: string;\n\n get #v1Url(): string {\n return `${this.#baseUrl}/v1`;\n }\n\n /**\n * Creates a new ProfileService instance.\n *\n * @param options - Constructor options.\n * @param options.messenger - The messenger scoped to ProfileService.\n * @param options.baseUrl - Base URL for the MetaMask Profile API.\n * @param options.policyOptions - Optional service policy configuration.\n */\n constructor({\n messenger,\n baseUrl,\n policyOptions,\n }: {\n messenger: ProfileServiceMessenger;\n baseUrl: string;\n policyOptions?: CreateServicePolicyOptions;\n }) {\n super({ name: serviceName, messenger, policyOptions });\n this.#baseUrl = baseUrl;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Gets the authentication headers for the request.\n *\n * @returns The authentication headers.\n */\n async #getAuthHeaders(): Promise<Record<string, string>> {\n const token = await this.messenger.call(\n 'AuthenticationController:getBearerToken',\n );\n return { Authorization: `Bearer ${token}` };\n }\n\n /**\n * Reads the error message from an unsuccessful API response body.\n *\n * The Profile API returns errors as `{ code, message }`. If the body is\n * missing, is not JSON, or does not contain a message, this returns\n * `undefined` so the caller can fall back to a generic message.\n *\n * @param response - The unsuccessful response.\n * @returns The API-provided error message, or `undefined` if unavailable.\n */\n async #readErrorMessage(response: Response): Promise<string | undefined> {\n try {\n const body: unknown = await response.json();\n const [error, result] = validate(body, ApiErrorResponseStruct);\n if (!error) {\n return result.message;\n }\n } catch {\n // The body is absent or not valid JSON; fall back to a generic message.\n }\n return undefined;\n }\n\n /**\n * Executes an authenticated HTTP request and returns the parsed JSON response.\n *\n * @param endpoint - The path relative to the v1 base URL.\n * @param options - Request options.\n * @param options.method - The HTTP method. Defaults to 'GET'.\n * @param options.error - The error message to use if the response is not OK.\n * @param options.json - Optional JSON body. If provided, sets Content-Type and serializes as body.\n * @returns The parsed JSON response.\n * @throws {HttpError} If the response is not a 2xx status code. The error\n * message includes the API-provided message when the response body contains one.\n */\n async #fetch(\n endpoint: string,\n options: { method: 'DELETE'; error: string },\n ): Promise<null>;\n\n async #fetch<ResponseType extends Json>(\n endpoint: string,\n options: {\n method?: string;\n error: string;\n json?: unknown;\n searchParams?: Record<string, string>;\n },\n ): Promise<ResponseType>;\n\n async #fetch<ResponseType extends Json>(\n endpoint: string,\n {\n method = 'GET',\n error,\n json,\n searchParams,\n }: {\n method?: string;\n error: string;\n json?: unknown;\n searchParams?: Record<string, string>;\n },\n ): Promise<ResponseType | null> {\n const authHeaders = await this.#getAuthHeaders();\n const url = new URL(`${this.#v1Url}/${endpoint}`);\n if (searchParams) {\n for (const [key, value] of Object.entries(searchParams)) {\n url.searchParams.append(key, value);\n }\n }\n const response = await fetch(url.toString(), {\n ...(method === 'GET' ? {} : { method }),\n headers: {\n ...authHeaders,\n ...(json === undefined ? {} : { 'Content-Type': 'application/json' }),\n },\n ...(json === undefined ? {} : { body: JSON.stringify(json) }),\n });\n if (!response.ok) {\n const apiMessage = await this.#readErrorMessage(response);\n throw new HttpError(\n response.status,\n apiMessage\n ? `${error}: ${response.status}: ${apiMessage}`\n : `${error}: ${response.status}`,\n );\n }\n if (method === 'DELETE') {\n return null;\n }\n return (await response.json()) as ResponseType;\n }\n\n /**\n * Fetches a profile by its identifier.\n *\n * @param profileId - The profile identifier to fetch.\n * @returns The profile data from the API.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response does not match the expected shape.\n */\n async getProfile(profileId: string): Promise<ProfileApiResponse> {\n return this.fetchQuery({\n queryKey: [`${this.name}:getProfile`, profileId],\n responseStruct: ProfileApiResponseStruct,\n queryFn: async () =>\n this.#fetch<ProfileApiResponse>(\n `profiles/${encodeURIComponent(profileId)}`,\n {\n error: ProfileServiceErrorMessage.GET_PROFILE_FAILED,\n },\n ),\n });\n }\n\n /**\n * Creates a new MetaMask profile.\n *\n * @param params - The profile creation parameters.\n * @returns The created profile data.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response do not match the expected shape.\n */\n async createProfile(\n params: CreateProfileParams,\n ): Promise<CreateProfileResponse> {\n return this.executeMutation({\n mutationKey: [`${this.name}:createProfile`],\n responseStruct: CreateProfileResponseStruct,\n mutationFn: async () =>\n this.#fetch<CreateProfileResponse>('profiles', {\n method: 'POST',\n error: ProfileServiceErrorMessage.CREATE_PROFILE_FAILED,\n json: params,\n }),\n });\n }\n\n /**\n * Fully replaces an existing profile (PUT).\n *\n * @param profileId - The identifier of the profile to replace.\n * @param params - The replacement profile data.\n * @returns The updated profile data.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response do not match the expected shape.\n */\n async replaceProfile(\n profileId: string,\n params: ReplaceProfileParams,\n ): Promise<ProfileApiResponse> {\n return this.executeMutation({\n mutationKey: [`${this.name}:replaceProfile`, profileId],\n responseStruct: ProfileApiResponseStruct,\n mutationFn: async () =>\n this.#fetch<ProfileApiResponse>(\n `profiles/${encodeURIComponent(profileId)}`,\n {\n method: 'PUT',\n error: ProfileServiceErrorMessage.REPLACE_PROFILE_FAILED,\n json: params,\n },\n ),\n });\n }\n\n /**\n * Partially updates an existing profile (PATCH).\n *\n * @param profileId - The identifier of the profile to update.\n * @param params - The fields to update.\n * @returns The updated profile data.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response do not match the expected shape.\n */\n async updateProfile(\n profileId: string,\n params: UpdateProfileParams,\n ): Promise<ProfileApiResponse> {\n return this.executeMutation({\n mutationKey: [`${this.name}:updateProfile`, profileId],\n responseStruct: ProfileApiResponseStruct,\n mutationFn: async () =>\n this.#fetch<ProfileApiResponse>(\n `profiles/${encodeURIComponent(profileId)}`,\n {\n method: 'PATCH',\n error: ProfileServiceErrorMessage.UPDATE_PROFILE_FAILED,\n json: params,\n },\n ),\n });\n }\n\n /**\n * Deletes a profile by its identifier.\n *\n * @param profileId - The identifier of the profile to delete.\n * @returns The result of the mutation.\n * @throws {HttpError} If the API returns a non-2xx response.\n */\n async deleteProfile(profileId: string): Promise<void> {\n return this.executeMutation({\n mutationKey: [`${this.name}:deleteProfile`, profileId],\n mutationFn: async () =>\n this.#fetch(`profiles/${encodeURIComponent(profileId)}`, {\n method: 'DELETE',\n error: ProfileServiceErrorMessage.DELETE_PROFILE_FAILED,\n }),\n });\n }\n\n /**\n * Checks whether a username is available.\n *\n * @param username - The username to check.\n * @returns Availability details including validity and normalized form.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response does not match the expected shape.\n */\n async checkUsernameAvailability(\n username: string,\n ): Promise<UsernameAvailabilityResponse> {\n return this.fetchQuery({\n queryKey: [`${this.name}:checkUsernameAvailability`, username],\n staleTime: 0,\n responseStruct: UsernameAvailabilityResponseStruct,\n queryFn: async () =>\n this.#fetch<UsernameAvailabilityResponse>(\n 'profiles/username/availability',\n {\n error:\n ProfileServiceErrorMessage.CHECK_USERNAME_AVAILABILITY_FAILED,\n searchParams: { username },\n },\n ),\n });\n }\n\n /**\n * Fetches the X OAuth PKCE authorization URL and its associated state parameter.\n * The profile is resolved server-side from the verified bearer token.\n *\n * @returns An object containing the authorization URL and the state token.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response does not match the expected shape.\n */\n async getXAuthUrl(): Promise<XAuthUrlResponse> {\n return this.executeMutation({\n mutationKey: [`${this.name}:getXAuthUrl`],\n responseStruct: XAuthUrlResponseStruct,\n mutationFn: async () =>\n this.#fetch<XAuthUrlResponse>('profiles/x/authentication-url', {\n error: ProfileServiceErrorMessage.GET_X_AUTH_URL_FAILED,\n }),\n });\n }\n\n /**\n * Completes the X OAuth PKCE flow and links the X account to the profile.\n * The profile is resolved server-side from the verified bearer token.\n *\n * @param params - The OAuth callback code and state from the X redirect.\n * @returns The linked X account data.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response does not match the expected shape.\n */\n async connectX(params: ConnectXParams): Promise<ConnectXResponse> {\n const response = await this.executeMutation({\n mutationKey: [`${this.name}:connectX`],\n responseStruct: ConnectXResponseStruct,\n mutationFn: async () =>\n this.#fetch<ConnectXResponse>('profiles/x/connect', {\n method: 'POST',\n error: ProfileServiceErrorMessage.CONNECT_X_FAILED,\n json: params,\n }),\n });\n // The response does not identify the profile, so drop every cached\n // profile: `connected_to_x` is now out of date.\n await this.invalidateQueries({ queryKey: [`${this.name}:getProfile`] });\n return response;\n }\n\n /**\n * Fetches the X account currently linked to the authenticated profile.\n * The profile is resolved server-side from the verified bearer token.\n *\n * @returns The linked X account data.\n * @throws {HttpError} If the API returns a non-2xx response.\n * @throws {StructError} If the response does not match the expected shape.\n */\n async getXAccount(): Promise<XAccountResponse> {\n return this.fetchQuery({\n queryKey: [`${this.name}:getXAccount`],\n staleTime: 0,\n responseStruct: ConnectXResponseStruct,\n queryFn: async () =>\n this.#fetch<ConnectXResponse>('profiles/x/account', {\n error: ProfileServiceErrorMessage.GET_X_ACCOUNT_FAILED,\n }),\n });\n }\n\n /**\n * Disconnects the X account linked to the authenticated profile.\n * Identity is resolved server-side from the verified bearer token, so the\n * request targets the shared account endpoint rather than a profile-scoped\n * path.\n * A `404` response is treated as success (no X account is linked), making\n * the operation idempotent.\n *\n * @param profileId - The ID of the profile whose cached data should be\n * invalidated after the disconnection. The API resolves the linked X account\n * from the bearer token, not from this value.\n * @returns The result of the mutation.\n * @throws {HttpError} If the API returns a non-2xx response other than `404`.\n */\n async disconnectX(profileId: string): Promise<void> {\n await this.executeMutation({\n mutationKey: [`${this.name}:disconnectX`, profileId],\n mutationFn: async () => {\n try {\n return await this.#fetch('profiles/x/account', {\n method: 'DELETE',\n error: ProfileServiceErrorMessage.DISCONNECT_X_FAILED,\n });\n } catch (error) {\n if (error instanceof HttpError && error.httpStatus === 404) {\n return null;\n }\n throw error;\n }\n },\n });\n // The cached profile no longer reflects `connected_to_x`.\n await this.invalidateQueries({\n queryKey: [`${this.name}:getProfile`, profileId],\n });\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/profile-controller",
|
|
3
|
-
"version": "1.2.1-preview-
|
|
3
|
+
"version": "1.2.1-preview-9a6250503",
|
|
4
4
|
"description": "Controller for managing user profile data including username, bio, avatar, and linked accounts via the MetaMask Profile API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ethereum",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"registry": "https://registry.npmjs.org/"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
|
+
"allow-scripts": "yarn workspace @metamask/core-monorepo run allow-scripts",
|
|
35
36
|
"build": "tsc --project tsconfig.build.json",
|
|
36
37
|
"build:all": "tsc --build tsconfig.build.json --verbose",
|
|
37
38
|
"build:clean": "yarn build:only-clean && yarn build",
|
|
@@ -46,6 +47,7 @@
|
|
|
46
47
|
"since-latest-release": "../../scripts/since-latest-release.sh",
|
|
47
48
|
"test": "vitest",
|
|
48
49
|
"test:clean": "vitest --clearCache",
|
|
50
|
+
"test:coverage": "vitest --coverage",
|
|
49
51
|
"test:verbose": "vitest --reporter=verbose",
|
|
50
52
|
"test:watch": "vitest --watch"
|
|
51
53
|
},
|
|
@@ -65,7 +67,7 @@
|
|
|
65
67
|
"typedoc": "^0.28.20",
|
|
66
68
|
"typedoc-plugin-missing-exports": "^4.1.4",
|
|
67
69
|
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
68
|
-
"vitest": "
|
|
70
|
+
"vitest": "5.0.3"
|
|
69
71
|
},
|
|
70
72
|
"engines": {
|
|
71
73
|
"node": "^22.14.0 || ^24 || ^26"
|