@oxyhq/core 1.11.10 → 1.11.12

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 (63) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/HttpService.js +26 -18
  3. package/dist/cjs/OxyServices.base.js +21 -0
  4. package/dist/cjs/crypto/signatureService.js +11 -11
  5. package/dist/cjs/mixins/OxyServices.managedAccounts.js +117 -0
  6. package/dist/cjs/mixins/OxyServices.user.js +11 -0
  7. package/dist/cjs/mixins/OxyServices.utility.js +81 -2
  8. package/dist/cjs/mixins/index.js +2 -0
  9. package/dist/cjs/utils/asyncUtils.js +34 -5
  10. package/dist/esm/.tsbuildinfo +1 -1
  11. package/dist/esm/HttpService.js +26 -18
  12. package/dist/esm/OxyServices.base.js +21 -0
  13. package/dist/esm/crypto/keyManager.js +3 -3
  14. package/dist/esm/crypto/polyfill.js +1 -1
  15. package/dist/esm/crypto/signatureService.js +12 -12
  16. package/dist/esm/mixins/OxyServices.language.js +1 -1
  17. package/dist/esm/mixins/OxyServices.managedAccounts.js +114 -0
  18. package/dist/esm/mixins/OxyServices.user.js +11 -0
  19. package/dist/esm/mixins/OxyServices.utility.js +81 -2
  20. package/dist/esm/mixins/index.js +2 -0
  21. package/dist/esm/utils/asyncUtils.js +34 -5
  22. package/dist/esm/utils/deviceManager.js +1 -1
  23. package/dist/types/.tsbuildinfo +1 -1
  24. package/dist/types/HttpService.d.ts +3 -0
  25. package/dist/types/OxyServices.base.d.ts +17 -0
  26. package/dist/types/index.d.ts +1 -0
  27. package/dist/types/mixins/OxyServices.analytics.d.ts +2 -0
  28. package/dist/types/mixins/OxyServices.assets.d.ts +2 -0
  29. package/dist/types/mixins/OxyServices.auth.d.ts +2 -0
  30. package/dist/types/mixins/OxyServices.developer.d.ts +2 -0
  31. package/dist/types/mixins/OxyServices.devices.d.ts +2 -0
  32. package/dist/types/mixins/OxyServices.features.d.ts +5 -1
  33. package/dist/types/mixins/OxyServices.fedcm.d.ts +2 -0
  34. package/dist/types/mixins/OxyServices.karma.d.ts +2 -0
  35. package/dist/types/mixins/OxyServices.language.d.ts +2 -0
  36. package/dist/types/mixins/OxyServices.location.d.ts +2 -0
  37. package/dist/types/mixins/OxyServices.managedAccounts.d.ts +125 -0
  38. package/dist/types/mixins/OxyServices.payment.d.ts +2 -0
  39. package/dist/types/mixins/OxyServices.popup.d.ts +2 -0
  40. package/dist/types/mixins/OxyServices.privacy.d.ts +2 -0
  41. package/dist/types/mixins/OxyServices.redirect.d.ts +2 -0
  42. package/dist/types/mixins/OxyServices.security.d.ts +2 -0
  43. package/dist/types/mixins/OxyServices.topics.d.ts +2 -0
  44. package/dist/types/mixins/OxyServices.user.d.ts +14 -0
  45. package/dist/types/mixins/OxyServices.utility.d.ts +22 -0
  46. package/dist/types/models/interfaces.d.ts +2 -0
  47. package/dist/types/utils/asyncUtils.d.ts +6 -2
  48. package/package.json +1 -1
  49. package/src/HttpService.ts +30 -11
  50. package/src/OxyServices.base.ts +23 -0
  51. package/src/crypto/keyManager.ts +3 -3
  52. package/src/crypto/polyfill.ts +1 -1
  53. package/src/crypto/signatureService.ts +13 -12
  54. package/src/index.ts +1 -0
  55. package/src/mixins/OxyServices.language.ts +1 -1
  56. package/src/mixins/OxyServices.managedAccounts.ts +147 -0
  57. package/src/mixins/OxyServices.user.ts +18 -0
  58. package/src/mixins/OxyServices.utility.ts +103 -2
  59. package/src/mixins/index.ts +2 -0
  60. package/src/models/interfaces.ts +3 -0
  61. package/src/utils/__tests__/asyncUtils.test.ts +187 -0
  62. package/src/utils/asyncUtils.ts +39 -9
  63. package/src/utils/deviceManager.ts +1 -1
@@ -50,6 +50,7 @@ export declare class HttpService {
50
50
  private tokenRefreshPromise;
51
51
  private tokenRefreshCooldownUntil;
52
52
  private _onTokenRefreshed;
53
+ private _actingAsUserId;
53
54
  private requestMetrics;
54
55
  constructor(config: OxyConfig);
55
56
  /**
@@ -93,6 +94,8 @@ export declare class HttpService {
93
94
  put<T = unknown>(url: string, data?: unknown, config?: Omit<RequestConfig, 'method' | 'url' | 'data'>): Promise<T>;
94
95
  patch<T = unknown>(url: string, data?: unknown, config?: Omit<RequestConfig, 'method' | 'url' | 'data'>): Promise<T>;
95
96
  delete<T = unknown>(url: string, config?: Omit<RequestConfig, 'method' | 'url'>): Promise<T>;
97
+ setActingAs(userId: string | null): void;
98
+ getActingAs(): string | null;
96
99
  setTokens(accessToken: string, refreshToken?: string): void;
97
100
  set onTokenRefreshed(callback: ((accessToken: string) => void) | null);
98
101
  clearTokens(): void;
@@ -81,6 +81,23 @@ export declare class OxyServicesBase {
81
81
  * Get the raw access token (for constructing anchor URLs when needed)
82
82
  */
83
83
  getAccessToken(): string | null;
84
+ /**
85
+ * Set the acting-as identity for managed accounts.
86
+ *
87
+ * When set, all subsequent API requests will include the `X-Acting-As` header,
88
+ * causing the server to attribute actions to the managed account. The
89
+ * authenticated user must be an authorized manager of the target account.
90
+ *
91
+ * Pass `null` to clear and revert to the authenticated user's own identity.
92
+ *
93
+ * @param userId - The managed account user ID, or null to clear
94
+ */
95
+ setActingAs(userId: string | null): void;
96
+ /**
97
+ * Get the current acting-as identity (managed account user ID), or null
98
+ * if operating as the authenticated user's own identity.
99
+ */
100
+ getActingAs(): string | null;
84
101
  /**
85
102
  * Wait for authentication to be ready
86
103
  *
@@ -25,6 +25,7 @@ export type { PopupAuthOptions } from './mixins/OxyServices.popup';
25
25
  export type { RedirectAuthOptions } from './mixins/OxyServices.redirect';
26
26
  export type { ServiceTokenResponse } from './mixins/OxyServices.auth';
27
27
  export type { ServiceApp } from './mixins/OxyServices.utility';
28
+ export type { CreateManagedAccountInput, ManagedAccountManager, ManagedAccount } from './mixins/OxyServices.managedAccounts';
28
29
  export { KeyManager, SignatureService, RecoveryPhraseService } from './crypto';
29
30
  export type { KeyPair, SignedMessage, AuthChallenge, RecoveryPhraseResult } from './crypto';
30
31
  export * from './models/interfaces';
@@ -50,6 +50,8 @@ export declare function OxyServicesAnalyticsMixin<T extends typeof OxyServicesBa
50
50
  getCurrentUserId(): string | null;
51
51
  hasValidToken(): boolean;
52
52
  getAccessToken(): string | null;
53
+ setActingAs(userId: string | null): void;
54
+ getActingAs(): string | null;
53
55
  waitForAuth(timeoutMs?: number): Promise<boolean>;
54
56
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
55
57
  maxRetries?: number;
@@ -128,6 +128,8 @@ export declare function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>
128
128
  getCurrentUserId(): string | null;
129
129
  hasValidToken(): boolean;
130
130
  getAccessToken(): string | null;
131
+ setActingAs(userId: string | null): void;
132
+ getActingAs(): string | null;
131
133
  waitForAuth(timeoutMs?: number): Promise<boolean>;
132
134
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
133
135
  maxRetries?: number;
@@ -206,6 +206,8 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
206
206
  getCurrentUserId(): string | null;
207
207
  hasValidToken(): boolean;
208
208
  getAccessToken(): string | null;
209
+ setActingAs(userId: string | null): void;
210
+ getActingAs(): string | null;
209
211
  waitForAuth(timeoutMs?: number): Promise<boolean>;
210
212
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
211
213
  maxRetries?: number;
@@ -83,6 +83,8 @@ export declare function OxyServicesDeveloperMixin<T extends typeof OxyServicesBa
83
83
  getCurrentUserId(): string | null;
84
84
  hasValidToken(): boolean;
85
85
  getAccessToken(): string | null;
86
+ setActingAs(userId: string | null): void;
87
+ getActingAs(): string | null;
86
88
  waitForAuth(timeoutMs?: number): Promise<boolean>;
87
89
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
88
90
  maxRetries?: number;
@@ -80,6 +80,8 @@ export declare function OxyServicesDevicesMixin<T extends typeof OxyServicesBase
80
80
  getCurrentUserId(): string | null;
81
81
  hasValidToken(): boolean;
82
82
  getAccessToken(): string | null;
83
+ setActingAs(userId: string | null): void;
84
+ getActingAs(): string | null;
83
85
  waitForAuth(timeoutMs?: number): Promise<boolean>;
84
86
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
85
87
  maxRetries?: number;
@@ -212,6 +212,8 @@ export declare function OxyServicesFeaturesMixin<T extends typeof OxyServicesBas
212
212
  getCurrentUserId(): string | null;
213
213
  hasValidToken(): boolean;
214
214
  getAccessToken(): string | null;
215
+ setActingAs(userId: string | null): void;
216
+ getActingAs(): string | null;
215
217
  waitForAuth(timeoutMs?: number): Promise<boolean>;
216
218
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
217
219
  maxRetries?: number;
@@ -223,7 +225,9 @@ export declare function OxyServicesFeaturesMixin<T extends typeof OxyServicesBas
223
225
  healthCheck(): Promise<{
224
226
  status: string;
225
227
  users?: number;
226
- timestamp?: string;
228
+ timestamp? /**
229
+ * Get FAQs
230
+ */: string;
227
231
  [key: string]: any;
228
232
  }>;
229
233
  };
@@ -188,6 +188,8 @@ export declare function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(
188
188
  getCurrentUserId(): string | null;
189
189
  hasValidToken(): boolean;
190
190
  getAccessToken(): string | null;
191
+ setActingAs(userId: string | null): void;
192
+ getActingAs(): string | null;
191
193
  waitForAuth(timeoutMs?: number): Promise<boolean>;
192
194
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
193
195
  maxRetries?: number;
@@ -69,6 +69,8 @@ export declare function OxyServicesKarmaMixin<T extends typeof OxyServicesBase>(
69
69
  getCurrentUserId(): string | null;
70
70
  hasValidToken(): boolean;
71
71
  getAccessToken(): string | null;
72
+ setActingAs(userId: string | null): void;
73
+ getActingAs(): string | null;
72
74
  waitForAuth(timeoutMs?: number): Promise<boolean>;
73
75
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
74
76
  maxRetries?: number;
@@ -65,6 +65,8 @@ export declare function OxyServicesLanguageMixin<T extends typeof OxyServicesBas
65
65
  getCurrentUserId(): string | null;
66
66
  hasValidToken(): boolean;
67
67
  getAccessToken(): string | null;
68
+ setActingAs(userId: string | null): void;
69
+ getActingAs(): string | null;
68
70
  waitForAuth(timeoutMs?: number): Promise<boolean>;
69
71
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
70
72
  maxRetries?: number;
@@ -48,6 +48,8 @@ export declare function OxyServicesLocationMixin<T extends typeof OxyServicesBas
48
48
  getCurrentUserId(): string | null;
49
49
  hasValidToken(): boolean;
50
50
  getAccessToken(): string | null;
51
+ setActingAs(userId: string | null): void;
52
+ getActingAs(): string | null;
51
53
  waitForAuth(timeoutMs?: number): Promise<boolean>;
52
54
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
53
55
  maxRetries?: number;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Managed Accounts Methods Mixin
3
+ *
4
+ * Provides SDK methods for creating and managing sub-accounts (managed identities).
5
+ * Managed accounts are full User documents without passwords, accessible only
6
+ * by their owners/managers via the X-Acting-As header mechanism.
7
+ */
8
+ import type { User } from '../models/interfaces';
9
+ import type { OxyServicesBase } from '../OxyServices.base';
10
+ export interface CreateManagedAccountInput {
11
+ username: string;
12
+ name?: {
13
+ first?: string;
14
+ last?: string;
15
+ };
16
+ bio?: string;
17
+ avatar?: string;
18
+ }
19
+ export interface ManagedAccountManager {
20
+ userId: string;
21
+ role: 'owner' | 'admin' | 'editor';
22
+ addedAt: string;
23
+ addedBy?: string;
24
+ }
25
+ export interface ManagedAccount {
26
+ accountId: string;
27
+ ownerId: string;
28
+ managers: ManagedAccountManager[];
29
+ account?: User;
30
+ createdAt?: string;
31
+ updatedAt?: string;
32
+ }
33
+ export declare function OxyServicesManagedAccountsMixin<T extends typeof OxyServicesBase>(Base: T): {
34
+ new (...args: any[]): {
35
+ /**
36
+ * Create a new managed account (sub-account).
37
+ *
38
+ * The server creates a User document with `isManagedAccount: true` and links
39
+ * it to the authenticated user as owner.
40
+ */
41
+ createManagedAccount(data: CreateManagedAccountInput): Promise<ManagedAccount>;
42
+ /**
43
+ * List all accounts the authenticated user manages.
44
+ */
45
+ getManagedAccounts(): Promise<ManagedAccount[]>;
46
+ /**
47
+ * Get details for a specific managed account.
48
+ */
49
+ getManagedAccountDetails(accountId: string): Promise<ManagedAccount>;
50
+ /**
51
+ * Update a managed account's profile data.
52
+ * Requires owner or admin role.
53
+ */
54
+ updateManagedAccount(accountId: string, data: Partial<CreateManagedAccountInput>): Promise<ManagedAccount>;
55
+ /**
56
+ * Delete a managed account permanently.
57
+ * Requires owner role.
58
+ */
59
+ deleteManagedAccount(accountId: string): Promise<void>;
60
+ /**
61
+ * Add a manager to a managed account.
62
+ * Requires owner or admin role on the account.
63
+ *
64
+ * @param accountId - The managed account to add the manager to
65
+ * @param userId - The user to grant management access
66
+ * @param role - The role to assign: 'admin' or 'editor'
67
+ */
68
+ addManager(accountId: string, userId: string, role: "admin" | "editor"): Promise<void>;
69
+ /**
70
+ * Remove a manager from a managed account.
71
+ * Requires owner role.
72
+ *
73
+ * @param accountId - The managed account
74
+ * @param userId - The manager to remove
75
+ */
76
+ removeManager(accountId: string, userId: string): Promise<void>;
77
+ httpService: import("../HttpService").HttpService;
78
+ cloudURL: string;
79
+ config: import("../OxyServices.base").OxyConfig;
80
+ __resetTokensForTests(): void;
81
+ makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
82
+ getBaseURL(): string;
83
+ getClient(): import("../HttpService").HttpService;
84
+ getMetrics(): {
85
+ totalRequests: number;
86
+ successfulRequests: number;
87
+ failedRequests: number;
88
+ cacheHits: number;
89
+ cacheMisses: number;
90
+ averageResponseTime: number;
91
+ };
92
+ clearCache(): void;
93
+ clearCacheEntry(key: string): void;
94
+ getCacheStats(): {
95
+ size: number;
96
+ hits: number;
97
+ misses: number;
98
+ hitRate: number;
99
+ };
100
+ getCloudURL(): string;
101
+ setTokens(accessToken: string, refreshToken?: string): void;
102
+ clearTokens(): void;
103
+ _cachedUserId: string | null | undefined;
104
+ _cachedAccessToken: string | null;
105
+ getCurrentUserId(): string | null;
106
+ hasValidToken(): boolean;
107
+ getAccessToken(): string | null;
108
+ setActingAs(userId: string | null): void;
109
+ getActingAs(): string | null;
110
+ waitForAuth(timeoutMs?: number): Promise<boolean>;
111
+ withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
112
+ maxRetries?: number;
113
+ retryDelay?: number;
114
+ authTimeoutMs?: number;
115
+ }): Promise<T_1>;
116
+ validate(): Promise<boolean>;
117
+ handleError(error: unknown): Error;
118
+ healthCheck(): Promise<{
119
+ status: string;
120
+ users?: number;
121
+ timestamp?: string;
122
+ [key: string]: any;
123
+ }>;
124
+ };
125
+ } & T;
@@ -95,6 +95,8 @@ export declare function OxyServicesPaymentMixin<T extends typeof OxyServicesBase
95
95
  getCurrentUserId(): string | null;
96
96
  hasValidToken(): boolean;
97
97
  getAccessToken(): string | null;
98
+ setActingAs(userId: string | null): void;
99
+ getActingAs(): string | null;
98
100
  waitForAuth(timeoutMs?: number): Promise<boolean>;
99
101
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
100
102
  maxRetries?: number;
@@ -185,6 +185,8 @@ export declare function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBa
185
185
  getCurrentUserId(): string | null;
186
186
  hasValidToken(): boolean;
187
187
  getAccessToken(): string | null;
188
+ setActingAs(userId: string | null): void;
189
+ getActingAs(): string | null;
188
190
  waitForAuth(timeoutMs?: number): Promise<boolean>;
189
191
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
190
192
  maxRetries?: number;
@@ -106,6 +106,8 @@ export declare function OxyServicesPrivacyMixin<T extends typeof OxyServicesBase
106
106
  getCurrentUserId(): string | null;
107
107
  hasValidToken(): boolean;
108
108
  getAccessToken(): string | null;
109
+ setActingAs(userId: string | null): void;
110
+ getActingAs(): string | null;
109
111
  waitForAuth(timeoutMs?: number): Promise<boolean>;
110
112
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
111
113
  maxRetries?: number;
@@ -222,6 +222,8 @@ export declare function OxyServicesRedirectAuthMixin<T extends typeof OxyService
222
222
  getCurrentUserId(): string | null;
223
223
  hasValidToken(): boolean;
224
224
  getAccessToken(): string | null;
225
+ setActingAs(userId: string | null): void;
226
+ getActingAs(): string | null;
225
227
  waitForAuth(timeoutMs?: number): Promise<boolean>;
226
228
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
227
229
  maxRetries?: number;
@@ -62,6 +62,8 @@ export declare function OxyServicesSecurityMixin<T extends typeof OxyServicesBas
62
62
  getCurrentUserId(): string | null;
63
63
  hasValidToken(): boolean;
64
64
  getAccessToken(): string | null;
65
+ setActingAs(userId: string | null): void;
66
+ getActingAs(): string | null;
65
67
  waitForAuth(timeoutMs?: number): Promise<boolean>;
66
68
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
67
69
  maxRetries?: number;
@@ -88,6 +88,8 @@ export declare function OxyServicesTopicsMixin<T extends typeof OxyServicesBase>
88
88
  getCurrentUserId(): string | null;
89
89
  hasValidToken(): boolean;
90
90
  getAccessToken(): string | null;
91
+ setActingAs(userId: string | null): void;
92
+ getActingAs(): string | null;
91
93
  waitForAuth(timeoutMs?: number): Promise<boolean>;
92
94
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
93
95
  maxRetries?: number;
@@ -10,6 +10,18 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
10
10
  * Get profile by username
11
11
  */
12
12
  getProfileByUsername(username: string): Promise<User>;
13
+ /**
14
+ * Lightweight username lookup for login flows.
15
+ * Returns minimal public info: exists, color, avatar, displayName.
16
+ * Faster than getProfileByUsername — no stats, no formatting.
17
+ */
18
+ lookupUsername(username: string): Promise<{
19
+ exists: boolean;
20
+ username: string;
21
+ color: string | null;
22
+ avatar: string | null;
23
+ displayName: string;
24
+ }>;
13
25
  /**
14
26
  * Search user profiles
15
27
  */
@@ -205,6 +217,8 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
205
217
  getCurrentUserId(): string | null;
206
218
  hasValidToken(): boolean;
207
219
  getAccessToken(): string | null;
220
+ setActingAs(userId: string | null): void;
221
+ getActingAs(): string | null;
208
222
  waitForAuth(timeoutMs?: number): Promise<boolean>;
209
223
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
210
224
  maxRetries?: number;
@@ -1,5 +1,13 @@
1
1
  import type { ApiError } from '../models/interfaces';
2
2
  import type { OxyServicesBase } from '../OxyServices.base';
3
+ /**
4
+ * Result from the managed-accounts verification endpoint.
5
+ * Indicates whether a user is authorized to act as a given managed account.
6
+ */
7
+ interface ActingAsVerification {
8
+ authorized: boolean;
9
+ role: 'owner' | 'admin' | 'editor';
10
+ }
3
11
  /**
4
12
  * Service app metadata attached to requests authenticated with service tokens
5
13
  */
@@ -28,6 +36,18 @@ interface AuthMiddlewareOptions {
28
36
  }
29
37
  export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase>(Base: T): {
30
38
  new (...args: any[]): {
39
+ /** @internal In-memory cache for acting-as verification results (TTL: 5 min) */
40
+ _actingAsCache: Map<string, {
41
+ result: ActingAsVerification | null;
42
+ expiresAt: number;
43
+ }>;
44
+ /**
45
+ * Verify that a user is authorized to act as a managed account.
46
+ * Results are cached in-memory for 5 minutes to avoid repeated API calls.
47
+ *
48
+ * @internal Used by the auth() middleware — not part of the public API
49
+ */
50
+ verifyActingAs(userId: string, accountId: string): Promise<ActingAsVerification | null>;
31
51
  /**
32
52
  * Fetch link metadata
33
53
  */
@@ -157,6 +177,8 @@ export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase
157
177
  getCurrentUserId(): string | null;
158
178
  hasValidToken(): boolean;
159
179
  getAccessToken(): string | null;
180
+ setActingAs(userId: string | null): void;
181
+ getActingAs(): string | null;
160
182
  waitForAuth(timeoutMs?: number): Promise<boolean>;
161
183
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
162
184
  maxRetries?: number;
@@ -64,6 +64,8 @@ export interface User {
64
64
  automation?: {
65
65
  ownerId?: string;
66
66
  };
67
+ isManagedAccount?: boolean;
68
+ managedBy?: string;
67
69
  [key: string]: unknown;
68
70
  }
69
71
  export interface LoginResponse {
@@ -13,8 +13,12 @@ export declare function parallelWithErrorHandling<T>(operations: (() => Promise<
13
13
  /**
14
14
  * Retry an async operation with exponential backoff
15
15
  *
16
- * By default, does not retry on 4xx errors (client errors).
17
- * Use shouldRetry callback to customize retry behavior.
16
+ * By default, does not retry on 4xx errors (client errors). The default
17
+ * predicate accepts both the axios-style `error.response.status` and the
18
+ * flat `error.status` shape produced by {@link handleHttpError}, so callers
19
+ * never accidentally retry a deterministic client failure.
20
+ *
21
+ * Use the `shouldRetry` callback to customize retry behavior.
18
22
  */
19
23
  export declare function retryAsync<T>(operation: () => Promise<T>, maxRetries?: number, baseDelay?: number, shouldRetry?: (error: any) => boolean): Promise<T>;
20
24
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/core",
3
- "version": "1.11.10",
3
+ "version": "1.11.12",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -17,7 +17,6 @@ import { TTLCache, registerCacheForCleanup } from './utils/cache';
17
17
  import { RequestDeduplicator, RequestQueue, SimpleLogger } from './utils/requestUtils';
18
18
  import { retryAsync } from './utils/asyncUtils';
19
19
  import { handleHttpError } from './utils/errorUtils';
20
- import { isDev } from './shared/utils/debugUtils';
21
20
  import { jwtDecode } from 'jwt-decode';
22
21
  import { isNative, getPlatformOS } from './utils/platform';
23
22
  import type { OxyConfig } from './models/interfaces';
@@ -131,6 +130,9 @@ export class HttpService {
131
130
  private tokenRefreshCooldownUntil: number = 0;
132
131
  private _onTokenRefreshed: ((accessToken: string) => void) | null = null;
133
132
 
133
+ // Acting-as identity for managed accounts
134
+ private _actingAsUserId: string | null = null;
135
+
134
136
  // Performance monitoring
135
137
  private requestMetrics = {
136
138
  totalRequests: 0,
@@ -278,9 +280,12 @@ export class HttpService {
278
280
  headers['X-Native-App'] = 'true';
279
281
  }
280
282
 
281
- // Debug logging for CSRF issues
282
- if (isStateChangingMethod && isDev()) {
283
- console.log('[HttpService] CSRF Debug:', {
283
+ // Debug logging for CSRF issues — routed through the SimpleLogger so
284
+ // it only fires when consumers opt in via `enableLogging`. Previously
285
+ // this was a bare console.log that leaked noise into every host app's
286
+ // stdout in development.
287
+ if (isStateChangingMethod) {
288
+ this.logger.debug('CSRF Debug:', {
284
289
  url,
285
290
  method,
286
291
  isNativeApp,
@@ -291,6 +296,11 @@ export class HttpService {
291
296
  });
292
297
  }
293
298
 
299
+ // Add X-Acting-As header for managed account identity delegation
300
+ if (this._actingAsUserId) {
301
+ headers['X-Acting-As'] = this._actingAsUserId;
302
+ }
303
+
294
304
  // Merge custom headers if provided
295
305
  if (config.headers) {
296
306
  Object.entries(config.headers).forEach(([key, value]) => {
@@ -516,14 +526,14 @@ export class HttpService {
516
526
  // Return cached token if available
517
527
  const cachedToken = this.tokenStore.getCsrfToken();
518
528
  if (cachedToken) {
519
- if (isDev()) console.log('[HttpService] Using cached CSRF token');
529
+ this.logger.debug('Using cached CSRF token');
520
530
  return cachedToken;
521
531
  }
522
532
 
523
533
  // Deduplicate concurrent CSRF token fetches
524
534
  const existingPromise = this.tokenStore.getCsrfTokenFetchPromise();
525
535
  if (existingPromise) {
526
- if (isDev()) console.log('[HttpService] Waiting for existing CSRF fetch');
536
+ this.logger.debug('Waiting for existing CSRF fetch');
527
537
  return existingPromise;
528
538
  }
529
539
 
@@ -531,7 +541,7 @@ export class HttpService {
531
541
  const maxAttempts = 2;
532
542
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
533
543
  try {
534
- if (isDev()) console.log('[HttpService] Fetching CSRF token from:', `${this.baseURL}/csrf-token`, `(attempt ${attempt})`);
544
+ this.logger.debug('Fetching CSRF token from:', `${this.baseURL}/csrf-token`, `(attempt ${attempt})`);
535
545
 
536
546
  // Use AbortController for timeout (more compatible than AbortSignal.timeout)
537
547
  const controller = new AbortController();
@@ -546,11 +556,11 @@ export class HttpService {
546
556
 
547
557
  clearTimeout(timeoutId);
548
558
 
549
- if (isDev()) console.log('[HttpService] CSRF fetch response:', response.status, response.ok);
559
+ this.logger.debug('CSRF fetch response:', response.status, response.ok);
550
560
 
551
561
  if (response.ok) {
552
562
  const data = await response.json() as { csrfToken?: string };
553
- if (isDev()) console.log('[HttpService] CSRF response data:', data);
563
+ this.logger.debug('CSRF response data:', data);
554
564
  const token = data.csrfToken || null;
555
565
  this.tokenStore.setCsrfToken(token);
556
566
  this.logger.debug('CSRF token fetched');
@@ -565,10 +575,10 @@ export class HttpService {
565
575
  return headerToken;
566
576
  }
567
577
 
568
- if (isDev()) console.log('[HttpService] CSRF fetch failed with status:', response.status);
578
+ this.logger.debug('CSRF fetch failed with status:', response.status);
569
579
  this.logger.warn('Failed to fetch CSRF token:', response.status);
570
580
  } catch (error) {
571
- if (isDev()) console.log('[HttpService] CSRF fetch error:', error);
581
+ this.logger.debug('CSRF fetch error:', error);
572
582
  this.logger.warn('CSRF token fetch error:', error);
573
583
  }
574
584
  // Wait before retry (500ms)
@@ -706,6 +716,15 @@ export class HttpService {
706
716
  return this.request<T>({ method: 'DELETE', url, ...config });
707
717
  }
708
718
 
719
+ // Acting-as identity management (managed accounts)
720
+ setActingAs(userId: string | null): void {
721
+ this._actingAsUserId = userId;
722
+ }
723
+
724
+ getActingAs(): string | null {
725
+ return this._actingAsUserId;
726
+ }
727
+
709
728
  // Token management
710
729
  setTokens(accessToken: string, refreshToken = ''): void {
711
730
  this.tokenStore.setTokens(accessToken, refreshToken);
@@ -182,6 +182,29 @@ export class OxyServicesBase {
182
182
  return this.httpService.getAccessToken();
183
183
  }
184
184
 
185
+ /**
186
+ * Set the acting-as identity for managed accounts.
187
+ *
188
+ * When set, all subsequent API requests will include the `X-Acting-As` header,
189
+ * causing the server to attribute actions to the managed account. The
190
+ * authenticated user must be an authorized manager of the target account.
191
+ *
192
+ * Pass `null` to clear and revert to the authenticated user's own identity.
193
+ *
194
+ * @param userId - The managed account user ID, or null to clear
195
+ */
196
+ public setActingAs(userId: string | null): void {
197
+ this.httpService.setActingAs(userId);
198
+ }
199
+
200
+ /**
201
+ * Get the current acting-as identity (managed account user ID), or null
202
+ * if operating as the authenticated user's own identity.
203
+ */
204
+ public getActingAs(): string | null {
205
+ return this.httpService.getActingAs();
206
+ }
207
+
185
208
  /**
186
209
  * Wait for authentication to be ready
187
210
  *
@@ -52,7 +52,7 @@ async function initSecureStore(): Promise<typeof import('expo-secure-store')> {
52
52
  try {
53
53
  // Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
54
54
  const moduleName = 'expo-secure-store';
55
- SecureStore = await import(moduleName);
55
+ SecureStore = await import(/* @vite-ignore */ moduleName);
56
56
  } catch (error) {
57
57
  const errorMessage = error instanceof Error ? error.message : String(error);
58
58
  throw new Error(`Failed to load expo-secure-store: ${errorMessage}. Make sure expo-secure-store is installed and properly configured.`);
@@ -76,7 +76,7 @@ async function initExpoCrypto(): Promise<typeof import('expo-crypto')> {
76
76
  if (!ExpoCrypto) {
77
77
  // Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
78
78
  const moduleName = 'expo-crypto';
79
- ExpoCrypto = await import(moduleName);
79
+ ExpoCrypto = await import(/* @vite-ignore */ moduleName);
80
80
  }
81
81
  return ExpoCrypto!;
82
82
  }
@@ -105,7 +105,7 @@ async function getSecureRandomBytes(length: number): Promise<Uint8Array> {
105
105
  // Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
106
106
  try {
107
107
  const cryptoModuleName = 'crypto';
108
- const nodeCrypto = await import(cryptoModuleName);
108
+ const nodeCrypto = await import(/* @vite-ignore */ cryptoModuleName);
109
109
  return new Uint8Array(nodeCrypto.randomBytes(length));
110
110
  } catch (error) {
111
111
  // Fallback to expo-crypto if Node crypto fails
@@ -44,7 +44,7 @@ function startExpoCryptoLoad(): void {
44
44
  expoCryptoLoadPromise = (async () => {
45
45
  try {
46
46
  const moduleName = 'expo-crypto';
47
- expoCryptoModule = await import(moduleName);
47
+ expoCryptoModule = await import(/* @vite-ignore */ moduleName);
48
48
  } catch {
49
49
  // expo-crypto not available — expected in non-RN environments
50
50
  }