@oxyhq/core 10.2.0 → 11.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/boot/sessionColdBoot.js +3 -3
  3. package/dist/cjs/crypto/keyManager.js +68 -69
  4. package/dist/cjs/crypto/signatureService.js +2 -2
  5. package/dist/cjs/index.js +17 -20
  6. package/dist/cjs/logger/index.js +149 -0
  7. package/dist/cjs/mixins/OxyServices.assets.js +4 -4
  8. package/dist/cjs/mixins/OxyServices.auth.js +104 -2
  9. package/dist/cjs/mixins/OxyServices.language.js +3 -5
  10. package/dist/cjs/mixins/OxyServices.privacy.js +2 -4
  11. package/dist/cjs/mixins/OxyServices.security.js +7 -11
  12. package/dist/cjs/mixins/OxyServices.topics.js +25 -15
  13. package/dist/cjs/mixins/OxyServices.user.js +4 -4
  14. package/dist/cjs/mixins/OxyServices.utility.js +18 -18
  15. package/dist/cjs/session/SessionClient.js +17 -17
  16. package/dist/cjs/session/accountDialogController.js +12 -12
  17. package/dist/cjs/session/authStateStore.js +5 -5
  18. package/dist/cjs/session/refresh.js +4 -4
  19. package/dist/cjs/session/socketLoader.js +2 -2
  20. package/dist/cjs/utils/asyncUtils.js +2 -2
  21. package/dist/cjs/utils/avatarUtils.js +2 -1
  22. package/dist/cjs/utils/deviceManager.js +7 -5
  23. package/dist/cjs/utils/errorUtils.js +3 -3
  24. package/dist/cjs/utils/oauthPkce.js +3 -3
  25. package/dist/cjs/utils/requestUtils.js +1 -1
  26. package/dist/cjs/utils/webauthnOrigin.js +51 -0
  27. package/dist/esm/.tsbuildinfo +1 -1
  28. package/dist/esm/boot/sessionColdBoot.js +1 -1
  29. package/dist/esm/crypto/keyManager.js +1 -2
  30. package/dist/esm/crypto/signatureService.js +1 -1
  31. package/dist/esm/index.js +6 -3
  32. package/dist/esm/logger/index.js +140 -0
  33. package/dist/esm/mixins/OxyServices.assets.js +2 -2
  34. package/dist/esm/mixins/OxyServices.auth.js +103 -1
  35. package/dist/esm/mixins/OxyServices.language.js +3 -5
  36. package/dist/esm/mixins/OxyServices.privacy.js +2 -4
  37. package/dist/esm/mixins/OxyServices.security.js +7 -11
  38. package/dist/esm/mixins/OxyServices.topics.js +25 -15
  39. package/dist/esm/mixins/OxyServices.user.js +1 -1
  40. package/dist/esm/mixins/OxyServices.utility.js +1 -1
  41. package/dist/esm/session/SessionClient.js +1 -1
  42. package/dist/esm/session/accountDialogController.js +1 -1
  43. package/dist/esm/session/authStateStore.js +1 -1
  44. package/dist/esm/session/refresh.js +1 -1
  45. package/dist/esm/session/socketLoader.js +1 -1
  46. package/dist/esm/utils/asyncUtils.js +1 -1
  47. package/dist/esm/utils/avatarUtils.js +2 -1
  48. package/dist/esm/utils/deviceManager.js +7 -5
  49. package/dist/esm/utils/errorUtils.js +1 -1
  50. package/dist/esm/utils/oauthPkce.js +1 -1
  51. package/dist/esm/utils/requestUtils.js +1 -1
  52. package/dist/esm/utils/webauthnOrigin.js +48 -0
  53. package/dist/types/.tsbuildinfo +1 -1
  54. package/dist/types/index.d.ts +4 -4
  55. package/dist/types/logger/index.d.ts +104 -0
  56. package/dist/types/mixins/OxyServices.auth.d.ts +58 -0
  57. package/dist/types/mixins/OxyServices.topics.d.ts +3 -3
  58. package/dist/types/models/Topic.d.ts +10 -0
  59. package/dist/types/utils/requestUtils.d.ts +1 -1
  60. package/dist/types/utils/webauthnOrigin.d.ts +32 -0
  61. package/package.json +17 -2
  62. package/src/boot/sessionColdBoot.ts +1 -1
  63. package/src/crypto/keyManager.ts +1 -2
  64. package/src/crypto/signatureService.ts +1 -1
  65. package/src/index.ts +22 -19
  66. package/src/logger/__tests__/logger.test.ts +207 -0
  67. package/src/logger/index.ts +217 -0
  68. package/src/mixins/OxyServices.assets.ts +3 -3
  69. package/src/mixins/OxyServices.auth.ts +134 -1
  70. package/src/mixins/OxyServices.language.ts +3 -5
  71. package/src/mixins/OxyServices.privacy.ts +2 -4
  72. package/src/mixins/OxyServices.security.ts +7 -11
  73. package/src/mixins/OxyServices.topics.ts +47 -17
  74. package/src/mixins/OxyServices.user.ts +1 -1
  75. package/src/mixins/OxyServices.utility.ts +1 -1
  76. package/src/mixins/__tests__/discoveryErrorHandling.test.ts +1 -1
  77. package/src/mixins/__tests__/topics.test.ts +156 -0
  78. package/src/mixins/__tests__/webauthnAuth.test.ts +206 -0
  79. package/src/models/Topic.ts +11 -0
  80. package/src/session/SessionClient.ts +1 -1
  81. package/src/session/__tests__/SessionClient.diagnostics.test.ts +1 -1
  82. package/src/session/__tests__/accountDialogController.test.ts +1 -1
  83. package/src/session/accountDialogController.ts +1 -1
  84. package/src/session/authStateStore.ts +1 -1
  85. package/src/session/refresh.ts +1 -1
  86. package/src/session/socketLoader.ts +1 -1
  87. package/src/utils/__tests__/webauthnOrigin.test.ts +83 -0
  88. package/src/utils/asyncUtils.ts +1 -1
  89. package/src/utils/avatarUtils.ts +3 -1
  90. package/src/utils/deviceManager.ts +8 -5
  91. package/src/utils/errorUtils.ts +1 -1
  92. package/src/utils/oauthPkce.ts +1 -1
  93. package/src/utils/requestUtils.ts +1 -1
  94. package/src/utils/webauthnOrigin.ts +52 -0
  95. package/dist/cjs/shared/utils/debugUtils.js +0 -80
  96. package/dist/cjs/utils/loggerUtils.js +0 -126
  97. package/dist/esm/shared/utils/debugUtils.js +0 -72
  98. package/dist/esm/utils/loggerUtils.js +0 -115
  99. package/dist/types/shared/utils/debugUtils.d.ts +0 -48
  100. package/dist/types/utils/loggerUtils.d.ts +0 -48
  101. package/src/shared/utils/__tests__/debugUtils.test.ts +0 -55
  102. package/src/shared/utils/debugUtils.ts +0 -78
  103. package/src/utils/loggerUtils.ts +0 -153
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ /**
3
+ * @oxyhq/core/logger — the ecosystem-wide logging chokepoint.
4
+ *
5
+ * A tiny, dependency-free, universal logger that works unchanged in React
6
+ * Native, browsers, Node, and Bun. Every Oxy app and package should log
7
+ * through here instead of calling `console.*` directly, so that level,
8
+ * formatting, and transport are controlled in ONE place.
9
+ *
10
+ * Design goals:
11
+ * - Zero dependencies. No Node-only imports, no `process.stdout` assumptions.
12
+ * - Four levels (`debug` | `info` | `warn` | `error`) plus `silent`.
13
+ * - Namespaced child loggers via `createLogger('mention:feed')` / `.child()`.
14
+ * - Structured context objects carried through to the sink.
15
+ * - A single pluggable sink so backends can pipe to pino / CloudWatch / etc.
16
+ * without touching any call site.
17
+ * - Debug is off in production by default (`__DEV__` on RN, `NODE_ENV`
18
+ * elsewhere); override globally with `configureLogger({ level })`.
19
+ *
20
+ * @module logger
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.logger = exports.consoleSink = void 0;
24
+ exports.isDev = isDev;
25
+ exports.configureLogger = configureLogger;
26
+ exports.getLoggerConfig = getLoggerConfig;
27
+ exports.resetLoggerConfig = resetLoggerConfig;
28
+ exports.createLogger = createLogger;
29
+ const LEVEL_WEIGHT = {
30
+ silent: 0,
31
+ error: 1,
32
+ warn: 2,
33
+ info: 3,
34
+ debug: 4,
35
+ };
36
+ /**
37
+ * True in development. Uses React Native's `__DEV__` when present, otherwise
38
+ * falls back to `process.env.NODE_ENV === 'development'`. Never throws.
39
+ */
40
+ function isDev() {
41
+ if (typeof __DEV__ !== 'undefined')
42
+ return __DEV__ === true;
43
+ try {
44
+ return typeof process !== 'undefined' && process.env?.NODE_ENV === 'development';
45
+ }
46
+ catch {
47
+ return false;
48
+ }
49
+ }
50
+ function formatPrefix(entry) {
51
+ const parts = [entry.timestamp, entry.level.toUpperCase()];
52
+ if (entry.namespace)
53
+ parts.push(`[${entry.namespace}]`);
54
+ return parts.join(' ');
55
+ }
56
+ /**
57
+ * Default sink: routes each entry to the matching `console` method. `info` and
58
+ * `debug` go to `console.log` (RN/browser-safe — `console.debug`/`console.info`
59
+ * are inconsistently surfaced across runtimes).
60
+ */
61
+ const consoleSink = (entry) => {
62
+ const extras = [];
63
+ if (entry.context && Object.keys(entry.context).length > 0)
64
+ extras.push(entry.context);
65
+ if (entry.error !== undefined)
66
+ extras.push(entry.error);
67
+ if (entry.args.length > 0)
68
+ extras.push(...entry.args);
69
+ const line = `${formatPrefix(entry)} ${entry.message}`;
70
+ if (entry.level === 'error')
71
+ console.error(line, ...extras);
72
+ else if (entry.level === 'warn')
73
+ console.warn(line, ...extras);
74
+ else
75
+ console.log(line, ...extras);
76
+ };
77
+ exports.consoleSink = consoleSink;
78
+ function defaultLevel() {
79
+ return isDev() ? 'debug' : 'info';
80
+ }
81
+ const config = {
82
+ level: defaultLevel(),
83
+ sink: exports.consoleSink,
84
+ };
85
+ /**
86
+ * Update the global logger configuration. Affects every logger instance
87
+ * (root and children) immediately. Pass `{ level }` to gate output and/or
88
+ * `{ sink }` to redirect transport (pino, CloudWatch, a test capture, …).
89
+ */
90
+ function configureLogger(partial) {
91
+ if (partial.level !== undefined)
92
+ config.level = partial.level;
93
+ if (partial.sink !== undefined)
94
+ config.sink = partial.sink;
95
+ }
96
+ /** Read a snapshot of the current global configuration. */
97
+ function getLoggerConfig() {
98
+ return { level: config.level, sink: config.sink };
99
+ }
100
+ /** Restore the default level (env-derived) and the console sink. */
101
+ function resetLoggerConfig() {
102
+ config.level = defaultLevel();
103
+ config.sink = exports.consoleSink;
104
+ }
105
+ function mergeContext(base, extra) {
106
+ if (!base)
107
+ return extra;
108
+ if (!extra)
109
+ return base;
110
+ return { ...base, ...extra };
111
+ }
112
+ function emit(level, namespace, baseContext, message, error, context, args) {
113
+ if (LEVEL_WEIGHT[level] > LEVEL_WEIGHT[config.level])
114
+ return;
115
+ config.sink({
116
+ level,
117
+ message,
118
+ namespace,
119
+ context: mergeContext(baseContext, context),
120
+ error,
121
+ args,
122
+ timestamp: new Date().toISOString(),
123
+ });
124
+ }
125
+ function makeLogger(namespace, baseContext) {
126
+ return {
127
+ namespace,
128
+ debug: (message, context, ...args) => emit('debug', namespace, baseContext, message, undefined, context, args),
129
+ info: (message, context, ...args) => emit('info', namespace, baseContext, message, undefined, context, args),
130
+ warn: (message, context, ...args) => emit('warn', namespace, baseContext, message, undefined, context, args),
131
+ error: (message, error, context, ...args) => emit('error', namespace, baseContext, message, error, context, args),
132
+ child: (childNamespace, childContext) => makeLogger(namespace ? `${namespace}:${childNamespace}` : childNamespace, mergeContext(baseContext, childContext)),
133
+ };
134
+ }
135
+ /** The shared root logger. Prefer a namespaced `createLogger(...)` in modules. */
136
+ exports.logger = makeLogger(undefined, undefined);
137
+ /**
138
+ * Create a namespaced logger.
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * const log = createLogger('mention:feed');
143
+ * log.info('loaded', { count: 20 });
144
+ * const sub = log.child('prefetch'); // namespace → 'mention:feed:prefetch'
145
+ * ```
146
+ */
147
+ function createLogger(namespace, context) {
148
+ return makeLogger(namespace, context);
149
+ }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OxyServicesAssetsMixin = OxyServicesAssetsMixin;
4
4
  const protocol_1 = require("@oxyhq/protocol");
5
- const loggerUtils_1 = require("../utils/loggerUtils");
5
+ const logger_1 = require("../logger");
6
6
  const errorUtils_1 = require("../utils/errorUtils");
7
7
  /**
8
8
  * Maximum number of ids sent per `POST /assets/service/by-ids` request. Matches
@@ -215,7 +215,7 @@ function OxyServicesAssetsMixin(Base) {
215
215
  return Array.isArray(entries) ? entries : [];
216
216
  }
217
217
  catch (error) {
218
- loggerUtils_1.logger.warn('getServiceAssetMetadataByIds: chunk failed, continuing with remaining chunks', {
218
+ logger_1.logger.warn('getServiceAssetMetadataByIds: chunk failed, continuing with remaining chunks', {
219
219
  method: 'getServiceAssetMetadataByIds',
220
220
  chunkSize: chunk.length,
221
221
  status: (0, errorUtils_1.extractErrorStatus)(error),
@@ -278,7 +278,7 @@ function OxyServicesAssetsMixin(Base) {
278
278
  return Array.isArray(entries) ? entries : [];
279
279
  }
280
280
  catch (error) {
281
- loggerUtils_1.logger.warn('getServiceAssetMetadataBySha256: chunk failed, continuing with remaining chunks', {
281
+ logger_1.logger.warn('getServiceAssetMetadataBySha256: chunk failed, continuing with remaining chunks', {
282
282
  method: 'getServiceAssetMetadataBySha256',
283
283
  chunkSize: chunk.length,
284
284
  status: (0, errorUtils_1.extractErrorStatus)(error),
@@ -362,7 +362,7 @@ function OxyServicesAssetsMixin(Base) {
362
362
  return response;
363
363
  }
364
364
  catch (error) {
365
- console.error('File upload error:', error);
365
+ logger_1.logger.error('File upload error', error, { component: 'OxyServices.assets' });
366
366
  let errorMessage = 'File upload failed';
367
367
  if (error instanceof Error) {
368
368
  errorMessage = error.message || errorMessage;
@@ -7,7 +7,7 @@ const OxyServices_errors_1 = require("../OxyServices.errors");
7
7
  const keyManager_1 = require("../crypto/keyManager");
8
8
  const signatureService_1 = require("../crypto/signatureService");
9
9
  const protocol_1 = require("@oxyhq/protocol");
10
- const loggerUtils_1 = require("../utils/loggerUtils");
10
+ const logger_1 = require("../logger");
11
11
  const userIdentity_1 = require("../utils/userIdentity");
12
12
  /**
13
13
  * Default lifetime of a "Sign in with Oxy" device-flow session / authorize code.
@@ -125,7 +125,7 @@ function OxyServicesAuthMixin(Base) {
125
125
  : Buffer.alloc(storedSecretBuf.length);
126
126
  const compareResult = nodeCrypto.timingSafeEqual(storedSecretBuf, compareBuf);
127
127
  if (!lengthMatch || !compareResult) {
128
- loggerUtils_1.logger.warn('[oxy.auth] Service token cache hit with mismatched secret', {
128
+ logger_1.logger.warn('[oxy.auth] Service token cache hit with mismatched secret', {
129
129
  component: 'auth',
130
130
  method: 'getServiceToken',
131
131
  });
@@ -852,6 +852,108 @@ function OxyServicesAuthMixin(Base) {
852
852
  throw this.handleError(error);
853
853
  }
854
854
  }
855
+ /**
856
+ * Begin a WebAuthn / passkey REGISTRATION ceremony. Requests the
857
+ * `PublicKeyCredentialCreationOptions` the browser's `navigator.credentials
858
+ * .create()` (or `@simplewebauthn/browser`'s `startRegistration`) needs.
859
+ *
860
+ * With a bearer token planted this links a passkey to the signed-in account
861
+ * (`username` ignored); without one it is a prospective signup and `username`
862
+ * is the desired handle. The returned options are OPAQUE — Oxy does not own
863
+ * their shape (the browser / `@simplewebauthn` does), so they pass through
864
+ * as `unknown` for the caller to hand straight to the ceremony.
865
+ */
866
+ async webauthnRegisterOptions(username) {
867
+ try {
868
+ return await this.makeRequest('POST', '/auth/webauthn/register/options', { ...(username !== undefined ? { username } : {}) }, { cache: false });
869
+ }
870
+ catch (error) {
871
+ throw this.handleError(error);
872
+ }
873
+ }
874
+ /**
875
+ * Finish a WebAuthn / passkey REGISTRATION ceremony. Forwards the opaque
876
+ * browser `RegistrationResponseJSON` (`response`) alongside the Oxy envelope
877
+ * (desired `username` for signup + the device-session naming fields).
878
+ *
879
+ * Two server branches, disambiguated by the response shape:
880
+ * - **Signup** (no bearer): the account is created and a session minted —
881
+ * the response carries `sessionId`, is the SAME {@link LoginResult}
882
+ * contract as `POST /auth/verify`, and its access token is planted here.
883
+ * - **Link** (bearer present): the passkey is attached to the signed-in
884
+ * account and the server returns `{ success, message }` with no session,
885
+ * which is returned verbatim (no token planting).
886
+ */
887
+ async webauthnRegisterVerify(response, envelope = {}) {
888
+ try {
889
+ const res = await this.makeRequest('POST', '/auth/webauthn/register/verify', { response, ...envelope }, { cache: false });
890
+ if (res && typeof res === 'object') {
891
+ const record = res;
892
+ // Signup branch: mints a session (LoginSessionResult, carries
893
+ // `sessionId`). Parse against the login contract and plant the token.
894
+ if ('sessionId' in record) {
895
+ const parsed = (0, contracts_1.safeParseContract)(contracts_1.loginResultSchema, record);
896
+ if (!parsed) {
897
+ throw new Error('auth/webauthn/register/verify returned an unexpected response shape');
898
+ }
899
+ if (!('twoFactorRequired' in parsed) && parsed.accessToken) {
900
+ this.setTokens(parsed.accessToken);
901
+ }
902
+ return parsed;
903
+ }
904
+ // Link branch: passkey attached to the signed-in account, no session.
905
+ if (record.success === true && typeof record.message === 'string') {
906
+ return { success: true, message: record.message };
907
+ }
908
+ }
909
+ throw new Error('auth/webauthn/register/verify returned an unexpected response shape');
910
+ }
911
+ catch (error) {
912
+ throw this.handleError(error);
913
+ }
914
+ }
915
+ /**
916
+ * Begin a WebAuthn / passkey AUTHENTICATION ceremony. Requests the
917
+ * `PublicKeyCredentialRequestOptions` the browser's `navigator.credentials
918
+ * .get()` (or `@simplewebauthn/browser`'s `startAuthentication`) needs.
919
+ *
920
+ * When `username` is present the server scopes `allowCredentials` to that
921
+ * user's passkeys (username-first); when omitted it returns an empty
922
+ * allow-list for the usernameless / discoverable-credential flow. The
923
+ * returned options are OPAQUE and pass through as `unknown`.
924
+ */
925
+ async webauthnLoginOptions(username) {
926
+ try {
927
+ return await this.makeRequest('POST', '/auth/webauthn/login/options', { ...(username !== undefined ? { username } : {}) }, { cache: false });
928
+ }
929
+ catch (error) {
930
+ throw this.handleError(error);
931
+ }
932
+ }
933
+ /**
934
+ * Finish a WebAuthn / passkey AUTHENTICATION ceremony. Forwards the opaque
935
+ * browser `AuthenticationResponseJSON` (`response`) alongside the
936
+ * device-session envelope. Resolves to the SAME {@link LoginResult} contract
937
+ * as `POST /auth/verify`; on the session arm the access token is planted
938
+ * immediately (mirroring {@link passwordSignIn}), and the response's
939
+ * `deviceId` + `deviceSecret` are the zero-cookie restore credential.
940
+ */
941
+ async webauthnLoginVerify(response, envelope = {}) {
942
+ try {
943
+ const res = await this.makeRequest('POST', '/auth/webauthn/login/verify', { response, ...envelope }, { cache: false });
944
+ const parsed = (0, contracts_1.safeParseContract)(contracts_1.loginResultSchema, res);
945
+ if (!parsed) {
946
+ throw new Error('auth/webauthn/login/verify returned an unexpected response shape');
947
+ }
948
+ if (!('twoFactorRequired' in parsed) && parsed.accessToken) {
949
+ this.setTokens(parsed.accessToken);
950
+ }
951
+ return parsed;
952
+ }
953
+ catch (error) {
954
+ throw this.handleError(error);
955
+ }
956
+ }
855
957
  /**
856
958
  * Exchange an OAuth authorization code (returned to the RP redirect URI
857
959
  * after password sign-in at auth.oxy.so) for a device-first session.
@@ -6,7 +6,7 @@ exports.OxyServicesLanguageMixin = OxyServicesLanguageMixin;
6
6
  */
7
7
  const languageUtils_1 = require("../utils/languageUtils");
8
8
  const protocol_1 = require("@oxyhq/protocol");
9
- const debugUtils_1 = require("../shared/utils/debugUtils");
9
+ const logger_1 = require("../logger");
10
10
  function OxyServicesLanguageMixin(Base) {
11
11
  return class extends Base {
12
12
  constructor(...args) {
@@ -31,7 +31,7 @@ function OxyServicesLanguageMixin(Base) {
31
31
  };
32
32
  }
33
33
  catch (error) {
34
- console.error('AsyncStorage not available in React Native:', error);
34
+ logger_1.logger.error('AsyncStorage not available in React Native', error, { component: 'OxyServices.language' });
35
35
  throw new Error('AsyncStorage is required in React Native environment');
36
36
  }
37
37
  }
@@ -86,9 +86,7 @@ function OxyServicesLanguageMixin(Base) {
86
86
  return null;
87
87
  }
88
88
  catch (error) {
89
- if ((0, debugUtils_1.isDev)()) {
90
- console.warn('Failed to get current language:', error);
91
- }
89
+ logger_1.logger.warn('Failed to get current language', { component: 'OxyServices.language' }, error);
92
90
  return null;
93
91
  }
94
92
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OxyServicesPrivacyMixin = OxyServicesPrivacyMixin;
4
- const debugUtils_1 = require("../shared/utils/debugUtils");
4
+ const logger_1 = require("../logger");
5
5
  function OxyServicesPrivacyMixin(Base) {
6
6
  return class extends Base {
7
7
  constructor(...args) {
@@ -29,9 +29,7 @@ function OxyServicesPrivacyMixin(Base) {
29
29
  }
30
30
  catch (error) {
31
31
  // If there's an error, assume not in list to avoid breaking functionality
32
- if ((0, debugUtils_1.isDev)()) {
33
- console.warn('Error checking user list:', error);
34
- }
32
+ logger_1.logger.warn('Error checking user list', { component: 'OxyServices.privacy' }, error);
35
33
  return false;
36
34
  }
37
35
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OxyServicesSecurityMixin = OxyServicesSecurityMixin;
4
- const debugUtils_1 = require("../shared/utils/debugUtils");
4
+ const logger_1 = require("../logger");
5
5
  function OxyServicesSecurityMixin(Base) {
6
6
  return class extends Base {
7
7
  constructor(...args) {
@@ -65,11 +65,9 @@ function OxyServicesSecurityMixin(Base) {
65
65
  await this.makeRequest('POST', '/security/activity/private-key-exported', { deviceId }, { cache: false });
66
66
  }
67
67
  catch (error) {
68
- // Don't throw - logging failures shouldn't break user flow
69
- // But log for monitoring
70
- if ((0, debugUtils_1.isDev)()) {
71
- console.warn('[OxyServices] Failed to log private key exported event:', error);
72
- }
68
+ // Don't throw - logging failures shouldn't break user flow, but surface
69
+ // for monitoring via the shared logger sink.
70
+ logger_1.logger.warn('[OxyServices] Failed to log private key exported event', { component: 'OxyServices.security' }, error);
73
71
  }
74
72
  }
75
73
  /**
@@ -82,11 +80,9 @@ function OxyServicesSecurityMixin(Base) {
82
80
  await this.makeRequest('POST', '/security/activity/backup-created', { deviceId }, { cache: false });
83
81
  }
84
82
  catch (error) {
85
- // Don't throw - logging failures shouldn't break user flow
86
- // But log for monitoring
87
- if ((0, debugUtils_1.isDev)()) {
88
- console.warn('[OxyServices] Failed to log backup created event:', error);
89
- }
83
+ // Don't throw - logging failures shouldn't break user flow, but surface
84
+ // for monitoring via the shared logger sink.
85
+ logger_1.logger.warn('[OxyServices] Failed to log backup created event', { component: 'OxyServices.security' }, error);
90
86
  }
91
87
  }
92
88
  };
@@ -17,10 +17,10 @@ function OxyServicesTopicsMixin(Base) {
17
17
  const params = {};
18
18
  if (locale)
19
19
  params.locale = locale;
20
- return await this.makeRequest('GET', '/topics/categories', params, {
21
- cache: true,
22
- cacheTTL: mixinHelpers_1.CACHE_TIMES.EXTRA_LONG,
23
- });
20
+ // `GET /topics/categories` returns `{ categories: TopicData[] }` — the
21
+ // SDK's `unwrapResponse` only unwraps `{ data }`, so unwrap here.
22
+ const response = await this.makeRequest('GET', '/topics/categories', params, { cache: true, cacheTTL: mixinHelpers_1.CACHE_TIMES.EXTRA_LONG });
23
+ return response.categories ?? [];
24
24
  }
25
25
  catch (error) {
26
26
  throw this.handleError(error);
@@ -37,9 +37,9 @@ function OxyServicesTopicsMixin(Base) {
37
37
  const params = { q: query };
38
38
  if (limit)
39
39
  params.limit = limit;
40
- return await this.makeRequest('GET', '/topics/search', params, {
41
- cache: false,
42
- });
40
+ // `GET /topics/search` returns `{ topics: TopicData[] }` — unwrap it.
41
+ const response = await this.makeRequest('GET', '/topics/search', params, { cache: false });
42
+ return response.topics ?? [];
43
43
  }
44
44
  catch (error) {
45
45
  throw this.handleError(error);
@@ -48,7 +48,7 @@ function OxyServicesTopicsMixin(Base) {
48
48
  /**
49
49
  * List topics with optional filters
50
50
  * @param options - Filter and pagination options
51
- * @returns List of topics
51
+ * @returns Paginated topics envelope (`topics` plus `total`/`limit`/`offset`)
52
52
  */
53
53
  async listTopics(options) {
54
54
  try {
@@ -63,10 +63,18 @@ function OxyServicesTopicsMixin(Base) {
63
63
  params.offset = options.offset;
64
64
  if (options?.locale)
65
65
  params.locale = options.locale;
66
- return await this.makeRequest('GET', '/topics', params, {
67
- cache: true,
68
- cacheTTL: mixinHelpers_1.CACHE_TIMES.SHORT,
69
- });
66
+ // `GET /topics` returns `{ topics, total, limit, offset }`. The pagination
67
+ // fields matter to callers, so return the whole envelope (typed) rather
68
+ // than throwing them away.
69
+ const response = await this.makeRequest('GET', '/topics', params, { cache: true, cacheTTL: mixinHelpers_1.CACHE_TIMES.SHORT });
70
+ const requestedLimit = typeof params.limit === 'number' ? params.limit : 0;
71
+ const requestedOffset = typeof params.offset === 'number' ? params.offset : 0;
72
+ return {
73
+ topics: response.topics ?? [],
74
+ total: response.total ?? response.topics?.length ?? 0,
75
+ limit: response.limit ?? requestedLimit,
76
+ offset: response.offset ?? requestedOffset,
77
+ };
70
78
  }
71
79
  catch (error) {
72
80
  throw this.handleError(error);
@@ -95,9 +103,11 @@ function OxyServicesTopicsMixin(Base) {
95
103
  */
96
104
  async resolveTopicNames(names) {
97
105
  try {
98
- return await this.makeRequest('POST', '/topics/resolve', { names }, {
99
- cache: false,
100
- });
106
+ // `POST /topics/resolve` returns `{ topics: Record<name, TopicData> }`
107
+ // (a name-keyed map, not an array). Unwrap and flatten to the resolved
108
+ // topics; each TopicData carries its own `name` for re-keying.
109
+ const response = await this.makeRequest('POST', '/topics/resolve', { names }, { cache: false });
110
+ return Object.values(response.topics ?? {});
101
111
  }
102
112
  catch (error) {
103
113
  throw this.handleError(error);
@@ -6,7 +6,7 @@ const apiUtils_1 = require("../utils/apiUtils");
6
6
  const keyManager_1 = require("../crypto/keyManager");
7
7
  const signatureService_1 = require("../crypto/signatureService");
8
8
  const userIdentity_1 = require("../utils/userIdentity");
9
- const loggerUtils_1 = require("../utils/loggerUtils");
9
+ const logger_1 = require("../logger");
10
10
  const errorUtils_1 = require("../utils/errorUtils");
11
11
  /**
12
12
  * Maximum number of ids sent per `POST /users/by-ids` request. Matches the
@@ -113,7 +113,7 @@ function OxyServicesUserMixin(Base) {
113
113
  // noise. Return contract is unchanged.
114
114
  const status = (0, errorUtils_1.extractErrorStatus)(error);
115
115
  const isNotFound = status === 404;
116
- loggerUtils_1.logger.debug(isNotFound ? 'resolveProfile: handle not found' : 'resolveProfile: discovery failed', {
116
+ logger_1.logger.debug(isNotFound ? 'resolveProfile: handle not found' : 'resolveProfile: discovery failed', {
117
117
  method: 'resolveProfile',
118
118
  handle,
119
119
  status,
@@ -194,7 +194,7 @@ function OxyServicesUserMixin(Base) {
194
194
  // caller (contract unchanged: rethrow via `handleError`). Add debug
195
195
  // observability first so a recurring upstream failure is diagnosable
196
196
  // — distinguishing an auth/transport problem from a server 5xx.
197
- loggerUtils_1.logger.debug('getProfileRecommendations: discovery read failed', {
197
+ logger_1.logger.debug('getProfileRecommendations: discovery read failed', {
198
198
  method: 'getProfileRecommendations',
199
199
  path: usesScoredPath ? 'POST' : 'GET',
200
200
  excludeTypes: options?.excludeTypes,
@@ -308,7 +308,7 @@ function OxyServicesUserMixin(Base) {
308
308
  return Array.isArray(users) ? users.map((user) => (0, userIdentity_1.normalizeUserIdentity)(user)) : [];
309
309
  }
310
310
  catch (error) {
311
- loggerUtils_1.logger.warn('getUsersByIds: chunk failed, continuing with remaining chunks', {
311
+ logger_1.logger.warn('getUsersByIds: chunk failed, continuing with remaining chunks', {
312
312
  method: 'getUsersByIds',
313
313
  mode: useServiceAuth ? 'service' : 'user',
314
314
  chunkSize: chunk.length,