@oxyhq/core 1.0.1 → 1.1.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/AuthManager.js +19 -9
- package/dist/cjs/CrossDomainAuth.js +2 -2
- package/dist/cjs/HttpService.js +9 -8
- package/dist/cjs/OxyServices.base.js +16 -3
- package/dist/cjs/crypto/keyManager.js +29 -24
- package/dist/cjs/crypto/polyfill.js +6 -1
- package/dist/cjs/crypto/signatureService.js +40 -31
- package/dist/cjs/i18n/index.js +36 -45
- package/dist/cjs/i18n/locales/ar-SA.json +114 -115
- package/dist/cjs/i18n/locales/ca-ES.json +114 -115
- package/dist/cjs/i18n/locales/de-DE.json +114 -115
- package/dist/cjs/i18n/locales/en-US.json +936 -936
- package/dist/cjs/i18n/locales/es-ES.json +924 -924
- package/dist/cjs/i18n/locales/fr-FR.json +114 -115
- package/dist/cjs/i18n/locales/it-IT.json +114 -115
- package/dist/cjs/i18n/locales/ja-JP.json +2 -2
- package/dist/cjs/i18n/locales/ko-KR.json +114 -115
- package/dist/cjs/i18n/locales/pt-PT.json +114 -115
- package/dist/cjs/i18n/locales/zh-CN.json +114 -115
- package/dist/cjs/mixins/OxyServices.fedcm.js +13 -41
- package/dist/cjs/mixins/OxyServices.language.js +5 -2
- package/dist/cjs/mixins/OxyServices.privacy.js +2 -1
- package/dist/cjs/mixins/OxyServices.security.js +3 -2
- package/dist/cjs/shared/utils/debugUtils.js +8 -1
- package/dist/cjs/utils/deviceManager.js +3 -1
- package/dist/cjs/utils/platform.js +3 -2
- package/dist/esm/AuthManager.js +19 -9
- package/dist/esm/CrossDomainAuth.js +2 -2
- package/dist/esm/HttpService.js +9 -8
- package/dist/esm/OxyServices.base.js +16 -3
- package/dist/esm/crypto/keyManager.js +29 -24
- package/dist/esm/crypto/polyfill.js +6 -1
- package/dist/esm/crypto/signatureService.js +40 -31
- package/dist/esm/i18n/index.js +11 -23
- package/dist/esm/i18n/locales/ar-SA.json +114 -115
- package/dist/esm/i18n/locales/ca-ES.json +114 -115
- package/dist/esm/i18n/locales/de-DE.json +114 -115
- package/dist/esm/i18n/locales/en-US.json +936 -936
- package/dist/esm/i18n/locales/es-ES.json +924 -924
- package/dist/esm/i18n/locales/fr-FR.json +114 -115
- package/dist/esm/i18n/locales/it-IT.json +114 -115
- package/dist/esm/i18n/locales/ja-JP.json +2 -2
- package/dist/esm/i18n/locales/ko-KR.json +114 -115
- package/dist/esm/i18n/locales/pt-PT.json +114 -115
- package/dist/esm/i18n/locales/zh-CN.json +114 -115
- package/dist/esm/mixins/OxyServices.fedcm.js +13 -41
- package/dist/esm/mixins/OxyServices.language.js +5 -2
- package/dist/esm/mixins/OxyServices.privacy.js +2 -1
- package/dist/esm/mixins/OxyServices.security.js +3 -2
- package/dist/esm/shared/utils/debugUtils.js +8 -1
- package/dist/esm/utils/deviceManager.js +3 -1
- package/dist/esm/utils/platform.js +3 -2
- package/dist/types/CrossDomainAuth.d.ts +2 -2
- package/dist/types/OxyServices.base.d.ts +4 -1
- package/dist/types/OxyServices.d.ts +13 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/mixins/OxyServices.analytics.d.ts +2 -0
- package/dist/types/mixins/OxyServices.assets.d.ts +2 -0
- package/dist/types/mixins/OxyServices.auth.d.ts +2 -0
- package/dist/types/mixins/OxyServices.developer.d.ts +2 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +2 -0
- package/dist/types/mixins/OxyServices.features.d.ts +2 -0
- package/dist/types/mixins/OxyServices.fedcm.d.ts +4 -2
- package/dist/types/mixins/OxyServices.karma.d.ts +2 -0
- package/dist/types/mixins/OxyServices.language.d.ts +2 -0
- package/dist/types/mixins/OxyServices.location.d.ts +2 -0
- package/dist/types/mixins/OxyServices.payment.d.ts +2 -0
- package/dist/types/mixins/OxyServices.popup.d.ts +2 -0
- package/dist/types/mixins/OxyServices.privacy.d.ts +2 -0
- package/dist/types/mixins/OxyServices.redirect.d.ts +2 -0
- package/dist/types/mixins/OxyServices.security.d.ts +2 -0
- package/dist/types/mixins/OxyServices.user.d.ts +2 -0
- package/dist/types/mixins/OxyServices.utility.d.ts +2 -0
- package/package.json +2 -3
- package/src/AuthManager.ts +25 -15
- package/src/CrossDomainAuth.ts +2 -2
- package/src/HttpService.ts +9 -8
- package/src/OxyServices.base.ts +21 -4
- package/src/OxyServices.ts +23 -2
- package/src/crypto/keyManager.ts +30 -25
- package/src/crypto/polyfill.ts +6 -1
- package/src/crypto/signatureService.ts +43 -37
- package/src/i18n/index.ts +33 -45
- package/src/index.ts +3 -0
- package/src/mixins/OxyServices.fedcm.ts +14 -44
- package/src/mixins/OxyServices.language.ts +6 -3
- package/src/mixins/OxyServices.privacy.ts +2 -1
- package/src/mixins/OxyServices.security.ts +3 -2
- package/src/shared/utils/__tests__/debugUtils.test.ts +55 -0
- package/src/shared/utils/debugUtils.ts +6 -1
- package/src/utils/deviceManager.ts +4 -2
- package/src/utils/platform.ts +3 -2
package/src/i18n/index.ts
CHANGED
|
@@ -1,52 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
13
|
-
const itIT = require('./locales/it-IT.json') as Record<string, any>;
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
15
|
-
const ptPT = require('./locales/pt-PT.json') as Record<string, any>;
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
17
|
-
const jaJP = require('./locales/ja-JP.json') as Record<string, any>;
|
|
18
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
19
|
-
const koKR = require('./locales/ko-KR.json') as Record<string, any>;
|
|
20
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
21
|
-
const zhCN = require('./locales/zh-CN.json') as Record<string, any>;
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
23
|
-
const arSA = require('./locales/ar-SA.json') as Record<string, any>;
|
|
1
|
+
import enUS from './locales/en-US.json';
|
|
2
|
+
import esES from './locales/es-ES.json';
|
|
3
|
+
import caES from './locales/ca-ES.json';
|
|
4
|
+
import frFR from './locales/fr-FR.json';
|
|
5
|
+
import deDE from './locales/de-DE.json';
|
|
6
|
+
import itIT from './locales/it-IT.json';
|
|
7
|
+
import ptPT from './locales/pt-PT.json';
|
|
8
|
+
import jaJP from './locales/ja-JP.json';
|
|
9
|
+
import koKR from './locales/ko-KR.json';
|
|
10
|
+
import zhCN from './locales/zh-CN.json';
|
|
11
|
+
import arSA from './locales/ar-SA.json';
|
|
24
12
|
|
|
25
13
|
export type LocaleDict = Record<string, any>;
|
|
26
14
|
|
|
27
15
|
const DICTS: Record<string, LocaleDict> = {
|
|
28
|
-
'en': enUS
|
|
29
|
-
'en-US': enUS
|
|
30
|
-
'es': esES
|
|
31
|
-
'es-ES': esES
|
|
32
|
-
'ca': caES
|
|
33
|
-
'ca-ES': caES
|
|
34
|
-
'fr': frFR
|
|
35
|
-
'fr-FR': frFR
|
|
36
|
-
'de': deDE
|
|
37
|
-
'de-DE': deDE
|
|
38
|
-
'it': itIT
|
|
39
|
-
'it-IT': itIT
|
|
40
|
-
'pt': ptPT
|
|
41
|
-
'pt-PT': ptPT
|
|
42
|
-
'ja': jaJP
|
|
43
|
-
'ja-JP': jaJP
|
|
44
|
-
'ko': koKR
|
|
45
|
-
'ko-KR': koKR
|
|
46
|
-
'zh': zhCN
|
|
47
|
-
'zh-CN': zhCN
|
|
48
|
-
'ar': arSA
|
|
49
|
-
'ar-SA': arSA
|
|
16
|
+
'en': enUS,
|
|
17
|
+
'en-US': enUS,
|
|
18
|
+
'es': esES,
|
|
19
|
+
'es-ES': esES,
|
|
20
|
+
'ca': caES,
|
|
21
|
+
'ca-ES': caES,
|
|
22
|
+
'fr': frFR,
|
|
23
|
+
'fr-FR': frFR,
|
|
24
|
+
'de': deDE,
|
|
25
|
+
'de-DE': deDE,
|
|
26
|
+
'it': itIT,
|
|
27
|
+
'it-IT': itIT,
|
|
28
|
+
'pt': ptPT,
|
|
29
|
+
'pt-PT': ptPT,
|
|
30
|
+
'ja': jaJP,
|
|
31
|
+
'ja-JP': jaJP,
|
|
32
|
+
'ko': koKR,
|
|
33
|
+
'ko-KR': koKR,
|
|
34
|
+
'zh': zhCN,
|
|
35
|
+
'zh-CN': zhCN,
|
|
36
|
+
'ar': arSA,
|
|
37
|
+
'ar-SA': arSA,
|
|
50
38
|
};
|
|
51
39
|
|
|
52
40
|
const FALLBACK = 'en-US';
|
package/src/index.ts
CHANGED
|
@@ -27,6 +27,9 @@ export type { StorageAdapter, AuthStateChangeCallback, AuthMethod, AuthManagerCo
|
|
|
27
27
|
|
|
28
28
|
export { CrossDomainAuth, createCrossDomainAuth } from './CrossDomainAuth';
|
|
29
29
|
export type { CrossDomainAuthOptions } from './CrossDomainAuth';
|
|
30
|
+
export type { FedCMAuthOptions, FedCMConfig } from './mixins/OxyServices.fedcm';
|
|
31
|
+
export type { PopupAuthOptions } from './mixins/OxyServices.popup';
|
|
32
|
+
export type { RedirectAuthOptions } from './mixins/OxyServices.redirect';
|
|
30
33
|
|
|
31
34
|
// --- Crypto / Identity ---
|
|
32
35
|
export { KeyManager, SignatureService, RecoveryPhraseService } from './crypto';
|
|
@@ -49,8 +49,8 @@ export function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
49
49
|
super(...(args as [any]));
|
|
50
50
|
}
|
|
51
51
|
public static readonly DEFAULT_CONFIG_URL = 'https://auth.oxy.so/fedcm.json';
|
|
52
|
-
public static readonly FEDCM_TIMEOUT =
|
|
53
|
-
public static readonly FEDCM_SILENT_TIMEOUT =
|
|
52
|
+
public static readonly FEDCM_TIMEOUT = 15000; // 15 seconds for interactive
|
|
53
|
+
public static readonly FEDCM_SILENT_TIMEOUT = 3000; // 3 seconds for silent mediation
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Check if FedCM is supported in the current browser
|
|
@@ -180,7 +180,10 @@ export function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
180
180
|
const clientId = this.getClientId();
|
|
181
181
|
debug.log('Silent SSO: Starting for', clientId);
|
|
182
182
|
|
|
183
|
-
//
|
|
183
|
+
// Only try silent mediation (no UI) - works if user previously consented.
|
|
184
|
+
// We intentionally do NOT fall back to optional mediation here because
|
|
185
|
+
// this runs on app startup — showing browser UI without user action is bad UX.
|
|
186
|
+
// Optional/interactive mediation should only happen when the user clicks "Sign In".
|
|
184
187
|
let credential: { token: string } | null = null;
|
|
185
188
|
|
|
186
189
|
try {
|
|
@@ -196,36 +199,14 @@ export function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
196
199
|
|
|
197
200
|
debug.log('Silent SSO: Silent mediation result:', { hasCredential: !!credential, hasToken: !!credential?.token });
|
|
198
201
|
} catch (silentError) {
|
|
199
|
-
// Silent mediation failed - this is expected if user hasn't consented before or is in quiet period
|
|
200
202
|
const errorName = silentError instanceof Error ? silentError.name : 'Unknown';
|
|
201
203
|
const errorMessage = silentError instanceof Error ? silentError.message : String(silentError);
|
|
202
|
-
debug.log('Silent SSO: Silent mediation
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
// If silent failed, try optional mediation which shows browser UI if needed
|
|
206
|
-
if (!credential || !credential.token) {
|
|
207
|
-
try {
|
|
208
|
-
const nonce = this.generateNonce();
|
|
209
|
-
debug.log('Silent SSO: Trying optional mediation (may show browser UI)...');
|
|
210
|
-
|
|
211
|
-
credential = await this.requestIdentityCredential({
|
|
212
|
-
configURL: (this.constructor as any).DEFAULT_CONFIG_URL,
|
|
213
|
-
clientId,
|
|
214
|
-
nonce,
|
|
215
|
-
mediation: 'optional',
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
debug.log('Silent SSO: Optional mediation result:', { hasCredential: !!credential, hasToken: !!credential?.token });
|
|
219
|
-
} catch (optionalError) {
|
|
220
|
-
const errorName = optionalError instanceof Error ? optionalError.name : 'Unknown';
|
|
221
|
-
const errorMessage = optionalError instanceof Error ? optionalError.message : String(optionalError);
|
|
222
|
-
debug.log('Silent SSO: Optional mediation also failed:', { name: errorName, message: errorMessage });
|
|
223
|
-
return null;
|
|
224
|
-
}
|
|
204
|
+
debug.log('Silent SSO: Silent mediation failed:', { name: errorName, message: errorMessage });
|
|
205
|
+
return null;
|
|
225
206
|
}
|
|
226
207
|
|
|
227
208
|
if (!credential || !credential.token) {
|
|
228
|
-
debug.log('Silent SSO: No credential returned (user
|
|
209
|
+
debug.log('Silent SSO: No credential returned (user not logged in at IdP or hasn\'t consented)');
|
|
229
210
|
return null;
|
|
230
211
|
}
|
|
231
212
|
|
|
@@ -392,9 +373,7 @@ export function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
392
373
|
* @private
|
|
393
374
|
*/
|
|
394
375
|
public async exchangeIdTokenForSession(idToken: string): Promise<SessionLoginResponse> {
|
|
395
|
-
debug.log('
|
|
396
|
-
debug.log('exchangeIdTokenForSession: Token length:', idToken?.length);
|
|
397
|
-
debug.log('exchangeIdTokenForSession: Token preview:', idToken?.substring(0, 50) + '...');
|
|
376
|
+
debug.log('Exchanging ID token for session...');
|
|
398
377
|
|
|
399
378
|
try {
|
|
400
379
|
const response = await this.makeRequest<SessionLoginResponse>(
|
|
@@ -404,23 +383,14 @@ export function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
404
383
|
{ cache: false }
|
|
405
384
|
);
|
|
406
385
|
|
|
407
|
-
debug.log('
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
hasUser: !!(response as any)?.user,
|
|
411
|
-
hasAccessToken: !!(response as any)?.accessToken,
|
|
412
|
-
userId: (response as any)?.user?.id,
|
|
413
|
-
username: (response as any)?.user?.username,
|
|
414
|
-
responseKeys: response ? Object.keys(response) : [],
|
|
386
|
+
debug.log('Token exchange complete:', {
|
|
387
|
+
hasSession: !!response?.sessionId,
|
|
388
|
+
hasUser: !!response?.user,
|
|
415
389
|
});
|
|
416
390
|
|
|
417
391
|
return response;
|
|
418
392
|
} catch (error) {
|
|
419
|
-
debug.error('
|
|
420
|
-
name: error instanceof Error ? error.name : 'Unknown',
|
|
421
|
-
message: error instanceof Error ? error.message : String(error),
|
|
422
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
423
|
-
});
|
|
393
|
+
debug.error('Token exchange failed:', error instanceof Error ? error.message : String(error));
|
|
424
394
|
throw error;
|
|
425
395
|
}
|
|
426
396
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { normalizeLanguageCode, getLanguageMetadata, getLanguageName, getNativeLanguageName } from '../utils/languageUtils';
|
|
5
5
|
import type { LanguageMetadata } from '../utils/languageUtils';
|
|
6
6
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
7
|
+
import { isDev } from '../shared/utils/debugUtils';
|
|
7
8
|
|
|
8
9
|
export function OxyServicesLanguageMixin<T extends typeof OxyServicesBase>(Base: T) {
|
|
9
10
|
return class extends Base {
|
|
@@ -22,8 +23,10 @@ export function OxyServicesLanguageMixin<T extends typeof OxyServicesBase>(Base:
|
|
|
22
23
|
|
|
23
24
|
if (isReactNative) {
|
|
24
25
|
try {
|
|
25
|
-
|
|
26
|
-
const
|
|
26
|
+
// Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
|
|
27
|
+
const moduleName = '@react-native-async-storage/async-storage';
|
|
28
|
+
const asyncStorageModule = await import(moduleName);
|
|
29
|
+
const storage = asyncStorageModule.default as unknown as { getItem: (key: string) => Promise<string | null>; setItem: (key: string, value: string) => Promise<void>; removeItem: (key: string) => Promise<void> };
|
|
27
30
|
return {
|
|
28
31
|
getItem: storage.getItem.bind(storage),
|
|
29
32
|
setItem: storage.setItem.bind(storage),
|
|
@@ -84,7 +87,7 @@ export function OxyServicesLanguageMixin<T extends typeof OxyServicesBase>(Base:
|
|
|
84
87
|
|
|
85
88
|
return null;
|
|
86
89
|
} catch (error) {
|
|
87
|
-
if (
|
|
90
|
+
if (isDev()) {
|
|
88
91
|
console.warn('Failed to get current language:', error);
|
|
89
92
|
}
|
|
90
93
|
return null;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { BlockedUser, RestrictedUser } from '../models/interfaces';
|
|
5
5
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
6
|
+
import { isDev } from '../shared/utils/debugUtils';
|
|
6
7
|
|
|
7
8
|
export function OxyServicesPrivacyMixin<T extends typeof OxyServicesBase>(Base: T) {
|
|
8
9
|
return class extends Base {
|
|
@@ -35,7 +36,7 @@ export function OxyServicesPrivacyMixin<T extends typeof OxyServicesBase>(Base:
|
|
|
35
36
|
});
|
|
36
37
|
} catch (error) {
|
|
37
38
|
// If there's an error, assume not in list to avoid breaking functionality
|
|
38
|
-
if (
|
|
39
|
+
if (isDev()) {
|
|
39
40
|
console.warn('Error checking user list:', error);
|
|
40
41
|
}
|
|
41
42
|
return false;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
5
5
|
import type { SecurityActivity, SecurityActivityResponse, SecurityEventType } from '../models/interfaces';
|
|
6
|
+
import { isDev } from '../shared/utils/debugUtils';
|
|
6
7
|
|
|
7
8
|
export function OxyServicesSecurityMixin<T extends typeof OxyServicesBase>(Base: T) {
|
|
8
9
|
return class extends Base {
|
|
@@ -71,7 +72,7 @@ export function OxyServicesSecurityMixin<T extends typeof OxyServicesBase>(Base:
|
|
|
71
72
|
} catch (error) {
|
|
72
73
|
// Don't throw - logging failures shouldn't break user flow
|
|
73
74
|
// But log for monitoring
|
|
74
|
-
if (
|
|
75
|
+
if (isDev()) {
|
|
75
76
|
console.warn('[OxyServices] Failed to log private key exported event:', error);
|
|
76
77
|
}
|
|
77
78
|
}
|
|
@@ -93,7 +94,7 @@ export function OxyServicesSecurityMixin<T extends typeof OxyServicesBase>(Base:
|
|
|
93
94
|
} catch (error) {
|
|
94
95
|
// Don't throw - logging failures shouldn't break user flow
|
|
95
96
|
// But log for monitoring
|
|
96
|
-
if (
|
|
97
|
+
if (isDev()) {
|
|
97
98
|
console.warn('[OxyServices] Failed to log backup created event:', error);
|
|
98
99
|
}
|
|
99
100
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for isDev() utility.
|
|
3
|
+
*
|
|
4
|
+
* These tests manipulate the global __DEV__ and process.env.NODE_ENV
|
|
5
|
+
* to verify isDev() works across RN, Node, and browser-like environments.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
describe('isDev', () => {
|
|
9
|
+
const originalDev = (globalThis as any).__DEV__;
|
|
10
|
+
const originalNodeEnv = process.env.NODE_ENV;
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
// Restore globals
|
|
14
|
+
if (originalDev === undefined) {
|
|
15
|
+
delete (globalThis as any).__DEV__;
|
|
16
|
+
} else {
|
|
17
|
+
(globalThis as any).__DEV__ = originalDev;
|
|
18
|
+
}
|
|
19
|
+
process.env.NODE_ENV = originalNodeEnv;
|
|
20
|
+
|
|
21
|
+
// Clear module cache so isDev re-evaluates
|
|
22
|
+
jest.resetModules();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
async function loadIsDev() {
|
|
26
|
+
const mod = await import('../debugUtils');
|
|
27
|
+
return mod.isDev;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
it('returns true when __DEV__ is true (React Native)', async () => {
|
|
31
|
+
(globalThis as any).__DEV__ = true;
|
|
32
|
+
const isDev = await loadIsDev();
|
|
33
|
+
expect(isDev()).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('returns false when __DEV__ is false', async () => {
|
|
37
|
+
(globalThis as any).__DEV__ = false;
|
|
38
|
+
const isDev = await loadIsDev();
|
|
39
|
+
expect(isDev()).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('falls back to NODE_ENV when __DEV__ is undefined', async () => {
|
|
43
|
+
delete (globalThis as any).__DEV__;
|
|
44
|
+
process.env.NODE_ENV = 'development';
|
|
45
|
+
const isDev = await loadIsDev();
|
|
46
|
+
expect(isDev()).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('returns false when NODE_ENV is production and __DEV__ is undefined', async () => {
|
|
50
|
+
delete (globalThis as any).__DEV__;
|
|
51
|
+
process.env.NODE_ENV = 'production';
|
|
52
|
+
const isDev = await loadIsDev();
|
|
53
|
+
expect(isDev()).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -14,7 +14,12 @@ declare const __DEV__: boolean | undefined;
|
|
|
14
14
|
* Check if running in development mode
|
|
15
15
|
*/
|
|
16
16
|
export const isDev = (): boolean => {
|
|
17
|
-
|
|
17
|
+
if (typeof __DEV__ !== 'undefined') return __DEV__;
|
|
18
|
+
try {
|
|
19
|
+
return typeof process !== 'undefined' && process.env?.NODE_ENV === 'development';
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
18
23
|
};
|
|
19
24
|
|
|
20
25
|
/**
|
|
@@ -42,8 +42,10 @@ export class DeviceManager {
|
|
|
42
42
|
}> {
|
|
43
43
|
if (this.isReactNative()) {
|
|
44
44
|
try {
|
|
45
|
-
|
|
46
|
-
const
|
|
45
|
+
// Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
|
|
46
|
+
const moduleName = '@react-native-async-storage/async-storage';
|
|
47
|
+
const asyncStorageModule = await import(moduleName);
|
|
48
|
+
const storage = asyncStorageModule.default as unknown as { getItem: (key: string) => Promise<string | null>; setItem: (key: string, value: string) => Promise<void>; removeItem: (key: string) => Promise<void> };
|
|
47
49
|
return {
|
|
48
50
|
getItem: storage.getItem.bind(storage),
|
|
49
51
|
setItem: storage.setItem.bind(storage),
|
package/src/utils/platform.ts
CHANGED
|
@@ -108,8 +108,9 @@ export async function initPlatformFromReactNative(): Promise<void> {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
try {
|
|
111
|
-
//
|
|
112
|
-
const
|
|
111
|
+
// Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
|
|
112
|
+
const moduleName = 'react-native';
|
|
113
|
+
const { Platform } = await import(moduleName);
|
|
113
114
|
setPlatformOS(Platform.OS as PlatformOS);
|
|
114
115
|
} catch {
|
|
115
116
|
// react-native not available, use detected platform
|