@metamask-previews/authenticated-user-storage 0.0.0-preview-8e9c439

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +20 -0
  3. package/README.md +135 -0
  4. package/dist/authenticated-user-storage-method-action-types.cjs +7 -0
  5. package/dist/authenticated-user-storage-method-action-types.cjs.map +1 -0
  6. package/dist/authenticated-user-storage-method-action-types.d.cts +58 -0
  7. package/dist/authenticated-user-storage-method-action-types.d.cts.map +1 -0
  8. package/dist/authenticated-user-storage-method-action-types.d.mts +58 -0
  9. package/dist/authenticated-user-storage-method-action-types.d.mts.map +1 -0
  10. package/dist/authenticated-user-storage-method-action-types.mjs +6 -0
  11. package/dist/authenticated-user-storage-method-action-types.mjs.map +1 -0
  12. package/dist/authenticated-user-storage.cjs +218 -0
  13. package/dist/authenticated-user-storage.cjs.map +1 -0
  14. package/dist/authenticated-user-storage.d.cts +123 -0
  15. package/dist/authenticated-user-storage.d.cts.map +1 -0
  16. package/dist/authenticated-user-storage.d.mts +123 -0
  17. package/dist/authenticated-user-storage.d.mts.map +1 -0
  18. package/dist/authenticated-user-storage.mjs +213 -0
  19. package/dist/authenticated-user-storage.mjs.map +1 -0
  20. package/dist/env.cjs +24 -0
  21. package/dist/env.cjs.map +1 -0
  22. package/dist/env.d.cts +10 -0
  23. package/dist/env.d.cts.map +1 -0
  24. package/dist/env.d.mts +10 -0
  25. package/dist/env.d.mts.map +1 -0
  26. package/dist/env.mjs +20 -0
  27. package/dist/env.mjs.map +1 -0
  28. package/dist/index.cjs +9 -0
  29. package/dist/index.cjs.map +1 -0
  30. package/dist/index.d.cts +7 -0
  31. package/dist/index.d.cts.map +1 -0
  32. package/dist/index.d.mts +7 -0
  33. package/dist/index.d.mts.map +1 -0
  34. package/dist/index.mjs +3 -0
  35. package/dist/index.mjs.map +1 -0
  36. package/dist/types.cjs +3 -0
  37. package/dist/types.cjs.map +1 -0
  38. package/dist/types.d.cts +90 -0
  39. package/dist/types.d.cts.map +1 -0
  40. package/dist/types.d.mts +90 -0
  41. package/dist/types.d.mts.map +1 -0
  42. package/dist/types.mjs +2 -0
  43. package/dist/types.mjs.map +1 -0
  44. package/dist/validators.cjs +91 -0
  45. package/dist/validators.cjs.map +1 -0
  46. package/dist/validators.d.cts +16 -0
  47. package/dist/validators.d.cts.map +1 -0
  48. package/dist/validators.d.mts +16 -0
  49. package/dist/validators.d.mts.map +1 -0
  50. package/dist/validators.mjs +86 -0
  51. package/dist/validators.mjs.map +1 -0
  52. package/package.json +78 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - Initial release ([#8260](https://github.com/MetaMask/core/pull/8260))
13
+ - `AuthenticatedUserStorageService` class with namespaced domain accessors: `delegations` (list, create, revoke) and `preferences` (getNotifications, putNotifications)
14
+
15
+ [Unreleased]: https://github.com/MetaMask/core/
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MetaMask
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # `@metamask/authenticated-user-storage`
2
+
3
+ A TypeScript SDK for MetaMask's Authenticated User Storage API. Unlike E2EE user-storage, authenticated user storage holds **structured JSON** scoped to the authenticated user. The server can read and validate the contents, which allows other backend services to consume the data (e.g. delegation execution, notification delivery).
4
+
5
+ The SDK currently supports two domains:
6
+
7
+ - **Delegations** -- immutable, EIP-712 signed delegation records (list, create, revoke).
8
+ - **Notification Preferences** -- mutable per-user notification settings (get, put).
9
+
10
+ ## Installation
11
+
12
+ `yarn add @metamask/authenticated-user-storage`
13
+
14
+ or
15
+
16
+ `npm install @metamask/authenticated-user-storage`
17
+
18
+ ## Usage
19
+
20
+ ### Creating a service
21
+
22
+ `AuthenticatedUserStorageService` extends `BaseDataService` and requires a messenger and an environment:
23
+
24
+ - **`messenger`** -- a namespaced messenger for registering actions and events. The messenger must have access to `AuthenticationController:getBearerToken` to retrieve access tokens.
25
+ - **`env`** -- selects the backend environment (`DEV`, `UAT`, or `PRD`).
26
+
27
+ ```typescript
28
+ import { Messenger } from '@metamask/messenger';
29
+ import { AuthenticatedUserStorageService } from '@metamask/authenticated-user-storage';
30
+ import type {
31
+ AuthenticatedUserStorageMessenger,
32
+ AuthenticatedUserStorageActions,
33
+ AuthenticatedUserStorageEvents,
34
+ } from '@metamask/authenticated-user-storage';
35
+
36
+ // Create the messenger
37
+ const messenger = new Messenger<
38
+ 'AuthenticatedUserStorageService',
39
+ AuthenticatedUserStorageActions,
40
+ AuthenticatedUserStorageEvents
41
+ >({
42
+ namespace: 'AuthenticatedUserStorageService',
43
+ parent: rootMessenger,
44
+ });
45
+
46
+ // Instantiate the service
47
+ const service = new AuthenticatedUserStorageService({
48
+ messenger,
49
+ environment: 'prod',
50
+ });
51
+ ```
52
+
53
+ The `environment` option selects the backend environment:
54
+
55
+ | Value | Server |
56
+ | -------- | ------------------------------------- |
57
+ | `'dev'` | `user-storage.dev-api.cx.metamask.io` |
58
+ | `'uat'` | `user-storage.uat-api.cx.metamask.io` |
59
+ | `'prod'` | `user-storage.api.cx.metamask.io` |
60
+
61
+ ### Calling methods via the messenger
62
+
63
+ Once instantiated, all service methods are available as messenger actions. This allows any consumer with access to the messenger to call them without needing a direct reference to the service instance:
64
+
65
+ ```typescript
66
+ const delegations = await rootMessenger.call(
67
+ 'AuthenticatedUserStorageService:listDelegations',
68
+ );
69
+ ```
70
+
71
+ ### Delegations
72
+
73
+ Delegations are immutable once stored. They can only be revoked (deleted), not updated.
74
+
75
+ ```typescript
76
+ import type { DelegationSubmission } from '@metamask/authenticated-user-storage';
77
+
78
+ // List all delegations for the authenticated user
79
+ const delegations = await service.listDelegations();
80
+
81
+ // Submit a new signed delegation
82
+ const submission: DelegationSubmission = {
83
+ signedDelegation: { ... },
84
+ metadata: { ... },
85
+ };
86
+ await service.createDelegation(submission, 'extension');
87
+
88
+ // Revoke a delegation by its hash
89
+ await service.revokeDelegation('0xdae6d1...');
90
+ ```
91
+
92
+ ### Notification preferences
93
+
94
+ Preferences are mutable. The first call creates the record; subsequent calls update it.
95
+
96
+ ```typescript
97
+ import type { NotificationPreferences } from '@metamask/authenticated-user-storage';
98
+
99
+ // Retrieve current preferences (returns null if none have been set)
100
+ const prefs = await service.getNotificationPreferences();
101
+
102
+ // Create or update preferences
103
+ const updated: NotificationPreferences = {
104
+ walletActivity: { ... },
105
+ marketing: { ... },
106
+ perps: { ... },
107
+ socialAI: { ... },
108
+ };
109
+ await service.putNotificationPreferences(updated, 'extension');
110
+ ```
111
+
112
+ ## Response validation
113
+
114
+ All API responses are validated at runtime using [`@metamask/superstruct`](https://github.com/MetaMask/superstruct) schemas before being returned to callers. If the server returns data that doesn't match the expected shape, the SDK throws with details about the structural mismatch rather than silently returning malformed data.
115
+
116
+ ## Error handling
117
+
118
+ HTTP errors are represented as `HttpError` from `@metamask/controller-utils`. All errors are encouraged to bubble up to the caller. The service policy provided by `BaseDataService` automatically retries transient failures before propagating the error.
119
+
120
+ ```typescript
121
+ import { HttpError } from '@metamask/controller-utils';
122
+
123
+ try {
124
+ await service.createDelegation(submission);
125
+ } catch (error) {
126
+ if (error instanceof HttpError) {
127
+ console.error(error.message);
128
+ // e.g. "Failed to create delegation: 409"
129
+ }
130
+ }
131
+ ```
132
+
133
+ ## Contributing
134
+
135
+ This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * This file is auto generated.
4
+ * Do not edit manually.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ //# sourceMappingURL=authenticated-user-storage-method-action-types.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticated-user-storage-method-action-types.cjs","sourceRoot":"","sources":["../src/authenticated-user-storage-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { AuthenticatedUserStorageService } from './authenticated-user-storage';\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 */\nexport type AuthenticatedUserStorageServiceListDelegationsAction = {\n type: `AuthenticatedUserStorageService:listDelegations`;\n handler: AuthenticatedUserStorageService['listDelegations'];\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 */\nexport type AuthenticatedUserStorageServiceCreateDelegationAction = {\n type: `AuthenticatedUserStorageService:createDelegation`;\n handler: AuthenticatedUserStorageService['createDelegation'];\n};\n\n/**\n * Revokes (deletes) a delegation record.\n *\n * @param delegationHash - The unique hash identifying the delegation.\n */\nexport type AuthenticatedUserStorageServiceRevokeDelegationAction = {\n type: `AuthenticatedUserStorageService:revokeDelegation`;\n handler: AuthenticatedUserStorageService['revokeDelegation'];\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 */\nexport type AuthenticatedUserStorageServiceGetNotificationPreferencesAction = {\n type: `AuthenticatedUserStorageService:getNotificationPreferences`;\n handler: AuthenticatedUserStorageService['getNotificationPreferences'];\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 */\nexport type AuthenticatedUserStorageServicePutNotificationPreferencesAction = {\n type: `AuthenticatedUserStorageService:putNotificationPreferences`;\n handler: AuthenticatedUserStorageService['putNotificationPreferences'];\n};\n\n/**\n * Union of all AuthenticatedUserStorageService action types.\n */\nexport type AuthenticatedUserStorageServiceMethodActions =\n | AuthenticatedUserStorageServiceListDelegationsAction\n | AuthenticatedUserStorageServiceCreateDelegationAction\n | AuthenticatedUserStorageServiceRevokeDelegationAction\n | AuthenticatedUserStorageServiceGetNotificationPreferencesAction\n | AuthenticatedUserStorageServicePutNotificationPreferencesAction;\n"]}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * This file is auto generated.
3
+ * Do not edit manually.
4
+ */
5
+ import type { AuthenticatedUserStorageService } from "./authenticated-user-storage.cjs";
6
+ /**
7
+ * Returns all delegation records belonging to the authenticated user.
8
+ *
9
+ * @returns An array of delegation records, or an empty array if none exist.
10
+ */
11
+ export type AuthenticatedUserStorageServiceListDelegationsAction = {
12
+ type: `AuthenticatedUserStorageService:listDelegations`;
13
+ handler: AuthenticatedUserStorageService['listDelegations'];
14
+ };
15
+ /**
16
+ * Stores a signed delegation record for the authenticated user.
17
+ *
18
+ * @param submission - The signed delegation and its metadata.
19
+ * @param clientType - Optional client type header.
20
+ */
21
+ export type AuthenticatedUserStorageServiceCreateDelegationAction = {
22
+ type: `AuthenticatedUserStorageService:createDelegation`;
23
+ handler: AuthenticatedUserStorageService['createDelegation'];
24
+ };
25
+ /**
26
+ * Revokes (deletes) a delegation record.
27
+ *
28
+ * @param delegationHash - The unique hash identifying the delegation.
29
+ */
30
+ export type AuthenticatedUserStorageServiceRevokeDelegationAction = {
31
+ type: `AuthenticatedUserStorageService:revokeDelegation`;
32
+ handler: AuthenticatedUserStorageService['revokeDelegation'];
33
+ };
34
+ /**
35
+ * Returns the notification preferences for the authenticated user.
36
+ *
37
+ * @returns The notification preferences object, or `null` if none have been
38
+ * set (404).
39
+ */
40
+ export type AuthenticatedUserStorageServiceGetNotificationPreferencesAction = {
41
+ type: `AuthenticatedUserStorageService:getNotificationPreferences`;
42
+ handler: AuthenticatedUserStorageService['getNotificationPreferences'];
43
+ };
44
+ /**
45
+ * Creates or updates the notification preferences for the authenticated user.
46
+ *
47
+ * @param prefs - The full notification preferences object.
48
+ * @param clientType - Optional client type header.
49
+ */
50
+ export type AuthenticatedUserStorageServicePutNotificationPreferencesAction = {
51
+ type: `AuthenticatedUserStorageService:putNotificationPreferences`;
52
+ handler: AuthenticatedUserStorageService['putNotificationPreferences'];
53
+ };
54
+ /**
55
+ * Union of all AuthenticatedUserStorageService action types.
56
+ */
57
+ export type AuthenticatedUserStorageServiceMethodActions = AuthenticatedUserStorageServiceListDelegationsAction | AuthenticatedUserStorageServiceCreateDelegationAction | AuthenticatedUserStorageServiceRevokeDelegationAction | AuthenticatedUserStorageServiceGetNotificationPreferencesAction | AuthenticatedUserStorageServicePutNotificationPreferencesAction;
58
+ //# sourceMappingURL=authenticated-user-storage-method-action-types.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticated-user-storage-method-action-types.d.cts","sourceRoot":"","sources":["../src/authenticated-user-storage-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,+BAA+B,EAAE,yCAAqC;AAEpF;;;;GAIG;AACH,MAAM,MAAM,oDAAoD,GAAG;IACjE,IAAI,EAAE,iDAAiD,CAAC;IACxD,OAAO,EAAE,+BAA+B,CAAC,iBAAiB,CAAC,CAAC;CAC7D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,qDAAqD,GAAG;IAClE,IAAI,EAAE,kDAAkD,CAAC;IACzD,OAAO,EAAE,+BAA+B,CAAC,kBAAkB,CAAC,CAAC;CAC9D,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,qDAAqD,GAAG;IAClE,IAAI,EAAE,kDAAkD,CAAC;IACzD,OAAO,EAAE,+BAA+B,CAAC,kBAAkB,CAAC,CAAC;CAC9D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,+DAA+D,GAAG;IAC5E,IAAI,EAAE,4DAA4D,CAAC;IACnE,OAAO,EAAE,+BAA+B,CAAC,4BAA4B,CAAC,CAAC;CACxE,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,+DAA+D,GAAG;IAC5E,IAAI,EAAE,4DAA4D,CAAC;IACnE,OAAO,EAAE,+BAA+B,CAAC,4BAA4B,CAAC,CAAC;CACxE,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,4CAA4C,GACpD,oDAAoD,GACpD,qDAAqD,GACrD,qDAAqD,GACrD,+DAA+D,GAC/D,+DAA+D,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * This file is auto generated.
3
+ * Do not edit manually.
4
+ */
5
+ import type { AuthenticatedUserStorageService } from "./authenticated-user-storage.mjs";
6
+ /**
7
+ * Returns all delegation records belonging to the authenticated user.
8
+ *
9
+ * @returns An array of delegation records, or an empty array if none exist.
10
+ */
11
+ export type AuthenticatedUserStorageServiceListDelegationsAction = {
12
+ type: `AuthenticatedUserStorageService:listDelegations`;
13
+ handler: AuthenticatedUserStorageService['listDelegations'];
14
+ };
15
+ /**
16
+ * Stores a signed delegation record for the authenticated user.
17
+ *
18
+ * @param submission - The signed delegation and its metadata.
19
+ * @param clientType - Optional client type header.
20
+ */
21
+ export type AuthenticatedUserStorageServiceCreateDelegationAction = {
22
+ type: `AuthenticatedUserStorageService:createDelegation`;
23
+ handler: AuthenticatedUserStorageService['createDelegation'];
24
+ };
25
+ /**
26
+ * Revokes (deletes) a delegation record.
27
+ *
28
+ * @param delegationHash - The unique hash identifying the delegation.
29
+ */
30
+ export type AuthenticatedUserStorageServiceRevokeDelegationAction = {
31
+ type: `AuthenticatedUserStorageService:revokeDelegation`;
32
+ handler: AuthenticatedUserStorageService['revokeDelegation'];
33
+ };
34
+ /**
35
+ * Returns the notification preferences for the authenticated user.
36
+ *
37
+ * @returns The notification preferences object, or `null` if none have been
38
+ * set (404).
39
+ */
40
+ export type AuthenticatedUserStorageServiceGetNotificationPreferencesAction = {
41
+ type: `AuthenticatedUserStorageService:getNotificationPreferences`;
42
+ handler: AuthenticatedUserStorageService['getNotificationPreferences'];
43
+ };
44
+ /**
45
+ * Creates or updates the notification preferences for the authenticated user.
46
+ *
47
+ * @param prefs - The full notification preferences object.
48
+ * @param clientType - Optional client type header.
49
+ */
50
+ export type AuthenticatedUserStorageServicePutNotificationPreferencesAction = {
51
+ type: `AuthenticatedUserStorageService:putNotificationPreferences`;
52
+ handler: AuthenticatedUserStorageService['putNotificationPreferences'];
53
+ };
54
+ /**
55
+ * Union of all AuthenticatedUserStorageService action types.
56
+ */
57
+ export type AuthenticatedUserStorageServiceMethodActions = AuthenticatedUserStorageServiceListDelegationsAction | AuthenticatedUserStorageServiceCreateDelegationAction | AuthenticatedUserStorageServiceRevokeDelegationAction | AuthenticatedUserStorageServiceGetNotificationPreferencesAction | AuthenticatedUserStorageServicePutNotificationPreferencesAction;
58
+ //# sourceMappingURL=authenticated-user-storage-method-action-types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticated-user-storage-method-action-types.d.mts","sourceRoot":"","sources":["../src/authenticated-user-storage-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,+BAA+B,EAAE,yCAAqC;AAEpF;;;;GAIG;AACH,MAAM,MAAM,oDAAoD,GAAG;IACjE,IAAI,EAAE,iDAAiD,CAAC;IACxD,OAAO,EAAE,+BAA+B,CAAC,iBAAiB,CAAC,CAAC;CAC7D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,qDAAqD,GAAG;IAClE,IAAI,EAAE,kDAAkD,CAAC;IACzD,OAAO,EAAE,+BAA+B,CAAC,kBAAkB,CAAC,CAAC;CAC9D,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,qDAAqD,GAAG;IAClE,IAAI,EAAE,kDAAkD,CAAC;IACzD,OAAO,EAAE,+BAA+B,CAAC,kBAAkB,CAAC,CAAC;CAC9D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,+DAA+D,GAAG;IAC5E,IAAI,EAAE,4DAA4D,CAAC;IACnE,OAAO,EAAE,+BAA+B,CAAC,4BAA4B,CAAC,CAAC;CACxE,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,+DAA+D,GAAG;IAC5E,IAAI,EAAE,4DAA4D,CAAC;IACnE,OAAO,EAAE,+BAA+B,CAAC,4BAA4B,CAAC,CAAC;CACxE,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,4CAA4C,GACpD,oDAAoD,GACpD,qDAAqD,GACrD,qDAAqD,GACrD,+DAA+D,GAC/D,+DAA+D,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * This file is auto generated.
3
+ * Do not edit manually.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=authenticated-user-storage-method-action-types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticated-user-storage-method-action-types.mjs","sourceRoot":"","sources":["../src/authenticated-user-storage-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { AuthenticatedUserStorageService } from './authenticated-user-storage';\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 */\nexport type AuthenticatedUserStorageServiceListDelegationsAction = {\n type: `AuthenticatedUserStorageService:listDelegations`;\n handler: AuthenticatedUserStorageService['listDelegations'];\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 */\nexport type AuthenticatedUserStorageServiceCreateDelegationAction = {\n type: `AuthenticatedUserStorageService:createDelegation`;\n handler: AuthenticatedUserStorageService['createDelegation'];\n};\n\n/**\n * Revokes (deletes) a delegation record.\n *\n * @param delegationHash - The unique hash identifying the delegation.\n */\nexport type AuthenticatedUserStorageServiceRevokeDelegationAction = {\n type: `AuthenticatedUserStorageService:revokeDelegation`;\n handler: AuthenticatedUserStorageService['revokeDelegation'];\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 */\nexport type AuthenticatedUserStorageServiceGetNotificationPreferencesAction = {\n type: `AuthenticatedUserStorageService:getNotificationPreferences`;\n handler: AuthenticatedUserStorageService['getNotificationPreferences'];\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 */\nexport type AuthenticatedUserStorageServicePutNotificationPreferencesAction = {\n type: `AuthenticatedUserStorageService:putNotificationPreferences`;\n handler: AuthenticatedUserStorageService['putNotificationPreferences'];\n};\n\n/**\n * Union of all AuthenticatedUserStorageService action types.\n */\nexport type AuthenticatedUserStorageServiceMethodActions =\n | AuthenticatedUserStorageServiceListDelegationsAction\n | AuthenticatedUserStorageServiceCreateDelegationAction\n | AuthenticatedUserStorageServiceRevokeDelegationAction\n | AuthenticatedUserStorageServiceGetNotificationPreferencesAction\n | AuthenticatedUserStorageServicePutNotificationPreferencesAction;\n"]}
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _AuthenticatedUserStorageService_instances, _AuthenticatedUserStorageService_environment, _AuthenticatedUserStorageService_getHeaders;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.AuthenticatedUserStorageService = exports.getAuthenticatedStorageUrl = exports.serviceName = void 0;
16
+ const base_data_service_1 = require("@metamask/base-data-service");
17
+ const controller_utils_1 = require("@metamask/controller-utils");
18
+ const env_1 = require("./env.cjs");
19
+ const validators_1 = require("./validators.cjs");
20
+ // === GENERAL ===
21
+ /**
22
+ * The name of the {@link AuthenticatedUserStorageService} service, used to
23
+ * namespace the service's actions and events.
24
+ */
25
+ exports.serviceName = 'AuthenticatedUserStorageService';
26
+ /**
27
+ * Builds the versioned API base URL for a given environment.
28
+ *
29
+ * @param environment - The target environment.
30
+ * @returns The base URL including the `/api/v1` path segment.
31
+ */
32
+ function getAuthenticatedStorageUrl(environment) {
33
+ return `${(0, env_1.getUserStorageApiUrl)(environment)}/api/v1`;
34
+ }
35
+ exports.getAuthenticatedStorageUrl = getAuthenticatedStorageUrl;
36
+ // === MESSENGER ===
37
+ const MESSENGER_EXPOSED_METHODS = [
38
+ 'listDelegations',
39
+ 'createDelegation',
40
+ 'revokeDelegation',
41
+ 'getNotificationPreferences',
42
+ 'putNotificationPreferences',
43
+ ];
44
+ // === SERVICE ===
45
+ /**
46
+ * Data service wrapping authenticated user-storage API endpoints.
47
+ *
48
+ * Provides methods for managing delegations and notification preferences
49
+ * for the authenticated user.
50
+ */
51
+ class AuthenticatedUserStorageService extends base_data_service_1.BaseDataService {
52
+ /**
53
+ * Constructs a new AuthenticatedUserStorageService.
54
+ *
55
+ * @param args - The constructor arguments.
56
+ * @param args.messenger - The messenger suited for this service.
57
+ * @param args.environment - The target environment (dev, uat, prod).
58
+ * @param args.policyOptions - Options to pass to `createServicePolicy`, which
59
+ * is used to wrap each request. See {@link CreateServicePolicyOptions}.
60
+ */
61
+ constructor({ messenger, environment, policyOptions, }) {
62
+ super({ name: exports.serviceName, messenger, policyOptions });
63
+ _AuthenticatedUserStorageService_instances.add(this);
64
+ _AuthenticatedUserStorageService_environment.set(this, void 0);
65
+ __classPrivateFieldSet(this, _AuthenticatedUserStorageService_environment, environment, "f");
66
+ this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
67
+ }
68
+ /**
69
+ * Returns all delegation records belonging to the authenticated user.
70
+ *
71
+ * @returns An array of delegation records, or an empty array if none exist.
72
+ */
73
+ async listDelegations() {
74
+ const url = `${getAuthenticatedStorageUrl(__classPrivateFieldGet(this, _AuthenticatedUserStorageService_environment, "f"))}/delegations`;
75
+ const data = await this.fetchQuery({
76
+ queryKey: [`${this.name}:listDelegations`],
77
+ queryFn: async () => {
78
+ const headers = await __classPrivateFieldGet(this, _AuthenticatedUserStorageService_instances, "m", _AuthenticatedUserStorageService_getHeaders).call(this);
79
+ const response = await fetch(url, { headers });
80
+ if (!response.ok) {
81
+ throw new controller_utils_1.HttpError(response.status, `Failed to list delegations: ${response.status}`);
82
+ }
83
+ return response.json();
84
+ },
85
+ });
86
+ (0, validators_1.assertDelegationResponseArray)(data);
87
+ return data;
88
+ }
89
+ /**
90
+ * Stores a signed delegation record for the authenticated user.
91
+ *
92
+ * @param submission - The signed delegation and its metadata.
93
+ * @param clientType - Optional client type header.
94
+ */
95
+ async createDelegation(submission, clientType) {
96
+ const url = `${getAuthenticatedStorageUrl(__classPrivateFieldGet(this, _AuthenticatedUserStorageService_environment, "f"))}/delegations`;
97
+ await this.fetchQuery({
98
+ queryKey: [
99
+ `${this.name}:createDelegation`,
100
+ submission.metadata.delegationHash,
101
+ ],
102
+ staleTime: 0,
103
+ queryFn: async () => {
104
+ const headers = await __classPrivateFieldGet(this, _AuthenticatedUserStorageService_instances, "m", _AuthenticatedUserStorageService_getHeaders).call(this, clientType);
105
+ const response = await fetch(url, {
106
+ method: 'POST',
107
+ headers,
108
+ body: JSON.stringify(submission),
109
+ });
110
+ if (!response.ok) {
111
+ throw new controller_utils_1.HttpError(response.status, `Failed to create delegation: ${response.status}`);
112
+ }
113
+ return null;
114
+ },
115
+ });
116
+ await this.invalidateQueries({
117
+ queryKey: [`${this.name}:listDelegations`],
118
+ });
119
+ }
120
+ /**
121
+ * Revokes (deletes) a delegation record.
122
+ *
123
+ * @param delegationHash - The unique hash identifying the delegation.
124
+ */
125
+ async revokeDelegation(delegationHash) {
126
+ const url = `${getAuthenticatedStorageUrl(__classPrivateFieldGet(this, _AuthenticatedUserStorageService_environment, "f"))}/delegations/${encodeURIComponent(delegationHash)}`;
127
+ await this.fetchQuery({
128
+ queryKey: [`${this.name}:revokeDelegation`, delegationHash],
129
+ staleTime: 0,
130
+ queryFn: async () => {
131
+ const headers = await __classPrivateFieldGet(this, _AuthenticatedUserStorageService_instances, "m", _AuthenticatedUserStorageService_getHeaders).call(this);
132
+ const response = await fetch(url, {
133
+ method: 'DELETE',
134
+ headers,
135
+ });
136
+ if (!response.ok) {
137
+ throw new controller_utils_1.HttpError(response.status, `Failed to revoke delegation: ${response.status}`);
138
+ }
139
+ return null;
140
+ },
141
+ });
142
+ await this.invalidateQueries({
143
+ queryKey: [`${this.name}:listDelegations`],
144
+ });
145
+ }
146
+ /**
147
+ * Returns the notification preferences for the authenticated user.
148
+ *
149
+ * @returns The notification preferences object, or `null` if none have been
150
+ * set (404).
151
+ */
152
+ async getNotificationPreferences() {
153
+ const url = `${getAuthenticatedStorageUrl(__classPrivateFieldGet(this, _AuthenticatedUserStorageService_environment, "f"))}/preferences/notifications`;
154
+ const data = await this.fetchQuery({
155
+ queryKey: [`${this.name}:getNotificationPreferences`],
156
+ queryFn: async () => {
157
+ const headers = await __classPrivateFieldGet(this, _AuthenticatedUserStorageService_instances, "m", _AuthenticatedUserStorageService_getHeaders).call(this);
158
+ const response = await fetch(url, { headers });
159
+ if (response.status === 404) {
160
+ return null;
161
+ }
162
+ if (!response.ok) {
163
+ throw new controller_utils_1.HttpError(response.status, `Failed to get notification preferences: ${response.status}`);
164
+ }
165
+ return response.json();
166
+ },
167
+ });
168
+ if (data === null) {
169
+ return null;
170
+ }
171
+ (0, validators_1.assertNotificationPreferences)(data);
172
+ return data;
173
+ }
174
+ /**
175
+ * Creates or updates the notification preferences for the authenticated user.
176
+ *
177
+ * @param prefs - The full notification preferences object.
178
+ * @param clientType - Optional client type header.
179
+ */
180
+ async putNotificationPreferences(prefs, clientType) {
181
+ const url = `${getAuthenticatedStorageUrl(__classPrivateFieldGet(this, _AuthenticatedUserStorageService_environment, "f"))}/preferences/notifications`;
182
+ await this.fetchQuery({
183
+ queryKey: [
184
+ `${this.name}:putNotificationPreferences`,
185
+ prefs,
186
+ ],
187
+ staleTime: 0,
188
+ queryFn: async () => {
189
+ const headers = await __classPrivateFieldGet(this, _AuthenticatedUserStorageService_instances, "m", _AuthenticatedUserStorageService_getHeaders).call(this, clientType);
190
+ const response = await fetch(url, {
191
+ method: 'PUT',
192
+ headers,
193
+ body: JSON.stringify(prefs),
194
+ });
195
+ if (!response.ok) {
196
+ throw new controller_utils_1.HttpError(response.status, `Failed to put notification preferences: ${response.status}`);
197
+ }
198
+ return null;
199
+ },
200
+ });
201
+ await this.invalidateQueries({
202
+ queryKey: [`${this.name}:getNotificationPreferences`],
203
+ });
204
+ }
205
+ }
206
+ exports.AuthenticatedUserStorageService = AuthenticatedUserStorageService;
207
+ _AuthenticatedUserStorageService_environment = new WeakMap(), _AuthenticatedUserStorageService_instances = new WeakSet(), _AuthenticatedUserStorageService_getHeaders = async function _AuthenticatedUserStorageService_getHeaders(clientType) {
208
+ const accessToken = await this.messenger.call('AuthenticationController:getBearerToken');
209
+ const headers = {
210
+ 'Content-Type': 'application/json',
211
+ Authorization: `Bearer ${accessToken}`,
212
+ };
213
+ if (clientType) {
214
+ headers['X-Client-Type'] = clientType;
215
+ }
216
+ return headers;
217
+ };
218
+ //# sourceMappingURL=authenticated-user-storage.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticated-user-storage.cjs","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"]}