@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.
Files changed (92) hide show
  1. package/dist/cjs/AuthManager.js +19 -9
  2. package/dist/cjs/CrossDomainAuth.js +2 -2
  3. package/dist/cjs/HttpService.js +9 -8
  4. package/dist/cjs/OxyServices.base.js +16 -3
  5. package/dist/cjs/crypto/keyManager.js +29 -24
  6. package/dist/cjs/crypto/polyfill.js +6 -1
  7. package/dist/cjs/crypto/signatureService.js +40 -31
  8. package/dist/cjs/i18n/index.js +36 -45
  9. package/dist/cjs/i18n/locales/ar-SA.json +114 -115
  10. package/dist/cjs/i18n/locales/ca-ES.json +114 -115
  11. package/dist/cjs/i18n/locales/de-DE.json +114 -115
  12. package/dist/cjs/i18n/locales/en-US.json +936 -936
  13. package/dist/cjs/i18n/locales/es-ES.json +924 -924
  14. package/dist/cjs/i18n/locales/fr-FR.json +114 -115
  15. package/dist/cjs/i18n/locales/it-IT.json +114 -115
  16. package/dist/cjs/i18n/locales/ja-JP.json +2 -2
  17. package/dist/cjs/i18n/locales/ko-KR.json +114 -115
  18. package/dist/cjs/i18n/locales/pt-PT.json +114 -115
  19. package/dist/cjs/i18n/locales/zh-CN.json +114 -115
  20. package/dist/cjs/mixins/OxyServices.fedcm.js +13 -41
  21. package/dist/cjs/mixins/OxyServices.language.js +5 -2
  22. package/dist/cjs/mixins/OxyServices.privacy.js +2 -1
  23. package/dist/cjs/mixins/OxyServices.security.js +3 -2
  24. package/dist/cjs/shared/utils/debugUtils.js +8 -1
  25. package/dist/cjs/utils/deviceManager.js +3 -1
  26. package/dist/cjs/utils/platform.js +3 -2
  27. package/dist/esm/AuthManager.js +19 -9
  28. package/dist/esm/CrossDomainAuth.js +2 -2
  29. package/dist/esm/HttpService.js +9 -8
  30. package/dist/esm/OxyServices.base.js +16 -3
  31. package/dist/esm/crypto/keyManager.js +29 -24
  32. package/dist/esm/crypto/polyfill.js +6 -1
  33. package/dist/esm/crypto/signatureService.js +40 -31
  34. package/dist/esm/i18n/index.js +11 -23
  35. package/dist/esm/i18n/locales/ar-SA.json +114 -115
  36. package/dist/esm/i18n/locales/ca-ES.json +114 -115
  37. package/dist/esm/i18n/locales/de-DE.json +114 -115
  38. package/dist/esm/i18n/locales/en-US.json +936 -936
  39. package/dist/esm/i18n/locales/es-ES.json +924 -924
  40. package/dist/esm/i18n/locales/fr-FR.json +114 -115
  41. package/dist/esm/i18n/locales/it-IT.json +114 -115
  42. package/dist/esm/i18n/locales/ja-JP.json +2 -2
  43. package/dist/esm/i18n/locales/ko-KR.json +114 -115
  44. package/dist/esm/i18n/locales/pt-PT.json +114 -115
  45. package/dist/esm/i18n/locales/zh-CN.json +114 -115
  46. package/dist/esm/mixins/OxyServices.fedcm.js +13 -41
  47. package/dist/esm/mixins/OxyServices.language.js +5 -2
  48. package/dist/esm/mixins/OxyServices.privacy.js +2 -1
  49. package/dist/esm/mixins/OxyServices.security.js +3 -2
  50. package/dist/esm/shared/utils/debugUtils.js +8 -1
  51. package/dist/esm/utils/deviceManager.js +3 -1
  52. package/dist/esm/utils/platform.js +3 -2
  53. package/dist/types/CrossDomainAuth.d.ts +2 -2
  54. package/dist/types/OxyServices.base.d.ts +4 -1
  55. package/dist/types/OxyServices.d.ts +13 -0
  56. package/dist/types/index.d.ts +3 -0
  57. package/dist/types/mixins/OxyServices.analytics.d.ts +2 -0
  58. package/dist/types/mixins/OxyServices.assets.d.ts +2 -0
  59. package/dist/types/mixins/OxyServices.auth.d.ts +2 -0
  60. package/dist/types/mixins/OxyServices.developer.d.ts +2 -0
  61. package/dist/types/mixins/OxyServices.devices.d.ts +2 -0
  62. package/dist/types/mixins/OxyServices.features.d.ts +2 -0
  63. package/dist/types/mixins/OxyServices.fedcm.d.ts +4 -2
  64. package/dist/types/mixins/OxyServices.karma.d.ts +2 -0
  65. package/dist/types/mixins/OxyServices.language.d.ts +2 -0
  66. package/dist/types/mixins/OxyServices.location.d.ts +2 -0
  67. package/dist/types/mixins/OxyServices.payment.d.ts +2 -0
  68. package/dist/types/mixins/OxyServices.popup.d.ts +2 -0
  69. package/dist/types/mixins/OxyServices.privacy.d.ts +2 -0
  70. package/dist/types/mixins/OxyServices.redirect.d.ts +2 -0
  71. package/dist/types/mixins/OxyServices.security.d.ts +2 -0
  72. package/dist/types/mixins/OxyServices.user.d.ts +2 -0
  73. package/dist/types/mixins/OxyServices.utility.d.ts +2 -0
  74. package/package.json +2 -3
  75. package/src/AuthManager.ts +25 -15
  76. package/src/CrossDomainAuth.ts +2 -2
  77. package/src/HttpService.ts +9 -8
  78. package/src/OxyServices.base.ts +21 -4
  79. package/src/OxyServices.ts +23 -2
  80. package/src/crypto/keyManager.ts +30 -25
  81. package/src/crypto/polyfill.ts +6 -1
  82. package/src/crypto/signatureService.ts +43 -37
  83. package/src/i18n/index.ts +33 -45
  84. package/src/index.ts +3 -0
  85. package/src/mixins/OxyServices.fedcm.ts +14 -44
  86. package/src/mixins/OxyServices.language.ts +6 -3
  87. package/src/mixins/OxyServices.privacy.ts +2 -1
  88. package/src/mixins/OxyServices.security.ts +3 -2
  89. package/src/shared/utils/__tests__/debugUtils.test.ts +55 -0
  90. package/src/shared/utils/debugUtils.ts +6 -1
  91. package/src/utils/deviceManager.ts +4 -2
  92. package/src/utils/platform.ts +3 -2
@@ -7,6 +7,7 @@
7
7
  import { ec as EC } from 'elliptic';
8
8
  import { isWeb, isIOS, isAndroid } from '../utils/platform';
9
9
  import { logger } from '../utils/loggerUtils';
10
+ import { isDev } from '../shared/utils/debugUtils';
10
11
  // Lazy imports for React Native specific modules
11
12
  let SecureStore = null;
12
13
  let ExpoCrypto = null;
@@ -41,7 +42,9 @@ const ANDROID_ACCOUNT_TYPE = 'com.oxy.account';
41
42
  async function initSecureStore() {
42
43
  if (!SecureStore) {
43
44
  try {
44
- SecureStore = await import('expo-secure-store');
45
+ // Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
46
+ const moduleName = 'expo-secure-store';
47
+ SecureStore = await import(moduleName);
45
48
  }
46
49
  catch (error) {
47
50
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -74,7 +77,9 @@ function isWebPlatform() {
74
77
  }
75
78
  async function initExpoCrypto() {
76
79
  if (!ExpoCrypto) {
77
- ExpoCrypto = await import('expo-crypto');
80
+ // Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
81
+ const moduleName = 'expo-crypto';
82
+ ExpoCrypto = await import(moduleName);
78
83
  }
79
84
  return ExpoCrypto;
80
85
  }
@@ -201,7 +206,7 @@ export class KeyManager {
201
206
  // Update cache
202
207
  KeyManager.cachedSharedPublicKey = publicKey;
203
208
  KeyManager.cachedHasSharedIdentity = true;
204
- if (__DEV__) {
209
+ if (isDev()) {
205
210
  logger.debug('Shared identity created successfully', { component: 'KeyManager' });
206
211
  }
207
212
  return publicKey;
@@ -235,7 +240,7 @@ export class KeyManager {
235
240
  return publicKey;
236
241
  }
237
242
  catch (error) {
238
- if (__DEV__) {
243
+ if (isDev()) {
239
244
  logger.warn('Failed to get shared public key', { component: 'KeyManager' }, error);
240
245
  }
241
246
  KeyManager.cachedSharedPublicKey = null;
@@ -268,7 +273,7 @@ export class KeyManager {
268
273
  return privateKey;
269
274
  }
270
275
  catch (error) {
271
- if (__DEV__) {
276
+ if (isDev()) {
272
277
  logger.warn('Failed to get shared private key', { component: 'KeyManager' }, error);
273
278
  }
274
279
  return null;
@@ -295,7 +300,7 @@ export class KeyManager {
295
300
  return hasShared;
296
301
  }
297
302
  catch (error) {
298
- if (__DEV__) {
303
+ if (isDev()) {
299
304
  logger.warn('Failed to check shared identity', { component: 'KeyManager' }, error);
300
305
  }
301
306
  KeyManager.cachedHasSharedIdentity = false;
@@ -338,7 +343,7 @@ export class KeyManager {
338
343
  // Update cache
339
344
  KeyManager.cachedSharedPublicKey = publicKey;
340
345
  KeyManager.cachedHasSharedIdentity = true;
341
- if (__DEV__) {
346
+ if (isDev()) {
342
347
  logger.debug('Shared identity imported successfully', { component: 'KeyManager' });
343
348
  }
344
349
  return publicKey;
@@ -372,12 +377,12 @@ export class KeyManager {
372
377
  await store.setItemAsync(STORAGE_KEYS.SHARED_SESSION_ID, sessionId);
373
378
  await store.setItemAsync(STORAGE_KEYS.SHARED_SESSION_TOKEN, accessToken);
374
379
  }
375
- if (__DEV__) {
380
+ if (isDev()) {
376
381
  logger.debug('Shared session stored successfully', { component: 'KeyManager' });
377
382
  }
378
383
  }
379
384
  catch (error) {
380
- if (__DEV__) {
385
+ if (isDev()) {
381
386
  logger.error('Failed to store shared session', error, { component: 'KeyManager' });
382
387
  }
383
388
  throw error;
@@ -417,7 +422,7 @@ export class KeyManager {
417
422
  return { sessionId, accessToken };
418
423
  }
419
424
  catch (error) {
420
- if (__DEV__) {
425
+ if (isDev()) {
421
426
  logger.warn('Failed to get shared session', { component: 'KeyManager' }, error);
422
427
  }
423
428
  return null;
@@ -447,12 +452,12 @@ export class KeyManager {
447
452
  await store.deleteItemAsync(STORAGE_KEYS.SHARED_SESSION_ID);
448
453
  await store.deleteItemAsync(STORAGE_KEYS.SHARED_SESSION_TOKEN);
449
454
  }
450
- if (__DEV__) {
455
+ if (isDev()) {
451
456
  logger.debug('Shared session cleared successfully', { component: 'KeyManager' });
452
457
  }
453
458
  }
454
459
  catch (error) {
455
- if (__DEV__) {
460
+ if (isDev()) {
456
461
  logger.error('Failed to clear shared session', error, { component: 'KeyManager' });
457
462
  }
458
463
  }
@@ -474,7 +479,7 @@ export class KeyManager {
474
479
  // Check if we already have a shared identity
475
480
  const hasShared = await KeyManager.hasSharedIdentity();
476
481
  if (hasShared) {
477
- if (__DEV__) {
482
+ if (isDev()) {
478
483
  logger.debug('Shared identity already exists, skipping migration', { component: 'KeyManager' });
479
484
  }
480
485
  return true;
@@ -482,20 +487,20 @@ export class KeyManager {
482
487
  // Get local identity
483
488
  const privateKey = await KeyManager.getPrivateKey();
484
489
  if (!privateKey) {
485
- if (__DEV__) {
490
+ if (isDev()) {
486
491
  logger.debug('No local identity to migrate', { component: 'KeyManager' });
487
492
  }
488
493
  return false;
489
494
  }
490
495
  // Import to shared storage
491
496
  await KeyManager.importSharedIdentity(privateKey);
492
- if (__DEV__) {
497
+ if (isDev()) {
493
498
  logger.debug('Successfully migrated local identity to shared identity', { component: 'KeyManager' });
494
499
  }
495
500
  return true;
496
501
  }
497
502
  catch (error) {
498
- if (__DEV__) {
503
+ if (isDev()) {
499
504
  logger.error('Failed to migrate to shared identity', error, { component: 'KeyManager' });
500
505
  }
501
506
  return false;
@@ -555,7 +560,7 @@ export class KeyManager {
555
560
  catch (error) {
556
561
  // If secure store is not available, return null (no identity)
557
562
  // This allows the app to continue functioning even if secure store fails to load
558
- if (__DEV__) {
563
+ if (isDev()) {
559
564
  logger.warn('Failed to access secure store', { component: 'KeyManager' }, error);
560
565
  }
561
566
  return null;
@@ -582,7 +587,7 @@ export class KeyManager {
582
587
  // If secure store is not available, return null (no identity)
583
588
  // Cache null to avoid repeated failed attempts
584
589
  KeyManager.cachedPublicKey = null;
585
- if (__DEV__) {
590
+ if (isDev()) {
586
591
  logger.warn('Failed to access secure store', { component: 'KeyManager' }, error);
587
592
  }
588
593
  return null;
@@ -609,7 +614,7 @@ export class KeyManager {
609
614
  // If we can't check, assume no identity (safer default)
610
615
  // Cache false to avoid repeated failed attempts
611
616
  KeyManager.cachedHasIdentity = false;
612
- if (__DEV__) {
617
+ if (isDev()) {
613
618
  logger.warn('Failed to check identity', { component: 'KeyManager' }, error);
614
619
  }
615
620
  return false;
@@ -642,12 +647,12 @@ export class KeyManager {
642
647
  if (!skipBackup) {
643
648
  try {
644
649
  const backupSuccess = await KeyManager.backupIdentity();
645
- if (!backupSuccess && typeof __DEV__ !== 'undefined' && __DEV__) {
650
+ if (!backupSuccess && isDev()) {
646
651
  logger.warn('Failed to backup identity before deletion - proceeding anyway', { component: 'KeyManager' });
647
652
  }
648
653
  }
649
654
  catch (backupError) {
650
- if (typeof __DEV__ !== 'undefined' && __DEV__) {
655
+ if (isDev()) {
651
656
  logger.warn('Failed to backup identity before deletion', { component: 'KeyManager' }, backupError);
652
657
  }
653
658
  }
@@ -692,7 +697,7 @@ export class KeyManager {
692
697
  return true;
693
698
  }
694
699
  catch (error) {
695
- if (typeof __DEV__ !== 'undefined' && __DEV__) {
700
+ if (isDev()) {
696
701
  logger.error('Failed to backup identity', error, { component: 'KeyManager' });
697
702
  }
698
703
  return false;
@@ -732,7 +737,7 @@ export class KeyManager {
732
737
  return true;
733
738
  }
734
739
  catch (error) {
735
- if (typeof __DEV__ !== 'undefined' && __DEV__) {
740
+ if (isDev()) {
736
741
  logger.error('Identity integrity check failed', error, { component: 'KeyManager' });
737
742
  }
738
743
  return false;
@@ -780,7 +785,7 @@ export class KeyManager {
780
785
  return false;
781
786
  }
782
787
  catch (error) {
783
- if (typeof __DEV__ !== 'undefined' && __DEV__) {
788
+ if (isDev()) {
784
789
  logger.error('Failed to restore identity from backup', error, { component: 'KeyManager' });
785
790
  }
786
791
  return false;
@@ -31,7 +31,12 @@ function getRandomBytesSync(byteCount) {
31
31
  if (!expoCryptoLoadAttempted) {
32
32
  expoCryptoLoadAttempted = true;
33
33
  try {
34
- expoCryptoModule = require('expo-crypto');
34
+ // Only use require() in CJS environments (Metro/Node). In ESM (Vite/browser),
35
+ // crypto.getRandomValues exists natively so this code path is never reached.
36
+ if (typeof require !== 'undefined') {
37
+ const moduleName = 'expo-crypto';
38
+ expoCryptoModule = require(moduleName);
39
+ }
35
40
  }
36
41
  catch {
37
42
  // expo-crypto not available — expected in non-RN environments
@@ -26,7 +26,9 @@ function isNodeJS() {
26
26
  */
27
27
  async function initExpoCrypto() {
28
28
  if (!ExpoCrypto) {
29
- ExpoCrypto = await import('expo-crypto');
29
+ // Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
30
+ const moduleName = 'expo-crypto';
31
+ ExpoCrypto = await import(moduleName);
30
32
  }
31
33
  return ExpoCrypto;
32
34
  }
@@ -34,25 +36,29 @@ async function initExpoCrypto() {
34
36
  * Compute SHA-256 hash of a string
35
37
  */
36
38
  async function sha256(message) {
37
- // In React Native, always use expo-crypto
38
- if (isReactNative() || !isNodeJS()) {
39
+ // In React Native, use expo-crypto
40
+ if (isReactNative()) {
39
41
  const Crypto = await initExpoCrypto();
40
42
  return Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, message);
41
43
  }
42
44
  // In Node.js, use Node's crypto module
43
- // Use Function constructor to prevent Metro bundler from statically analyzing this require
44
- // This ensures the require is only evaluated in Node.js runtime, not during Metro bundling
45
- try {
46
- // eslint-disable-next-line @typescript-eslint/no-implied-eval
47
- const getCrypto = new Function('return require("crypto")');
48
- const crypto = getCrypto();
49
- return crypto.createHash('sha256').update(message).digest('hex');
50
- }
51
- catch (error) {
52
- // Fallback to expo-crypto if Node crypto fails
53
- const Crypto = await initExpoCrypto();
54
- return Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, message);
45
+ if (isNodeJS()) {
46
+ try {
47
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
48
+ const getCrypto = new Function('return require("crypto")');
49
+ const nodeCrypto = getCrypto();
50
+ return nodeCrypto.createHash('sha256').update(message).digest('hex');
51
+ }
52
+ catch {
53
+ // Fall through to Web Crypto API
54
+ }
55
55
  }
56
+ // Browser: use Web Crypto API
57
+ const encoder = new TextEncoder();
58
+ const data = encoder.encode(message);
59
+ const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', data);
60
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
61
+ return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
56
62
  }
57
63
  export class SignatureService {
58
64
  /**
@@ -60,29 +66,32 @@ export class SignatureService {
60
66
  * Uses expo-crypto in React Native, crypto.randomBytes in Node.js
61
67
  */
62
68
  static async generateChallenge() {
63
- if (isReactNative() || !isNodeJS()) {
64
- // Use expo-crypto for React Native (expo-random is deprecated)
69
+ // In React Native, use expo-crypto
70
+ if (isReactNative()) {
65
71
  const Crypto = await initExpoCrypto();
66
72
  const randomBytes = await Crypto.getRandomBytesAsync(32);
67
73
  return Array.from(randomBytes)
68
74
  .map((b) => b.toString(16).padStart(2, '0'))
69
75
  .join('');
70
76
  }
71
- // Node.js fallback
72
- try {
73
- // eslint-disable-next-line @typescript-eslint/no-implied-eval
74
- const getCrypto = new Function('return require("crypto")');
75
- const crypto = getCrypto();
76
- return crypto.randomBytes(32).toString('hex');
77
- }
78
- catch (error) {
79
- // Fallback to expo-crypto if Node crypto fails
80
- const Crypto = await initExpoCrypto();
81
- const randomBytes = await Crypto.getRandomBytesAsync(32);
82
- return Array.from(randomBytes)
83
- .map((b) => b.toString(16).padStart(2, '0'))
84
- .join('');
77
+ // In Node.js, use Node's crypto module
78
+ if (isNodeJS()) {
79
+ try {
80
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
81
+ const getCrypto = new Function('return require("crypto")');
82
+ const nodeCrypto = getCrypto();
83
+ return nodeCrypto.randomBytes(32).toString('hex');
84
+ }
85
+ catch {
86
+ // Fall through to Web Crypto API
87
+ }
85
88
  }
89
+ // Browser: use Web Crypto API
90
+ const bytes = new Uint8Array(32);
91
+ globalThis.crypto.getRandomValues(bytes);
92
+ return Array.from(bytes)
93
+ .map(b => b.toString(16).padStart(2, '0'))
94
+ .join('');
86
95
  }
87
96
  /**
88
97
  * Hash a message using SHA-256
@@ -1,26 +1,14 @@
1
- // Use JSON locale files (RN Metro supports static requires reliably)
2
- // eslint-disable-next-line @typescript-eslint/no-var-requires
3
- const enUS = require('./locales/en-US.json');
4
- // eslint-disable-next-line @typescript-eslint/no-var-requires
5
- const esES = require('./locales/es-ES.json');
6
- // eslint-disable-next-line @typescript-eslint/no-var-requires
7
- const caES = require('./locales/ca-ES.json');
8
- // eslint-disable-next-line @typescript-eslint/no-var-requires
9
- const frFR = require('./locales/fr-FR.json');
10
- // eslint-disable-next-line @typescript-eslint/no-var-requires
11
- const deDE = require('./locales/de-DE.json');
12
- // eslint-disable-next-line @typescript-eslint/no-var-requires
13
- const itIT = require('./locales/it-IT.json');
14
- // eslint-disable-next-line @typescript-eslint/no-var-requires
15
- const ptPT = require('./locales/pt-PT.json');
16
- // eslint-disable-next-line @typescript-eslint/no-var-requires
17
- const jaJP = require('./locales/ja-JP.json');
18
- // eslint-disable-next-line @typescript-eslint/no-var-requires
19
- const koKR = require('./locales/ko-KR.json');
20
- // eslint-disable-next-line @typescript-eslint/no-var-requires
21
- const zhCN = require('./locales/zh-CN.json');
22
- // eslint-disable-next-line @typescript-eslint/no-var-requires
23
- const arSA = require('./locales/ar-SA.json');
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
  const DICTS = {
25
13
  'en': enUS,
26
14
  'en-US': enUS,
@@ -1,120 +1,119 @@
1
1
  {
2
- "signin": {
3
- "title": "تسجيل الدخول",
4
- "subtitle": "سجل الدخول للمتابعة",
5
- "addAccountTitle": "إضافة حساب آخر",
6
- "addAccountSubtitle": "تسجيل الدخول بحساب آخر",
7
- "username": {
8
- "label": "اسم المستخدم",
9
- "placeholder": "أدخل اسم المستخدم",
10
- "helper": "3-30 حرفًا، أحرف وأرقام فقط",
11
- "required": "يرجى إدخال اسم المستخدم.",
12
- "minLength": "يجب أن يكون اسم المستخدم 3 أحرف على الأقل."
2
+ "signin": {
3
+ "title": "تسجيل الدخول",
4
+ "subtitle": "سجل الدخول للمتابعة",
5
+ "addAccountTitle": "إضافة حساب آخر",
6
+ "addAccountSubtitle": "تسجيل الدخول بحساب آخر",
7
+ "username": {
8
+ "label": "اسم المستخدم",
9
+ "placeholder": "أدخل اسم المستخدم",
10
+ "helper": "3-30 حرفًا، أحرف وأرقام فقط",
11
+ "required": "يرجى إدخال اسم المستخدم.",
12
+ "minLength": "يجب أن يكون اسم المستخدم 3 أحرف على الأقل."
13
+ },
14
+ "password": {
15
+ "label": "كلمة المرور",
16
+ "placeholder": "أدخل كلمة المرور",
17
+ "required": "يرجى إدخال كلمة المرور.",
18
+ "hint": "أدخل كلمة المرور لتسجيل الدخول"
19
+ },
20
+ "actions": {
21
+ "continue": "متابعة",
22
+ "back": "رجوع",
23
+ "signIn": "تسجيل الدخول",
24
+ "verify": "التحقق",
25
+ "openAccountSwitcher": "التبديل إلى حساب آخر",
26
+ "openAccountSwitcherSubtitle": "{{count}} حسابات أخرى متاحة",
27
+ "openAccountSwitcherSubtitle_singular": "حساب آخر واحد متاح",
28
+ "openAccountSwitcherSubtitle_zero": "راجع حساباتك المحفوظة",
29
+ "manageAccounts": "إدارة الحسابات المحفوظة",
30
+ "manageAccountsSubtitle": "مراجعة الجلسات أو إزالتها أو تسجيل الخروج",
31
+ "loadingOtherAccounts": "جاري تحميل الحسابات الأخرى…",
32
+ "switchAccountFailed": "لم نتمكن من التبديل بين الحسابات. يرجى المحاولة مرة أخرى."
33
+ },
34
+ "forgotPrompt": "نسيت كلمة المرور؟",
35
+ "security": {
36
+ "dataSecure": "بياناتك مشفرة وآمنة"
37
+ },
38
+ "currentlySignedInAs": "مسجل الدخول حاليًا كـ",
39
+ "alreadySignedInWith": "مسجل الدخول بالفعل بـ",
40
+ "alreadySignedIn": "مسجل الدخول بالفعل",
41
+ "alreadySignedInMessage": "هذا الحساب مسجل الدخول بالفعل. المتابعة بهذا الحساب؟",
42
+ "continueWithAccount": "متابعة",
43
+ "currentAccount": "الحالي",
44
+ "or": "أو",
45
+ "viewAllAccounts": "عرض {{count}} المزيد",
46
+ "status": {
47
+ "accountSwitched": "استخدام {{name}} الآن"
48
+ }
13
49
  },
14
- "password": {
15
- "label": "كلمة المرور",
16
- "placeholder": "أدخل كلمة المرور",
17
- "required": "يرجى إدخال كلمة المرور.",
18
- "hint": "أدخل كلمة المرور لتسجيل الدخول"
50
+ "signup": {
51
+ "welcome": {
52
+ "title": "مرحبًا بك في Oxy!",
53
+ "subtitle": "أنشئ حسابك في خطوات قليلة",
54
+ "haveAccount": "لديك حساب بالفعل؟",
55
+ "signInCta": "تسجيل الدخول"
56
+ },
57
+ "identity": {
58
+ "title": "من أنت؟",
59
+ "subtitle": "اختر اسم المستخدم وأدخل بريدك الإلكتروني"
60
+ },
61
+ "username": {
62
+ "helper": "3-30 حرفًا، أحرف وأرقام فقط",
63
+ "required": "يرجى إدخال اسم مستخدم",
64
+ "minLength": "يجب أن يكون اسم المستخدم 3 أحرف على الأقل"
65
+ },
66
+ "email": {
67
+ "required": "يرجى إدخال عنوان بريد إلكتروني",
68
+ "invalid": "يرجى إدخال عنوان بريد إلكتروني صالح",
69
+ "helper": "لن نشارك بريدك الإلكتروني أبدًا"
70
+ },
71
+ "security": {
72
+ "title": "أمّن حسابك",
73
+ "subtitle": "أنشئ كلمة مرور قوية لحماية حسابك"
74
+ },
75
+ "password": {
76
+ "helper": "8 أحرف على الأقل",
77
+ "required": "يرجى إدخال كلمة مرور",
78
+ "minLength": "يجب أن تكون كلمة المرور 8 أحرف على الأقل",
79
+ "confirmRequired": "يرجى تأكيد كلمة المرور",
80
+ "mismatch": "كلمات المرور غير متطابقة",
81
+ "confirmHint": "أعد إدخال كلمة المرور للتأكيد"
82
+ },
83
+ "summary": {
84
+ "title": "أوشكت على الانتهاء!",
85
+ "subtitle": "راجع معلوماتك وأنشئ حسابك",
86
+ "sectionTitle": "معلومات الحساب",
87
+ "fields": {
88
+ "username": "اسم المستخدم",
89
+ "email": "البريد الإلكتروني",
90
+ "password": "كلمة المرور"
91
+ },
92
+ "notSet": "غير محدد",
93
+ "securityTip": "لمزيد من الأمان، قم بتمكين المصادقة الحيوية من إعدادات الحساب بعد إنشاء حسابك.",
94
+ "legalReminder": "بإنشاء حساب، فإنك توافق على شروط الخدمة وسياسة الخصوصية الخاصة بنا."
95
+ }
19
96
  },
20
- "actions": {
21
- "continue": "متابعة",
22
- "back": "رجوع",
23
- "signIn": "تسجيل الدخول",
24
- "verify": "التحقق",
25
- "openAccountSwitcher": "التبديل إلى حساب آخر",
26
- "openAccountSwitcherSubtitle": "{{count}} حسابات أخرى متاحة",
27
- "openAccountSwitcherSubtitle_singular": "حساب آخر واحد متاح",
28
- "openAccountSwitcherSubtitle_zero": "راجع حساباتك المحفوظة",
29
- "manageAccounts": "إدارة الحسابات المحفوظة",
30
- "manageAccountsSubtitle": "مراجعة الجلسات أو إزالتها أو تسجيل الخروج",
31
- "loadingOtherAccounts": "جاري تحميل الحسابات الأخرى…",
32
- "switchAccountFailed": "لم نتمكن من التبديل بين الحسابات. يرجى المحاولة مرة أخرى."
33
- },
34
- "forgotPrompt": "نسيت كلمة المرور؟",
35
- "security": {
36
- "dataSecure": "بياناتك مشفرة وآمنة"
37
- },
38
- "currentlySignedInAs": "مسجل الدخول حاليًا كـ",
39
- "alreadySignedInWith": "مسجل الدخول بالفعل بـ",
40
- "alreadySignedIn": "مسجل الدخول بالفعل",
41
- "alreadySignedInMessage": "هذا الحساب مسجل الدخول بالفعل. المتابعة بهذا الحساب؟",
42
- "continueWithAccount": "متابعة",
43
- "currentAccount": "الحالي",
44
- "or": "أو",
45
- "viewAllAccounts": "عرض {{count}} المزيد",
46
- "status": {
47
- "accountSwitched": "استخدام {{name}} الآن"
48
- },
49
- },
50
- "signup": {
51
- "welcome": {
52
- "title": "مرحبًا بك في Oxy!",
53
- "subtitle": "أنشئ حسابك في خطوات قليلة",
54
- "haveAccount": "لديك حساب بالفعل؟",
55
- "signInCta": "تسجيل الدخول"
56
- },
57
- "identity": {
58
- "title": "من أنت؟",
59
- "subtitle": "اختر اسم المستخدم وأدخل بريدك الإلكتروني"
60
- },
61
- "username": {
62
- "helper": "3-30 حرفًا، أحرف وأرقام فقط",
63
- "required": "يرجى إدخال اسم مستخدم",
64
- "minLength": "يجب أن يكون اسم المستخدم 3 أحرف على الأقل"
65
- },
66
- "email": {
67
- "required": "يرجى إدخال عنوان بريد إلكتروني",
68
- "invalid": "يرجى إدخال عنوان بريد إلكتروني صالح",
69
- "helper": "لن نشارك بريدك الإلكتروني أبدًا"
70
- },
71
- "security": {
72
- "title": "أمّن حسابك",
73
- "subtitle": "أنشئ كلمة مرور قوية لحماية حسابك"
74
- },
75
- "password": {
76
- "helper": "8 أحرف على الأقل",
77
- "required": "يرجى إدخال كلمة مرور",
78
- "minLength": "يجب أن تكون كلمة المرور 8 أحرف على الأقل",
79
- "confirmRequired": "يرجى تأكيد كلمة المرور",
80
- "mismatch": "كلمات المرور غير متطابقة",
81
- "confirmHint": "أعد إدخال كلمة المرور للتأكيد"
82
- },
83
- "summary": {
84
- "title": "أوشكت على الانتهاء!",
85
- "subtitle": "راجع معلوماتك وأنشئ حسابك",
86
- "sectionTitle": "معلومات الحساب",
87
- "fields": {
88
- "username": "اسم المستخدم",
89
- "email": "البريد الإلكتروني",
90
- "password": "كلمة المرور"
91
- },
92
- "notSet": "غير محدد",
93
- "securityTip": "لمزيد من الأمان، قم بتمكين المصادقة الحيوية من إعدادات الحساب بعد إنشاء حسابك.",
94
- "legalReminder": "بإنشاء حساب، فإنك توافق على شروط الخدمة وسياسة الخصوصية الخاصة بنا."
95
- }
96
- },
97
- "common": {
98
- "actions": {
99
- "back": "رجوع",
100
- "continue": "متابعة",
101
- "next": "التالي",
102
- "getStarted": "ابدأ",
103
- "createAccount": "إنشاء حساب",
104
- "signIn": "تسجيل الدخول",
105
- "verify": "التحقق",
106
- "resetPassword": "إعادة تعيين كلمة المرور"
107
- },
108
- "links": {
109
- "recoverAccount": "استعادة حسابك",
110
- "signUp": "التسجيل"
111
- },
112
- "labels": {
113
- "username": "اسم المستخدم",
114
- "email": "البريد الإلكتروني",
115
- "password": "كلمة المرور",
116
- "confirmPassword": "تأكيد كلمة المرور"
97
+ "common": {
98
+ "actions": {
99
+ "back": "رجوع",
100
+ "continue": "متابعة",
101
+ "next": "التالي",
102
+ "getStarted": "ابدأ",
103
+ "createAccount": "إنشاء حساب",
104
+ "signIn": "تسجيل الدخول",
105
+ "verify": "التحقق",
106
+ "resetPassword": "إعادة تعيين كلمة المرور"
107
+ },
108
+ "links": {
109
+ "recoverAccount": "استعادة حسابك",
110
+ "signUp": "التسجيل"
111
+ },
112
+ "labels": {
113
+ "username": "اسم المستخدم",
114
+ "email": "البريد الإلكتروني",
115
+ "password": "كلمة المرور",
116
+ "confirmPassword": "تأكيد كلمة المرور"
117
+ }
117
118
  }
118
- }
119
119
  }
120
-