@oxyhq/core 10.2.0 → 11.0.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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/boot/sessionColdBoot.js +3 -3
- package/dist/cjs/crypto/keyManager.js +68 -69
- package/dist/cjs/crypto/signatureService.js +2 -2
- package/dist/cjs/index.js +13 -21
- package/dist/cjs/logger/index.js +149 -0
- package/dist/cjs/mixins/OxyServices.assets.js +4 -4
- package/dist/cjs/mixins/OxyServices.auth.js +2 -2
- package/dist/cjs/mixins/OxyServices.language.js +3 -5
- package/dist/cjs/mixins/OxyServices.privacy.js +2 -4
- package/dist/cjs/mixins/OxyServices.security.js +7 -11
- package/dist/cjs/mixins/OxyServices.topics.js +25 -15
- package/dist/cjs/mixins/OxyServices.user.js +4 -4
- package/dist/cjs/mixins/OxyServices.utility.js +18 -18
- package/dist/cjs/session/SessionClient.js +17 -17
- package/dist/cjs/session/accountDialogController.js +12 -12
- package/dist/cjs/session/authStateStore.js +5 -5
- package/dist/cjs/session/refresh.js +4 -4
- package/dist/cjs/session/socketLoader.js +2 -2
- package/dist/cjs/utils/asyncUtils.js +2 -2
- package/dist/cjs/utils/avatarUtils.js +2 -1
- package/dist/cjs/utils/deviceManager.js +7 -5
- package/dist/cjs/utils/errorUtils.js +3 -3
- package/dist/cjs/utils/oauthPkce.js +3 -3
- package/dist/cjs/utils/requestUtils.js +1 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/boot/sessionColdBoot.js +1 -1
- package/dist/esm/crypto/keyManager.js +1 -2
- package/dist/esm/crypto/signatureService.js +1 -1
- package/dist/esm/index.js +2 -3
- package/dist/esm/logger/index.js +140 -0
- package/dist/esm/mixins/OxyServices.assets.js +2 -2
- package/dist/esm/mixins/OxyServices.auth.js +1 -1
- package/dist/esm/mixins/OxyServices.language.js +3 -5
- package/dist/esm/mixins/OxyServices.privacy.js +2 -4
- package/dist/esm/mixins/OxyServices.security.js +7 -11
- package/dist/esm/mixins/OxyServices.topics.js +25 -15
- package/dist/esm/mixins/OxyServices.user.js +1 -1
- package/dist/esm/mixins/OxyServices.utility.js +1 -1
- package/dist/esm/session/SessionClient.js +1 -1
- package/dist/esm/session/accountDialogController.js +1 -1
- package/dist/esm/session/authStateStore.js +1 -1
- package/dist/esm/session/refresh.js +1 -1
- package/dist/esm/session/socketLoader.js +1 -1
- package/dist/esm/utils/asyncUtils.js +1 -1
- package/dist/esm/utils/avatarUtils.js +2 -1
- package/dist/esm/utils/deviceManager.js +7 -5
- package/dist/esm/utils/errorUtils.js +1 -1
- package/dist/esm/utils/oauthPkce.js +1 -1
- package/dist/esm/utils/requestUtils.js +1 -1
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +3 -4
- package/dist/types/logger/index.d.ts +104 -0
- package/dist/types/mixins/OxyServices.topics.d.ts +3 -3
- package/dist/types/models/Topic.d.ts +10 -0
- package/dist/types/utils/requestUtils.d.ts +1 -1
- package/package.json +16 -1
- package/src/boot/sessionColdBoot.ts +1 -1
- package/src/crypto/keyManager.ts +1 -2
- package/src/crypto/signatureService.ts +1 -1
- package/src/index.ts +17 -19
- package/src/logger/__tests__/logger.test.ts +207 -0
- package/src/logger/index.ts +217 -0
- package/src/mixins/OxyServices.assets.ts +3 -3
- package/src/mixins/OxyServices.auth.ts +1 -1
- package/src/mixins/OxyServices.language.ts +3 -5
- package/src/mixins/OxyServices.privacy.ts +2 -4
- package/src/mixins/OxyServices.security.ts +7 -11
- package/src/mixins/OxyServices.topics.ts +47 -17
- package/src/mixins/OxyServices.user.ts +1 -1
- package/src/mixins/OxyServices.utility.ts +1 -1
- package/src/mixins/__tests__/discoveryErrorHandling.test.ts +1 -1
- package/src/mixins/__tests__/topics.test.ts +156 -0
- package/src/models/Topic.ts +11 -0
- package/src/session/SessionClient.ts +1 -1
- package/src/session/__tests__/SessionClient.diagnostics.test.ts +1 -1
- package/src/session/__tests__/accountDialogController.test.ts +1 -1
- package/src/session/accountDialogController.ts +1 -1
- package/src/session/authStateStore.ts +1 -1
- package/src/session/refresh.ts +1 -1
- package/src/session/socketLoader.ts +1 -1
- package/src/utils/asyncUtils.ts +1 -1
- package/src/utils/avatarUtils.ts +3 -1
- package/src/utils/deviceManager.ts +8 -5
- package/src/utils/errorUtils.ts +1 -1
- package/src/utils/oauthPkce.ts +1 -1
- package/src/utils/requestUtils.ts +1 -1
- package/dist/cjs/shared/utils/debugUtils.js +0 -80
- package/dist/cjs/utils/loggerUtils.js +0 -126
- package/dist/esm/shared/utils/debugUtils.js +0 -72
- package/dist/esm/utils/loggerUtils.js +0 -115
- package/dist/types/shared/utils/debugUtils.d.ts +0 -48
- package/dist/types/utils/loggerUtils.d.ts +0 -48
- package/src/shared/utils/__tests__/debugUtils.test.ts +0 -55
- package/src/shared/utils/debugUtils.ts +0 -78
- 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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
128
|
+
logger_1.logger.warn('[oxy.auth] Service token cache hit with mismatched secret', {
|
|
129
129
|
component: 'auth',
|
|
130
130
|
method: 'getServiceToken',
|
|
131
131
|
});
|
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
70
|
-
|
|
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
|
-
//
|
|
87
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
41
|
-
|
|
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
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
99
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
@@ -10,7 +10,7 @@ exports.OxyServicesUtilityMixin = OxyServicesUtilityMixin;
|
|
|
10
10
|
const jwt_decode_1 = require("jwt-decode");
|
|
11
11
|
const protocol_1 = require("@oxyhq/protocol");
|
|
12
12
|
const apiUtils_1 = require("../utils/apiUtils");
|
|
13
|
-
const
|
|
13
|
+
const logger_1 = require("../logger");
|
|
14
14
|
/**
|
|
15
15
|
* Expected JWT audience for tokens issued by the Oxy auth service.
|
|
16
16
|
*/
|
|
@@ -91,7 +91,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
91
91
|
return verified;
|
|
92
92
|
}
|
|
93
93
|
catch (error) {
|
|
94
|
-
|
|
94
|
+
logger_1.logger.warn('[oxy.auth] verifyServiceActingAs lookup failed — caching negative result', {
|
|
95
95
|
component: 'auth',
|
|
96
96
|
method: 'verifyServiceActingAs',
|
|
97
97
|
appId,
|
|
@@ -191,7 +191,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
191
191
|
const authHeader = Array.isArray(rawAuthHeader) ? rawAuthHeader[0] : rawAuthHeader;
|
|
192
192
|
const token = authHeader?.startsWith('Bearer ') ? authHeader.substring(7) : null;
|
|
193
193
|
if (debug) {
|
|
194
|
-
|
|
194
|
+
logger_1.logger.debug(`[oxy.auth] ${req.method} ${req.path} | token: ${!!token}`, {
|
|
195
195
|
component: 'auth',
|
|
196
196
|
method: 'auth',
|
|
197
197
|
});
|
|
@@ -219,7 +219,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
219
219
|
}
|
|
220
220
|
catch (decodeError) {
|
|
221
221
|
if (debug) {
|
|
222
|
-
|
|
222
|
+
logger_1.logger.debug('[oxy.auth] Token decode failed', {
|
|
223
223
|
component: 'auth',
|
|
224
224
|
method: 'auth',
|
|
225
225
|
}, decodeError);
|
|
@@ -282,7 +282,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
282
282
|
verifyError instanceof ServiceTokenSignatureError ||
|
|
283
283
|
verifyError instanceof ServiceTokenClaimError) {
|
|
284
284
|
if (debug) {
|
|
285
|
-
|
|
285
|
+
logger_1.logger.debug('[oxy.auth] Service token rejected', {
|
|
286
286
|
component: 'auth',
|
|
287
287
|
method: 'auth.serviceToken',
|
|
288
288
|
reason: verifyError.name,
|
|
@@ -309,7 +309,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
309
309
|
return onError(error);
|
|
310
310
|
return res.status(401).json(error);
|
|
311
311
|
}
|
|
312
|
-
|
|
312
|
+
logger_1.logger.error('[oxy.auth] Unexpected error during service token verification', verifyError, {
|
|
313
313
|
component: 'auth',
|
|
314
314
|
method: 'auth.serviceToken',
|
|
315
315
|
});
|
|
@@ -360,7 +360,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
360
360
|
if (oxyUserId) {
|
|
361
361
|
const grant = await oxyInstance.verifyServiceActingAs(appId, oxyUserId);
|
|
362
362
|
if (!grant || !grant.authorized) {
|
|
363
|
-
|
|
363
|
+
logger_1.logger.warn('[oxy.auth] Service token rejected — no delegation grant', {
|
|
364
364
|
component: 'auth',
|
|
365
365
|
method: 'auth.serviceToken',
|
|
366
366
|
appId,
|
|
@@ -393,7 +393,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
393
393
|
scopes: Array.isArray(decoded.scopes) ? decoded.scopes : [],
|
|
394
394
|
};
|
|
395
395
|
if (debug) {
|
|
396
|
-
|
|
396
|
+
logger_1.logger.debug(`[oxy.auth] Service token OK app=${decoded.appName} delegateUser=${oxyUserId || '(none)'}`, {
|
|
397
397
|
component: 'auth',
|
|
398
398
|
method: 'auth.serviceToken',
|
|
399
399
|
});
|
|
@@ -470,7 +470,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
470
470
|
req.user = { id: userId };
|
|
471
471
|
}
|
|
472
472
|
if (debug) {
|
|
473
|
-
|
|
473
|
+
logger_1.logger.debug(`[oxy.auth] OK user=${userId} session=${decoded.sessionId}`, {
|
|
474
474
|
component: 'auth',
|
|
475
475
|
method: 'auth',
|
|
476
476
|
});
|
|
@@ -479,7 +479,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
479
479
|
}
|
|
480
480
|
catch (validationError) {
|
|
481
481
|
if (debug) {
|
|
482
|
-
|
|
482
|
+
logger_1.logger.debug('[oxy.auth] Session validation failed', {
|
|
483
483
|
component: 'auth',
|
|
484
484
|
method: 'auth',
|
|
485
485
|
}, validationError);
|
|
@@ -526,7 +526,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
526
526
|
// Loading the full user is best-effort here; the basic { id }
|
|
527
527
|
// object is already attached. Log so misconfigured deployments
|
|
528
528
|
// can be diagnosed instead of silently failing.
|
|
529
|
-
|
|
529
|
+
logger_1.logger.warn('[oxy.auth] loadUser fallback — could not fetch full profile', {
|
|
530
530
|
component: 'auth',
|
|
531
531
|
method: 'auth.loadUser',
|
|
532
532
|
userId,
|
|
@@ -534,7 +534,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
534
534
|
}
|
|
535
535
|
}
|
|
536
536
|
if (debug) {
|
|
537
|
-
|
|
537
|
+
logger_1.logger.debug(`[oxy.auth] OK user=${userId} (no session)`, {
|
|
538
538
|
component: 'auth',
|
|
539
539
|
method: 'auth',
|
|
540
540
|
});
|
|
@@ -550,7 +550,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
550
550
|
details: handled.details,
|
|
551
551
|
};
|
|
552
552
|
if (debug) {
|
|
553
|
-
|
|
553
|
+
logger_1.logger.debug('[oxy.auth] Error', {
|
|
554
554
|
component: 'auth',
|
|
555
555
|
method: 'auth',
|
|
556
556
|
}, apiError);
|
|
@@ -601,7 +601,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
601
601
|
}
|
|
602
602
|
catch (decodeError) {
|
|
603
603
|
if (debug) {
|
|
604
|
-
|
|
604
|
+
logger_1.logger.debug('[oxy.authSocket] Token decode failed', {
|
|
605
605
|
component: 'auth',
|
|
606
606
|
method: 'authSocket',
|
|
607
607
|
}, decodeError);
|
|
@@ -641,7 +641,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
641
641
|
}
|
|
642
642
|
catch (validateErr) {
|
|
643
643
|
if (debug) {
|
|
644
|
-
|
|
644
|
+
logger_1.logger.debug('[oxy.authSocket] Session validation failed', {
|
|
645
645
|
component: 'auth',
|
|
646
646
|
method: 'authSocket',
|
|
647
647
|
}, validateErr);
|
|
@@ -658,7 +658,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
658
658
|
socket.data.token = token;
|
|
659
659
|
socket.user = { id: userId, userId, sessionId: decoded.sessionId };
|
|
660
660
|
if (debug) {
|
|
661
|
-
|
|
661
|
+
logger_1.logger.debug(`[oxy.authSocket] OK user=${userId}`, {
|
|
662
662
|
component: 'auth',
|
|
663
663
|
method: 'authSocket',
|
|
664
664
|
});
|
|
@@ -667,7 +667,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
667
667
|
}
|
|
668
668
|
catch (err) {
|
|
669
669
|
if (debug) {
|
|
670
|
-
|
|
670
|
+
logger_1.logger.debug('[oxy.authSocket] Error', {
|
|
671
671
|
component: 'auth',
|
|
672
672
|
method: 'authSocket',
|
|
673
673
|
}, err);
|
|
@@ -752,7 +752,7 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
752
752
|
next();
|
|
753
753
|
return;
|
|
754
754
|
}
|
|
755
|
-
|
|
755
|
+
logger_1.logger.warn('[oxy.auth] Service token missing required scope', {
|
|
756
756
|
component: 'auth',
|
|
757
757
|
method: 'requireScope',
|
|
758
758
|
appId: req.serviceApp.appId,
|