@oxyhq/core 3.2.0 → 3.4.0

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 (73) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/AuthManager.js +3 -1
  3. package/dist/cjs/HttpService.js +89 -0
  4. package/dist/cjs/OxyServices.js +1 -1
  5. package/dist/cjs/constants/version.js +1 -1
  6. package/dist/cjs/i18n/locales/en-US.json +44 -44
  7. package/dist/cjs/i18n/locales/es-ES.json +44 -44
  8. package/dist/cjs/i18n/locales/locales/en-US.json +44 -44
  9. package/dist/cjs/i18n/locales/locales/es-ES.json +44 -44
  10. package/dist/cjs/index.js +4 -0
  11. package/dist/cjs/mixins/OxyServices.applications.js +3 -1
  12. package/dist/cjs/mixins/OxyServices.reputation.js +244 -0
  13. package/dist/cjs/mixins/OxyServices.workspaces.js +3 -1
  14. package/dist/cjs/mixins/index.js +2 -2
  15. package/dist/cjs/utils/accountUtils.js +12 -5
  16. package/dist/cjs/utils/ssoReturn.js +80 -33
  17. package/dist/esm/.tsbuildinfo +1 -1
  18. package/dist/esm/AuthManager.js +3 -1
  19. package/dist/esm/HttpService.js +89 -0
  20. package/dist/esm/OxyServices.js +1 -1
  21. package/dist/esm/constants/version.js +1 -1
  22. package/dist/esm/i18n/locales/en-US.json +44 -44
  23. package/dist/esm/i18n/locales/es-ES.json +44 -44
  24. package/dist/esm/i18n/locales/locales/en-US.json +44 -44
  25. package/dist/esm/i18n/locales/locales/es-ES.json +44 -44
  26. package/dist/esm/index.js +4 -0
  27. package/dist/esm/mixins/OxyServices.applications.js +3 -1
  28. package/dist/esm/mixins/OxyServices.reputation.js +241 -0
  29. package/dist/esm/mixins/OxyServices.workspaces.js +3 -1
  30. package/dist/esm/mixins/index.js +2 -2
  31. package/dist/esm/utils/accountUtils.js +12 -5
  32. package/dist/esm/utils/ssoReturn.js +80 -33
  33. package/dist/types/.tsbuildinfo +1 -1
  34. package/dist/types/HttpService.d.ts +57 -0
  35. package/dist/types/OxyServices.d.ts +1 -1
  36. package/dist/types/constants/version.d.ts +2 -2
  37. package/dist/types/index.d.ts +2 -1
  38. package/dist/types/mixins/OxyServices.applications.d.ts +8 -2
  39. package/dist/types/mixins/OxyServices.features.d.ts +0 -1
  40. package/dist/types/mixins/OxyServices.reputation.d.ts +436 -0
  41. package/dist/types/mixins/OxyServices.workspaces.d.ts +8 -2
  42. package/dist/types/mixins/index.d.ts +2 -2
  43. package/dist/types/models/interfaces.d.ts +15 -26
  44. package/dist/types/utils/accountUtils.d.ts +17 -4
  45. package/dist/types/utils/ssoReturn.d.ts +30 -9
  46. package/package.json +2 -1
  47. package/src/AuthManager.ts +3 -1
  48. package/src/HttpService.ts +91 -0
  49. package/src/OxyServices.ts +1 -1
  50. package/src/__tests__/httpServiceCache.test.ts +198 -0
  51. package/src/constants/version.ts +1 -1
  52. package/src/i18n/locales/en-US.json +44 -44
  53. package/src/i18n/locales/es-ES.json +44 -44
  54. package/src/index.ts +32 -4
  55. package/src/mixins/OxyServices.applications.ts +8 -2
  56. package/src/mixins/OxyServices.auth.ts +2 -1
  57. package/src/mixins/OxyServices.features.ts +0 -1
  58. package/src/mixins/OxyServices.reputation.ts +674 -0
  59. package/src/mixins/OxyServices.workspaces.ts +8 -2
  60. package/src/mixins/__tests__/reputation.test.ts +408 -0
  61. package/src/mixins/index.ts +3 -3
  62. package/src/models/interfaces.ts +16 -32
  63. package/src/utils/__tests__/accountUtils.test.ts +142 -0
  64. package/src/utils/__tests__/consumeSsoReturn.test.ts +229 -37
  65. package/src/utils/accountUtils.ts +20 -5
  66. package/src/utils/ssoReturn.ts +98 -37
  67. package/dist/cjs/mixins/OxyServices.developer.js +0 -97
  68. package/dist/cjs/mixins/OxyServices.karma.js +0 -108
  69. package/dist/esm/mixins/OxyServices.developer.js +0 -94
  70. package/dist/esm/mixins/OxyServices.karma.js +0 -105
  71. package/dist/types/mixins/OxyServices.developer.d.ts +0 -106
  72. package/dist/types/mixins/OxyServices.karma.d.ts +0 -92
  73. package/src/mixins/OxyServices.karma.ts +0 -111
@@ -135,9 +135,22 @@ export interface ConsumeSsoReturnDeps {
135
135
  * location changed via `history.replaceState`, which does NOT itself emit
136
136
  * `popstate`. Default: dispatch a real `PopStateEvent` on `window` when
137
137
  * present; no-op off-web. Called ONLY after a successful same-origin
138
- * dest restore (never when the dest is rejected/absent). NEVER throws.
138
+ * dest restore on the `ok` path (never when the dest is rejected/absent).
139
+ * NEVER throws.
139
140
  */
140
141
  dispatchPopState?: () => void;
142
+ /**
143
+ * Hard, full-document navigation used to leave the internal callback path on
144
+ * every NON-`ok` outcome (`none`/`error`, state-mismatch, missing code,
145
+ * failed exchange, missing sessionId). A SOFT `history.replaceState` +
146
+ * synthetic `popstate` does NOT reliably make Expo Router / TanStack Router
147
+ * re-resolve away from the 404 they have already rendered for the
148
+ * unregistered callback route — so for these outcomes (where there is no
149
+ * in-memory session to preserve) a full navigation is both safe and
150
+ * guaranteed to clear the 404. Default: `window.location.replace(url)` when
151
+ * present; feature-detected end to end so it never throws off-web.
152
+ */
153
+ hardRedirect?: (url: string) => void;
141
154
  }
142
155
 
143
156
  /**
@@ -164,14 +177,22 @@ export interface ConsumeSsoReturnDeps {
164
177
  * treated exactly like "no session" (never loops, never rethrows).
165
178
  * - On EVERY consumed outcome (ok, none, error, state-mismatch, no-code,
166
179
  * failed-exchange, no-sessionId) — not just ok — if the page landed on
167
- * {@link SSO_CALLBACK_PATH}, the real pre-bounce destination is restored
168
- * from the DEST key so the user is never stranded on the internal callback
169
- * path. Same-origin only (an attacker-planted cross-origin or relative-evil
170
- * dest is rejected). The DEST key is removed unconditionally.
171
- * - After a same-origin dest restore (which uses `history.replaceState`, that
172
- * does NOT itself emit `popstate`), a synthetic `popstate` is dispatched so
173
- * URL-driven routers (Expo Router / React Navigation web) re-sync to the
174
- * restored route. It is NOT dispatched when the dest is rejected/absent.
180
+ * {@link SSO_CALLBACK_PATH}, the user is taken to a same-origin TARGET so
181
+ * they are never stranded on the internal callback path (which is an
182
+ * unregistered route in every consumer router a hard 404). The target is
183
+ * the stored DEST when it parses as same-origin (an attacker-planted
184
+ * cross-origin / protocol-relative dest is rejected), ELSE the app root
185
+ * (`origin + '/'`). The DEST key is removed unconditionally.
186
+ * - For the `ok` outcome the target is applied via a SOFT
187
+ * `history.replaceState` + synthetic `popstate` so the freshly exchanged
188
+ * in-memory session the provider is about to commit is preserved (no
189
+ * reload). `popstate` is dispatched only on the `ok` same-origin restore.
190
+ * - For every NON-`ok` outcome there is no in-memory session to preserve, and
191
+ * the consumer router has ALREADY synchronously rendered its 404 for the
192
+ * unregistered callback route — a soft replaceState+popstate does not
193
+ * reliably make it re-resolve. So these outcomes perform a HARD
194
+ * full-document navigation to the target (`hardRedirect`), which is both
195
+ * safe (nothing to lose) and guaranteed to clear the 404 in every router.
175
196
  *
176
197
  * Total: this function NEVER throws. Off-web it is a no-op returning `null`.
177
198
  *
@@ -214,6 +235,21 @@ export async function consumeSsoReturn(
214
235
  }
215
236
  });
216
237
 
238
+ // Default: a hard, full-document navigation used to leave the callback path
239
+ // on non-`ok` outcomes. Feature-detected end to end so it never throws in any
240
+ // environment (SSR / native / a stubbed location without `replace`).
241
+ const hardRedirect =
242
+ deps.hardRedirect ??
243
+ ((url: string) => {
244
+ if (
245
+ typeof window !== 'undefined' &&
246
+ window.location &&
247
+ typeof window.location.replace === 'function'
248
+ ) {
249
+ window.location.replace(url);
250
+ }
251
+ });
252
+
217
253
  const ret = parseSsoReturnFragment(location.hash);
218
254
  if (!ret) {
219
255
  // Not an oxy_sso fragment — nothing to do (do NOT touch any flags).
@@ -241,39 +277,56 @@ export async function consumeSsoReturn(
241
277
  storage.setItem(ssoAttemptedKey(origin), '1');
242
278
  };
243
279
 
244
- // Restore the user's real pre-bounce destination so they are never stranded
245
- // on the internal callback path invoked on EVERY consumed outcome, not just
246
- // success. Same-origin only never honour a cross-origin/protocol-relative
247
- // dest that could have been planted to redirect the user. The DEST key is
248
- // removed unconditionally. After a successful same-origin restore a synthetic
249
- // `popstate` is dispatched so URL-driven routers re-sync.
250
- const restoreDest = (): void => {
251
- if (location.pathname === SSO_CALLBACK_PATH) {
252
- const dest = storage.getItem(ssoDestKey(origin));
253
- if (dest) {
254
- try {
255
- const destUrl = new URL(dest, origin);
256
- if (destUrl.origin === origin) {
257
- history.replaceState(
258
- null,
259
- '',
260
- destUrl.pathname + destUrl.search + destUrl.hash,
261
- );
262
- dispatchPopState();
263
- }
264
- } catch {
265
- // Malformed stored destination leave the URL on the callback path.
280
+ // Compute the same-origin TARGET to leave the callback path for. Returns the
281
+ // stored DEST when present AND it parses as same-origin (never honour a
282
+ // cross-origin / protocol-relative dest that could have been planted to
283
+ // redirect the user), ELSE the app root (`origin + '/'`) so the user is never
284
+ // stranded on the internal callback path even when no dest was stored. The
285
+ // DEST key is removed unconditionally. Returns the relative path+search+hash
286
+ // (so it can be fed to either `history.replaceState` or a `hardRedirect`),
287
+ // or `null` when the page is not on the callback path (nothing to leave).
288
+ const consumeCallbackTarget = (): string | null => {
289
+ if (location.pathname !== SSO_CALLBACK_PATH) {
290
+ // Not on the callback path — still drop the dest key (consumed) but there
291
+ // is nothing to navigate away from.
292
+ storage.removeItem(ssoDestKey(origin));
293
+ return null;
294
+ }
295
+ const dest = storage.getItem(ssoDestKey(origin));
296
+ storage.removeItem(ssoDestKey(origin));
297
+ if (dest) {
298
+ try {
299
+ const destUrl = new URL(dest, origin);
300
+ if (destUrl.origin === origin) {
301
+ return destUrl.pathname + destUrl.search + destUrl.hash;
266
302
  }
303
+ } catch {
304
+ // Malformed stored destination — fall through to the app-root fallback.
267
305
  }
268
306
  }
269
- storage.removeItem(ssoDestKey(origin));
307
+ // No dest, a cross-origin/protocol-relative dest, or an unparseable dest:
308
+ // fall back to the app root so the router always leaves the 404.
309
+ return '/';
310
+ };
311
+
312
+ // Non-`ok` outcomes: there is no in-memory session to preserve, and the
313
+ // consumer router has already rendered its 404 for the unregistered callback
314
+ // route — a soft replaceState+popstate does not reliably make it re-resolve.
315
+ // Perform a HARD full-document navigation to the target (safe: nothing to
316
+ // lose; guaranteed: every router leaves the 404). Off the callback path this
317
+ // is a no-op (target is null).
318
+ const leaveCallbackHard = (): void => {
319
+ const target = consumeCallbackTarget();
320
+ if (target !== null) {
321
+ hardRedirect(origin + target);
322
+ }
270
323
  };
271
324
 
272
325
  if (ret.kind === 'none' || ret.kind === 'error') {
273
326
  // The central IdP had no session (or the bounce failed). Record it so we do
274
327
  // not bounce again this tab — the definitive loop breaker.
275
328
  markNoSession();
276
- restoreDest();
329
+ leaveCallbackHard();
277
330
  return null;
278
331
  }
279
332
 
@@ -281,7 +334,7 @@ export async function consumeSsoReturn(
281
334
  // Forged / replayed / stale fragment, or a malformed ok with no code. Treat
282
335
  // exactly like "no session": never exchange, never loop.
283
336
  markNoSession();
284
- restoreDest();
337
+ leaveCallbackHard();
285
338
  return null;
286
339
  }
287
340
 
@@ -291,17 +344,25 @@ export async function consumeSsoReturn(
291
344
  } catch (error) {
292
345
  onExchangeError?.(error);
293
346
  markNoSession();
294
- restoreDest();
347
+ leaveCallbackHard();
295
348
  return null;
296
349
  }
297
350
 
298
351
  if (!session?.sessionId) {
299
352
  markNoSession();
300
- restoreDest();
353
+ leaveCallbackHard();
301
354
  return null;
302
355
  }
303
356
 
304
- restoreDest();
357
+ // `ok`: the provider is about to commit the freshly exchanged in-memory
358
+ // session — do NOT hard-redirect (a full navigation would discard it). Use a
359
+ // SOFT `history.replaceState` to the target + a synthetic `popstate` so
360
+ // URL-driven routers re-sync to the restored route without a reload.
361
+ const target = consumeCallbackTarget();
362
+ if (target !== null) {
363
+ history.replaceState(null, '', target);
364
+ dispatchPopState();
365
+ }
305
366
 
306
367
  return session;
307
368
  }
@@ -1,97 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OxyServicesDeveloperMixin = OxyServicesDeveloperMixin;
4
- const mixinHelpers_1 = require("./mixinHelpers");
5
- function OxyServicesDeveloperMixin(Base) {
6
- return class extends Base {
7
- constructor(...args) {
8
- super(...args);
9
- }
10
- /**
11
- * Get developer apps for the current user
12
- * @returns Array of developer apps
13
- */
14
- async getDeveloperApps() {
15
- try {
16
- const res = await this.makeRequest('GET', '/developer/apps', undefined, {
17
- cache: true,
18
- cacheTTL: mixinHelpers_1.CACHE_TIMES.MEDIUM,
19
- });
20
- return res.apps || [];
21
- }
22
- catch (error) {
23
- throw this.handleError(error);
24
- }
25
- }
26
- /**
27
- * Create a new developer app
28
- * @param data - Developer app configuration
29
- * @returns Created developer app
30
- */
31
- async createDeveloperApp(data) {
32
- try {
33
- const res = await this.makeRequest('POST', '/developer/apps', data, { cache: false });
34
- return res.app;
35
- }
36
- catch (error) {
37
- throw this.handleError(error);
38
- }
39
- }
40
- /**
41
- * Get a specific developer app
42
- */
43
- async getDeveloperApp(appId) {
44
- try {
45
- const res = await this.makeRequest('GET', `/developer/apps/${appId}`, undefined, {
46
- cache: true,
47
- cacheTTL: mixinHelpers_1.CACHE_TIMES.LONG,
48
- });
49
- return res.app;
50
- }
51
- catch (error) {
52
- throw this.handleError(error);
53
- }
54
- }
55
- /**
56
- * Update a developer app
57
- * @param appId - The developer app ID
58
- * @param data - Updated app configuration
59
- * @returns Updated developer app
60
- */
61
- async updateDeveloperApp(appId, data) {
62
- try {
63
- const res = await this.makeRequest('PATCH', `/developer/apps/${appId}`, data, { cache: false });
64
- return res.app;
65
- }
66
- catch (error) {
67
- throw this.handleError(error);
68
- }
69
- }
70
- /**
71
- * Regenerate API secret for a developer app
72
- * @param appId - The developer app ID
73
- * @returns App with new secret
74
- */
75
- async regenerateDeveloperAppSecret(appId) {
76
- try {
77
- return await this.makeRequest('POST', `/developer/apps/${appId}/regenerate-secret`, undefined, { cache: false });
78
- }
79
- catch (error) {
80
- throw this.handleError(error);
81
- }
82
- }
83
- /**
84
- * Delete a developer app
85
- * @param appId - The developer app ID
86
- * @returns Deletion result
87
- */
88
- async deleteDeveloperApp(appId) {
89
- try {
90
- return await this.makeRequest('DELETE', `/developer/apps/${appId}`, undefined, { cache: false });
91
- }
92
- catch (error) {
93
- throw this.handleError(error);
94
- }
95
- }
96
- };
97
- }
@@ -1,108 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OxyServicesKarmaMixin = OxyServicesKarmaMixin;
4
- const mixinHelpers_1 = require("./mixinHelpers");
5
- function OxyServicesKarmaMixin(Base) {
6
- return class extends Base {
7
- constructor(...args) {
8
- super(...args);
9
- }
10
- /**
11
- * Get user karma
12
- */
13
- async getUserKarma(userId) {
14
- try {
15
- return await this.makeRequest('GET', `/karma/${userId}`, undefined, {
16
- cache: true,
17
- cacheTTL: 2 * 60 * 1000, // 2 minutes cache
18
- });
19
- }
20
- catch (error) {
21
- throw this.handleError(error);
22
- }
23
- }
24
- /**
25
- * Give karma to user
26
- */
27
- async giveKarma(userId, amount, reason) {
28
- try {
29
- return await this.makeRequest('POST', `/karma/${userId}/give`, {
30
- amount,
31
- reason
32
- }, { cache: false });
33
- }
34
- catch (error) {
35
- throw this.handleError(error);
36
- }
37
- }
38
- /**
39
- * Get user karma total
40
- * @param userId - The user ID
41
- * @returns User karma total
42
- */
43
- async getUserKarmaTotal(userId) {
44
- try {
45
- return await this.makeRequest('GET', `/karma/${userId}/total`, undefined, {
46
- cache: true,
47
- cacheTTL: mixinHelpers_1.CACHE_TIMES.MEDIUM,
48
- });
49
- }
50
- catch (error) {
51
- throw this.handleError(error);
52
- }
53
- }
54
- /**
55
- * Get user karma history
56
- * @param userId - The user ID
57
- * @param limit - Optional limit for results
58
- * @param offset - Optional offset for pagination
59
- * @returns User karma history
60
- */
61
- async getUserKarmaHistory(userId, limit, offset) {
62
- try {
63
- const params = {};
64
- if (limit)
65
- params.limit = limit;
66
- if (offset)
67
- params.offset = offset;
68
- return await this.makeRequest('GET', `/karma/${userId}/history`, params, {
69
- cache: true,
70
- cacheTTL: mixinHelpers_1.CACHE_TIMES.MEDIUM,
71
- });
72
- }
73
- catch (error) {
74
- throw this.handleError(error);
75
- }
76
- }
77
- /**
78
- * Get karma leaderboard
79
- * @returns Karma leaderboard
80
- */
81
- async getKarmaLeaderboard() {
82
- try {
83
- return await this.makeRequest('GET', '/karma/leaderboard', undefined, {
84
- cache: true,
85
- cacheTTL: mixinHelpers_1.CACHE_TIMES.LONG,
86
- });
87
- }
88
- catch (error) {
89
- throw this.handleError(error);
90
- }
91
- }
92
- /**
93
- * Get karma rules
94
- * @returns Karma rules
95
- */
96
- async getKarmaRules() {
97
- try {
98
- return await this.makeRequest('GET', '/karma/rules', undefined, {
99
- cache: true,
100
- cacheTTL: mixinHelpers_1.CACHE_TIMES.EXTRA_LONG, // Rules don't change often
101
- });
102
- }
103
- catch (error) {
104
- throw this.handleError(error);
105
- }
106
- }
107
- };
108
- }
@@ -1,94 +0,0 @@
1
- import { CACHE_TIMES } from './mixinHelpers.js';
2
- export function OxyServicesDeveloperMixin(Base) {
3
- return class extends Base {
4
- constructor(...args) {
5
- super(...args);
6
- }
7
- /**
8
- * Get developer apps for the current user
9
- * @returns Array of developer apps
10
- */
11
- async getDeveloperApps() {
12
- try {
13
- const res = await this.makeRequest('GET', '/developer/apps', undefined, {
14
- cache: true,
15
- cacheTTL: CACHE_TIMES.MEDIUM,
16
- });
17
- return res.apps || [];
18
- }
19
- catch (error) {
20
- throw this.handleError(error);
21
- }
22
- }
23
- /**
24
- * Create a new developer app
25
- * @param data - Developer app configuration
26
- * @returns Created developer app
27
- */
28
- async createDeveloperApp(data) {
29
- try {
30
- const res = await this.makeRequest('POST', '/developer/apps', data, { cache: false });
31
- return res.app;
32
- }
33
- catch (error) {
34
- throw this.handleError(error);
35
- }
36
- }
37
- /**
38
- * Get a specific developer app
39
- */
40
- async getDeveloperApp(appId) {
41
- try {
42
- const res = await this.makeRequest('GET', `/developer/apps/${appId}`, undefined, {
43
- cache: true,
44
- cacheTTL: CACHE_TIMES.LONG,
45
- });
46
- return res.app;
47
- }
48
- catch (error) {
49
- throw this.handleError(error);
50
- }
51
- }
52
- /**
53
- * Update a developer app
54
- * @param appId - The developer app ID
55
- * @param data - Updated app configuration
56
- * @returns Updated developer app
57
- */
58
- async updateDeveloperApp(appId, data) {
59
- try {
60
- const res = await this.makeRequest('PATCH', `/developer/apps/${appId}`, data, { cache: false });
61
- return res.app;
62
- }
63
- catch (error) {
64
- throw this.handleError(error);
65
- }
66
- }
67
- /**
68
- * Regenerate API secret for a developer app
69
- * @param appId - The developer app ID
70
- * @returns App with new secret
71
- */
72
- async regenerateDeveloperAppSecret(appId) {
73
- try {
74
- return await this.makeRequest('POST', `/developer/apps/${appId}/regenerate-secret`, undefined, { cache: false });
75
- }
76
- catch (error) {
77
- throw this.handleError(error);
78
- }
79
- }
80
- /**
81
- * Delete a developer app
82
- * @param appId - The developer app ID
83
- * @returns Deletion result
84
- */
85
- async deleteDeveloperApp(appId) {
86
- try {
87
- return await this.makeRequest('DELETE', `/developer/apps/${appId}`, undefined, { cache: false });
88
- }
89
- catch (error) {
90
- throw this.handleError(error);
91
- }
92
- }
93
- };
94
- }
@@ -1,105 +0,0 @@
1
- import { CACHE_TIMES } from './mixinHelpers.js';
2
- export function OxyServicesKarmaMixin(Base) {
3
- return class extends Base {
4
- constructor(...args) {
5
- super(...args);
6
- }
7
- /**
8
- * Get user karma
9
- */
10
- async getUserKarma(userId) {
11
- try {
12
- return await this.makeRequest('GET', `/karma/${userId}`, undefined, {
13
- cache: true,
14
- cacheTTL: 2 * 60 * 1000, // 2 minutes cache
15
- });
16
- }
17
- catch (error) {
18
- throw this.handleError(error);
19
- }
20
- }
21
- /**
22
- * Give karma to user
23
- */
24
- async giveKarma(userId, amount, reason) {
25
- try {
26
- return await this.makeRequest('POST', `/karma/${userId}/give`, {
27
- amount,
28
- reason
29
- }, { cache: false });
30
- }
31
- catch (error) {
32
- throw this.handleError(error);
33
- }
34
- }
35
- /**
36
- * Get user karma total
37
- * @param userId - The user ID
38
- * @returns User karma total
39
- */
40
- async getUserKarmaTotal(userId) {
41
- try {
42
- return await this.makeRequest('GET', `/karma/${userId}/total`, undefined, {
43
- cache: true,
44
- cacheTTL: CACHE_TIMES.MEDIUM,
45
- });
46
- }
47
- catch (error) {
48
- throw this.handleError(error);
49
- }
50
- }
51
- /**
52
- * Get user karma history
53
- * @param userId - The user ID
54
- * @param limit - Optional limit for results
55
- * @param offset - Optional offset for pagination
56
- * @returns User karma history
57
- */
58
- async getUserKarmaHistory(userId, limit, offset) {
59
- try {
60
- const params = {};
61
- if (limit)
62
- params.limit = limit;
63
- if (offset)
64
- params.offset = offset;
65
- return await this.makeRequest('GET', `/karma/${userId}/history`, params, {
66
- cache: true,
67
- cacheTTL: CACHE_TIMES.MEDIUM,
68
- });
69
- }
70
- catch (error) {
71
- throw this.handleError(error);
72
- }
73
- }
74
- /**
75
- * Get karma leaderboard
76
- * @returns Karma leaderboard
77
- */
78
- async getKarmaLeaderboard() {
79
- try {
80
- return await this.makeRequest('GET', '/karma/leaderboard', undefined, {
81
- cache: true,
82
- cacheTTL: CACHE_TIMES.LONG,
83
- });
84
- }
85
- catch (error) {
86
- throw this.handleError(error);
87
- }
88
- }
89
- /**
90
- * Get karma rules
91
- * @returns Karma rules
92
- */
93
- async getKarmaRules() {
94
- try {
95
- return await this.makeRequest('GET', '/karma/rules', undefined, {
96
- cache: true,
97
- cacheTTL: CACHE_TIMES.EXTRA_LONG, // Rules don't change often
98
- });
99
- }
100
- catch (error) {
101
- throw this.handleError(error);
102
- }
103
- }
104
- };
105
- }