@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
package/dist/types/index.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export type { DeviceFingerprint, StoredDeviceInfo } from './utils/deviceManager'
|
|
|
57
57
|
export type { OxyConfig, PrivacySettings, NotificationPreferences, UserPreferences, User, LoginResponse, Notification, Wallet, Transaction, BlockedUser, RestrictedUser, TransferFundsRequest, PurchaseRequest, WithdrawalRequest, TransactionResponse, PaginationInfo, SearchProfilesResponse, ApiError, PaymentMethod, PaymentRequest, PaymentResponse, AnalyticsData, FollowerDetails, ContentViewer, FileMetadata, FileUploadResponse, FileListResponse, FileUpdateRequest, FileDeleteResponse, RNFileDescriptor, AssetUploadInput, FileVisibility, AssetLink, AssetMetadata, AssetVariant, Asset, AssetInitRequest, AssetInitResponse, AssetCompleteRequest, AssetLinkRequest, AssetUnlinkRequest, AssetUrlResponse, AssetDeleteSummary, AssetUpdateVisibilityRequest, AssetUpdateVisibilityResponse, ServiceAssetMetadata, ServiceAssetMetadataBySha, AccountStorageCategoryUsage, AccountStorageUsageResponse, SecurityEventType, SecurityEventSeverity, SecurityActivity, SecurityActivityResponse, AssetUploadProgress, DeviceLinkedSession, DeviceLinkedSessionsResponse, DeviceLinkedSessionLogoutResponse, UpdateDeviceNameResponse, } from './models/interfaces';
|
|
58
58
|
export { SECURITY_EVENT_SEVERITY_MAP } from './models/interfaces';
|
|
59
59
|
export { TopicType, TopicSource } from './models/Topic';
|
|
60
|
-
export type { TopicData, TopicTranslation } from './models/Topic';
|
|
60
|
+
export type { TopicData, TopicTranslation, TopicListResult } from './models/Topic';
|
|
61
61
|
export { SUPPORTED_LANGUAGES, FALLBACK_LOCALE, getBaseLanguage, normalizeLocale, isSupportedLocale, getLanguageMetadata, getLanguageName, getNativeLanguageName, isRTLLocale, getUserLanguages, getPrimaryLanguage, } from './utils/languageUtils';
|
|
62
62
|
export type { SupportedLanguage } from './utils/languageUtils';
|
|
63
63
|
export { getPlatformOS, setPlatformOS, isWeb, isNative, isIOS, isAndroid, isWebBrowser, } from './utils/platform';
|
|
@@ -68,7 +68,6 @@ export type { ThemeValue } from './shared/utils/themeUtils';
|
|
|
68
68
|
export { HttpStatus, getErrorStatus, getErrorMessage, isAlreadyRegisteredError, isUnauthorizedError, isForbiddenError, isNotFoundError, isRateLimitError, isServerError, isNetworkError, isRetryableError, } from './shared/utils/errorUtils';
|
|
69
69
|
export { DEFAULT_CIRCUIT_BREAKER_CONFIG, createCircuitBreakerState, calculateBackoffInterval, recordFailure, recordSuccess, shouldAllowRequest, delay, withRetry, } from './shared/utils/networkUtils';
|
|
70
70
|
export type { CircuitBreakerState, CircuitBreakerConfig } from './shared/utils/networkUtils';
|
|
71
|
-
export { isDev, debugLog, debugWarn, debugError, createDebugLogger, } from './shared/utils/debugUtils';
|
|
72
71
|
export { translate } from './i18n';
|
|
73
72
|
export { buildSearchParams, buildUrl, buildPaginationParams, safeJsonParse, } from './utils/apiUtils';
|
|
74
73
|
export type { PaginationParams, ApiResponse, ErrorResponse, } from './utils/apiUtils';
|
|
@@ -76,8 +75,8 @@ export { ErrorCodes, createApiError, handleHttpError, validateRequiredFields, }
|
|
|
76
75
|
export { retryAsync } from './utils/asyncUtils';
|
|
77
76
|
export { EMAIL_REGEX, USERNAME_REGEX, PASSWORD_REGEX, isValidEmail, isValidUsername, isValidPassword, isValidDisplayName, isRequiredString, isRequiredNumber, isRequiredBoolean, isValidArray, isValidObject, isValidUUID, isValidURL, isValidDate, isValidFileSize, isValidFileType, sanitizeString, sanitizeHTML, isValidObjectId, validateAndSanitizeUserInput, } from './utils/validationUtils';
|
|
78
77
|
export { normalizeInlineText, normalizeMultilineText, } from './utils/textNormalization';
|
|
79
|
-
export { logger,
|
|
80
|
-
export type { LogContext } from './
|
|
78
|
+
export { logger, createLogger, configureLogger, getLoggerConfig, resetLoggerConfig, consoleSink, isDev, } from './logger';
|
|
79
|
+
export type { Logger, LogLevel, EmittableLogLevel, LogContext, LogEntry, LogSink, LoggerConfig, } from './logger';
|
|
81
80
|
export { updateAvatarVisibility } from './utils/avatarUtils';
|
|
82
81
|
export { buildAccountsArray, createQuickAccount, getAccountDisplayName, getAccountFallbackHandle, formatPublicKeyHandle, getAccountColor, } from './utils/accountUtils';
|
|
83
82
|
export type { QuickAccount, DisplayNameUserShape } from './utils/accountUtils';
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @oxyhq/core/logger — the ecosystem-wide logging chokepoint.
|
|
3
|
+
*
|
|
4
|
+
* A tiny, dependency-free, universal logger that works unchanged in React
|
|
5
|
+
* Native, browsers, Node, and Bun. Every Oxy app and package should log
|
|
6
|
+
* through here instead of calling `console.*` directly, so that level,
|
|
7
|
+
* formatting, and transport are controlled in ONE place.
|
|
8
|
+
*
|
|
9
|
+
* Design goals:
|
|
10
|
+
* - Zero dependencies. No Node-only imports, no `process.stdout` assumptions.
|
|
11
|
+
* - Four levels (`debug` | `info` | `warn` | `error`) plus `silent`.
|
|
12
|
+
* - Namespaced child loggers via `createLogger('mention:feed')` / `.child()`.
|
|
13
|
+
* - Structured context objects carried through to the sink.
|
|
14
|
+
* - A single pluggable sink so backends can pipe to pino / CloudWatch / etc.
|
|
15
|
+
* without touching any call site.
|
|
16
|
+
* - Debug is off in production by default (`__DEV__` on RN, `NODE_ENV`
|
|
17
|
+
* elsewhere); override globally with `configureLogger({ level })`.
|
|
18
|
+
*
|
|
19
|
+
* @module logger
|
|
20
|
+
*/
|
|
21
|
+
/** Ordered severity levels. `silent` disables all output. */
|
|
22
|
+
export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';
|
|
23
|
+
/** The levels that actually emit an entry (everything but `silent`). */
|
|
24
|
+
export type EmittableLogLevel = Exclude<LogLevel, 'silent'>;
|
|
25
|
+
/**
|
|
26
|
+
* Structured, JSON-friendly context attached to a log line. The well-known
|
|
27
|
+
* keys are optional hints; arbitrary keys are allowed so callers can attach
|
|
28
|
+
* whatever structured fields their sink cares about.
|
|
29
|
+
*/
|
|
30
|
+
export interface LogContext {
|
|
31
|
+
component?: string;
|
|
32
|
+
method?: string;
|
|
33
|
+
userId?: string;
|
|
34
|
+
sessionId?: string;
|
|
35
|
+
requestId?: string;
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
}
|
|
38
|
+
/** A single normalized log record handed to the active sink. */
|
|
39
|
+
export interface LogEntry {
|
|
40
|
+
level: EmittableLogLevel;
|
|
41
|
+
message: string;
|
|
42
|
+
/** Colon-joined namespace, e.g. `mention:feed`. Absent for the root logger. */
|
|
43
|
+
namespace?: string;
|
|
44
|
+
/** Merged structured context (logger base context + per-call context). */
|
|
45
|
+
context?: LogContext;
|
|
46
|
+
/** The error value passed to `.error(message, error, …)`, if any. */
|
|
47
|
+
error?: unknown;
|
|
48
|
+
/** Extra variadic args passed after the context. */
|
|
49
|
+
args: unknown[];
|
|
50
|
+
/** ISO-8601 timestamp of when the entry was created. */
|
|
51
|
+
timestamp: string;
|
|
52
|
+
}
|
|
53
|
+
/** A transport that receives every emitted (level-passing) entry. */
|
|
54
|
+
export type LogSink = (entry: LogEntry) => void;
|
|
55
|
+
/** Global logger configuration. */
|
|
56
|
+
export interface LoggerConfig {
|
|
57
|
+
level: LogLevel;
|
|
58
|
+
sink: LogSink;
|
|
59
|
+
}
|
|
60
|
+
/** A namespaced logger. Instances are cheap; derive children with `.child()`. */
|
|
61
|
+
export interface Logger {
|
|
62
|
+
/** The logger's colon-joined namespace, if any. */
|
|
63
|
+
readonly namespace?: string;
|
|
64
|
+
debug(message: string, context?: LogContext, ...args: unknown[]): void;
|
|
65
|
+
info(message: string, context?: LogContext, ...args: unknown[]): void;
|
|
66
|
+
warn(message: string, context?: LogContext, ...args: unknown[]): void;
|
|
67
|
+
error(message: string, error?: unknown, context?: LogContext, ...args: unknown[]): void;
|
|
68
|
+
/** Derive a child logger with an extended namespace and merged base context. */
|
|
69
|
+
child(namespace: string, context?: LogContext): Logger;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* True in development. Uses React Native's `__DEV__` when present, otherwise
|
|
73
|
+
* falls back to `process.env.NODE_ENV === 'development'`. Never throws.
|
|
74
|
+
*/
|
|
75
|
+
export declare function isDev(): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Default sink: routes each entry to the matching `console` method. `info` and
|
|
78
|
+
* `debug` go to `console.log` (RN/browser-safe — `console.debug`/`console.info`
|
|
79
|
+
* are inconsistently surfaced across runtimes).
|
|
80
|
+
*/
|
|
81
|
+
export declare const consoleSink: LogSink;
|
|
82
|
+
/**
|
|
83
|
+
* Update the global logger configuration. Affects every logger instance
|
|
84
|
+
* (root and children) immediately. Pass `{ level }` to gate output and/or
|
|
85
|
+
* `{ sink }` to redirect transport (pino, CloudWatch, a test capture, …).
|
|
86
|
+
*/
|
|
87
|
+
export declare function configureLogger(partial: Partial<LoggerConfig>): void;
|
|
88
|
+
/** Read a snapshot of the current global configuration. */
|
|
89
|
+
export declare function getLoggerConfig(): Readonly<LoggerConfig>;
|
|
90
|
+
/** Restore the default level (env-derived) and the console sink. */
|
|
91
|
+
export declare function resetLoggerConfig(): void;
|
|
92
|
+
/** The shared root logger. Prefer a namespaced `createLogger(...)` in modules. */
|
|
93
|
+
export declare const logger: Logger;
|
|
94
|
+
/**
|
|
95
|
+
* Create a namespaced logger.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* const log = createLogger('mention:feed');
|
|
100
|
+
* log.info('loaded', { count: 20 });
|
|
101
|
+
* const sub = log.child('prefetch'); // namespace → 'mention:feed:prefetch'
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare function createLogger(namespace?: string, context?: LogContext): Logger;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Provides methods for topic discovery and management
|
|
5
5
|
*/
|
|
6
6
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
7
|
-
import type { TopicData, TopicTranslation } from '../models/Topic';
|
|
7
|
+
import type { TopicData, TopicListResult, TopicTranslation } from '../models/Topic';
|
|
8
8
|
export declare function OxyServicesTopicsMixin<T extends typeof OxyServicesBase>(Base: T): {
|
|
9
9
|
new (...args: any[]): {
|
|
10
10
|
/**
|
|
@@ -23,7 +23,7 @@ export declare function OxyServicesTopicsMixin<T extends typeof OxyServicesBase>
|
|
|
23
23
|
/**
|
|
24
24
|
* List topics with optional filters
|
|
25
25
|
* @param options - Filter and pagination options
|
|
26
|
-
* @returns
|
|
26
|
+
* @returns Paginated topics envelope (`topics` plus `total`/`limit`/`offset`)
|
|
27
27
|
*/
|
|
28
28
|
listTopics(options?: {
|
|
29
29
|
type?: string;
|
|
@@ -31,7 +31,7 @@ export declare function OxyServicesTopicsMixin<T extends typeof OxyServicesBase>
|
|
|
31
31
|
limit?: number;
|
|
32
32
|
offset?: number;
|
|
33
33
|
locale?: string;
|
|
34
|
-
}): Promise<
|
|
34
|
+
}): Promise<TopicListResult>;
|
|
35
35
|
/**
|
|
36
36
|
* Get a single topic by slug
|
|
37
37
|
* @param slug - Topic slug
|
|
@@ -30,3 +30,13 @@ export interface TopicData {
|
|
|
30
30
|
createdAt: string;
|
|
31
31
|
updatedAt: string;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Paginated result of {@link OxyServices.listTopics}. Mirrors the `GET /topics`
|
|
35
|
+
* envelope so callers keep access to `total`/`offset` for pagination.
|
|
36
|
+
*/
|
|
37
|
+
export interface TopicListResult {
|
|
38
|
+
topics: TopicData[];
|
|
39
|
+
total: number;
|
|
40
|
+
limit: number;
|
|
41
|
+
offset: number;
|
|
42
|
+
}
|
|
@@ -93,7 +93,7 @@ export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
|
|
|
93
93
|
* Simple logger with level support
|
|
94
94
|
*
|
|
95
95
|
* Lightweight logger for HTTP clients and utilities.
|
|
96
|
-
* For
|
|
96
|
+
* For structured, namespaced, level-gated logging, use `@oxyhq/core/logger`.
|
|
97
97
|
*
|
|
98
98
|
* @example
|
|
99
99
|
* ```typescript
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.0",
|
|
4
4
|
"description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"*": {
|
|
10
10
|
"server": [
|
|
11
11
|
"dist/types/server/index.d.ts"
|
|
12
|
+
],
|
|
13
|
+
"logger": [
|
|
14
|
+
"dist/types/logger/index.d.ts"
|
|
12
15
|
]
|
|
13
16
|
}
|
|
14
17
|
},
|
|
@@ -41,6 +44,18 @@
|
|
|
41
44
|
},
|
|
42
45
|
"default": "./dist/esm/server/index.js"
|
|
43
46
|
},
|
|
47
|
+
"./logger": {
|
|
48
|
+
"react-native": "./dist/esm/logger/index.js",
|
|
49
|
+
"import": {
|
|
50
|
+
"types": "./dist/types/logger/index.d.ts",
|
|
51
|
+
"default": "./dist/esm/logger/index.js"
|
|
52
|
+
},
|
|
53
|
+
"require": {
|
|
54
|
+
"types": "./dist/types/logger/index.d.ts",
|
|
55
|
+
"default": "./dist/cjs/logger/index.js"
|
|
56
|
+
},
|
|
57
|
+
"default": "./dist/esm/logger/index.js"
|
|
58
|
+
},
|
|
44
59
|
"./package.json": "./package.json"
|
|
45
60
|
},
|
|
46
61
|
"files": [
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { runColdBoot, type ColdBootOutcome, type ColdBootStep } from '../utils/coldBoot';
|
|
24
24
|
import { isNative as detectNative } from '../utils/platform';
|
|
25
|
-
import { logger } from '../
|
|
25
|
+
import { logger } from '../logger';
|
|
26
26
|
import { TOKEN_REFRESH_LEAD_MS, refreshDeviceSecretArm } from '../session/refresh';
|
|
27
27
|
import type { OxyServices } from '../OxyServices';
|
|
28
28
|
import type { AuthStateStore } from '../session/authStateStore';
|
package/src/crypto/keyManager.ts
CHANGED
|
@@ -9,8 +9,7 @@ import { ec as EC } from 'elliptic';
|
|
|
9
9
|
import type { ECKeyPair } from 'elliptic';
|
|
10
10
|
import { isWeb, isIOS, isAndroid } from '../utils/platform';
|
|
11
11
|
import { type ExpoCryptoLike, type ExpoSecureStoreLike, isReactNative, isNodeJS, loadExpoCrypto, loadNodeCrypto, loadSecureStore, loadSharedIdentityBridge } from '@oxyhq/protocol';
|
|
12
|
-
import { logger } from '../
|
|
13
|
-
import { isDev } from '../shared/utils/debugUtils';
|
|
12
|
+
import { isDev, logger } from '../logger';
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* Options for expo-secure-store calls made by KeyManager.
|
package/src/index.ts
CHANGED
|
@@ -317,7 +317,7 @@ export { SECURITY_EVENT_SEVERITY_MAP } from './models/interfaces';
|
|
|
317
317
|
|
|
318
318
|
// Topic enums + type
|
|
319
319
|
export { TopicType, TopicSource } from './models/Topic';
|
|
320
|
-
export type { TopicData, TopicTranslation } from './models/Topic';
|
|
320
|
+
export type { TopicData, TopicTranslation, TopicListResult } from './models/Topic';
|
|
321
321
|
|
|
322
322
|
// ---------------------------------------------------------------------------
|
|
323
323
|
// Languages
|
|
@@ -402,14 +402,6 @@ export {
|
|
|
402
402
|
} from './shared/utils/networkUtils';
|
|
403
403
|
export type { CircuitBreakerState, CircuitBreakerConfig } from './shared/utils/networkUtils';
|
|
404
404
|
|
|
405
|
-
export {
|
|
406
|
-
isDev,
|
|
407
|
-
debugLog,
|
|
408
|
-
debugWarn,
|
|
409
|
-
debugError,
|
|
410
|
-
createDebugLogger,
|
|
411
|
-
} from './shared/utils/debugUtils';
|
|
412
|
-
|
|
413
405
|
// ---------------------------------------------------------------------------
|
|
414
406
|
// i18n
|
|
415
407
|
// ---------------------------------------------------------------------------
|
|
@@ -475,20 +467,26 @@ export {
|
|
|
475
467
|
} from './utils/textNormalization';
|
|
476
468
|
|
|
477
469
|
// ---------------------------------------------------------------------------
|
|
478
|
-
// Logging
|
|
470
|
+
// Logging — the ecosystem-wide chokepoint (also at subpath `@oxyhq/core/logger`)
|
|
479
471
|
// ---------------------------------------------------------------------------
|
|
480
472
|
export {
|
|
481
473
|
logger,
|
|
474
|
+
createLogger,
|
|
475
|
+
configureLogger,
|
|
476
|
+
getLoggerConfig,
|
|
477
|
+
resetLoggerConfig,
|
|
478
|
+
consoleSink,
|
|
479
|
+
isDev,
|
|
480
|
+
} from './logger';
|
|
481
|
+
export type {
|
|
482
|
+
Logger,
|
|
482
483
|
LogLevel,
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
logPerformance,
|
|
490
|
-
} from './utils/loggerUtils';
|
|
491
|
-
export type { LogContext } from './utils/loggerUtils';
|
|
484
|
+
EmittableLogLevel,
|
|
485
|
+
LogContext,
|
|
486
|
+
LogEntry,
|
|
487
|
+
LogSink,
|
|
488
|
+
LoggerConfig,
|
|
489
|
+
} from './logger';
|
|
492
490
|
|
|
493
491
|
// ---------------------------------------------------------------------------
|
|
494
492
|
// Avatars
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import {
|
|
2
|
+
configureLogger,
|
|
3
|
+
consoleSink,
|
|
4
|
+
createLogger,
|
|
5
|
+
getLoggerConfig,
|
|
6
|
+
isDev,
|
|
7
|
+
logger,
|
|
8
|
+
resetLoggerConfig,
|
|
9
|
+
type LogEntry,
|
|
10
|
+
} from '../index';
|
|
11
|
+
|
|
12
|
+
/** Install a capturing sink and return the collected entries. */
|
|
13
|
+
function captureEntries(): LogEntry[] {
|
|
14
|
+
const entries: LogEntry[] = [];
|
|
15
|
+
configureLogger({ sink: (entry) => entries.push(entry) });
|
|
16
|
+
return entries;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('logger', () => {
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
resetLoggerConfig();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('level gating', () => {
|
|
25
|
+
it('emits only levels at or below the configured level', () => {
|
|
26
|
+
const entries = captureEntries();
|
|
27
|
+
configureLogger({ level: 'warn' });
|
|
28
|
+
|
|
29
|
+
logger.debug('d');
|
|
30
|
+
logger.info('i');
|
|
31
|
+
logger.warn('w');
|
|
32
|
+
logger.error('e');
|
|
33
|
+
|
|
34
|
+
expect(entries.map((entry) => entry.level)).toEqual(['warn', 'error']);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('emits everything at debug level', () => {
|
|
38
|
+
const entries = captureEntries();
|
|
39
|
+
configureLogger({ level: 'debug' });
|
|
40
|
+
|
|
41
|
+
logger.debug('d');
|
|
42
|
+
logger.info('i');
|
|
43
|
+
logger.warn('w');
|
|
44
|
+
logger.error('e');
|
|
45
|
+
|
|
46
|
+
expect(entries.map((entry) => entry.level)).toEqual(['debug', 'info', 'warn', 'error']);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('emits nothing at silent level', () => {
|
|
50
|
+
const entries = captureEntries();
|
|
51
|
+
configureLogger({ level: 'silent' });
|
|
52
|
+
|
|
53
|
+
logger.debug('d');
|
|
54
|
+
logger.info('i');
|
|
55
|
+
logger.warn('w');
|
|
56
|
+
logger.error('e');
|
|
57
|
+
|
|
58
|
+
expect(entries).toHaveLength(0);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('namespacing', () => {
|
|
63
|
+
it('root logger has no namespace', () => {
|
|
64
|
+
const entries = captureEntries();
|
|
65
|
+
configureLogger({ level: 'debug' });
|
|
66
|
+
|
|
67
|
+
logger.info('hello');
|
|
68
|
+
|
|
69
|
+
expect(entries[0].namespace).toBeUndefined();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('createLogger sets the namespace', () => {
|
|
73
|
+
const entries = captureEntries();
|
|
74
|
+
configureLogger({ level: 'debug' });
|
|
75
|
+
|
|
76
|
+
createLogger('mention:feed').info('loaded');
|
|
77
|
+
|
|
78
|
+
expect(entries[0].namespace).toBe('mention:feed');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('child() colon-joins nested namespaces', () => {
|
|
82
|
+
const entries = captureEntries();
|
|
83
|
+
configureLogger({ level: 'debug' });
|
|
84
|
+
|
|
85
|
+
createLogger('mention:feed').child('prefetch').info('done');
|
|
86
|
+
|
|
87
|
+
expect(entries[0].namespace).toBe('mention:feed:prefetch');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('structured context', () => {
|
|
92
|
+
it('passes per-call context through to the sink', () => {
|
|
93
|
+
const entries = captureEntries();
|
|
94
|
+
configureLogger({ level: 'debug' });
|
|
95
|
+
|
|
96
|
+
logger.info('hi', { userId: 'u1', requestId: 'r1' });
|
|
97
|
+
|
|
98
|
+
expect(entries[0].context).toEqual({ userId: 'u1', requestId: 'r1' });
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('merges base context with per-call context (per-call wins)', () => {
|
|
102
|
+
const entries = captureEntries();
|
|
103
|
+
configureLogger({ level: 'debug' });
|
|
104
|
+
|
|
105
|
+
createLogger('svc', { component: 'Svc', userId: 'base' }).warn('x', { userId: 'call', method: 'm' });
|
|
106
|
+
|
|
107
|
+
expect(entries[0].context).toEqual({ component: 'Svc', userId: 'call', method: 'm' });
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('captures the error value on error()', () => {
|
|
111
|
+
const entries = captureEntries();
|
|
112
|
+
configureLogger({ level: 'debug' });
|
|
113
|
+
const boom = new Error('boom');
|
|
114
|
+
|
|
115
|
+
logger.error('failed', boom, { component: 'Svc' });
|
|
116
|
+
|
|
117
|
+
expect(entries[0].error).toBe(boom);
|
|
118
|
+
expect(entries[0].context).toEqual({ component: 'Svc' });
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('forwards trailing variadic args', () => {
|
|
122
|
+
const entries = captureEntries();
|
|
123
|
+
configureLogger({ level: 'debug' });
|
|
124
|
+
|
|
125
|
+
logger.warn('x', { component: 'Svc' }, 1, 'two');
|
|
126
|
+
|
|
127
|
+
expect(entries[0].args).toEqual([1, 'two']);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('sink override', () => {
|
|
132
|
+
it('routes all output through the configured sink', () => {
|
|
133
|
+
const sink = jest.fn();
|
|
134
|
+
configureLogger({ level: 'debug', sink });
|
|
135
|
+
|
|
136
|
+
logger.info('routed');
|
|
137
|
+
|
|
138
|
+
expect(sink).toHaveBeenCalledTimes(1);
|
|
139
|
+
expect(sink.mock.calls[0][0]).toMatchObject({ level: 'info', message: 'routed' });
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('resetLoggerConfig restores the console sink', () => {
|
|
143
|
+
configureLogger({ sink: jest.fn() });
|
|
144
|
+
resetLoggerConfig();
|
|
145
|
+
expect(getLoggerConfig().sink).toBe(consoleSink);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('consoleSink', () => {
|
|
150
|
+
it('routes error to console.error and warn to console.warn', () => {
|
|
151
|
+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
152
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
153
|
+
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
154
|
+
const now = new Date().toISOString();
|
|
155
|
+
|
|
156
|
+
consoleSink({ level: 'error', message: 'e', args: [], timestamp: now });
|
|
157
|
+
consoleSink({ level: 'warn', message: 'w', args: [], timestamp: now });
|
|
158
|
+
consoleSink({ level: 'info', message: 'i', args: [], timestamp: now });
|
|
159
|
+
|
|
160
|
+
expect(errorSpy).toHaveBeenCalledTimes(1);
|
|
161
|
+
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
162
|
+
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
163
|
+
|
|
164
|
+
errorSpy.mockRestore();
|
|
165
|
+
warnSpy.mockRestore();
|
|
166
|
+
logSpy.mockRestore();
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('isDev', () => {
|
|
171
|
+
// `isDev` reads the RN-injected global `__DEV__` first; model it as an
|
|
172
|
+
// optional global so we can exercise the NODE_ENV fallback without `as any`.
|
|
173
|
+
const globalWithDev = globalThis as typeof globalThis & { __DEV__?: boolean };
|
|
174
|
+
const originalDev = globalWithDev.__DEV__;
|
|
175
|
+
const originalNodeEnv = process.env.NODE_ENV;
|
|
176
|
+
|
|
177
|
+
afterEach(() => {
|
|
178
|
+
// Setting to `undefined` makes `typeof __DEV__` read as 'undefined', the
|
|
179
|
+
// same signal `isDev` keys off — no need for the `delete` operator.
|
|
180
|
+
globalWithDev.__DEV__ = originalDev;
|
|
181
|
+
process.env.NODE_ENV = originalNodeEnv;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('returns true when __DEV__ is true (React Native)', () => {
|
|
185
|
+
globalWithDev.__DEV__ = true;
|
|
186
|
+
expect(isDev()).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('returns false when __DEV__ is false regardless of NODE_ENV', () => {
|
|
190
|
+
globalWithDev.__DEV__ = false;
|
|
191
|
+
process.env.NODE_ENV = 'development';
|
|
192
|
+
expect(isDev()).toBe(false);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('falls back to NODE_ENV=development when __DEV__ is undefined', () => {
|
|
196
|
+
globalWithDev.__DEV__ = undefined;
|
|
197
|
+
process.env.NODE_ENV = 'development';
|
|
198
|
+
expect(isDev()).toBe(true);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('returns false when NODE_ENV is production and __DEV__ is undefined', () => {
|
|
202
|
+
globalWithDev.__DEV__ = undefined;
|
|
203
|
+
process.env.NODE_ENV = 'production';
|
|
204
|
+
expect(isDev()).toBe(false);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
});
|