@oxyhq/core 12.5.0 → 12.5.1

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.
@@ -5,10 +5,22 @@
5
5
  * Ensures Buffer and crypto.getRandomValues are available
6
6
  * across all platforms (Node.js, Browser, React Native).
7
7
  *
8
- * - Browser/Node.js: Uses native crypto
9
- * - React Native: Uses expo-crypto (statically imported via the
10
- * per-platform `platform/crypto` module in `@oxyhq/protocol` — see that
11
- * file's doc-comment for how platform routing works).
8
+ * Guard order when installing a `getRandomValues` shim (see bottom of file and
9
+ * {@link cryptoPolyfill}):
10
+ *
11
+ * 1. A REAL `globalThis.crypto.getRandomValues` used as-is (browser, Node
12
+ * >= 20, modern Hermes). The shim below is only installed when the host is
13
+ * missing it, so this branch is the install-time gate.
14
+ * 2. Node — backed by the built-in `node:crypto` module (`webcrypto`, else
15
+ * `randomFillSync`). This is what a Node runtime WITHOUT a global WebCrypto
16
+ * (Node 18 script entrypoints, some embedded hosts) falls back to.
17
+ * 3. React Native — `expo-crypto.getRandomBytes` (statically imported via the
18
+ * per-platform `platform/crypto` module in `@oxyhq/protocol`).
19
+ *
20
+ * Historically step (2) delegated to `@oxyhq/protocol`'s RN-only
21
+ * `getRandomBytesRN`, which THROWS on Node — so any Node host lacking a global
22
+ * WebCrypto crashed here instead of getting randomness. It is now a proper
23
+ * Node-backed implementation.
12
24
  */
13
25
  Object.defineProperty(exports, "__esModule", { value: true });
14
26
  exports.Buffer = void 0;
@@ -32,23 +44,79 @@ if (!globalObject.Buffer) {
32
44
  globalObject.Buffer = buffer_1.Buffer;
33
45
  }
34
46
  /**
35
- * Synchronous random-bytes shim. On RN, this delegates to
36
- * `expo-crypto.getRandomBytes` (statically imported by the RN variant of
37
- * `@oxyhq/protocol`'s `platform/crypto`, so available without any async
38
- * warm-up). On Node /
39
- * browser, this throws — but is never called there because both platforms
40
- * already provide `globalThis.crypto.getRandomValues` natively.
47
+ * Lazily-resolved `node:crypto` module, cached after the first attempt.
48
+ * `undefined` = not tried yet; `null` = tried and unavailable (non-Node host).
49
+ */
50
+ let cachedNodeCrypto;
51
+ /**
52
+ * Synchronously load `node:crypto` on a Node runtime, or `null` elsewhere.
53
+ *
54
+ * Uses a guarded, Node-only `require`. Every runtime that actually reaches this
55
+ * branch has a working CommonJS `require`: `@oxyhq/core` publishes no
56
+ * `"type": "module"`, so Node loads it as CommonJS and the `require` free
57
+ * variable is present. Browsers never reach here (they own `globalThis.crypto`,
58
+ * so this polyfill is never installed) and React Native takes the
59
+ * `getRandomBytesRN` branch — so the `node:crypto` reference is dead code in
60
+ * those bundles, and Expo's Metro resolver shims `node:*` builtins, keeping
61
+ * web/native bundles green.
62
+ */
63
+ function loadNodeCryptoSync() {
64
+ if (cachedNodeCrypto !== undefined) {
65
+ return cachedNodeCrypto;
66
+ }
67
+ if (typeof require !== 'function') {
68
+ cachedNodeCrypto = null;
69
+ return cachedNodeCrypto;
70
+ }
71
+ try {
72
+ cachedNodeCrypto = require('node:crypto');
73
+ }
74
+ catch {
75
+ // No Node crypto (unexpected on a real Node host) — degrade to the next
76
+ // mechanism rather than crash.
77
+ cachedNodeCrypto = null;
78
+ }
79
+ return cachedNodeCrypto;
80
+ }
81
+ /**
82
+ * Fill `array` with cryptographically-secure random bytes from `node:crypto`.
83
+ * Prefers `webcrypto.getRandomValues`; falls back to `randomFillSync`. Returns
84
+ * `false` when Node crypto is unavailable so the caller can try the next
85
+ * mechanism.
41
86
  */
42
- function getRandomBytesSync(byteCount) {
43
- // `getRandomBytesRN` throws on non-RN platforms. That's fine: this
44
- // function is only ever called as a fallback when the native
45
- // `globalThis.crypto.getRandomValues` is missing, which on a normal
46
- // Node/browser host never happens.
47
- return (0, protocol_1.getRandomBytesRN)(byteCount);
87
+ function fillFromNodeCrypto(array) {
88
+ const nodeCrypto = loadNodeCryptoSync();
89
+ if (!nodeCrypto) {
90
+ return false;
91
+ }
92
+ const webcrypto = nodeCrypto.webcrypto;
93
+ if (webcrypto && typeof webcrypto.getRandomValues === 'function') {
94
+ try {
95
+ webcrypto.getRandomValues(array);
96
+ return true;
97
+ }
98
+ catch {
99
+ // `webcrypto.getRandomValues` rejects non-integer views (Float*Array,
100
+ // DataView); fall through to `randomFillSync`, which accepts any view.
101
+ }
102
+ }
103
+ if (typeof nodeCrypto.randomFillSync === 'function') {
104
+ nodeCrypto.randomFillSync(array);
105
+ return true;
106
+ }
107
+ return false;
48
108
  }
49
109
  const cryptoPolyfill = {
50
110
  getRandomValues(array) {
51
- const bytes = getRandomBytesSync(array.byteLength);
111
+ // Node: back the CSPRNG with `node:crypto`. This is the path that matters on
112
+ // Node runtimes shipping WITHOUT a global WebCrypto — where delegating to
113
+ // the RN-only expo-crypto stub would throw.
114
+ if ((0, protocol_1.isNodeJS)() && fillFromNodeCrypto(array)) {
115
+ return array;
116
+ }
117
+ // React Native (and any non-Node host without WebCrypto): synchronous
118
+ // expo-crypto via @oxyhq/protocol's RN `platform/crypto` variant.
119
+ const bytes = (0, protocol_1.getRandomBytesRN)(array.byteLength);
52
120
  const uint8View = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
53
121
  uint8View.set(bytes);
54
122
  return array;