@oxyhq/services 5.13.12 → 5.13.16

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 (209) hide show
  1. package/README.md +10 -0
  2. package/lib/commonjs/core/OxyServices.base.js +271 -0
  3. package/lib/commonjs/core/OxyServices.base.js.map +1 -0
  4. package/lib/commonjs/core/OxyServices.errors.js +26 -0
  5. package/lib/commonjs/core/OxyServices.errors.js.map +1 -0
  6. package/lib/commonjs/core/OxyServices.js +58 -2009
  7. package/lib/commonjs/core/OxyServices.js.map +1 -1
  8. package/lib/commonjs/core/mixins/OxyServices.analytics.js +60 -0
  9. package/lib/commonjs/core/mixins/OxyServices.analytics.js.map +1 -0
  10. package/lib/commonjs/core/mixins/OxyServices.assets.js +406 -0
  11. package/lib/commonjs/core/mixins/OxyServices.assets.js.map +1 -0
  12. package/lib/commonjs/core/mixins/OxyServices.auth.js +303 -0
  13. package/lib/commonjs/core/mixins/OxyServices.auth.js.map +1 -0
  14. package/lib/commonjs/core/mixins/OxyServices.developer.js +115 -0
  15. package/lib/commonjs/core/mixins/OxyServices.developer.js.map +1 -0
  16. package/lib/commonjs/core/mixins/OxyServices.devices.js +119 -0
  17. package/lib/commonjs/core/mixins/OxyServices.devices.js.map +1 -0
  18. package/lib/commonjs/core/mixins/OxyServices.karma.js +117 -0
  19. package/lib/commonjs/core/mixins/OxyServices.karma.js.map +1 -0
  20. package/lib/commonjs/core/mixins/OxyServices.language.js +124 -0
  21. package/lib/commonjs/core/mixins/OxyServices.language.js.map +1 -0
  22. package/lib/commonjs/core/mixins/OxyServices.location.js +55 -0
  23. package/lib/commonjs/core/mixins/OxyServices.location.js.map +1 -0
  24. package/lib/commonjs/core/mixins/OxyServices.payment.js +66 -0
  25. package/lib/commonjs/core/mixins/OxyServices.payment.js.map +1 -0
  26. package/lib/commonjs/core/mixins/OxyServices.privacy.js +174 -0
  27. package/lib/commonjs/core/mixins/OxyServices.privacy.js.map +1 -0
  28. package/lib/commonjs/core/mixins/OxyServices.totp.js +53 -0
  29. package/lib/commonjs/core/mixins/OxyServices.totp.js.map +1 -0
  30. package/lib/commonjs/core/mixins/OxyServices.user.js +389 -0
  31. package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -0
  32. package/lib/commonjs/core/mixins/OxyServices.utility.js +161 -0
  33. package/lib/commonjs/core/mixins/OxyServices.utility.js.map +1 -0
  34. package/lib/commonjs/core/mixins/index.js +39 -0
  35. package/lib/commonjs/core/mixins/index.js.map +1 -0
  36. package/lib/commonjs/core/mixins/mixinHelpers.js +62 -0
  37. package/lib/commonjs/core/mixins/mixinHelpers.js.map +1 -0
  38. package/lib/commonjs/index.js.map +1 -1
  39. package/lib/commonjs/ui/context/OxyContext.js +26 -47
  40. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  41. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +239 -1
  42. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
  43. package/lib/commonjs/utils/apiUtils.js +0 -14
  44. package/lib/commonjs/utils/apiUtils.js.map +1 -1
  45. package/lib/commonjs/utils/asyncUtils.js +0 -20
  46. package/lib/commonjs/utils/asyncUtils.js.map +1 -1
  47. package/lib/module/core/OxyServices.base.js +265 -0
  48. package/lib/module/core/OxyServices.base.js.map +1 -0
  49. package/lib/module/core/OxyServices.errors.js +20 -0
  50. package/lib/module/core/OxyServices.errors.js.map +1 -0
  51. package/lib/module/core/OxyServices.js +43 -2005
  52. package/lib/module/core/OxyServices.js.map +1 -1
  53. package/lib/module/core/mixins/OxyServices.analytics.js +56 -0
  54. package/lib/module/core/mixins/OxyServices.analytics.js.map +1 -0
  55. package/lib/module/core/mixins/OxyServices.assets.js +402 -0
  56. package/lib/module/core/mixins/OxyServices.assets.js.map +1 -0
  57. package/lib/module/core/mixins/OxyServices.auth.js +299 -0
  58. package/lib/module/core/mixins/OxyServices.auth.js.map +1 -0
  59. package/lib/module/core/mixins/OxyServices.developer.js +111 -0
  60. package/lib/module/core/mixins/OxyServices.developer.js.map +1 -0
  61. package/lib/module/core/mixins/OxyServices.devices.js +115 -0
  62. package/lib/module/core/mixins/OxyServices.devices.js.map +1 -0
  63. package/lib/module/core/mixins/OxyServices.karma.js +113 -0
  64. package/lib/module/core/mixins/OxyServices.karma.js.map +1 -0
  65. package/lib/module/core/mixins/OxyServices.language.js +120 -0
  66. package/lib/module/core/mixins/OxyServices.language.js.map +1 -0
  67. package/lib/module/core/mixins/OxyServices.location.js +51 -0
  68. package/lib/module/core/mixins/OxyServices.location.js.map +1 -0
  69. package/lib/module/core/mixins/OxyServices.payment.js +62 -0
  70. package/lib/module/core/mixins/OxyServices.payment.js.map +1 -0
  71. package/lib/module/core/mixins/OxyServices.privacy.js +170 -0
  72. package/lib/module/core/mixins/OxyServices.privacy.js.map +1 -0
  73. package/lib/module/core/mixins/OxyServices.totp.js +49 -0
  74. package/lib/module/core/mixins/OxyServices.totp.js.map +1 -0
  75. package/lib/module/core/mixins/OxyServices.user.js +385 -0
  76. package/lib/module/core/mixins/OxyServices.user.js.map +1 -0
  77. package/lib/module/core/mixins/OxyServices.utility.js +156 -0
  78. package/lib/module/core/mixins/OxyServices.utility.js.map +1 -0
  79. package/lib/module/core/mixins/index.js +36 -0
  80. package/lib/module/core/mixins/index.js.map +1 -0
  81. package/lib/module/core/mixins/mixinHelpers.js +56 -0
  82. package/lib/module/core/mixins/mixinHelpers.js.map +1 -0
  83. package/lib/module/index.js.map +1 -1
  84. package/lib/module/ui/context/OxyContext.js +26 -47
  85. package/lib/module/ui/context/OxyContext.js.map +1 -1
  86. package/lib/module/ui/navigation/types.js.map +1 -1
  87. package/lib/module/ui/screens/PrivacySettingsScreen.js +241 -3
  88. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
  89. package/lib/module/utils/apiUtils.js +0 -13
  90. package/lib/module/utils/apiUtils.js.map +1 -1
  91. package/lib/module/utils/asyncUtils.js +0 -20
  92. package/lib/module/utils/asyncUtils.js.map +1 -1
  93. package/lib/typescript/core/OxyServices.base.d.ts +123 -0
  94. package/lib/typescript/core/OxyServices.base.d.ts.map +1 -0
  95. package/lib/typescript/core/OxyServices.d.ts +969 -682
  96. package/lib/typescript/core/OxyServices.d.ts.map +1 -1
  97. package/lib/typescript/core/OxyServices.errors.d.ts +12 -0
  98. package/lib/typescript/core/OxyServices.errors.d.ts.map +1 -0
  99. package/lib/typescript/core/mixins/OxyServices.analytics.d.ts +70 -0
  100. package/lib/typescript/core/mixins/OxyServices.analytics.d.ts.map +1 -0
  101. package/lib/typescript/core/mixins/OxyServices.assets.d.ts +159 -0
  102. package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -0
  103. package/lib/typescript/core/mixins/OxyServices.auth.d.ts +168 -0
  104. package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -0
  105. package/lib/typescript/core/mixins/OxyServices.developer.d.ts +103 -0
  106. package/lib/typescript/core/mixins/OxyServices.developer.d.ts.map +1 -0
  107. package/lib/typescript/core/mixins/OxyServices.devices.d.ts +93 -0
  108. package/lib/typescript/core/mixins/OxyServices.devices.d.ts.map +1 -0
  109. package/lib/typescript/core/mixins/OxyServices.karma.d.ts +89 -0
  110. package/lib/typescript/core/mixins/OxyServices.karma.d.ts.map +1 -0
  111. package/lib/typescript/core/mixins/OxyServices.language.d.ts +85 -0
  112. package/lib/typescript/core/mixins/OxyServices.language.d.ts.map +1 -0
  113. package/lib/typescript/core/mixins/OxyServices.location.d.ts +68 -0
  114. package/lib/typescript/core/mixins/OxyServices.location.d.ts.map +1 -0
  115. package/lib/typescript/core/mixins/OxyServices.payment.d.ts +74 -0
  116. package/lib/typescript/core/mixins/OxyServices.payment.d.ts.map +1 -0
  117. package/lib/typescript/core/mixins/OxyServices.privacy.d.ts +126 -0
  118. package/lib/typescript/core/mixins/OxyServices.privacy.d.ts.map +1 -0
  119. package/lib/typescript/core/mixins/OxyServices.totp.d.ts +69 -0
  120. package/lib/typescript/core/mixins/OxyServices.totp.d.ts.map +1 -0
  121. package/lib/typescript/core/mixins/OxyServices.user.d.ts +189 -0
  122. package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -0
  123. package/lib/typescript/core/mixins/OxyServices.utility.d.ts +97 -0
  124. package/lib/typescript/core/mixins/OxyServices.utility.d.ts.map +1 -0
  125. package/lib/typescript/core/mixins/index.d.ts +898 -0
  126. package/lib/typescript/core/mixins/index.d.ts.map +1 -0
  127. package/lib/typescript/core/mixins/mixinHelpers.d.ts +32 -0
  128. package/lib/typescript/core/mixins/mixinHelpers.d.ts.map +1 -0
  129. package/lib/typescript/index.d.ts +1 -1
  130. package/lib/typescript/index.d.ts.map +1 -1
  131. package/lib/typescript/models/interfaces.d.ts +36 -0
  132. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  133. package/lib/typescript/ui/context/OxyContext.d.ts +2 -6
  134. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  135. package/lib/typescript/ui/navigation/types.d.ts +0 -1
  136. package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
  137. package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  138. package/lib/typescript/utils/apiUtils.d.ts +0 -7
  139. package/lib/typescript/utils/apiUtils.d.ts.map +1 -1
  140. package/lib/typescript/utils/asyncUtils.d.ts +0 -11
  141. package/lib/typescript/utils/asyncUtils.d.ts.map +1 -1
  142. package/package.json +1 -1
  143. package/src/core/OxyServices.base.ts +311 -0
  144. package/src/core/OxyServices.errors.ts +26 -0
  145. package/src/core/OxyServices.ts +43 -2026
  146. package/src/core/mixins/OxyServices.analytics.ts +53 -0
  147. package/src/core/mixins/OxyServices.assets.ts +390 -0
  148. package/src/core/mixins/OxyServices.auth.ts +275 -0
  149. package/src/core/mixins/OxyServices.developer.ts +114 -0
  150. package/src/core/mixins/OxyServices.devices.ts +103 -0
  151. package/src/core/mixins/OxyServices.karma.ts +111 -0
  152. package/src/core/mixins/OxyServices.language.ts +127 -0
  153. package/src/core/mixins/OxyServices.location.ts +46 -0
  154. package/src/core/mixins/OxyServices.payment.ts +59 -0
  155. package/src/core/mixins/OxyServices.privacy.ts +182 -0
  156. package/src/core/mixins/OxyServices.totp.ts +36 -0
  157. package/src/core/mixins/OxyServices.user.ts +380 -0
  158. package/src/core/mixins/OxyServices.utility.ts +187 -0
  159. package/src/core/mixins/index.ts +58 -0
  160. package/src/core/mixins/mixinHelpers.ts +69 -0
  161. package/src/index.ts +4 -0
  162. package/src/models/interfaces.ts +40 -0
  163. package/src/ui/context/OxyContext.tsx +35 -53
  164. package/src/ui/navigation/types.ts +0 -1
  165. package/src/ui/screens/PrivacySettingsScreen.tsx +240 -2
  166. package/src/utils/apiUtils.ts +0 -14
  167. package/src/utils/asyncUtils.ts +0 -20
  168. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +0 -192
  169. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +0 -1
  170. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +0 -142
  171. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +0 -1
  172. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js +0 -113
  173. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js.map +0 -1
  174. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js +0 -132
  175. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js.map +0 -1
  176. package/lib/commonjs/ui/screens/internal/SignUpSummaryStep.js +0 -83
  177. package/lib/commonjs/ui/screens/internal/SignUpSummaryStep.js.map +0 -1
  178. package/lib/commonjs/ui/screens/internal/SignUpWelcomeStep.js +0 -58
  179. package/lib/commonjs/ui/screens/internal/SignUpWelcomeStep.js.map +0 -1
  180. package/lib/module/ui/screens/internal/SignInPasswordStep.js +0 -186
  181. package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +0 -1
  182. package/lib/module/ui/screens/internal/SignInUsernameStep.js +0 -136
  183. package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +0 -1
  184. package/lib/module/ui/screens/internal/SignUpIdentityStep.js +0 -108
  185. package/lib/module/ui/screens/internal/SignUpIdentityStep.js.map +0 -1
  186. package/lib/module/ui/screens/internal/SignUpSecurityStep.js +0 -127
  187. package/lib/module/ui/screens/internal/SignUpSecurityStep.js.map +0 -1
  188. package/lib/module/ui/screens/internal/SignUpSummaryStep.js +0 -78
  189. package/lib/module/ui/screens/internal/SignUpSummaryStep.js.map +0 -1
  190. package/lib/module/ui/screens/internal/SignUpWelcomeStep.js +0 -53
  191. package/lib/module/ui/screens/internal/SignUpWelcomeStep.js.map +0 -1
  192. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts +0 -28
  193. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +0 -1
  194. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts +0 -25
  195. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts.map +0 -1
  196. package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts +0 -20
  197. package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts.map +0 -1
  198. package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts +0 -24
  199. package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts.map +0 -1
  200. package/lib/typescript/ui/screens/internal/SignUpSummaryStep.d.ts +0 -15
  201. package/lib/typescript/ui/screens/internal/SignUpSummaryStep.d.ts.map +0 -1
  202. package/lib/typescript/ui/screens/internal/SignUpWelcomeStep.d.ts +0 -13
  203. package/lib/typescript/ui/screens/internal/SignUpWelcomeStep.d.ts.map +0 -1
  204. package/src/ui/screens/internal/SignInPasswordStep.tsx +0 -184
  205. package/src/ui/screens/internal/SignInUsernameStep.tsx +0 -145
  206. package/src/ui/screens/internal/SignUpIdentityStep.tsx +0 -112
  207. package/src/ui/screens/internal/SignUpSecurityStep.tsx +0 -132
  208. package/src/ui/screens/internal/SignUpSummaryStep.tsx +0 -66
  209. package/src/ui/screens/internal/SignUpWelcomeStep.tsx +0 -52
@@ -3,14 +3,23 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.oxyClient = exports.OxyServices = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.OXY_CLOUD_URL = exports.OXY_API_URL = void 0;
7
- var _jwtDecode = require("jwt-decode");
8
- var _languageUtils = require("../utils/languageUtils");
9
- var _errorUtils = require("../utils/errorUtils");
10
- var _apiUtils = require("../utils/apiUtils");
11
- var _HttpClient = require("./HttpClient");
12
- var _RequestManager = require("./RequestManager");
13
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /**
6
+ exports.OXY_CLOUD_URL = exports.OXY_API_URL = void 0;
7
+ Object.defineProperty(exports, "OxyAuthenticationError", {
8
+ enumerable: true,
9
+ get: function () {
10
+ return _OxyServices.OxyAuthenticationError;
11
+ }
12
+ });
13
+ Object.defineProperty(exports, "OxyAuthenticationTimeoutError", {
14
+ enumerable: true,
15
+ get: function () {
16
+ return _OxyServices.OxyAuthenticationTimeoutError;
17
+ }
18
+ });
19
+ exports.oxyClient = exports.OxyServices = void 0;
20
+ var _OxyServices = require("./OxyServices.errors");
21
+ var _mixins = require("./mixins");
22
+ /**
14
23
  * OxyServices - Unified client for Oxy API and Oxy Cloud
15
24
  *
16
25
  * # Usage Examples
@@ -32,7 +41,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
32
41
  * // Upload a file (browser File API)
33
42
  * const fileInput = document.querySelector('input[type=file]');
34
43
  * const file = fileInput.files[0];
35
- * await oxy.uploadFile(file);
44
+ * await oxy.uploadRawFile(file);
36
45
  *
37
46
  * // Get a file stream URL for <img src>
38
47
  * const url = oxy.getFileStreamUrl('fileId');
@@ -67,2020 +76,60 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
67
76
  * - `cloudURL`: Oxy Cloud/CDN endpoint (e.g., https://cloud.oxy.so)
68
77
  *
69
78
  * See method JSDoc for more details and options.
70
- */ /**
71
- * OxyConfig - Configuration for OxyServices
72
- * @property baseURL - The Oxy API base URL (e.g., https://api.oxy.so)
73
- * @property cloudURL - The Oxy Cloud (file storage/CDN) URL (e.g., https://cloud.oxy.so)
74
79
  */
75
- /**
76
- * Custom error types for better error handling
77
- */
78
- class OxyAuthenticationError extends Error {
79
- constructor(message, code = 'AUTH_ERROR', status = 401) {
80
- super(message);
81
- this.name = 'OxyAuthenticationError';
82
- this.code = code;
83
- this.status = status;
84
- }
85
- }
86
- exports.OxyAuthenticationError = OxyAuthenticationError;
87
- class OxyAuthenticationTimeoutError extends OxyAuthenticationError {
88
- constructor(operationName, timeoutMs) {
89
- super(`Authentication timeout (${timeoutMs}ms): ${operationName} requires user authentication. Please ensure the user is logged in before calling this method.`, 'AUTH_TIMEOUT', 408);
90
- this.name = 'OxyAuthenticationTimeoutError';
91
- }
92
- }
80
+
81
+ // Import mixin composition helper
93
82
 
94
83
  /**
95
84
  * OxyServices - Unified client library for interacting with the Oxy API
96
85
  *
97
86
  * This class provides all API functionality in one simple, easy-to-use interface.
98
- * Architecture:
99
- * - HttpClient: Handles HTTP communication and authentication
100
- * - RequestManager: Handles caching, deduplication, queuing, and retry
101
- * - OxyServices: Provides high-level API methods
87
+ *
88
+ * ## Architecture
89
+ * - **HttpClient**: Handles HTTP communication and authentication
90
+ * - **RequestManager**: Handles caching, deduplication, queuing, and retry
91
+ * - **OxyServices**: Provides high-level API methods
92
+ *
93
+ * ## Mixin Composition
94
+ * The class is composed using TypeScript mixins for better code organization:
95
+ * - **Base**: Core infrastructure (HTTP client, request management, error handling)
96
+ * - **Auth**: Authentication and session management
97
+ * - **User**: User profiles, follow, notifications
98
+ * - **TOTP**: Two-factor authentication enrollment
99
+ * - **Privacy**: Blocked and restricted users
100
+ * - **Language**: Language detection and metadata
101
+ * - **Payment**: Payment processing
102
+ * - **Karma**: Karma system
103
+ * - **Assets**: File upload and asset management
104
+ * - **Developer**: Developer API management
105
+ * - **Location**: Location-based features
106
+ * - **Analytics**: Analytics tracking
107
+ * - **Devices**: Device management
108
+ * - **Utility**: Utility methods and Express middleware
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * const oxy = new OxyServices({
113
+ * baseURL: 'https://api.oxy.so',
114
+ * cloudURL: 'https://cloud.oxy.so'
115
+ * });
116
+ * ```
102
117
  */
103
- exports.OxyAuthenticationTimeoutError = OxyAuthenticationTimeoutError;
104
- class OxyServices {
105
- /**
106
- * Creates a new instance of the OxyServices client
107
- * @param config - Configuration for the client
108
- * config.baseURL: Oxy API URL (e.g., https://api.oxy.so)
109
- * config.cloudURL: Oxy Cloud URL (e.g., https://cloud.oxy.so)
110
- */
111
- constructor(config) {
112
- this.config = config;
113
- this.cloudURL = config.cloudURL || OXY_CLOUD_URL;
114
-
115
- // Initialize HTTP client (handles authentication and interceptors)
116
- this.httpClient = new _HttpClient.HttpClient(config);
117
-
118
- // Initialize request manager (handles caching, deduplication, queuing, retry)
119
- this.requestManager = new _RequestManager.RequestManager(this.httpClient, config);
120
- }
121
-
122
- // Test-only utility to reset global tokens between jest tests
123
- static __resetTokensForTests() {
124
- _HttpClient.HttpClient.__resetTokensForTests();
125
- }
126
-
127
- /**
128
- * Make a request with all performance optimizations
129
- * This is the main method for all API calls - ensures authentication and performance features
130
- */
131
- async makeRequest(method, url, data, options = {}) {
132
- return this.requestManager.request(method, url, data, options);
133
- }
134
-
135
- // ============================================================================
136
- // CORE METHODS (HTTP Client, Token Management, Error Handling)
137
- // ============================================================================
138
-
139
- /**
140
- * Get the configured Oxy API base URL
141
- */
142
- getBaseURL() {
143
- return this.httpClient.getBaseURL();
144
- }
145
-
146
- /**
147
- * Get the HTTP client instance
148
- * Useful for advanced use cases where direct access to the HTTP client is needed
149
- */
150
- getClient() {
151
- return this.httpClient;
152
- }
153
-
154
- /**
155
- * Get performance metrics
156
- */
157
- getMetrics() {
158
- return this.requestManager.getMetrics();
159
- }
160
-
161
- /**
162
- * Clear request cache
163
- */
164
- clearCache() {
165
- this.requestManager.clearCache();
166
- }
167
-
168
- /**
169
- * Clear specific cache entry
170
- */
171
- clearCacheEntry(key) {
172
- this.requestManager.clearCacheEntry(key);
173
- }
174
-
175
- /**
176
- * Get cache statistics
177
- */
178
- getCacheStats() {
179
- return this.requestManager.getCacheStats();
180
- }
181
-
182
- /**
183
- * Get the configured Oxy Cloud (file storage/CDN) URL
184
- */
185
- getCloudURL() {
186
- return this.cloudURL;
187
- }
188
-
189
- /**
190
- * Set authentication tokens
191
- */
192
- setTokens(accessToken, refreshToken = '') {
193
- this.httpClient.setTokens(accessToken, refreshToken);
194
- }
195
-
196
- /**
197
- * Clear stored authentication tokens
198
- */
199
- clearTokens() {
200
- this.httpClient.clearTokens();
201
- }
202
-
203
- /**
204
- * Get the current user ID from the access token
205
- */
206
- getCurrentUserId() {
207
- const accessToken = this.httpClient.getAccessToken();
208
- if (!accessToken) {
209
- return null;
210
- }
211
- try {
212
- const decoded = (0, _jwtDecode.jwtDecode)(accessToken);
213
- return decoded.userId || decoded.id || null;
214
- } catch (error) {
215
- return null;
216
- }
217
- }
218
-
219
- /**
220
- * Check if the client has a valid access token
221
- */
222
- hasAccessToken() {
223
- return this.httpClient.hasAccessToken();
224
- }
225
-
226
- /**
227
- * Check if the client has a valid access token (public method)
228
- */
229
- hasValidToken() {
230
- return this.httpClient.hasAccessToken();
231
- }
232
-
233
- /**
234
- * Get the raw access token (for constructing anchor URLs when needed)
235
- */
236
- getAccessToken() {
237
- return this.httpClient.getAccessToken();
238
- }
239
-
240
- /**
241
- * Wait for authentication to be ready (public method)
242
- * Useful for apps that want to ensure authentication is complete before proceeding
243
- */
244
- async waitForAuth(timeoutMs = 5000) {
245
- return this.waitForAuthentication(timeoutMs);
246
- }
247
-
248
- /**
249
- * Wait for authentication to be ready with timeout
250
- */
251
- async waitForAuthentication(timeoutMs = 5000) {
252
- const startTime = Date.now();
253
- const checkInterval = 100; // Check every 100ms
254
-
255
- while (Date.now() - startTime < timeoutMs) {
256
- if (this.httpClient.hasAccessToken()) {
257
- return true;
258
- }
259
- await new Promise(resolve => setTimeout(resolve, checkInterval));
260
- }
261
- return false;
262
- }
263
-
264
- /**
265
- * Execute a function with automatic authentication retry logic
266
- * This handles the common case where API calls are made before authentication completes
267
- */
268
- async withAuthRetry(operation, operationName, options = {}) {
269
- const {
270
- maxRetries = 2,
271
- retryDelay = 1000,
272
- authTimeoutMs = 5000
273
- } = options;
274
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
275
- try {
276
- // First attempt: check if we have a token
277
- if (!this.httpClient.hasAccessToken()) {
278
- if (attempt === 0) {
279
- // On first attempt, wait briefly for authentication to complete
280
- const authReady = await this.waitForAuthentication(authTimeoutMs);
281
- if (!authReady) {
282
- throw new OxyAuthenticationTimeoutError(operationName, authTimeoutMs);
283
- }
284
- } else {
285
- // On retry attempts, fail immediately if no token
286
- throw new OxyAuthenticationError(`Authentication required: ${operationName} requires a valid access token.`, 'AUTH_REQUIRED');
287
- }
288
- }
289
-
290
- // Execute the operation
291
- return await operation();
292
- } catch (error) {
293
- const isLastAttempt = attempt === maxRetries;
294
- const isAuthError = error?.response?.status === 401 || error?.code === 'MISSING_TOKEN' || error?.message?.includes('Authentication') || error instanceof OxyAuthenticationError;
295
- if (isAuthError && !isLastAttempt && !(error instanceof OxyAuthenticationTimeoutError)) {
296
- await new Promise(resolve => setTimeout(resolve, retryDelay));
297
- continue;
298
- }
299
-
300
- // If it's not an auth error, or it's the last attempt, throw the error
301
- if (error instanceof OxyAuthenticationError) {
302
- throw error;
303
- }
304
- throw this.handleError(error);
305
- }
306
- }
307
-
308
- // This should never be reached, but TypeScript requires it
309
- throw new OxyAuthenticationError(`${operationName} failed after ${maxRetries + 1} attempts`);
310
- }
311
-
312
- /**
313
- * Validate the current access token with the server
314
- */
315
- async validate() {
316
- if (!this.hasAccessToken()) {
317
- return false;
318
- }
319
- try {
320
- const res = await this.makeRequest('GET', '/api/auth/validate', undefined, {
321
- cache: false,
322
- retry: false
323
- });
324
- return res.valid === true;
325
- } catch (error) {
326
- return false;
327
- }
328
- }
329
-
330
- /**
331
- * Centralized error handling
332
- */
333
- handleError(error) {
334
- const api = (0, _errorUtils.handleHttpError)(error);
335
- const err = new Error(api.message);
336
- err.code = api.code;
337
- err.status = api.status;
338
- err.details = api.details;
339
- return err;
340
- }
341
-
342
- /**
343
- * Health check endpoint
344
- */
345
- async healthCheck() {
346
- try {
347
- return await this.makeRequest('GET', '/health', undefined, {
348
- cache: false
349
- });
350
- } catch (error) {
351
- throw this.handleError(error);
352
- }
353
- }
354
-
355
- // ============================================================================
356
- // AUTHENTICATION METHODS
357
- // ============================================================================
358
-
359
- /**
360
- * Sign up a new user
361
- */
362
- async signUp(username, email, password) {
363
- try {
364
- const res = await this.makeRequest('POST', '/api/auth/signup', {
365
- username,
366
- email,
367
- password
368
- }, {
369
- cache: false
370
- });
371
- if (!res || typeof res === 'object' && Object.keys(res).length === 0) {
372
- throw new OxyAuthenticationError('Sign up failed', 'SIGNUP_FAILED', 400);
373
- }
374
- return res;
375
- } catch (error) {
376
- throw this.handleError(error);
377
- }
378
- }
118
+ // Compose all mixins into the final OxyServices class
119
+ const OxyServicesComposed = (0, _mixins.composeOxyServices)();
379
120
 
380
- /**
381
- * Request account recovery (send verification code)
382
- */
383
- async requestRecovery(identifier) {
384
- try {
385
- return await this.makeRequest('POST', '/api/auth/recover/request', {
386
- identifier
387
- }, {
388
- cache: false
389
- });
390
- } catch (error) {
391
- throw this.handleError(error);
392
- }
393
- }
394
-
395
- /**
396
- * Verify recovery code
397
- */
398
- async verifyRecoveryCode(identifier, code) {
399
- try {
400
- return await this.makeRequest('POST', '/api/auth/recover/verify', {
401
- identifier,
402
- code
403
- }, {
404
- cache: false
405
- });
406
- } catch (error) {
407
- throw this.handleError(error);
408
- }
409
- }
410
-
411
- /**
412
- * Reset password using verified code
413
- */
414
- async resetPassword(identifier, code, newPassword) {
415
- try {
416
- return await this.makeRequest('POST', '/api/auth/recover/reset', {
417
- identifier,
418
- code,
419
- newPassword
420
- }, {
421
- cache: false
422
- });
423
- } catch (error) {
424
- throw this.handleError(error);
425
- }
426
- }
427
-
428
- /**
429
- * Reset password using TOTP code (recommended recovery)
430
- */
431
- async resetPasswordWithTotp(identifier, code, newPassword) {
432
- try {
433
- return await this.makeRequest('POST', '/api/auth/recover/totp/reset', {
434
- identifier,
435
- code,
436
- newPassword
437
- }, {
438
- cache: false
439
- });
440
- } catch (error) {
441
- throw this.handleError(error);
442
- }
443
- }
444
- async resetPasswordWithBackupCode(identifier, backupCode, newPassword) {
445
- try {
446
- return await this.makeRequest('POST', '/api/auth/recover/backup/reset', {
447
- identifier,
448
- backupCode,
449
- newPassword
450
- }, {
451
- cache: false
452
- });
453
- } catch (error) {
454
- throw this.handleError(error);
455
- }
456
- }
457
- async resetPasswordWithRecoveryKey(identifier, recoveryKey, newPassword) {
458
- try {
459
- return await this.makeRequest('POST', '/api/auth/recover/recovery-key/reset', {
460
- identifier,
461
- recoveryKey,
462
- newPassword
463
- }, {
464
- cache: false
465
- });
466
- } catch (error) {
467
- throw this.handleError(error);
468
- }
469
- }
470
-
471
- /**
472
- * Sign in with device management
473
- */
474
- async signIn(username, password, deviceName, deviceFingerprint) {
475
- try {
476
- return await this.makeRequest('POST', '/api/auth/login', {
477
- username,
478
- password,
479
- deviceName,
480
- deviceFingerprint
481
- }, {
482
- cache: false
483
- });
484
- } catch (error) {
485
- throw this.handleError(error);
486
- }
487
- }
488
-
489
- /**
490
- * Complete login by verifying TOTP with MFA token
491
- */
492
- async verifyTotpLogin(mfaToken, code) {
493
- try {
494
- return await this.makeRequest('POST', '/api/auth/totp/verify-login', {
495
- mfaToken,
496
- code
497
- }, {
498
- cache: false
499
- });
500
- } catch (error) {
501
- throw this.handleError(error);
502
- }
503
- }
504
-
505
- /**
506
- * Get user by session ID
507
- */
508
- async getUserBySession(sessionId) {
509
- try {
510
- return await this.makeRequest('GET', `/api/session/user/${sessionId}`, undefined, {
511
- cache: true,
512
- cacheTTL: 2 * 60 * 1000 // 2 minutes cache for user data
513
- });
514
- } catch (error) {
515
- throw this.handleError(error);
516
- }
517
- }
518
-
519
- /**
520
- * Batch get multiple user profiles by session IDs (optimized for account switching)
521
- * Returns array of { sessionId, user } objects
522
- */
523
- async getUsersBySessions(sessionIds) {
524
- try {
525
- if (!Array.isArray(sessionIds) || sessionIds.length === 0) {
526
- return [];
527
- }
528
-
529
- // Deduplicate and sort sessionIds for consistent cache keys
530
- const uniqueSessionIds = Array.from(new Set(sessionIds)).sort();
531
- return await this.makeRequest('POST', '/api/session/users/batch', {
532
- sessionIds: uniqueSessionIds
533
- }, {
534
- cache: true,
535
- cacheTTL: 2 * 60 * 1000,
536
- // 2 minutes cache
537
- deduplicate: true // Important for batch requests
538
- });
539
- } catch (error) {
540
- throw this.handleError(error);
541
- }
542
- }
543
-
544
- /**
545
- * Get access token by session ID and set it in the token store
546
- */
547
- async getTokenBySession(sessionId) {
548
- try {
549
- const res = await this.makeRequest('GET', `/api/session/token/${sessionId}`, undefined, {
550
- cache: false,
551
- retry: false
552
- });
553
-
554
- // Set the token in the centralized token store
555
- this.setTokens(res.accessToken);
556
- return res;
557
- } catch (error) {
558
- throw this.handleError(error);
559
- }
560
- }
561
-
562
- /**
563
- * Get sessions by session ID
564
- */
565
- async getSessionsBySessionId(sessionId) {
566
- try {
567
- return await this.makeRequest('GET', `/api/session/sessions/${sessionId}`, undefined, {
568
- cache: false
569
- });
570
- } catch (error) {
571
- throw this.handleError(error);
572
- }
573
- }
574
-
575
- /**
576
- * Logout from a specific session
577
- */
578
- async logoutSession(sessionId, targetSessionId) {
579
- try {
580
- const url = targetSessionId ? `/api/session/logout/${sessionId}/${targetSessionId}` : `/api/session/logout/${sessionId}`;
581
- await this.makeRequest('POST', url, undefined, {
582
- cache: false
583
- });
584
- } catch (error) {
585
- throw this.handleError(error);
586
- }
587
- }
588
-
589
- /**
590
- * Logout from all sessions
591
- */
592
- async logoutAllSessions(sessionId) {
593
- try {
594
- await this.makeRequest('POST', `/api/session/logout-all/${sessionId}`, undefined, {
595
- cache: false
596
- });
597
- } catch (error) {
598
- throw this.handleError(error);
599
- }
600
- }
601
-
602
- /**
603
- * Validate session
604
- */
605
- async validateSession(sessionId, options = {}) {
606
- try {
607
- const params = new URLSearchParams();
608
- if (options.deviceFingerprint) {
609
- params.append('deviceFingerprint', options.deviceFingerprint);
610
- }
611
- if (options.useHeaderValidation) {
612
- params.append('useHeaderValidation', 'true');
613
- }
614
- const url = `/api/session/validate/${sessionId}`;
615
- const urlParams = {};
616
- if (options.deviceFingerprint) urlParams.deviceFingerprint = options.deviceFingerprint;
617
- if (options.useHeaderValidation) urlParams.useHeaderValidation = 'true';
618
- return await this.makeRequest('GET', url, urlParams, {
619
- cache: false
620
- });
621
- } catch (error) {
622
- throw this.handleError(error);
623
- }
624
- }
625
-
626
- /**
627
- * Check username availability
628
- */
629
- async checkUsernameAvailability(username) {
630
- try {
631
- return await this.makeRequest('GET', `/api/auth/check-username/${username}`, undefined, {
632
- cache: false
633
- });
634
- } catch (error) {
635
- throw this.handleError(error);
636
- }
637
- }
638
-
639
- /**
640
- * Check email availability
641
- */
642
- async checkEmailAvailability(email) {
643
- try {
644
- return await this.makeRequest('GET', `/api/auth/check-email/${email}`, undefined, {
645
- cache: false
646
- });
647
- } catch (error) {
648
- throw this.handleError(error);
649
- }
650
- }
651
-
652
- // ============================================================================
653
- // USER METHODS
654
- // ============================================================================
655
-
656
- /**
657
- * Get profile by username
658
- */
659
- async getProfileByUsername(username) {
660
- try {
661
- return await this.makeRequest('GET', `/api/profiles/username/${username}`, undefined, {
662
- cache: true,
663
- cacheTTL: 5 * 60 * 1000 // 5 minutes cache for profiles
664
- });
665
- } catch (error) {
666
- throw this.handleError(error);
667
- }
668
- }
669
-
670
- // ============================================================================
671
- // TOTP ENROLLMENT
672
- // ============================================================================
673
-
674
- async startTotpEnrollment(sessionId) {
675
- try {
676
- // Note: x-session-id header is handled by HttpClient interceptors if needed
677
- return await this.makeRequest('POST', '/api/auth/totp/enroll/start', {
678
- sessionId
679
- }, {
680
- cache: false
681
- });
682
- } catch (error) {
683
- throw this.handleError(error);
684
- }
685
- }
686
- async verifyTotpEnrollment(sessionId, code) {
687
- try {
688
- return await this.makeRequest('POST', '/api/auth/totp/enroll/verify', {
689
- sessionId,
690
- code
691
- }, {
692
- cache: false
693
- });
694
- } catch (error) {
695
- throw this.handleError(error);
696
- }
697
- }
698
- async disableTotp(sessionId, code) {
699
- try {
700
- return await this.makeRequest('POST', '/api/auth/totp/disable', {
701
- sessionId,
702
- code
703
- }, {
704
- cache: false
705
- });
706
- } catch (error) {
707
- throw this.handleError(error);
708
- }
709
- }
710
-
711
- /**
712
- * Search user profiles
713
- */
714
- async searchProfiles(query, pagination) {
715
- try {
716
- const params = {
717
- query,
718
- ...pagination
719
- };
720
- const searchParams = (0, _apiUtils.buildSearchParams)(params);
721
- const paramsObj = {};
722
- searchParams.forEach((value, key) => {
723
- paramsObj[key] = value;
724
- });
725
- return await this.makeRequest('GET', '/api/profiles/search', paramsObj, {
726
- cache: true,
727
- cacheTTL: 2 * 60 * 1000 // 2 minutes cache
728
- });
729
- } catch (error) {
730
- throw this.handleError(error);
731
- }
732
- }
733
-
734
- /**
735
- * Get profile recommendations
736
- */
737
- async getProfileRecommendations() {
738
- return this.withAuthRetry(async () => {
739
- return await this.makeRequest('GET', '/api/profiles/recommendations', undefined, {
740
- cache: true
741
- });
742
- }, 'getProfileRecommendations');
743
- }
744
-
745
- /**
746
- * Get user by ID
747
- */
748
- async getUserById(userId) {
749
- try {
750
- return await this.makeRequest('GET', `/api/users/${userId}`, undefined, {
751
- cache: true,
752
- cacheTTL: 5 * 60 * 1000 // 5 minutes cache
753
- });
754
- } catch (error) {
755
- throw this.handleError(error);
756
- }
757
- }
758
-
759
- /**
760
- * Get current user
761
- */
762
- async getCurrentUser() {
763
- return this.withAuthRetry(async () => {
764
- return await this.makeRequest('GET', '/api/users/me', undefined, {
765
- cache: true,
766
- cacheTTL: 1 * 60 * 1000 // 1 minute cache for current user
767
- });
768
- }, 'getCurrentUser');
769
- }
770
-
771
- /**
772
- * Update user profile
773
- */
774
- async updateProfile(updates) {
775
- try {
776
- return await this.makeRequest('PUT', '/api/users/me', updates, {
777
- cache: false
778
- });
779
- } catch (error) {
780
- throw this.handleError(error);
781
- }
782
- }
783
-
784
- /**
785
- * Get privacy settings for a user
786
- * @param userId - The user ID (defaults to current user)
787
- */
788
- async getPrivacySettings(userId) {
789
- try {
790
- const id = userId || (await this.getCurrentUser()).id;
791
- return await this.makeRequest('GET', `/api/privacy/${id}/privacy`, undefined, {
792
- cache: true,
793
- cacheTTL: 2 * 60 * 1000 // 2 minutes cache
794
- });
795
- } catch (error) {
796
- throw this.handleError(error);
797
- }
798
- }
799
-
800
- /**
801
- * Update privacy settings
802
- * @param settings - Partial privacy settings object
803
- * @param userId - The user ID (defaults to current user)
804
- */
805
- async updatePrivacySettings(settings, userId) {
806
- try {
807
- const id = userId || (await this.getCurrentUser()).id;
808
- return await this.makeRequest('PATCH', `/api/privacy/${id}/privacy`, settings, {
809
- cache: false
810
- });
811
- } catch (error) {
812
- throw this.handleError(error);
813
- }
814
- }
815
-
816
- /**
817
- * Request account verification
818
- */
819
- async requestAccountVerification(reason, evidence) {
820
- try {
821
- return await this.makeRequest('POST', '/api/users/verify/request', {
822
- reason,
823
- evidence
824
- }, {
825
- cache: false
826
- });
827
- } catch (error) {
828
- throw this.handleError(error);
829
- }
830
- }
831
-
832
- /**
833
- * Download account data export
834
- */
835
- async downloadAccountData(format = 'json') {
836
- try {
837
- // Use axios instance directly for blob responses since RequestManager doesn't handle blobs
838
- const axiosInstance = this.httpClient.getAxiosInstance();
839
- const response = await axiosInstance.get(`/api/users/me/data?format=${format}`, {
840
- responseType: 'blob'
841
- });
842
- return response.data;
843
- } catch (error) {
844
- throw this.handleError(error);
845
- }
846
- }
847
-
848
- /**
849
- * Delete account permanently
850
- * @param password - User password for confirmation
851
- * @param confirmText - Confirmation text (usually username)
852
- */
853
- async deleteAccount(password, confirmText) {
854
- try {
855
- return await this.makeRequest('DELETE', '/api/users/me', {
856
- password,
857
- confirmText
858
- }, {
859
- cache: false
860
- });
861
- } catch (error) {
862
- throw this.handleError(error);
863
- }
864
- }
865
-
866
- // ============================================================================
867
- // LANGUAGE METHODS
868
- // ============================================================================
869
-
870
- /**
871
- * Get the current language from storage or user profile
872
- * @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
873
- * @returns The current language code (e.g., 'en-US') or null if not set
874
- */
875
- async getCurrentLanguage(storageKeyPrefix = 'oxy_session') {
876
- try {
877
- // First try to get from user profile if authenticated
878
- try {
879
- const user = await this.getCurrentUser();
880
- const userLanguage = user?.language;
881
- if (userLanguage) {
882
- return (0, _languageUtils.normalizeLanguageCode)(userLanguage) || userLanguage;
883
- }
884
- } catch (e) {
885
- // User not authenticated or error, continue to storage
886
- }
887
-
888
- // Fall back to storage
889
- const storage = await this.getStorage();
890
- const storageKey = `${storageKeyPrefix}_language`;
891
- const storedLanguage = await storage.getItem(storageKey);
892
- if (storedLanguage) {
893
- return (0, _languageUtils.normalizeLanguageCode)(storedLanguage) || storedLanguage;
894
- }
895
- return null;
896
- } catch (error) {
897
- if (__DEV__) {
898
- console.warn('Failed to get current language:', error);
899
- }
900
- return null;
901
- }
902
- }
903
-
904
- /**
905
- * Get the current language with metadata (name, nativeName, etc.)
906
- * @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
907
- * @returns Language metadata object or null if not set
908
- */
909
- async getCurrentLanguageMetadata(storageKeyPrefix = 'oxy_session') {
910
- const languageCode = await this.getCurrentLanguage(storageKeyPrefix);
911
- return (0, _languageUtils.getLanguageMetadata)(languageCode);
912
- }
913
-
914
- /**
915
- * Get the current language name (e.g., 'English')
916
- * @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
917
- * @returns Language name or null if not set
918
- */
919
- async getCurrentLanguageName(storageKeyPrefix = 'oxy_session') {
920
- const languageCode = await this.getCurrentLanguage(storageKeyPrefix);
921
- if (!languageCode) return null;
922
- return (0, _languageUtils.getLanguageName)(languageCode);
923
- }
924
-
925
- /**
926
- * Get the current native language name (e.g., 'Español')
927
- * @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
928
- * @returns Native language name or null if not set
929
- */
930
- async getCurrentNativeLanguageName(storageKeyPrefix = 'oxy_session') {
931
- const languageCode = await this.getCurrentLanguage(storageKeyPrefix);
932
- if (!languageCode) return null;
933
- return (0, _languageUtils.getNativeLanguageName)(languageCode);
934
- }
935
-
936
- /**
937
- * Get appropriate storage for the platform (similar to DeviceManager)
938
- * @private
939
- */
940
- async getStorage() {
941
- const isReactNative = typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
942
- if (isReactNative) {
943
- try {
944
- const asyncStorageModule = await Promise.resolve().then(() => _interopRequireWildcard(require('@react-native-async-storage/async-storage')));
945
- const storage = asyncStorageModule.default;
946
- return {
947
- getItem: storage.getItem.bind(storage),
948
- setItem: storage.setItem.bind(storage),
949
- removeItem: storage.removeItem.bind(storage)
950
- };
951
- } catch (error) {
952
- console.error('AsyncStorage not available in React Native:', error);
953
- throw new Error('AsyncStorage is required in React Native environment');
954
- }
955
- } else {
956
- // Use localStorage for web
957
- return {
958
- getItem: async key => {
959
- if (typeof window !== 'undefined' && window.localStorage) {
960
- return localStorage.getItem(key);
961
- }
962
- return null;
963
- },
964
- setItem: async (key, value) => {
965
- if (typeof window !== 'undefined' && window.localStorage) {
966
- localStorage.setItem(key, value);
967
- }
968
- },
969
- removeItem: async key => {
970
- if (typeof window !== 'undefined' && window.localStorage) {
971
- localStorage.removeItem(key);
972
- }
973
- }
974
- };
975
- }
976
- }
977
-
978
- /**
979
- * Update user by ID (admin function)
980
- */
981
- async updateUser(userId, updates) {
982
- try {
983
- return await this.makeRequest('PUT', `/api/users/${userId}`, updates, {
984
- cache: false
985
- });
986
- } catch (error) {
987
- throw this.handleError(error);
988
- }
989
- }
990
-
991
- /**
992
- * Follow a user
993
- */
994
- async followUser(userId) {
995
- try {
996
- return await this.makeRequest('POST', `/api/users/${userId}/follow`, undefined, {
997
- cache: false
998
- });
999
- } catch (error) {
1000
- throw this.handleError(error);
1001
- }
1002
- }
1003
-
1004
- /**
1005
- * Unfollow a user
1006
- */
1007
- async unfollowUser(userId) {
1008
- try {
1009
- return await this.makeRequest('DELETE', `/api/users/${userId}/follow`, undefined, {
1010
- cache: false
1011
- });
1012
- } catch (error) {
1013
- throw this.handleError(error);
1014
- }
1015
- }
1016
-
1017
- /**
1018
- * Get follow status
1019
- */
1020
- async getFollowStatus(userId) {
1021
- try {
1022
- return await this.makeRequest('GET', `/api/users/${userId}/follow-status`, undefined, {
1023
- cache: true,
1024
- cacheTTL: 1 * 60 * 1000 // 1 minute cache
1025
- });
1026
- } catch (error) {
1027
- throw this.handleError(error);
1028
- }
1029
- }
1030
-
1031
- /**
1032
- * Get user followers
1033
- */
1034
- async getUserFollowers(userId, pagination) {
1035
- try {
1036
- const params = (0, _apiUtils.buildPaginationParams)(pagination || {});
1037
- const response = await this.makeRequest('GET', `/api/users/${userId}/followers`, params, {
1038
- cache: true,
1039
- cacheTTL: 2 * 60 * 1000 // 2 minutes cache
1040
- });
1041
- return {
1042
- followers: response.data || [],
1043
- total: response.pagination.total,
1044
- hasMore: response.pagination.hasMore
1045
- };
1046
- } catch (error) {
1047
- throw this.handleError(error);
1048
- }
1049
- }
1050
-
1051
- /**
1052
- * Get user following
1053
- */
1054
- async getUserFollowing(userId, pagination) {
1055
- try {
1056
- const params = (0, _apiUtils.buildPaginationParams)(pagination || {});
1057
- const response = await this.makeRequest('GET', `/api/users/${userId}/following`, params, {
1058
- cache: true,
1059
- cacheTTL: 2 * 60 * 1000 // 2 minutes cache
1060
- });
1061
- return {
1062
- following: response.data || [],
1063
- total: response.pagination.total,
1064
- hasMore: response.pagination.hasMore
1065
- };
1066
- } catch (error) {
1067
- throw this.handleError(error);
1068
- }
1069
- }
1070
-
1071
- /**
1072
- * Get notifications
1073
- */
1074
- async getNotifications() {
1075
- return this.withAuthRetry(async () => {
1076
- return await this.makeRequest('GET', '/api/notifications', undefined, {
1077
- cache: false // Don't cache notifications - always get fresh data
1078
- });
1079
- }, 'getNotifications');
1080
- }
1081
-
1082
- /**
1083
- * Get unread notification count
1084
- */
1085
- async getUnreadCount() {
1086
- try {
1087
- const res = await this.makeRequest('GET', '/api/notifications/unread-count', undefined, {
1088
- cache: false // Don't cache unread count - always get fresh data
1089
- });
1090
- return res.count;
1091
- } catch (error) {
1092
- throw this.handleError(error);
1093
- }
1094
- }
1095
-
1096
- /**
1097
- * Create notification
1098
- */
1099
- async createNotification(data) {
1100
- try {
1101
- return await this.makeRequest('POST', '/api/notifications', data, {
1102
- cache: false
1103
- });
1104
- } catch (error) {
1105
- throw this.handleError(error);
1106
- }
1107
- }
1108
-
1109
- /**
1110
- * Mark notification as read
1111
- */
1112
- async markNotificationAsRead(notificationId) {
1113
- try {
1114
- await this.makeRequest('PUT', `/api/notifications/${notificationId}/read`, undefined, {
1115
- cache: false
1116
- });
1117
- } catch (error) {
1118
- throw this.handleError(error);
1119
- }
1120
- }
1121
-
1122
- /**
1123
- * Mark all notifications as read
1124
- */
1125
- async markAllNotificationsAsRead() {
1126
- try {
1127
- await this.makeRequest('PUT', '/api/notifications/read-all', undefined, {
1128
- cache: false
1129
- });
1130
- } catch (error) {
1131
- throw this.handleError(error);
1132
- }
1133
- }
1134
-
1135
- /**
1136
- * Delete notification
1137
- */
1138
- async deleteNotification(notificationId) {
1139
- try {
1140
- await this.makeRequest('DELETE', `/api/notifications/${notificationId}`, undefined, {
1141
- cache: false
1142
- });
1143
- } catch (error) {
1144
- throw this.handleError(error);
1145
- }
1146
- }
1147
-
1148
- // ============================================================================
1149
- // PAYMENT METHODS
1150
- // ============================================================================
1151
-
1152
- /**
1153
- * Create a payment
1154
- */
1155
- async createPayment(data) {
1156
- try {
1157
- return await this.makeRequest('POST', '/api/payments', data, {
1158
- cache: false
1159
- });
1160
- } catch (error) {
1161
- throw this.handleError(error);
1162
- }
1163
- }
1164
-
1165
- /**
1166
- * Get payment by ID
1167
- */
1168
- async getPayment(paymentId) {
1169
- try {
1170
- return await this.makeRequest('GET', `/api/payments/${paymentId}`, undefined, {
1171
- cache: true,
1172
- cacheTTL: 5 * 60 * 1000 // 5 minutes cache
1173
- });
1174
- } catch (error) {
1175
- throw this.handleError(error);
1176
- }
1177
- }
1178
-
1179
- /**
1180
- * Get user payments
1181
- */
1182
- async getUserPayments() {
1183
- try {
1184
- return await this.makeRequest('GET', '/api/payments/user', undefined, {
1185
- cache: false // Don't cache user payments - always get fresh data
1186
- });
1187
- } catch (error) {
1188
- throw this.handleError(error);
1189
- }
1190
- }
1191
-
1192
- // ============================================================================
1193
- // KARMA METHODS
1194
- // ============================================================================
1195
-
1196
- /**
1197
- * Get user karma
1198
- */
1199
- async getUserKarma(userId) {
1200
- try {
1201
- return await this.makeRequest('GET', `/api/karma/${userId}`, undefined, {
1202
- cache: true,
1203
- cacheTTL: 2 * 60 * 1000 // 2 minutes cache
1204
- });
1205
- } catch (error) {
1206
- throw this.handleError(error);
1207
- }
1208
- }
1209
-
1210
- /**
1211
- * Give karma to user
1212
- */
1213
- async giveKarma(userId, amount, reason) {
1214
- try {
1215
- return await this.makeRequest('POST', `/api/karma/${userId}/give`, {
1216
- amount,
1217
- reason
1218
- }, {
1219
- cache: false
1220
- });
1221
- } catch (error) {
1222
- throw this.handleError(error);
1223
- }
1224
- }
1225
-
1226
- /**
1227
- * Get user karma total
1228
- */
1229
- async getUserKarmaTotal(userId) {
1230
- try {
1231
- return await this.makeRequest('GET', `/api/karma/${userId}/total`, undefined, {
1232
- cache: true,
1233
- cacheTTL: 2 * 60 * 1000 // 2 minutes cache
1234
- });
1235
- } catch (error) {
1236
- throw this.handleError(error);
1237
- }
1238
- }
1239
-
1240
- /**
1241
- * Get user karma history
1242
- */
1243
- async getUserKarmaHistory(userId, limit, offset) {
1244
- try {
1245
- const params = {};
1246
- if (limit) params.limit = limit;
1247
- if (offset) params.offset = offset;
1248
- return await this.makeRequest('GET', `/api/karma/${userId}/history`, params, {
1249
- cache: true,
1250
- cacheTTL: 2 * 60 * 1000 // 2 minutes cache
1251
- });
1252
- } catch (error) {
1253
- throw this.handleError(error);
1254
- }
1255
- }
1256
-
1257
- /**
1258
- * Get karma leaderboard
1259
- */
1260
- async getKarmaLeaderboard() {
1261
- try {
1262
- return await this.makeRequest('GET', '/api/karma/leaderboard', undefined, {
1263
- cache: true,
1264
- cacheTTL: 5 * 60 * 1000 // 5 minutes cache
1265
- });
1266
- } catch (error) {
1267
- throw this.handleError(error);
1268
- }
1269
- }
1270
-
1271
- /**
1272
- * Get karma rules
1273
- */
1274
- async getKarmaRules() {
1275
- try {
1276
- return await this.makeRequest('GET', '/api/karma/rules', undefined, {
1277
- cache: true,
1278
- cacheTTL: 30 * 60 * 1000 // 30 minutes cache (rules don't change often)
1279
- });
1280
- } catch (error) {
1281
- throw this.handleError(error);
1282
- }
1283
- }
1284
-
1285
- // ============================================================================
1286
- // FILE METHODS (LEGACY - Using Asset Service)
1287
- // ============================================================================
1288
-
1289
- /**
1290
- * Delete file
1291
- */
1292
- async deleteFile(fileId) {
1293
- try {
1294
- // Central Asset Service delete with force=true behavior controlled by caller via assetDelete
1295
- return await this.makeRequest('DELETE', `/api/assets/${encodeURIComponent(fileId)}`, undefined, {
1296
- cache: false
1297
- });
1298
- } catch (error) {
1299
- throw this.handleError(error);
1300
- }
1301
- }
1302
-
1303
- /**
1304
- * Get file download URL (API streaming proxy, attaches token for <img src>)
1305
- */
1306
- getFileDownloadUrl(fileId, variant, expiresIn) {
1307
- const base = this.getBaseURL();
1308
- const params = new URLSearchParams();
1309
- if (variant) params.set('variant', variant);
1310
- if (expiresIn) params.set('expiresIn', String(expiresIn));
1311
- params.set('fallback', 'placeholderVisible');
1312
- const token = this.httpClient.getAccessToken();
1313
- if (token) params.set('token', token);
1314
-
1315
- // Use params.toString() to detect whether there are query params.
1316
- // URLSearchParams.size is not a standard property across all JS runtimes
1317
- // (some environments like React Native may not implement it), which
1318
- // caused the query string to be omitted on native. Checking the
1319
- // serialized string is reliable everywhere.
1320
- const qs = params.toString();
1321
- return `${base}/api/assets/${encodeURIComponent(fileId)}/stream${qs ? `?${qs}` : ''}`;
1322
- }
1323
-
1324
- /**
1325
- * Get file stream URL (direct Oxy Cloud/CDN URL, no token)
1326
- */
1327
- getFileStreamUrl(fileId) {
1328
- return `${this.getCloudURL()}/files/${fileId}/stream`;
1329
- }
1330
-
1331
- // ...existing code...
1332
-
1333
- /**
1334
- * List user files
1335
- */
1336
- async listUserFiles(limit, offset) {
1337
- try {
1338
- const paramsObj = {};
1339
- if (limit) paramsObj.limit = limit;
1340
- if (offset) paramsObj.offset = offset;
1341
- return await this.makeRequest('GET', '/api/assets', paramsObj, {
1342
- cache: false // Don't cache file lists - always get fresh data
1343
- });
1344
- } catch (error) {
1345
- throw this.handleError(error);
1346
- }
1347
- }
1348
-
1349
- // (removed legacy downloadFileContent; use getFileContentAsBlob/Text which resolve CAS URL first)
1350
-
1351
- /**
1352
- * Get file content as text
1353
- */
1354
- async getFileContentAsText(fileId, variant) {
1355
- try {
1356
- const params = variant ? {
1357
- variant
1358
- } : undefined;
1359
- const urlRes = await this.makeRequest('GET', `/api/assets/${encodeURIComponent(fileId)}/url`, params, {
1360
- cache: true,
1361
- cacheTTL: 10 * 60 * 1000 // 10 minutes cache for URLs
1362
- });
1363
- const downloadUrl = urlRes?.url;
1364
- const response = await fetch(downloadUrl);
1365
- return await response.text();
1366
- } catch (error) {
1367
- throw this.handleError(error);
1368
- }
1369
- }
1370
-
1371
- /**
1372
- * Get file content as blob
1373
- */
1374
- async getFileContentAsBlob(fileId, variant) {
1375
- try {
1376
- const params = variant ? {
1377
- variant
1378
- } : undefined;
1379
- const urlRes = await this.makeRequest('GET', `/api/assets/${encodeURIComponent(fileId)}/url`, params, {
1380
- cache: true,
1381
- cacheTTL: 10 * 60 * 1000 // 10 minutes cache for URLs
1382
- });
1383
- const downloadUrl = urlRes?.url;
1384
- const response = await fetch(downloadUrl);
1385
- return await response.blob();
1386
- } catch (error) {
1387
- throw this.handleError(error);
1388
- }
1389
- }
1390
-
1391
- /**
1392
- * Upload raw file data
1393
- */
1394
- async uploadRawFile(file, visibility, metadata) {
1395
- // Switch to Central Asset Service upload flow
1396
- return this.assetUpload(file, visibility, metadata);
1397
- }
1398
-
1399
- // ============================================================================
1400
- // CENTRAL ASSET SERVICE METHODS
1401
- // ============================================================================
1402
-
1403
- /**
1404
- * Calculate SHA256 hash of file content
1405
- */
1406
- async calculateSHA256(file) {
1407
- const buffer = await file.arrayBuffer();
1408
- const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
1409
- const hashArray = Array.from(new Uint8Array(hashBuffer));
1410
- return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
1411
- }
1412
-
1413
- /**
1414
- * Initialize asset upload - returns pre-signed URL and file ID
1415
- */
1416
- async assetInit(sha256, size, mime) {
1417
- try {
1418
- return await this.makeRequest('POST', '/api/assets/init', {
1419
- sha256,
1420
- size,
1421
- mime
1422
- }, {
1423
- cache: false
1424
- });
1425
- } catch (error) {
1426
- throw this.handleError(error);
1427
- }
1428
- }
1429
-
1430
- /**
1431
- * Complete asset upload - commit metadata and trigger variant generation
1432
- */
1433
- async assetComplete(fileId, originalName, size, mime, visibility, metadata) {
1434
- try {
1435
- return await this.makeRequest('POST', '/api/assets/complete', {
1436
- fileId,
1437
- originalName,
1438
- size,
1439
- mime,
1440
- visibility,
1441
- metadata
1442
- }, {
1443
- cache: false
1444
- });
1445
- } catch (error) {
1446
- throw this.handleError(error);
1447
- }
1448
- }
1449
-
1450
- /**
1451
- * Upload file using Central Asset Service
1452
- */
1453
- async assetUpload(file, visibility, metadata, onProgress) {
1454
- try {
1455
- // Calculate SHA256
1456
- const sha256 = await this.calculateSHA256(file);
1457
-
1458
- // Initialize upload
1459
- const initResponse = await this.assetInit(sha256, file.size, file.type);
1460
-
1461
- // Try presigned URL first
1462
- try {
1463
- await this.uploadToPresignedUrl(initResponse.uploadUrl, file, onProgress);
1464
- } catch (e) {
1465
- // Fallback: direct upload via API to avoid CORS issues
1466
- const fd = new FormData();
1467
- fd.append('file', file);
1468
- // Use httpClient directly for FormData uploads (bypasses RequestManager for special handling)
1469
- await this.httpClient.request({
1470
- method: 'POST',
1471
- url: `/api/assets/${encodeURIComponent(initResponse.fileId)}/upload-direct`,
1472
- data: fd
1473
- });
1474
- }
1475
-
1476
- // Complete upload
1477
- return await this.assetComplete(initResponse.fileId, file.name, file.size, file.type, visibility, metadata);
1478
- } catch (error) {
1479
- throw this.handleError(error);
1480
- }
1481
- }
1482
-
1483
- /**
1484
- * Upload file to pre-signed URL
1485
- */
1486
- async uploadToPresignedUrl(url, file, onProgress) {
1487
- return new Promise((resolve, reject) => {
1488
- const xhr = new XMLHttpRequest();
1489
- xhr.upload.addEventListener('progress', event => {
1490
- if (event.lengthComputable && onProgress) {
1491
- const progress = event.loaded / event.total * 100;
1492
- onProgress(progress);
1493
- }
1494
- });
1495
- xhr.addEventListener('load', () => {
1496
- if (xhr.status >= 200 && xhr.status < 300) {
1497
- resolve();
1498
- } else {
1499
- reject(new Error(`Upload failed with status ${xhr.status}`));
1500
- }
1501
- });
1502
- xhr.addEventListener('error', () => {
1503
- reject(new Error('Upload failed'));
1504
- });
1505
- xhr.open('PUT', url);
1506
- xhr.setRequestHeader('Content-Type', file.type);
1507
- xhr.send(file);
1508
- });
1509
- }
1510
-
1511
- /**
1512
- * Link asset to an entity
1513
- */
1514
- async assetLink(fileId, app, entityType, entityId, visibility, webhookUrl) {
1515
- try {
1516
- const body = {
1517
- app,
1518
- entityType,
1519
- entityId
1520
- };
1521
- if (visibility) body.visibility = visibility;
1522
- if (webhookUrl) body.webhookUrl = webhookUrl;
1523
- return await this.makeRequest('POST', `/api/assets/${fileId}/links`, body, {
1524
- cache: false
1525
- });
1526
- } catch (error) {
1527
- throw this.handleError(error);
1528
- }
1529
- }
1530
-
1531
- /**
1532
- * Unlink asset from an entity
1533
- */
1534
- async assetUnlink(fileId, app, entityType, entityId) {
1535
- try {
1536
- return await this.makeRequest('DELETE', `/api/assets/${fileId}/links`, {
1537
- app,
1538
- entityType,
1539
- entityId
1540
- }, {
1541
- cache: false
1542
- });
1543
- } catch (error) {
1544
- throw this.handleError(error);
1545
- }
1546
- }
1547
-
1548
- /**
1549
- * Get asset metadata
1550
- */
1551
- async assetGet(fileId) {
1552
- try {
1553
- return await this.makeRequest('GET', `/api/assets/${fileId}`, undefined, {
1554
- cache: true,
1555
- cacheTTL: 5 * 60 * 1000 // 5 minutes cache
1556
- });
1557
- } catch (error) {
1558
- throw this.handleError(error);
1559
- }
1560
- }
1561
-
1562
- /**
1563
- * Get asset URL (CDN or signed URL)
1564
- */
1565
- async assetGetUrl(fileId, variant, expiresIn) {
1566
- try {
1567
- const params = {};
1568
- if (variant) params.variant = variant;
1569
- if (expiresIn) params.expiresIn = expiresIn;
1570
- return await this.makeRequest('GET', `/api/assets/${fileId}/url`, params, {
1571
- cache: true,
1572
- cacheTTL: 10 * 60 * 1000 // 10 minutes cache for URLs
1573
- });
1574
- } catch (error) {
1575
- throw this.handleError(error);
1576
- }
1577
- }
1578
-
1579
- /**
1580
- * Restore asset from trash
1581
- */
1582
- async assetRestore(fileId) {
1583
- try {
1584
- return await this.makeRequest('POST', `/api/assets/${fileId}/restore`, undefined, {
1585
- cache: false
1586
- });
1587
- } catch (error) {
1588
- throw this.handleError(error);
1589
- }
1590
- }
1591
-
1592
- /**
1593
- * Delete asset with optional force
1594
- */
1595
- async assetDelete(fileId, force = false) {
1596
- try {
1597
- const params = force ? {
1598
- force: 'true'
1599
- } : undefined;
1600
- return await this.makeRequest('DELETE', `/api/assets/${fileId}`, params, {
1601
- cache: false
1602
- });
1603
- } catch (error) {
1604
- throw this.handleError(error);
1605
- }
1606
- }
1607
-
1608
- /**
1609
- * Get list of available variants for an asset
1610
- */
1611
- async assetGetVariants(fileId) {
1612
- try {
1613
- const assetData = await this.assetGet(fileId);
1614
- return assetData.file?.variants || [];
1615
- } catch (error) {
1616
- throw this.handleError(error);
1617
- }
1618
- }
1619
-
1620
- /**
1621
- * Update asset visibility
1622
- * @param fileId - The file ID
1623
- * @param visibility - New visibility level ('private', 'public', or 'unlisted')
1624
- * @returns Updated asset information
1625
- */
1626
- async assetUpdateVisibility(fileId, visibility) {
1627
- try {
1628
- return await this.makeRequest('PATCH', `/api/assets/${fileId}/visibility`, {
1629
- visibility
1630
- }, {
1631
- cache: false
1632
- });
1633
- } catch (error) {
1634
- throw this.handleError(error);
1635
- }
1636
- }
1637
-
1638
- /**
1639
- * Helper: Upload and link avatar with automatic public visibility
1640
- * @param file - The avatar file
1641
- * @param userId - User ID to link to
1642
- * @param app - App name (defaults to 'profiles')
1643
- * @returns The uploaded and linked asset
1644
- */
1645
- async uploadAvatar(file, userId, app = 'profiles') {
1646
- try {
1647
- // Upload as public
1648
- const asset = await this.assetUpload(file, 'public');
1649
-
1650
- // Link to user profile as avatar
1651
- await this.assetLink(asset.file.id, app, 'avatar', userId, 'public');
1652
- return asset;
1653
- } catch (error) {
1654
- throw this.handleError(error);
1655
- }
1656
- }
1657
-
1658
- /**
1659
- * Helper: Upload and link profile banner with automatic public visibility
1660
- * @param file - The banner file
1661
- * @param userId - User ID to link to
1662
- * @param app - App name (defaults to 'profiles')
1663
- * @returns The uploaded and linked asset
1664
- */
1665
- async uploadProfileBanner(file, userId, app = 'profiles') {
1666
- try {
1667
- // Upload as public
1668
- const asset = await this.assetUpload(file, 'public');
1669
-
1670
- // Link to user profile as banner
1671
- await this.assetLink(asset.file.id, app, 'profile-banner', userId, 'public');
1672
- return asset;
1673
- } catch (error) {
1674
- throw this.handleError(error);
1675
- }
1676
- }
1677
-
1678
- // ============================================================================
1679
- // DEVELOPER API METHODS
1680
- // ============================================================================
1681
-
1682
- /**
1683
- * Get developer apps for the current user
1684
- */
1685
- async getDeveloperApps() {
1686
- try {
1687
- const res = await this.makeRequest('GET', '/api/developer/apps', undefined, {
1688
- cache: true,
1689
- cacheTTL: 2 * 60 * 1000 // 2 minutes cache
1690
- });
1691
- return res.apps || [];
1692
- } catch (error) {
1693
- throw this.handleError(error);
1694
- }
1695
- }
1696
-
1697
- /**
1698
- * Create a new developer app
1699
- */
1700
- async createDeveloperApp(data) {
1701
- try {
1702
- const res = await this.makeRequest('POST', '/api/developer/apps', data, {
1703
- cache: false
1704
- });
1705
- return res.app;
1706
- } catch (error) {
1707
- throw this.handleError(error);
1708
- }
1709
- }
1710
-
1711
- /**
1712
- * Get a specific developer app
1713
- */
1714
- async getDeveloperApp(appId) {
1715
- try {
1716
- const res = await this.makeRequest('GET', `/api/developer/apps/${appId}`, undefined, {
1717
- cache: true,
1718
- cacheTTL: 5 * 60 * 1000 // 5 minutes cache
1719
- });
1720
- return res.app;
1721
- } catch (error) {
1722
- throw this.handleError(error);
1723
- }
1724
- }
1725
-
1726
- /**
1727
- * Update a developer app
1728
- */
1729
- async updateDeveloperApp(appId, data) {
1730
- try {
1731
- const res = await this.makeRequest('PATCH', `/api/developer/apps/${appId}`, data, {
1732
- cache: false
1733
- });
1734
- return res.app;
1735
- } catch (error) {
1736
- throw this.handleError(error);
1737
- }
1738
- }
1739
-
1740
- /**
1741
- * Regenerate API secret for a developer app
1742
- */
1743
- async regenerateDeveloperAppSecret(appId) {
1744
- try {
1745
- return await this.makeRequest('POST', `/api/developer/apps/${appId}/regenerate-secret`, undefined, {
1746
- cache: false
1747
- });
1748
- } catch (error) {
1749
- throw this.handleError(error);
1750
- }
1751
- }
1752
-
1753
- /**
1754
- * Delete a developer app
1755
- */
1756
- async deleteDeveloperApp(appId) {
1757
- try {
1758
- return await this.makeRequest('DELETE', `/api/developer/apps/${appId}`, undefined, {
1759
- cache: false
1760
- });
1761
- } catch (error) {
1762
- throw this.handleError(error);
1763
- }
1764
- }
1765
-
1766
- // ============================================================================
1767
- // LOCATION METHODS
1768
- // ============================================================================
1769
-
1770
- /**
1771
- * Update user location
1772
- */
1773
- async updateLocation(latitude, longitude) {
1774
- try {
1775
- return await this.makeRequest('POST', '/api/location', {
1776
- latitude,
1777
- longitude
1778
- }, {
1779
- cache: false
1780
- });
1781
- } catch (error) {
1782
- throw this.handleError(error);
1783
- }
1784
- }
1785
-
1786
- /**
1787
- * Get nearby users
1788
- */
1789
- async getNearbyUsers(radius) {
1790
- try {
1791
- const params = radius ? {
1792
- radius
1793
- } : undefined;
1794
- return await this.makeRequest('GET', '/api/location/nearby', params, {
1795
- cache: false // Don't cache location data - always get fresh data
1796
- });
1797
- } catch (error) {
1798
- throw this.handleError(error);
1799
- }
1800
- }
1801
-
1802
- // ============================================================================
1803
- // ANALYTICS METHODS
1804
- // ============================================================================
1805
-
1806
- /**
1807
- * Track event
1808
- */
1809
- async trackEvent(eventName, properties) {
1810
- try {
1811
- await this.makeRequest('POST', '/api/analytics/events', {
1812
- event: eventName,
1813
- properties
1814
- }, {
1815
- cache: false,
1816
- retry: false
1817
- }); // Don't retry analytics events
1818
- } catch (error) {
1819
- throw this.handleError(error);
1820
- }
1821
- }
1822
-
1823
- /**
1824
- * Get analytics data
1825
- */
1826
- async getAnalytics(startDate, endDate) {
1827
- try {
1828
- const params = {};
1829
- if (startDate) params.startDate = startDate;
1830
- if (endDate) params.endDate = endDate;
1831
- return await this.makeRequest('GET', '/api/analytics', params, {
1832
- cache: true,
1833
- cacheTTL: 5 * 60 * 1000 // 5 minutes cache
1834
- });
1835
- } catch (error) {
1836
- throw this.handleError(error);
1837
- }
1838
- }
1839
-
1840
- // ============================================================================
1841
- // DEVICE METHODS
1842
- // ============================================================================
1843
-
1844
- /**
1845
- * Register device
1846
- */
1847
- async registerDevice(deviceData) {
1848
- try {
1849
- return await this.makeRequest('POST', '/api/devices', deviceData, {
1850
- cache: false
1851
- });
1852
- } catch (error) {
1853
- throw this.handleError(error);
1854
- }
1855
- }
1856
-
1857
- /**
1858
- * Get user devices
1859
- */
1860
- async getUserDevices() {
1861
- try {
1862
- return await this.makeRequest('GET', '/api/devices', undefined, {
1863
- cache: false // Don't cache device list - always get fresh data
1864
- });
1865
- } catch (error) {
1866
- throw this.handleError(error);
1867
- }
1868
- }
1869
-
1870
- /**
1871
- * Remove device
1872
- */
1873
- async removeDevice(deviceId) {
1874
- try {
1875
- await this.makeRequest('DELETE', `/api/devices/${deviceId}`, undefined, {
1876
- cache: false
1877
- });
1878
- } catch (error) {
1879
- throw this.handleError(error);
1880
- }
1881
- }
1882
-
1883
- /**
1884
- * Get device sessions
1885
- * Note: Not cached by default to ensure fresh data, but can be cached via makeRequest if needed
1886
- */
1887
- async getDeviceSessions(sessionId) {
1888
- try {
1889
- // Use makeRequest for consistent error handling and optional caching
1890
- // Cache disabled by default to ensure fresh session data
1891
- return await this.makeRequest('GET', `/api/session/device/sessions/${sessionId}`, undefined, {
1892
- cache: false,
1893
- // Don't cache sessions - always get fresh data
1894
- deduplicate: true // Deduplicate concurrent requests for same sessionId
1895
- });
1896
- } catch (error) {
1897
- throw this.handleError(error);
1898
- }
1899
- }
1900
-
1901
- /**
1902
- * Logout all device sessions
1903
- */
1904
- async logoutAllDeviceSessions(sessionId, deviceId, excludeCurrent) {
1905
- try {
1906
- const params = new URLSearchParams();
1907
- if (deviceId) params.append('deviceId', deviceId);
1908
- if (excludeCurrent) params.append('excludeCurrent', 'true');
1909
- const urlParams = {};
1910
- params.forEach((value, key) => {
1911
- urlParams[key] = value;
1912
- });
1913
- return await this.makeRequest('POST', `/api/session/device/logout-all/${sessionId}`, urlParams, {
1914
- cache: false
1915
- });
1916
- } catch (error) {
1917
- throw this.handleError(error);
1918
- }
1919
- }
1920
-
1921
- /**
1922
- * Update device name
1923
- */
1924
- async updateDeviceName(sessionId, deviceName) {
1925
- try {
1926
- return await this.makeRequest('PUT', `/api/session/device/name/${sessionId}`, {
1927
- deviceName
1928
- }, {
1929
- cache: false
1930
- });
1931
- } catch (error) {
1932
- throw this.handleError(error);
1933
- }
1934
- }
1935
-
1936
- // ============================================================================
1937
- // UTILITY METHODS
1938
- // ============================================================================
1939
-
1940
- /**
1941
- * Fetch link metadata
1942
- */
1943
- async fetchLinkMetadata(url) {
1944
- try {
1945
- return await this.makeRequest('GET', '/api/link-metadata', {
1946
- url
1947
- }, {
1948
- cache: true,
1949
- cacheTTL: 30 * 60 * 1000 // 30 minutes cache for link metadata
1950
- });
1951
- } catch (error) {
1952
- throw this.handleError(error);
1953
- }
1954
- }
1955
-
1956
- /**
1957
- * Simple Express.js authentication middleware
1958
- *
1959
- * Built-in authentication middleware that validates JWT tokens and adds user data to requests.
1960
- *
1961
- * @example
1962
- * ```typescript
1963
- * // Basic usage - just add it to your routes
1964
- * app.use('/api/protected', oxyServices.auth());
1965
- *
1966
- * // With debug logging
1967
- * app.use('/api/protected', oxyServices.auth({ debug: true }));
1968
- *
1969
- * // With custom error handling
1970
- * app.use('/api/protected', oxyServices.auth({
1971
- * onError: (error) => console.error('Auth failed:', error)
1972
- * }));
1973
- *
1974
- * // Load full user data
1975
- * app.use('/api/protected', oxyServices.auth({ loadUser: true }));
1976
- * ```
1977
- *
1978
- * @param options Optional configuration
1979
- * @param options.debug Enable debug logging (default: false)
1980
- * @param options.onError Custom error handler
1981
- * @param options.loadUser Load full user data (default: false for performance)
1982
- * @param options.session Use session-based validation (default: false)
1983
- * @returns Express middleware function
1984
- */
1985
- auth(options = {}) {
1986
- const {
1987
- debug = false,
1988
- onError,
1989
- loadUser = false,
1990
- session = false
1991
- } = options;
1992
-
1993
- // Return a synchronous middleware function
1994
- return (req, res, next) => {
1995
- try {
1996
- // Extract token from Authorization header
1997
- const authHeader = req.headers['authorization'];
1998
- const token = authHeader?.startsWith('Bearer ') ? authHeader.substring(7) : null;
1999
- if (debug) {
2000
- console.log(`🔐 Auth: Processing ${req.method} ${req.path}`);
2001
- console.log(`🔐 Auth: Token present: ${!!token}`);
2002
- }
2003
- if (!token) {
2004
- const error = {
2005
- message: 'Access token required',
2006
- code: 'MISSING_TOKEN',
2007
- status: 401
2008
- };
2009
- if (debug) console.log(`❌ Auth: Missing token`);
2010
- if (onError) return onError(error);
2011
- return res.status(401).json(error);
2012
- }
2013
-
2014
- // Decode and validate token
2015
- let decoded;
2016
- try {
2017
- decoded = (0, _jwtDecode.jwtDecode)(token);
2018
- if (debug) {
2019
- console.log(`🔐 Auth: Token decoded, User ID: ${decoded.userId || decoded.id}`);
2020
- }
2021
- } catch (decodeError) {
2022
- const error = {
2023
- message: 'Invalid token format',
2024
- code: 'INVALID_TOKEN_FORMAT',
2025
- status: 403
2026
- };
2027
- if (debug) console.log(`❌ Auth: Token decode failed`);
2028
- if (onError) return onError(error);
2029
- return res.status(403).json(error);
2030
- }
2031
- const userId = decoded.userId || decoded.id;
2032
- if (!userId) {
2033
- const error = {
2034
- message: 'Token missing user ID',
2035
- code: 'INVALID_TOKEN_PAYLOAD',
2036
- status: 403
2037
- };
2038
- if (debug) console.log(`❌ Auth: Token missing user ID`);
2039
- if (onError) return onError(error);
2040
- return res.status(403).json(error);
2041
- }
2042
-
2043
- // Check token expiration
2044
- if (decoded.exp && decoded.exp < Math.floor(Date.now() / 1000)) {
2045
- const error = {
2046
- message: 'Token expired',
2047
- code: 'TOKEN_EXPIRED',
2048
- status: 403
2049
- };
2050
- if (debug) console.log(`❌ Auth: Token expired`);
2051
- if (onError) return onError(error);
2052
- return res.status(403).json(error);
2053
- }
2054
-
2055
- // For now, skip session validation to keep it simple
2056
- // Session validation can be added later if needed
2057
-
2058
- // Set request properties immediately
2059
- req.userId = userId;
2060
- req.accessToken = token;
2061
- req.user = {
2062
- id: userId
2063
- };
2064
- if (debug) {
2065
- console.log(`✅ Auth: Authentication successful for user ${userId}`);
2066
- }
2067
- next();
2068
- } catch (error) {
2069
- const apiError = this.handleError(error);
2070
- if (debug) {
2071
- console.log(`❌ Auth: Unexpected error:`, apiError);
2072
- }
2073
- if (onError) return onError(apiError);
2074
- return res.status(apiError && apiError.status || 500).json(apiError);
2075
- }
2076
- };
121
+ // Export as a named class to avoid TypeScript issues with anonymous class types
122
+ class OxyServices extends OxyServicesComposed {
123
+ constructor(config) {
124
+ super(config);
2077
125
  }
2078
126
  }
2079
127
 
128
+ // Re-export error classes for convenience
129
+ exports.OxyServices = OxyServices;
2080
130
  /**
2081
131
  * Export the default Oxy Cloud URL (for backward compatibility)
2082
132
  */
2083
- exports.OxyServices = OxyServices;
2084
133
  const OXY_CLOUD_URL = exports.OXY_CLOUD_URL = 'https://cloud.oxy.so';
2085
134
 
2086
135
  /**