@oxyhq/core 4.0.0 → 5.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.
Files changed (119) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/HttpService.js +6 -18
  3. package/dist/cjs/OxyServices.base.js +0 -21
  4. package/dist/cjs/crypto/keyManager.js +7 -7
  5. package/dist/cjs/crypto/polyfill.js +6 -5
  6. package/dist/cjs/crypto/signatureService.js +44 -220
  7. package/dist/cjs/i18n/locales/en-US.json +391 -17
  8. package/dist/cjs/i18n/locales/es-ES.json +391 -17
  9. package/dist/cjs/i18n/locales/locales/en-US.json +391 -17
  10. package/dist/cjs/i18n/locales/locales/es-ES.json +391 -17
  11. package/dist/cjs/index.js +4 -8
  12. package/dist/cjs/mixins/OxyServices.accounts.js +54 -0
  13. package/dist/cjs/mixins/OxyServices.assets.js +2 -2
  14. package/dist/cjs/mixins/OxyServices.auth.js +3 -3
  15. package/dist/cjs/mixins/OxyServices.civic.js +3 -3
  16. package/dist/cjs/mixins/OxyServices.language.js +2 -2
  17. package/dist/cjs/mixins/OxyServices.utility.js +7 -95
  18. package/dist/cjs/utils/cacheKey.js +17 -19
  19. package/dist/cjs/utils/deviceManager.js +2 -2
  20. package/dist/cjs/utils/platform.js +0 -14
  21. package/dist/esm/.tsbuildinfo +1 -1
  22. package/dist/esm/HttpService.js +6 -18
  23. package/dist/esm/OxyServices.base.js +0 -21
  24. package/dist/esm/crypto/keyManager.js +4 -4
  25. package/dist/esm/crypto/polyfill.js +5 -4
  26. package/dist/esm/crypto/signatureService.js +39 -214
  27. package/dist/esm/i18n/locales/en-US.json +391 -17
  28. package/dist/esm/i18n/locales/es-ES.json +391 -17
  29. package/dist/esm/i18n/locales/locales/en-US.json +391 -17
  30. package/dist/esm/i18n/locales/locales/es-ES.json +391 -17
  31. package/dist/esm/index.js +1 -2
  32. package/dist/esm/mixins/OxyServices.accounts.js +54 -0
  33. package/dist/esm/mixins/OxyServices.assets.js +1 -1
  34. package/dist/esm/mixins/OxyServices.auth.js +1 -1
  35. package/dist/esm/mixins/OxyServices.civic.js +3 -3
  36. package/dist/esm/mixins/OxyServices.language.js +1 -1
  37. package/dist/esm/mixins/OxyServices.utility.js +6 -94
  38. package/dist/esm/utils/cacheKey.js +17 -19
  39. package/dist/esm/utils/deviceManager.js +1 -1
  40. package/dist/esm/utils/platform.js +0 -12
  41. package/dist/types/.tsbuildinfo +1 -1
  42. package/dist/types/HttpService.d.ts +3 -6
  43. package/dist/types/OxyServices.base.d.ts +0 -17
  44. package/dist/types/crypto/polyfill.d.ts +2 -2
  45. package/dist/types/crypto/signatureService.d.ts +18 -84
  46. package/dist/types/index.d.ts +3 -4
  47. package/dist/types/mixins/OxyServices.accounts.d.ts +57 -5
  48. package/dist/types/mixins/OxyServices.analytics.d.ts +0 -2
  49. package/dist/types/mixins/OxyServices.appData.d.ts +0 -2
  50. package/dist/types/mixins/OxyServices.assets.d.ts +0 -2
  51. package/dist/types/mixins/OxyServices.auth.d.ts +0 -2
  52. package/dist/types/mixins/OxyServices.civic.d.ts +3 -5
  53. package/dist/types/mixins/OxyServices.connectedApps.d.ts +0 -2
  54. package/dist/types/mixins/OxyServices.contacts.d.ts +0 -2
  55. package/dist/types/mixins/OxyServices.devices.d.ts +0 -2
  56. package/dist/types/mixins/OxyServices.features.d.ts +0 -2
  57. package/dist/types/mixins/OxyServices.fedcm.d.ts +0 -2
  58. package/dist/types/mixins/OxyServices.identity.d.ts +8 -5
  59. package/dist/types/mixins/OxyServices.language.d.ts +0 -2
  60. package/dist/types/mixins/OxyServices.links.d.ts +0 -2
  61. package/dist/types/mixins/OxyServices.location.d.ts +0 -2
  62. package/dist/types/mixins/OxyServices.nodes.d.ts +0 -44
  63. package/dist/types/mixins/OxyServices.payment.d.ts +0 -2
  64. package/dist/types/mixins/OxyServices.privacy.d.ts +0 -2
  65. package/dist/types/mixins/OxyServices.redirect.d.ts +0 -2
  66. package/dist/types/mixins/OxyServices.reputation.d.ts +0 -2
  67. package/dist/types/mixins/OxyServices.security.d.ts +0 -2
  68. package/dist/types/mixins/OxyServices.silent.d.ts +0 -2
  69. package/dist/types/mixins/OxyServices.sso.d.ts +0 -2
  70. package/dist/types/mixins/OxyServices.topics.d.ts +0 -2
  71. package/dist/types/mixins/OxyServices.user.d.ts +0 -2
  72. package/dist/types/mixins/OxyServices.utility.d.ts +0 -32
  73. package/dist/types/server/auth.d.ts +0 -6
  74. package/dist/types/server/index.d.ts +1 -1
  75. package/dist/types/utils/cacheKey.d.ts +6 -7
  76. package/dist/types/utils/platform.d.ts +0 -8
  77. package/package.json +4 -7
  78. package/src/HttpService.ts +6 -22
  79. package/src/OxyServices.base.ts +0 -23
  80. package/src/__tests__/httpServiceCache.test.ts +0 -19
  81. package/src/crypto/__tests__/keyManager.atomicity.test.ts +2 -1
  82. package/src/crypto/__tests__/keyManager.test.ts +9 -7
  83. package/src/crypto/__tests__/signChallengeShared.test.ts +2 -1
  84. package/src/crypto/__tests__/signedRecord.test.ts +37 -150
  85. package/src/crypto/keyManager.ts +28 -17
  86. package/src/crypto/polyfill.ts +5 -4
  87. package/src/crypto/signatureService.ts +67 -255
  88. package/src/i18n/locales/en-US.json +391 -17
  89. package/src/i18n/locales/es-ES.json +391 -17
  90. package/src/index.ts +3 -3
  91. package/src/mixins/OxyServices.accounts.ts +91 -3
  92. package/src/mixins/OxyServices.assets.ts +1 -1
  93. package/src/mixins/OxyServices.auth.ts +1 -1
  94. package/src/mixins/OxyServices.civic.ts +6 -17
  95. package/src/mixins/OxyServices.identity.ts +8 -2
  96. package/src/mixins/OxyServices.language.ts +1 -1
  97. package/src/mixins/OxyServices.nodes.ts +1 -12
  98. package/src/mixins/OxyServices.utility.ts +6 -119
  99. package/src/mixins/__tests__/OxyServices.civic.test.ts +2 -2
  100. package/src/mixins/__tests__/accounts.test.ts +70 -0
  101. package/src/server/auth.ts +0 -7
  102. package/src/server/index.ts +0 -1
  103. package/src/utils/__tests__/cacheKey.test.ts +0 -0
  104. package/src/utils/cacheKey.ts +16 -21
  105. package/src/utils/deviceManager.ts +1 -1
  106. package/src/utils/platform.ts +0 -14
  107. package/dist/cjs/crypto/canonicalJson.js +0 -107
  108. package/dist/cjs/utils/platformCrypto.js +0 -165
  109. package/dist/cjs/utils/platformCrypto.native.js +0 -123
  110. package/dist/esm/crypto/canonicalJson.js +0 -104
  111. package/dist/esm/utils/platformCrypto.js +0 -125
  112. package/dist/esm/utils/platformCrypto.native.js +0 -80
  113. package/dist/types/crypto/canonicalJson.d.ts +0 -44
  114. package/dist/types/utils/platformCrypto.d.ts +0 -87
  115. package/dist/types/utils/platformCrypto.native.d.ts +0 -54
  116. package/src/crypto/__tests__/canonicalJson.test.ts +0 -116
  117. package/src/crypto/canonicalJson.ts +0 -120
  118. package/src/utils/platformCrypto.native.ts +0 -101
  119. package/src/utils/platformCrypto.ts +0 -145
@@ -5,7 +5,7 @@ exports.OxyServicesAuthMixin = OxyServicesAuthMixin;
5
5
  const OxyServices_errors_1 = require("../OxyServices.errors");
6
6
  const keyManager_1 = require("../crypto/keyManager");
7
7
  const signatureService_1 = require("../crypto/signatureService");
8
- const platformCrypto_1 = require("../utils/platformCrypto");
8
+ const protocol_1 = require("@oxyhq/protocol");
9
9
  const loggerUtils_1 = require("../utils/loggerUtils");
10
10
  const userIdentity_1 = require("../utils/userIdentity");
11
11
  /**
@@ -61,7 +61,7 @@ function OxyServicesAuthMixin(Base) {
61
61
  * @internal
62
62
  */
63
63
  async _hashApiKey(apiKey) {
64
- const nodeCrypto = await (0, platformCrypto_1.loadNodeCrypto)();
64
+ const nodeCrypto = await (0, protocol_1.loadNodeCrypto)();
65
65
  return nodeCrypto.createHash('sha256').update(apiKey).digest('hex');
66
66
  }
67
67
  /**
@@ -113,7 +113,7 @@ function OxyServicesAuthMixin(Base) {
113
113
  // Verify the secret on every cache hit, regardless of token freshness.
114
114
  // Constant-time compare prevents timing oracles on the stored secret.
115
115
  if (entry) {
116
- const nodeCrypto = await (0, platformCrypto_1.loadNodeCrypto)();
116
+ const nodeCrypto = await (0, protocol_1.loadNodeCrypto)();
117
117
  const storedSecretBuf = entry.secretBuf;
118
118
  const lengthMatch = storedSecretBuf.length === providedSecretBuf.length;
119
119
  // Always run timingSafeEqual on equal-length inputs to keep timing flat.
@@ -4,7 +4,7 @@ exports.parseIdPayload = parseIdPayload;
4
4
  exports.parseAttestPayload = parseAttestPayload;
5
5
  exports.verifyPublicCardAttestation = verifyPublicCardAttestation;
6
6
  exports.OxyServicesCivicMixin = OxyServicesCivicMixin;
7
- const canonicalJson_1 = require("../crypto/canonicalJson");
7
+ const protocol_1 = require("@oxyhq/protocol");
8
8
  const signatureService_1 = require("../crypto/signatureService");
9
9
  const OxyServices_identity_1 = require("./OxyServices.identity");
10
10
  const mixinHelpers_1 = require("./mixinHelpers");
@@ -156,7 +156,7 @@ function parseAttestPayload(raw) {
156
156
  * key, matching the server which omits absent keys entirely) and checks the
157
157
  * `ES256K-DER-SHA256` signature against `attestation.publicKey`.
158
158
  *
159
- * NEVER throws: `SignatureService.verify` already swallows malformed-input
159
+ * NEVER throws: `verifySignature` already swallows malformed-input
160
160
  * errors and returns `false`, and an absent attestation short-circuits to
161
161
  * `false`. A pure, reusable helper (Commons can call it on a cached card).
162
162
  *
@@ -171,7 +171,7 @@ async function verifyPublicCardAttestation(card, attestation) {
171
171
  if (!signature || !publicKey) {
172
172
  return false;
173
173
  }
174
- return signatureService_1.SignatureService.verify((0, canonicalJson_1.canonicalize)(card), signature, publicKey);
174
+ return (0, protocol_1.verifySignature)((0, protocol_1.canonicalize)(card), signature, publicKey);
175
175
  }
176
176
  function OxyServicesCivicMixin(Base) {
177
177
  return class extends Base {
@@ -5,7 +5,7 @@ exports.OxyServicesLanguageMixin = OxyServicesLanguageMixin;
5
5
  * Language Methods Mixin
6
6
  */
7
7
  const languageUtils_1 = require("../utils/languageUtils");
8
- const platformCrypto_1 = require("../utils/platformCrypto");
8
+ const protocol_1 = require("@oxyhq/protocol");
9
9
  const debugUtils_1 = require("../shared/utils/debugUtils");
10
10
  function OxyServicesLanguageMixin(Base) {
11
11
  return class extends Base {
@@ -22,7 +22,7 @@ function OxyServicesLanguageMixin(Base) {
22
22
  // `loadAsyncStorage` is per-platform: the RN variant statically imports
23
23
  // @react-native-async-storage/async-storage, the default variant throws
24
24
  // (never called outside RN because of the `isReactNative` gate above).
25
- const asyncStorageModule = await (0, platformCrypto_1.loadAsyncStorage)();
25
+ const asyncStorageModule = await (0, protocol_1.loadAsyncStorage)();
26
26
  const storage = asyncStorageModule.default;
27
27
  return {
28
28
  getItem: storage.getItem.bind(storage),
@@ -8,7 +8,7 @@ exports.OxyServicesUtilityMixin = OxyServicesUtilityMixin;
8
8
  * and Express.js authentication middleware
9
9
  */
10
10
  const jwt_decode_1 = require("jwt-decode");
11
- const platformCrypto_1 = require("../utils/platformCrypto");
11
+ const protocol_1 = require("@oxyhq/protocol");
12
12
  const loggerUtils_1 = require("../utils/loggerUtils");
13
13
  const mixinHelpers_1 = require("./mixinHelpers");
14
14
  /**
@@ -50,8 +50,6 @@ function OxyServicesUtilityMixin(Base) {
50
50
  // monorepo-wide refactor of the mixin pipeline.
51
51
  constructor(...args) {
52
52
  super(...args);
53
- /** @internal In-memory cache for acting-as verification results (TTL: 5 min) */
54
- this._actingAsCache = new Map();
55
53
  /**
56
54
  * In-memory cache for service-acting-as verification.
57
55
  * Negative results are cached for 1min to avoid hammering the verify
@@ -61,46 +59,6 @@ function OxyServicesUtilityMixin(Base) {
61
59
  */
62
60
  this._serviceActingAsCache = new Map();
63
61
  }
64
- /**
65
- * Verify that a user is authorized to act as an account (direct membership
66
- * or inherited via an ancestor). Backed by `GET /accounts/verify-acting-as`.
67
- * Results are cached in-memory for 5 minutes to avoid repeated API calls.
68
- *
69
- * @internal Used by the auth() middleware — not part of the public API
70
- */
71
- async verifyActingAs(userId, accountId) {
72
- const cacheKey = `${userId}:${accountId}`;
73
- const now = Date.now();
74
- // Check cache
75
- const cached = this._actingAsCache.get(cacheKey);
76
- if (cached && cached.expiresAt > now) {
77
- return cached.result;
78
- }
79
- // Query the API
80
- try {
81
- const result = await this.makeRequest('GET', '/accounts/verify-acting-as', { accountId, userId }, { cache: false, retry: false, timeout: 5000 });
82
- // Cache successful result for 5 minutes
83
- this._actingAsCache.set(cacheKey, {
84
- result: result && result.authorized ? result : null,
85
- expiresAt: now + 5 * 60 * 1000,
86
- });
87
- return result && result.authorized ? result : null;
88
- }
89
- catch (error) {
90
- loggerUtils_1.logger.warn('[oxy.auth] verifyActingAs lookup failed — caching negative result', {
91
- component: 'auth',
92
- method: 'verifyActingAs',
93
- userId,
94
- accountId,
95
- }, error);
96
- // Cache negative result for 1 minute to avoid hammering on transient errors
97
- this._actingAsCache.set(cacheKey, {
98
- result: null,
99
- expiresAt: now + 1 * 60 * 1000,
100
- });
101
- return null;
102
- }
103
- }
104
62
  /**
105
63
  * Verify that a service app holds an active delegation grant authorising
106
64
  * it to act on behalf of `userId`. Returns the grant (with allowed scopes)
@@ -221,47 +179,6 @@ function OxyServicesUtilityMixin(Base) {
221
179
  const oxyInstance = this;
222
180
  // Return an async middleware function
223
181
  return async (req, res, next) => {
224
- // Process X-Acting-As header for managed account identity delegation.
225
- // Called after successful authentication, before next(). If the header
226
- // is present, verifies authorization and swaps the request identity to
227
- // the managed account, preserving the original user for audit trails.
228
- const processActingAs = async () => {
229
- const actingAsUserId = req.headers['x-acting-as'];
230
- if (!actingAsUserId || typeof actingAsUserId !== 'string')
231
- return true; // No header, proceed normally
232
- const currentUserId = req.userId;
233
- if (!currentUserId)
234
- return true; // No authenticated user yet — nothing to swap
235
- const verification = await oxyInstance.verifyActingAs(currentUserId, actingAsUserId);
236
- if (!verification) {
237
- const error = {
238
- error: 'ACTING_AS_UNAUTHORIZED',
239
- message: 'Not authorized to act as this account',
240
- code: 'ACTING_AS_UNAUTHORIZED',
241
- status: 403,
242
- };
243
- if (onError) {
244
- onError(error);
245
- }
246
- else {
247
- res.status(403).json(error);
248
- }
249
- return false;
250
- }
251
- // Preserve original user for audit trails
252
- req.originalUser = { id: currentUserId, ...(req.user ?? {}) };
253
- req.actingAs = { userId: actingAsUserId, role: verification.role };
254
- // Swap user identity to the managed account
255
- req.userId = actingAsUserId;
256
- req.user = { id: actingAsUserId, _id: actingAsUserId };
257
- if (debug) {
258
- loggerUtils_1.logger.debug(`[oxy.auth] Acting as ${actingAsUserId} (role=${verification.role}) original=${currentUserId}`, {
259
- component: 'auth',
260
- method: 'auth.processActingAs',
261
- });
262
- }
263
- return true;
264
- };
265
182
  try {
266
183
  // Extract token from Authorization header.
267
184
  // Node/Express normalizes `Authorization` to a string; we guard
@@ -343,9 +260,9 @@ function OxyServicesUtilityMixin(Base) {
343
260
  // Signature verification uses a manual HMAC-SHA256 compare because
344
261
  // this file ships into RN/web bundles where `jsonwebtoken` is
345
262
  // unavailable. The middleware only ever runs on Node hosts (see
346
- // platformCrypto's doc-comment), and `loadNodeCrypto` is per-
347
- // platform: the RN variant throws so Metro never bundles a Node
348
- // built-in reference.
263
+ // `@oxyhq/protocol`'s `platform/crypto` doc-comment), and
264
+ // `loadNodeCrypto` is per-platform: the RN variant throws so Metro
265
+ // never bundles a Node built-in reference.
349
266
  try {
350
267
  await verifyServiceTokenSignature(token, jwtSecret);
351
268
  verifyServiceTokenClaims(decoded, {
@@ -554,10 +471,7 @@ function OxyServicesUtilityMixin(Base) {
554
471
  method: 'auth',
555
472
  });
556
473
  }
557
- // Process X-Acting-As header before proceeding
558
- if (await processActingAs())
559
- return next();
560
- return;
474
+ return next();
561
475
  }
562
476
  catch (validationError) {
563
477
  if (debug) {
@@ -621,9 +535,7 @@ function OxyServicesUtilityMixin(Base) {
621
535
  method: 'auth',
622
536
  });
623
537
  }
624
- // Process X-Acting-As header before proceeding
625
- if (await processActingAs())
626
- next();
538
+ next();
627
539
  }
628
540
  catch (error) {
629
541
  const handled = oxyInstance.handleError(error);
@@ -861,7 +773,7 @@ function OxyServicesUtilityMixin(Base) {
861
773
  * `ServiceTokenSignatureError` on signature mismatch — both map to 401.
862
774
  */
863
775
  async function verifyServiceTokenSignature(token, secret) {
864
- const nodeCrypto = await (0, platformCrypto_1.loadNodeCrypto)();
776
+ const nodeCrypto = await (0, protocol_1.loadNodeCrypto)();
865
777
  const { createHmac, timingSafeEqual } = nodeCrypto;
866
778
  const parts = token.split('.');
867
779
  if (parts.length !== 3) {
@@ -4,8 +4,8 @@
4
4
  *
5
5
  * Extracted from {@link HttpService} so the identity-tag derivation is a pure,
6
6
  * independently testable function with no dependency on instance/token state.
7
- * The HTTP service injects the live access token and acting-as id; everything
8
- * here is referentially transparent given those inputs.
7
+ * The HTTP service injects the live access token; everything here is
8
+ * referentially transparent given that input.
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.ANON_IDENTITY = void 0;
@@ -63,25 +63,23 @@ function fnv1a32(str) {
63
63
  *
64
64
  * We use the decoded user id rather than the raw JWT so the token never lands
65
65
  * in a cache key (no token leakage through any cache-key logging, no key bloat).
66
- * The acting-as id is folded in because managed-account responses differ per
67
- * acting identity and `X-Acting-As` already changes the server response for
68
- * the same bearer token.
66
+ * Switching into a managed account mints a REAL new session whose access token
67
+ * carries the target account's id, so the identity tag changes naturally on a
68
+ * switch there is no separate acting-as discriminator to fold in.
69
69
  *
70
70
  * @param accessToken The current bearer access token, or `null` when anonymous.
71
- * @param actingAsUserId The active managed-account id, or `null`.
72
71
  */
73
- function computeIdentityTag(accessToken, actingAsUserId) {
74
- let principal = exports.ANON_IDENTITY;
75
- if (accessToken) {
76
- try {
77
- const decoded = (0, jwt_decode_1.jwtDecode)(accessToken);
78
- principal = decoded.userId || decoded.id || `t${fnv1a32(accessToken)}`;
79
- }
80
- catch {
81
- // Undecodable token — still partition it away from anon and from other
82
- // tokens via a hash. Never silently fall back to ANON_IDENTITY.
83
- principal = `t${fnv1a32(accessToken)}`;
84
- }
72
+ function computeIdentityTag(accessToken) {
73
+ if (!accessToken) {
74
+ return exports.ANON_IDENTITY;
75
+ }
76
+ try {
77
+ const decoded = (0, jwt_decode_1.jwtDecode)(accessToken);
78
+ return decoded.userId || decoded.id || `t${fnv1a32(accessToken)}`;
79
+ }
80
+ catch {
81
+ // Undecodable token still partition it away from anon and from other
82
+ // tokens via a hash. Never silently fall back to ANON_IDENTITY.
83
+ return `t${fnv1a32(accessToken)}`;
85
84
  }
86
- return actingAsUserId ? `${principal}~as${actingAsUserId}` : principal;
87
85
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DeviceManager = void 0;
4
- const platformCrypto_1 = require("./platformCrypto");
4
+ const protocol_1 = require("@oxyhq/protocol");
5
5
  /**
6
6
  * Client-side device management utility
7
7
  * Handles persistent device identification across app sessions
@@ -22,7 +22,7 @@ class DeviceManager {
22
22
  // `loadAsyncStorage` is per-platform: the RN variant statically imports
23
23
  // @react-native-async-storage/async-storage, the default variant throws
24
24
  // (never called outside RN because of the `isReactNative()` gate above).
25
- const asyncStorageModule = await (0, platformCrypto_1.loadAsyncStorage)();
25
+ const asyncStorageModule = await (0, protocol_1.loadAsyncStorage)();
26
26
  const storage = asyncStorageModule.default;
27
27
  return {
28
28
  getItem: storage.getItem.bind(storage),
@@ -12,8 +12,6 @@ exports.isWeb = isWeb;
12
12
  exports.isNative = isNative;
13
13
  exports.isIOS = isIOS;
14
14
  exports.isAndroid = isAndroid;
15
- exports.isReactNative = isReactNative;
16
- exports.isNodeJS = isNodeJS;
17
15
  exports.setPlatformOS = setPlatformOS;
18
16
  /**
19
17
  * Detect the current platform without importing react-native
@@ -84,18 +82,6 @@ function isIOS() {
84
82
  function isAndroid() {
85
83
  return getPlatformOS() === 'android';
86
84
  }
87
- /**
88
- * Check if running in React Native
89
- */
90
- function isReactNative() {
91
- return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
92
- }
93
- /**
94
- * Check if running in Node.js
95
- */
96
- function isNodeJS() {
97
- return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
98
- }
99
85
  /**
100
86
  * Set the platform OS explicitly
101
87
  * Called by React Native entry point to register the platform