@oxyhq/core 3.18.1 → 4.0.1

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/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/OxyServices.js +3 -2
  3. package/dist/cjs/i18n/locales/en-US.json +391 -17
  4. package/dist/cjs/i18n/locales/es-ES.json +391 -17
  5. package/dist/cjs/i18n/locales/locales/en-US.json +391 -17
  6. package/dist/cjs/i18n/locales/locales/es-ES.json +391 -17
  7. package/dist/cjs/mixins/OxyServices.accounts.js +480 -0
  8. package/dist/cjs/mixins/OxyServices.connectedApps.js +73 -0
  9. package/dist/cjs/mixins/OxyServices.utility.js +3 -2
  10. package/dist/cjs/mixins/index.js +9 -6
  11. package/dist/esm/.tsbuildinfo +1 -1
  12. package/dist/esm/OxyServices.js +3 -2
  13. package/dist/esm/i18n/locales/en-US.json +391 -17
  14. package/dist/esm/i18n/locales/es-ES.json +391 -17
  15. package/dist/esm/i18n/locales/locales/en-US.json +391 -17
  16. package/dist/esm/i18n/locales/locales/es-ES.json +391 -17
  17. package/dist/esm/mixins/OxyServices.accounts.js +477 -0
  18. package/dist/esm/mixins/OxyServices.connectedApps.js +70 -0
  19. package/dist/esm/mixins/OxyServices.utility.js +3 -2
  20. package/dist/esm/mixins/index.js +9 -6
  21. package/dist/types/.tsbuildinfo +1 -1
  22. package/dist/types/OxyServices.d.ts +3 -2
  23. package/dist/types/index.d.ts +2 -3
  24. package/dist/types/mixins/OxyServices.accounts.d.ts +642 -0
  25. package/dist/types/mixins/OxyServices.auth.d.ts +1 -1
  26. package/dist/types/mixins/OxyServices.connectedApps.d.ts +168 -0
  27. package/dist/types/mixins/OxyServices.utility.d.ts +6 -3
  28. package/dist/types/mixins/index.d.ts +3 -4
  29. package/package.json +1 -1
  30. package/src/OxyServices.ts +3 -2
  31. package/src/i18n/locales/en-US.json +391 -17
  32. package/src/i18n/locales/es-ES.json +391 -17
  33. package/src/index.ts +33 -34
  34. package/src/mixins/OxyServices.accounts.ts +1079 -0
  35. package/src/mixins/OxyServices.auth.ts +1 -1
  36. package/src/mixins/OxyServices.connectedApps.ts +165 -0
  37. package/src/mixins/OxyServices.utility.ts +7 -4
  38. package/src/mixins/__tests__/accounts.test.ts +667 -0
  39. package/src/mixins/__tests__/connectedApps.test.ts +1 -1
  40. package/src/mixins/index.ts +11 -9
  41. package/dist/cjs/mixins/OxyServices.applications.js +0 -350
  42. package/dist/cjs/mixins/OxyServices.managedAccounts.js +0 -143
  43. package/dist/cjs/mixins/OxyServices.workspaces.js +0 -181
  44. package/dist/esm/mixins/OxyServices.applications.js +0 -347
  45. package/dist/esm/mixins/OxyServices.managedAccounts.js +0 -140
  46. package/dist/esm/mixins/OxyServices.workspaces.js +0 -178
  47. package/dist/types/mixins/OxyServices.applications.d.ts +0 -496
  48. package/dist/types/mixins/OxyServices.managedAccounts.d.ts +0 -145
  49. package/dist/types/mixins/OxyServices.workspaces.d.ts +0 -219
  50. package/src/mixins/OxyServices.applications.ts +0 -773
  51. package/src/mixins/OxyServices.managedAccounts.ts +0 -173
  52. package/src/mixins/OxyServices.workspaces.ts +0 -351
@@ -18,8 +18,8 @@ import { OxyServicesLanguageMixin } from './OxyServices.language';
18
18
  import { OxyServicesPaymentMixin } from './OxyServices.payment';
19
19
  import { OxyServicesReputationMixin } from './OxyServices.reputation';
20
20
  import { OxyServicesAssetsMixin } from './OxyServices.assets';
21
- import { OxyServicesApplicationsMixin } from './OxyServices.applications';
22
- import { OxyServicesWorkspacesMixin } from './OxyServices.workspaces';
21
+ import { OxyServicesAccountsMixin } from './OxyServices.accounts';
22
+ import { OxyServicesConnectedAppsMixin } from './OxyServices.connectedApps';
23
23
  import { OxyServicesLocationMixin } from './OxyServices.location';
24
24
  import { OxyServicesAnalyticsMixin } from './OxyServices.analytics';
25
25
  import { OxyServicesDevicesMixin } from './OxyServices.devices';
@@ -27,7 +27,6 @@ import { OxyServicesSecurityMixin } from './OxyServices.security';
27
27
  import { OxyServicesUtilityMixin } from './OxyServices.utility';
28
28
  import { OxyServicesFeaturesMixin } from './OxyServices.features';
29
29
  import { OxyServicesTopicsMixin } from './OxyServices.topics';
30
- import { OxyServicesManagedAccountsMixin } from './OxyServices.managedAccounts';
31
30
  import { OxyServicesContactsMixin } from './OxyServices.contacts';
32
31
  import { OxyServicesAppDataMixin } from './OxyServices.appData';
33
32
  import { OxyServicesCivicMixin } from './OxyServices.civic';
@@ -56,15 +55,14 @@ type AllMixinInstances =
56
55
  & InstanceType<ReturnType<typeof OxyServicesPaymentMixin<typeof OxyServicesBase>>>
57
56
  & InstanceType<ReturnType<typeof OxyServicesReputationMixin<typeof OxyServicesBase>>>
58
57
  & InstanceType<ReturnType<typeof OxyServicesAssetsMixin<typeof OxyServicesBase>>>
59
- & InstanceType<ReturnType<typeof OxyServicesApplicationsMixin<typeof OxyServicesBase>>>
60
- & InstanceType<ReturnType<typeof OxyServicesWorkspacesMixin<typeof OxyServicesBase>>>
58
+ & InstanceType<ReturnType<typeof OxyServicesAccountsMixin<typeof OxyServicesBase>>>
59
+ & InstanceType<ReturnType<typeof OxyServicesConnectedAppsMixin<typeof OxyServicesBase>>>
61
60
  & InstanceType<ReturnType<typeof OxyServicesLocationMixin<typeof OxyServicesBase>>>
62
61
  & InstanceType<ReturnType<typeof OxyServicesAnalyticsMixin<typeof OxyServicesBase>>>
63
62
  & InstanceType<ReturnType<typeof OxyServicesDevicesMixin<typeof OxyServicesBase>>>
64
63
  & InstanceType<ReturnType<typeof OxyServicesSecurityMixin<typeof OxyServicesBase>>>
65
64
  & InstanceType<ReturnType<typeof OxyServicesFeaturesMixin<typeof OxyServicesBase>>>
66
65
  & InstanceType<ReturnType<typeof OxyServicesTopicsMixin<typeof OxyServicesBase>>>
67
- & InstanceType<ReturnType<typeof OxyServicesManagedAccountsMixin<typeof OxyServicesBase>>>
68
66
  & InstanceType<ReturnType<typeof OxyServicesContactsMixin<typeof OxyServicesBase>>>
69
67
  & InstanceType<ReturnType<typeof OxyServicesAppDataMixin<typeof OxyServicesBase>>>
70
68
  & InstanceType<ReturnType<typeof OxyServicesCivicMixin<typeof OxyServicesBase>>>
@@ -125,15 +123,19 @@ const MIXIN_PIPELINE: MixinFunction[] = [
125
123
  OxyServicesPaymentMixin,
126
124
  OxyServicesReputationMixin,
127
125
  OxyServicesAssetsMixin,
128
- OxyServicesApplicationsMixin,
129
- OxyServicesWorkspacesMixin,
126
+ // Unified account graph + the applications owned within it. The clean-cut
127
+ // replacement for the former managedAccounts + workspaces + applications
128
+ // (account-management) mixins.
129
+ OxyServicesAccountsMixin,
130
+ // OAuth-consent surface (public app identity + connected-app grants). Kept
131
+ // separate from account ownership.
132
+ OxyServicesConnectedAppsMixin,
130
133
  OxyServicesLocationMixin,
131
134
  OxyServicesAnalyticsMixin,
132
135
  OxyServicesDevicesMixin,
133
136
  OxyServicesSecurityMixin,
134
137
  OxyServicesFeaturesMixin,
135
138
  OxyServicesTopicsMixin,
136
- OxyServicesManagedAccountsMixin,
137
139
  OxyServicesContactsMixin,
138
140
  OxyServicesAppDataMixin,
139
141
  // Civic / Commons "Oxy ID" (public signed cards, Oxy ID QR payload)
@@ -1,350 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OxyServicesApplicationsMixin = OxyServicesApplicationsMixin;
4
- const mixinHelpers_1 = require("./mixinHelpers");
5
- function OxyServicesApplicationsMixin(Base) {
6
- return class extends Base {
7
- constructor(...args) {
8
- super(...args);
9
- }
10
- /**
11
- * Resolve an OAuth client identifier to the owning application's PUBLIC
12
- * identity. No authentication required — the API returns only sanitized,
13
- * display-safe metadata ({@link PublicApplication}). Use this to render the
14
- * requesting application's name/icon in consent, authorize, and device-flow
15
- * approval UIs before any session exists.
16
- *
17
- * @param clientId - The OAuth `client_id` (an active credential's public
18
- * key). URL-encoded before being placed in the path.
19
- */
20
- async getPublicApplication(clientId) {
21
- try {
22
- const res = await this.makeRequest('GET', `/auth/oauth/client/${encodeURIComponent(clientId)}`, undefined, { cache: true, cacheTTL: mixinHelpers_1.CACHE_TIMES.MEDIUM });
23
- return res.application;
24
- }
25
- catch (error) {
26
- throw this.handleError(error);
27
- }
28
- }
29
- /**
30
- * List the OAuth-authorized applications the current user has connected —
31
- * the third-party apps the user granted access to via the consent flow.
32
- * Each entry is a {@link ConnectedApp} carrying the application's display
33
- * identity, the granted scopes, and when the grant was first made and last
34
- * exercised. Requires an authenticated session.
35
- *
36
- * Backed by `GET /auth/grants`. The response is briefly cached
37
- * (identity-scoped); {@link revokeAppGrant} busts that cache so a revoke is
38
- * reflected on the next read.
39
- */
40
- async listConnectedApps() {
41
- try {
42
- return await this.makeRequest('GET', '/auth/grants', undefined, { cache: true, cacheTTL: mixinHelpers_1.CACHE_TIMES.SHORT });
43
- }
44
- catch (error) {
45
- throw this.handleError(error);
46
- }
47
- }
48
- /**
49
- * Revoke the current user's grant for a connected application, identified by
50
- * its application `_id` (a {@link ConnectedApp.applicationId}, NOT a
51
- * credential/client id — keyed by application so the revocation survives
52
- * credential rotation). After this the application can no longer act on the
53
- * user's behalf until it is re-authorized.
54
- *
55
- * Backed by `DELETE /auth/grants/:applicationId`. On success the cached
56
- * connected-apps list (`GET:/auth/grants`) is invalidated so the next
57
- * {@link listConnectedApps} read reflects the removal.
58
- *
59
- * @param applicationId - The connected application's Mongo `_id`.
60
- */
61
- async revokeAppGrant(applicationId) {
62
- try {
63
- await this.makeRequest('DELETE', `/auth/grants/${applicationId}`, undefined, { cache: false });
64
- // A revoke removes an entry from the user's connected-apps list; bust
65
- // the cached `GET /auth/grants` so the next read re-fetches.
66
- this.clearCacheEntry('GET:/auth/grants');
67
- }
68
- catch (error) {
69
- throw this.handleError(error);
70
- }
71
- }
72
- /**
73
- * List applications the current user is an active member of.
74
- *
75
- * @param workspaceId - Optional workspace `_id` to scope the listing to
76
- * applications belonging to that workspace. When provided it is appended
77
- * as a `workspaceId` query parameter (URL-encoded). The query string is
78
- * part of the request path, so the response cache keys on it
79
- * automatically — scoped and unscoped lists never collide.
80
- */
81
- async getApplications(workspaceId) {
82
- try {
83
- const path = workspaceId
84
- ? `/applications?workspaceId=${encodeURIComponent(workspaceId)}`
85
- : '/applications';
86
- const res = await this.makeRequest('GET', path, undefined, { cache: true, cacheTTL: mixinHelpers_1.CACHE_TIMES.MEDIUM });
87
- return res.applications ?? [];
88
- }
89
- catch (error) {
90
- throw this.handleError(error);
91
- }
92
- }
93
- /**
94
- * Create a new application. The caller becomes its `owner`.
95
- * @param data - Application configuration. Staff-only fields are ignored.
96
- */
97
- async createApplication(data) {
98
- try {
99
- const res = await this.makeRequest('POST', '/applications', data, { cache: false });
100
- // Bust every cached application list (unscoped + per-workspace) so the
101
- // new application appears on the next `getApplications()` read.
102
- this._invalidateApplicationLists();
103
- return res.application;
104
- }
105
- catch (error) {
106
- throw this.handleError(error);
107
- }
108
- }
109
- /**
110
- * Fetch a single application by id.
111
- * @param applicationId - The application's Mongo `_id`.
112
- */
113
- async getApplication(applicationId) {
114
- try {
115
- const res = await this.makeRequest('GET', `/applications/${applicationId}`, undefined, { cache: true, cacheTTL: mixinHelpers_1.CACHE_TIMES.LONG });
116
- return res.application;
117
- }
118
- catch (error) {
119
- throw this.handleError(error);
120
- }
121
- }
122
- /**
123
- * Update an application's mutable fields.
124
- * @param applicationId - The application's Mongo `_id`.
125
- * @param data - Subset of updatable fields. Staff-only fields are ignored.
126
- */
127
- async updateApplication(applicationId, data) {
128
- try {
129
- const res = await this.makeRequest('PATCH', `/applications/${applicationId}`, data, { cache: false });
130
- // Bust the cached detail and every list (which embeds application
131
- // fields) so neither serves the pre-update snapshot.
132
- this.clearCacheEntry(`GET:/applications/${applicationId}`);
133
- this._invalidateApplicationLists();
134
- return res.application;
135
- }
136
- catch (error) {
137
- throw this.handleError(error);
138
- }
139
- }
140
- /**
141
- * Soft-delete an application (owner only).
142
- * @param applicationId - The application's Mongo `_id`.
143
- */
144
- async deleteApplication(applicationId) {
145
- try {
146
- const result = await this.makeRequest('DELETE', `/applications/${applicationId}`, undefined, { cache: false });
147
- // Bust every cached representation of the deleted application.
148
- this.clearCacheEntry(`GET:/applications/${applicationId}`);
149
- this.clearCacheEntry(`GET:/applications/${applicationId}/members`);
150
- this.clearCacheEntry(`GET:/applications/${applicationId}/credentials`);
151
- this._invalidateApplicationLists();
152
- return result;
153
- }
154
- catch (error) {
155
- throw this.handleError(error);
156
- }
157
- }
158
- /**
159
- * List members of an application.
160
- * @param applicationId - The application's Mongo `_id`.
161
- */
162
- async getApplicationMembers(applicationId) {
163
- try {
164
- const res = await this.makeRequest('GET', `/applications/${applicationId}/members`, undefined, { cache: true, cacheTTL: mixinHelpers_1.CACHE_TIMES.MEDIUM });
165
- return res.members ?? [];
166
- }
167
- catch (error) {
168
- throw this.handleError(error);
169
- }
170
- }
171
- /**
172
- * Add a member to an application.
173
- * @param applicationId - The application's Mongo `_id`.
174
- * @param data - Target user's username or email and role (never `owner`).
175
- * The server resolves `usernameOrEmail` to a user; an unknown value yields
176
- * a 404 "User not found".
177
- */
178
- async inviteApplicationMember(applicationId, data) {
179
- try {
180
- const res = await this.makeRequest('POST', `/applications/${applicationId}/members`, data, { cache: false });
181
- this._invalidateApplicationMembership(applicationId);
182
- return res.member;
183
- }
184
- catch (error) {
185
- throw this.handleError(error);
186
- }
187
- }
188
- /**
189
- * Change a member's role.
190
- * @param applicationId - The application's Mongo `_id`.
191
- * @param memberId - The member's Mongo `_id`.
192
- * @param data - New role.
193
- */
194
- async updateApplicationMember(applicationId, memberId, data) {
195
- try {
196
- const res = await this.makeRequest('PATCH', `/applications/${applicationId}/members/${memberId}`, data, { cache: false });
197
- this._invalidateApplicationMembership(applicationId);
198
- return res.member;
199
- }
200
- catch (error) {
201
- throw this.handleError(error);
202
- }
203
- }
204
- /**
205
- * Remove a member from an application.
206
- * @param applicationId - The application's Mongo `_id`.
207
- * @param memberId - The member's Mongo `_id`.
208
- */
209
- async removeApplicationMember(applicationId, memberId) {
210
- try {
211
- const result = await this.makeRequest('DELETE', `/applications/${applicationId}/members/${memberId}`, undefined, { cache: false });
212
- this._invalidateApplicationMembership(applicationId);
213
- return result;
214
- }
215
- catch (error) {
216
- throw this.handleError(error);
217
- }
218
- }
219
- /**
220
- * Transfer ownership of an application to another member (owner only).
221
- * Demotes the current owner to `admin` and promotes the target to `owner`.
222
- * @param applicationId - The application's Mongo `_id`.
223
- * @param data - Target user id.
224
- */
225
- async transferApplicationOwnership(applicationId, data) {
226
- try {
227
- const result = await this.makeRequest('POST', `/applications/${applicationId}/transfer-ownership`, data, { cache: false });
228
- // Ownership change alters roles in the member list AND the detail, and
229
- // can change which applications the caller "owns" in the list view.
230
- this._invalidateApplicationMembership(applicationId);
231
- this._invalidateApplicationLists();
232
- return result;
233
- }
234
- catch (error) {
235
- throw this.handleError(error);
236
- }
237
- }
238
- /**
239
- * List an application's credentials. The response NEVER includes secrets.
240
- * @param applicationId - The application's Mongo `_id`.
241
- */
242
- async getApplicationCredentials(applicationId) {
243
- try {
244
- const res = await this.makeRequest('GET', `/applications/${applicationId}/credentials`, undefined, { cache: true, cacheTTL: mixinHelpers_1.CACHE_TIMES.MEDIUM });
245
- return res.credentials ?? [];
246
- }
247
- catch (error) {
248
- throw this.handleError(error);
249
- }
250
- }
251
- /**
252
- * Create a credential. The plaintext `secret` is returned exactly ONCE;
253
- * the server stores only a hash and will never return it again.
254
- * @param applicationId - The application's Mongo `_id`.
255
- * @param data - Credential configuration.
256
- */
257
- async createApplicationCredential(applicationId, data) {
258
- try {
259
- const result = await this.makeRequest('POST', `/applications/${applicationId}/credentials`, data, { cache: false });
260
- this.clearCacheEntry(`GET:/applications/${applicationId}/credentials`);
261
- return result;
262
- }
263
- catch (error) {
264
- throw this.handleError(error);
265
- }
266
- }
267
- /**
268
- * Rotate a credential's secret. The new plaintext `secret` is returned
269
- * exactly ONCE, along with audit fields: `rotatedFrom` (the previous
270
- * credentialId) and `graceExpiresAt` (ISO string for the grace window during
271
- * which the old credential is still honoured).
272
- * @param applicationId - The application's Mongo `_id`.
273
- * @param credentialId - The credential's Mongo `_id`.
274
- */
275
- async rotateApplicationCredential(applicationId, credentialId) {
276
- try {
277
- const result = await this.makeRequest('POST', `/applications/${applicationId}/credentials/${credentialId}/rotate`, undefined, { cache: false });
278
- // Rotation changes credential status/audit fields surfaced by the
279
- // credentials list (`rotatedFrom`, grace window, new active credential).
280
- this.clearCacheEntry(`GET:/applications/${applicationId}/credentials`);
281
- return result;
282
- }
283
- catch (error) {
284
- throw this.handleError(error);
285
- }
286
- }
287
- /**
288
- * Revoke a credential (`status='revoked'`). Revoked credentials can no
289
- * longer authenticate.
290
- * @param applicationId - The application's Mongo `_id`.
291
- * @param credentialId - The credential's Mongo `_id`.
292
- */
293
- async revokeApplicationCredential(applicationId, credentialId) {
294
- try {
295
- const result = await this.makeRequest('DELETE', `/applications/${applicationId}/credentials/${credentialId}`, undefined, { cache: false });
296
- // Revocation flips the credential's status in the cached list.
297
- this.clearCacheEntry(`GET:/applications/${applicationId}/credentials`);
298
- return result;
299
- }
300
- catch (error) {
301
- throw this.handleError(error);
302
- }
303
- }
304
- /**
305
- * Fetch usage statistics for an application.
306
- * @param applicationId - The application's Mongo `_id`.
307
- * @param period - Time window (defaults to the server default).
308
- */
309
- async getApplicationUsage(applicationId, period) {
310
- try {
311
- return await this.makeRequest('GET', `/applications/${applicationId}/usage`, period ? { period } : undefined, { cache: true, cacheTTL: mixinHelpers_1.CACHE_TIMES.SHORT });
312
- }
313
- catch (error) {
314
- throw this.handleError(error);
315
- }
316
- }
317
- /**
318
- * Bust every cached application list. `getApplications(workspaceId?)` keys
319
- * the unscoped list as `GET:/applications` and each workspace-scoped list as
320
- * `GET:/applications?workspaceId=<id>` (the query string is part of the URL
321
- * path). A change to list membership (create/delete/ownership transfer)
322
- * invalidates all of them, so we clear the unscoped entry plus every
323
- * `?workspaceId=` variant via a prefix sweep. The prefix `GET:/applications?`
324
- * matches only the query-string list variants, never the `GET:/applications/<id>…`
325
- * detail/sub-resource keys.
326
- *
327
- * Internal helper (leading underscore); not part of the supported public
328
- * surface. Public rather than `private` because mixins compose into an
329
- * exported anonymous class, where TypeScript cannot represent a private
330
- * member in the emitted declaration file (TS4094).
331
- */
332
- _invalidateApplicationLists() {
333
- this.clearCacheEntry('GET:/applications');
334
- this.clearCacheByPrefix('GET:/applications?');
335
- }
336
- /**
337
- * Bust the cached member list and detail for an application after a
338
- * membership mutation. The member list (`getApplicationMembers`) and the
339
- * detail (`getApplication`, which can embed member counts) both go stale
340
- * when the member set or a member's role changes.
341
- *
342
- * Internal helper (leading underscore); see `_invalidateApplicationLists`
343
- * for why this is public rather than `private`.
344
- */
345
- _invalidateApplicationMembership(applicationId) {
346
- this.clearCacheEntry(`GET:/applications/${applicationId}/members`);
347
- this.clearCacheEntry(`GET:/applications/${applicationId}`);
348
- }
349
- };
350
- }
@@ -1,143 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OxyServicesManagedAccountsMixin = OxyServicesManagedAccountsMixin;
4
- function OxyServicesManagedAccountsMixin(Base) {
5
- return class extends Base {
6
- constructor(...args) {
7
- super(...args);
8
- }
9
- /**
10
- * Create a new managed account (sub-account).
11
- *
12
- * The server creates a User document with `isManagedAccount: true` and links
13
- * it to the authenticated user as owner. Invalidates the cached
14
- * `GET /managed-accounts` list (~2-minute TTL, identity-scoped) so the next
15
- * read includes the newly created account.
16
- */
17
- async createManagedAccount(data) {
18
- try {
19
- const result = await this.makeRequest('POST', '/managed-accounts', data, {
20
- cache: false,
21
- });
22
- this.clearCacheEntry('GET:/managed-accounts');
23
- return result;
24
- }
25
- catch (error) {
26
- throw this.handleError(error);
27
- }
28
- }
29
- /**
30
- * List all accounts the authenticated user manages.
31
- */
32
- async getManagedAccounts() {
33
- try {
34
- return await this.makeRequest('GET', '/managed-accounts', undefined, {
35
- cache: true,
36
- cacheTTL: 2 * 60 * 1000, // 2 minutes cache
37
- });
38
- }
39
- catch (error) {
40
- throw this.handleError(error);
41
- }
42
- }
43
- /**
44
- * Get details for a specific managed account.
45
- */
46
- async getManagedAccountDetails(accountId) {
47
- try {
48
- return await this.makeRequest('GET', `/managed-accounts/${accountId}`, undefined, {
49
- cache: true,
50
- cacheTTL: 2 * 60 * 1000,
51
- });
52
- }
53
- catch (error) {
54
- throw this.handleError(error);
55
- }
56
- }
57
- /**
58
- * Update a managed account's profile data.
59
- * Requires owner or admin role.
60
- *
61
- * Invalidates both the cached detail (`GET /managed-accounts/<id>`) and the
62
- * cached list (`GET /managed-accounts`, which embeds account profile data)
63
- * so neither serves the pre-update snapshot within their ~2-minute TTL.
64
- */
65
- async updateManagedAccount(accountId, data) {
66
- try {
67
- const result = await this.makeRequest('PUT', `/managed-accounts/${accountId}`, data, {
68
- cache: false,
69
- });
70
- this.clearCacheEntry(`GET:/managed-accounts/${accountId}`);
71
- this.clearCacheEntry('GET:/managed-accounts');
72
- return result;
73
- }
74
- catch (error) {
75
- throw this.handleError(error);
76
- }
77
- }
78
- /**
79
- * Delete a managed account permanently.
80
- * Requires owner role.
81
- *
82
- * Invalidates the cached detail and list responses so the deleted account
83
- * is not served from cache.
84
- */
85
- async deleteManagedAccount(accountId) {
86
- try {
87
- await this.makeRequest('DELETE', `/managed-accounts/${accountId}`, undefined, {
88
- cache: false,
89
- });
90
- this.clearCacheEntry(`GET:/managed-accounts/${accountId}`);
91
- this.clearCacheEntry('GET:/managed-accounts');
92
- }
93
- catch (error) {
94
- throw this.handleError(error);
95
- }
96
- }
97
- /**
98
- * Add a manager to a managed account.
99
- * Requires owner or admin role on the account.
100
- *
101
- * Mutates the account's `managers[]`, which is returned by the detail and
102
- * list reads — invalidate both so they re-fetch the updated manager set.
103
- *
104
- * @param accountId - The managed account to add the manager to
105
- * @param userId - The user to grant management access
106
- * @param role - The role to assign: 'admin' or 'editor'
107
- */
108
- async addManager(accountId, userId, role) {
109
- try {
110
- await this.makeRequest('POST', `/managed-accounts/${accountId}/managers`, { userId, role }, {
111
- cache: false,
112
- });
113
- this.clearCacheEntry(`GET:/managed-accounts/${accountId}`);
114
- this.clearCacheEntry('GET:/managed-accounts');
115
- }
116
- catch (error) {
117
- throw this.handleError(error);
118
- }
119
- }
120
- /**
121
- * Remove a manager from a managed account.
122
- * Requires owner role.
123
- *
124
- * Invalidates the detail and list responses so the updated `managers[]`
125
- * is observed on the next read (see `addManager`).
126
- *
127
- * @param accountId - The managed account
128
- * @param userId - The manager to remove
129
- */
130
- async removeManager(accountId, userId) {
131
- try {
132
- await this.makeRequest('DELETE', `/managed-accounts/${accountId}/managers/${userId}`, undefined, {
133
- cache: false,
134
- });
135
- this.clearCacheEntry(`GET:/managed-accounts/${accountId}`);
136
- this.clearCacheEntry('GET:/managed-accounts');
137
- }
138
- catch (error) {
139
- throw this.handleError(error);
140
- }
141
- }
142
- };
143
- }