hermes-test 0.2.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.
@@ -0,0 +1,334 @@
1
+ // Hermes runtime polyfills for hermes-test
2
+ // These run before any bundled code (injected via esbuild banner).
3
+ // Hermes lacks these APIs since they normally come from the RN native runtime.
4
+
5
+ // React checks process.env.NODE_ENV at load time
6
+ if (typeof globalThis.process === 'undefined') {
7
+ globalThis.process = { env: { NODE_ENV: 'test', JEST_WORKER_ID: '1' } };
8
+ } else if (!globalThis.process.env) {
9
+ globalThis.process.env = { NODE_ENV: 'test', JEST_WORKER_ID: '1' };
10
+ } else {
11
+ // Always set JEST_WORKER_ID so RTK Query apiBaseQuery uses the mock domain (apiMockDomain)
12
+ // rather than the real AWS domain. Runs before any bundled module-level code.
13
+ if (!globalThis.process.env.JEST_WORKER_ID) {
14
+ globalThis.process.env.JEST_WORKER_ID = '1';
15
+ }
16
+ }
17
+
18
+ // process.nextTick — many Node.js-style tests use this
19
+ if (typeof globalThis.process.nextTick === 'undefined') {
20
+ globalThis.process.nextTick = function(fn) {
21
+ Promise.resolve().then(fn);
22
+ };
23
+ }
24
+
25
+ // Object.fromEntries — ES2019, may not exist in older Hermes builds
26
+ if (typeof Object.fromEntries === 'undefined') {
27
+ Object.fromEntries = function(iterable) {
28
+ var obj = {};
29
+ if (iterable && typeof iterable[Symbol.iterator] === 'function') {
30
+ var iter = iterable[Symbol.iterator]();
31
+ var next;
32
+ while (!(next = iter.next()).done) {
33
+ obj[next.value[0]] = next.value[1];
34
+ }
35
+ } else if (iterable && typeof iterable.forEach === 'function') {
36
+ iterable.forEach(function(pair) { obj[pair[0]] = pair[1]; });
37
+ }
38
+ return obj;
39
+ };
40
+ }
41
+
42
+ // crypto.getRandomValues — needed by uuid and other crypto-dependent libs
43
+ if (typeof globalThis.crypto === 'undefined') {
44
+ globalThis.crypto = {};
45
+ }
46
+ if (typeof globalThis.crypto.getRandomValues === 'undefined') {
47
+ globalThis.crypto.getRandomValues = function(arr) {
48
+ for (var i = 0; i < arr.length; i++) {
49
+ arr[i] = Math.floor(Math.random() * 256);
50
+ }
51
+ return arr;
52
+ };
53
+ }
54
+
55
+ // MessageChannel polyfill — React 19's scheduler uses it for async work
56
+ if (typeof globalThis.MessageChannel === 'undefined') {
57
+ globalThis.MessageChannel = function() {
58
+ var cb = null;
59
+ this.port1 = { onmessage: null };
60
+ this.port2 = {
61
+ postMessage: function() {
62
+ if (cb) { var fn = cb; cb = null; fn({ data: undefined }); }
63
+ }
64
+ };
65
+ var self = this;
66
+ Object.defineProperty(this.port1, 'onmessage', {
67
+ set: function(fn) { cb = fn; },
68
+ get: function() { return cb; }
69
+ });
70
+ };
71
+ }
72
+
73
+ // Timer polyfills — React scheduler needs these
74
+ (function() {
75
+ var queue = [];
76
+ var timerIdCounter = 1;
77
+ var timers = {};
78
+
79
+ if (typeof globalThis.setImmediate === 'undefined') {
80
+ globalThis.setImmediate = function(fn) { queue.push(fn); };
81
+ }
82
+
83
+ // Flush all async work: Hermes microtask queue (promises) + our polyfill queues (timers).
84
+ // The C++ bridge installs a native __HT_drain that calls Hermes's drainMicrotasks().
85
+ // We wrap it to also flush our setImmediate/setTimeout polyfill queues.
86
+ var nativeDrain = globalThis.__HT_drain || function() {};
87
+ globalThis.__HT_drain = function() {
88
+ // 1. Drain Hermes's internal promise/microtask queue
89
+ nativeDrain();
90
+ // 2. Flush our setImmediate queue
91
+ var limit = 1000;
92
+ while (queue.length > 0 && limit-- > 0) { queue.shift()(); }
93
+ // 3. Flush pending timers
94
+ var ids = Object.keys(timers);
95
+ for (var i = 0; i < ids.length; i++) {
96
+ var t = timers[ids[i]];
97
+ if (t) { delete timers[ids[i]]; t(); }
98
+ }
99
+ // 4. Drain again (timer callbacks may have queued more microtasks)
100
+ nativeDrain();
101
+ };
102
+
103
+ if (typeof globalThis.setTimeout === 'undefined') {
104
+ globalThis.setTimeout = function(fn, delay) {
105
+ var id = timerIdCounter++;
106
+ if (!delay || delay <= 0) {
107
+ queue.push(fn);
108
+ } else {
109
+ timers[id] = fn;
110
+ }
111
+ return id;
112
+ };
113
+ }
114
+
115
+ if (typeof globalThis.clearTimeout === 'undefined') {
116
+ globalThis.clearTimeout = function(id) { delete timers[id]; };
117
+ }
118
+
119
+ if (typeof globalThis.console === 'undefined') {
120
+ globalThis.console = {
121
+ log: function() {},
122
+ warn: function() {},
123
+ error: function() {},
124
+ info: function() {},
125
+ debug: function() {},
126
+ };
127
+ }
128
+ })();
129
+
130
+ // Web API polyfills — needed for RTK Query's fetchBaseQuery
131
+ (function() {
132
+ // AbortController / AbortSignal
133
+ if (typeof globalThis.AbortController === 'undefined') {
134
+ function AbortSignal() { this.aborted = false; this._listeners = []; }
135
+ AbortSignal.prototype.addEventListener = function(type, fn) { this._listeners.push(fn); };
136
+ AbortSignal.prototype.removeEventListener = function(type, fn) {
137
+ this._listeners = this._listeners.filter(function(f) { return f !== fn; });
138
+ };
139
+
140
+ function AbortController() { this.signal = new AbortSignal(); }
141
+ AbortController.prototype.abort = function() {
142
+ this.signal.aborted = true;
143
+ for (var i = 0; i < this.signal._listeners.length; i++) {
144
+ try { this.signal._listeners[i](); } catch(e) {}
145
+ }
146
+ };
147
+
148
+ globalThis.AbortController = AbortController;
149
+ globalThis.AbortSignal = AbortSignal;
150
+ }
151
+
152
+ // Headers
153
+ if (typeof globalThis.Headers === 'undefined') {
154
+ function Headers(init) {
155
+ this._map = {};
156
+ if (init) {
157
+ if (typeof init.forEach === 'function') {
158
+ init.forEach(function(v, k) { this._map[k.toLowerCase()] = v; }.bind(this));
159
+ } else {
160
+ var keys = Object.keys(init);
161
+ for (var i = 0; i < keys.length; i++) {
162
+ this._map[keys[i].toLowerCase()] = init[keys[i]];
163
+ }
164
+ }
165
+ }
166
+ }
167
+ Headers.prototype.get = function(k) { return this._map[k.toLowerCase()] || null; };
168
+ Headers.prototype.has = function(k) { return k.toLowerCase() in this._map; };
169
+ Headers.prototype.set = function(k, v) { this._map[k.toLowerCase()] = v; };
170
+ Headers.prototype.append = function(k, v) {
171
+ k = k.toLowerCase();
172
+ this._map[k] = this._map[k] ? this._map[k] + ', ' + v : v;
173
+ };
174
+ Headers.prototype.delete = function(k) { delete this._map[k.toLowerCase()]; };
175
+ Headers.prototype.forEach = function(fn) {
176
+ var keys = Object.keys(this._map);
177
+ for (var i = 0; i < keys.length; i++) fn(this._map[keys[i]], keys[i], this);
178
+ };
179
+ Headers.prototype.entries = function() { return Object.entries(this._map); };
180
+ globalThis.Headers = Headers;
181
+ }
182
+
183
+ // URLSearchParams — always install BEFORE URL: native Hermes version may not parse correctly
184
+ {
185
+ function URLSearchParams(init) {
186
+ this._params = [];
187
+ if (typeof init === 'string') {
188
+ init = init.replace(/^\?/, '');
189
+ var pairs = init.split('&');
190
+ for (var i = 0; i < pairs.length; i++) {
191
+ if (!pairs[i]) continue;
192
+ var kv = pairs[i].split('=');
193
+ this._params.push([decodeURIComponent(kv[0]), decodeURIComponent(kv.slice(1).join('='))]);
194
+ }
195
+ } else if (init && typeof init === 'object') {
196
+ if (Array.isArray(init)) {
197
+ // Array of [key, value] pairs
198
+ for (var i = 0; i < init.length; i++) {
199
+ this._params.push([String(init[i][0]), String(init[i][1])]);
200
+ }
201
+ } else if (typeof init[Symbol.iterator] === 'function') {
202
+ // Iterable (e.g. another URLSearchParams instance)
203
+ var iter = init[Symbol.iterator]();
204
+ var next;
205
+ while (!(next = iter.next()).done) {
206
+ this._params.push([String(next.value[0]), String(next.value[1])]);
207
+ }
208
+ } else if (typeof init.forEach === 'function') {
209
+ // URLSearchParams-like with forEach(value, key)
210
+ init.forEach(function(v, k) { this._params.push([String(k), String(v)]); }.bind(this));
211
+ } else {
212
+ // Plain object: { key: value }
213
+ var keys = Object.keys(init);
214
+ for (var i = 0; i < keys.length; i++) {
215
+ this._params.push([keys[i], String(init[keys[i]])]);
216
+ }
217
+ }
218
+ }
219
+ }
220
+ URLSearchParams.prototype.get = function(k) {
221
+ for (var i = 0; i < this._params.length; i++) {
222
+ if (this._params[i][0] === k) return this._params[i][1];
223
+ }
224
+ return null;
225
+ };
226
+ URLSearchParams.prototype.has = function(k) {
227
+ for (var i = 0; i < this._params.length; i++) {
228
+ if (this._params[i][0] === k) return true;
229
+ }
230
+ return false;
231
+ };
232
+ URLSearchParams.prototype.set = function(k, v) {
233
+ for (var i = 0; i < this._params.length; i++) {
234
+ if (this._params[i][0] === k) { this._params[i][1] = String(v); return; }
235
+ }
236
+ this._params.push([k, String(v)]);
237
+ };
238
+ URLSearchParams.prototype.append = function(k, v) { this._params.push([k, String(v)]); };
239
+ URLSearchParams.prototype['delete'] = function(k) {
240
+ this._params = this._params.filter(function(p) { return p[0] !== k; });
241
+ };
242
+ URLSearchParams.prototype.entries = function() {
243
+ var params = this._params;
244
+ var i = 0;
245
+ return { next: function() {
246
+ if (i < params.length) return { value: [params[i][0], params[i++][1]], done: false };
247
+ return { value: undefined, done: true };
248
+ }};
249
+ };
250
+ URLSearchParams.prototype.keys = function() {
251
+ var params = this._params;
252
+ var i = 0;
253
+ return { next: function() {
254
+ if (i < params.length) return { value: params[i++][0], done: false };
255
+ return { value: undefined, done: true };
256
+ }};
257
+ };
258
+ URLSearchParams.prototype.values = function() {
259
+ var params = this._params;
260
+ var i = 0;
261
+ return { next: function() {
262
+ if (i < params.length) return { value: params[i++][1], done: false };
263
+ return { value: undefined, done: true };
264
+ }};
265
+ };
266
+ URLSearchParams.prototype.forEach = function(fn, thisArg) {
267
+ for (var i = 0; i < this._params.length; i++) {
268
+ fn.call(thisArg, this._params[i][1], this._params[i][0], this);
269
+ }
270
+ };
271
+ URLSearchParams.prototype.toString = function() {
272
+ return this._params.map(function(p) { return encodeURIComponent(p[0]) + '=' + encodeURIComponent(p[1]); }).join('&');
273
+ };
274
+ URLSearchParams.prototype[Symbol.iterator] = URLSearchParams.prototype.entries;
275
+ globalThis.URLSearchParams = URLSearchParams;
276
+ }
277
+
278
+ // URL — always install: Hermes has a built-in URL that doesn't parse searchParams correctly
279
+ {
280
+ function URL(url, base) {
281
+ if (base && url.indexOf('://') === -1) {
282
+ url = base.replace(/\/$/, '') + '/' + url.replace(/^\//, '');
283
+ }
284
+ this.href = url;
285
+ var match = url.match(/^(https?:)\/\/([^/:?#]+)(:\d+)?(\/[^?#]*)?(\?[^#]*)?(#.*)?$/);
286
+ if (match) {
287
+ this.protocol = match[1];
288
+ this.hostname = match[2];
289
+ this.port = match[3] ? match[3].slice(1) : '';
290
+ this.pathname = match[4] || '/';
291
+ this.search = match[5] || '';
292
+ this.hash = match[6] || '';
293
+ this.host = this.hostname + (this.port ? ':' + this.port : '');
294
+ this.origin = this.protocol + '//' + this.host;
295
+ } else {
296
+ this.protocol = ''; this.hostname = ''; this.port = '';
297
+ this.pathname = url; this.search = ''; this.hash = '';
298
+ this.host = ''; this.origin = '';
299
+ }
300
+ this.searchParams = new globalThis.URLSearchParams(this.search);
301
+ }
302
+ URL.prototype.toString = function() { return this.href; };
303
+ globalThis.URL = URL;
304
+ }
305
+
306
+ // Request (minimal — RTK Query checks typeof Request)
307
+ if (typeof globalThis.Request === 'undefined') {
308
+ globalThis.Request = function Request(url, init) {
309
+ this.url = typeof url === 'string' ? url : url.href;
310
+ this.method = (init && init.method) || 'GET';
311
+ this.headers = new globalThis.Headers(init && init.headers);
312
+ this.body = init && init.body;
313
+ };
314
+ }
315
+
316
+ // Stub fetch (mockFetch will override with handler-based implementation)
317
+ if (typeof globalThis.fetch === 'undefined') {
318
+ globalThis.fetch = function() {
319
+ return Promise.reject(new Error('fetch not configured — use mockFetch() to register handlers'));
320
+ };
321
+ }
322
+
323
+ // Response (minimal)
324
+ if (typeof globalThis.Response === 'undefined') {
325
+ globalThis.Response = function Response(body, init) {
326
+ this.body = body;
327
+ this.status = (init && init.status) || 200;
328
+ this.ok = this.status >= 200 && this.status < 300;
329
+ this.headers = new globalThis.Headers(init && init.headers);
330
+ };
331
+ globalThis.Response.prototype.json = function() { return Promise.resolve(JSON.parse(this.body)); };
332
+ globalThis.Response.prototype.text = function() { return Promise.resolve(String(this.body)); };
333
+ }
334
+ })();
@@ -0,0 +1,54 @@
1
+ // In-memory AsyncStorage shim for hermes-test.
2
+ // Provides a fresh store per test file. Data persists within a file,
3
+ // resets between files via __HT_resetAsyncStorage().
4
+
5
+ var _store = {};
6
+
7
+ var AsyncStorage = {
8
+ getItem: function(key) {
9
+ var val = _store[key] !== undefined ? _store[key] : null;
10
+ return Promise.resolve(val);
11
+ },
12
+ setItem: function(key, value) {
13
+ _store[key] = value;
14
+ return Promise.resolve();
15
+ },
16
+ removeItem: function(key) {
17
+ delete _store[key];
18
+ return Promise.resolve();
19
+ },
20
+ multiRemove: function(keys) {
21
+ for (var i = 0; i < keys.length; i++) {
22
+ delete _store[keys[i]];
23
+ }
24
+ return Promise.resolve();
25
+ },
26
+ multiGet: function(keys) {
27
+ var result = [];
28
+ for (var i = 0; i < keys.length; i++) {
29
+ result.push([keys[i], _store[keys[i]] !== undefined ? _store[keys[i]] : null]);
30
+ }
31
+ return Promise.resolve(result);
32
+ },
33
+ multiSet: function(pairs) {
34
+ for (var i = 0; i < pairs.length; i++) {
35
+ _store[pairs[i][0]] = pairs[i][1];
36
+ }
37
+ return Promise.resolve();
38
+ },
39
+ getAllKeys: function() {
40
+ return Promise.resolve(Object.keys(_store));
41
+ },
42
+ clear: function() {
43
+ _store = {};
44
+ return Promise.resolve();
45
+ },
46
+ };
47
+
48
+ // Reset hook — called between test files by the harness
49
+ globalThis.__HT_resetAsyncStorage = function() {
50
+ _store = {};
51
+ };
52
+
53
+ module.exports = AsyncStorage;
54
+ module.exports.default = AsyncStorage;
@@ -0,0 +1,20 @@
1
+ // Built-in react-i18next shim for hermes-test.
2
+ // Identity translation: t('key') returns 'key'.
3
+ // Covers useTranslation, withTranslation, Trans, initReactI18next.
4
+
5
+ var t = function(key) { return key; };
6
+ var i18n = {
7
+ language: 'en',
8
+ changeLanguage: function() { return Promise.resolve(); },
9
+ use: function() { return i18n; },
10
+ init: function() { return Promise.resolve(); },
11
+ };
12
+
13
+ module.exports = {
14
+ useTranslation: function() { return { t: t, i18n: i18n, ready: true }; },
15
+ withTranslation: function() { return function(component) { return component; }; },
16
+ Trans: function(props) { return props.children || null; },
17
+ Translation: function(props) { return props.children(t, { i18n: i18n }); },
18
+ initReactI18next: { type: '3rdParty', init: function() {} },
19
+ I18nextProvider: function(props) { return props.children; },
20
+ };
@@ -0,0 +1,8 @@
1
+ // Built-in react-native-launch-arguments shim for hermes-test.
2
+ // Returns empty launch arguments — native module not available in test environment.
3
+
4
+ module.exports = {
5
+ LaunchArguments: {
6
+ value: function() { return {}; },
7
+ },
8
+ };
@@ -0,0 +1,168 @@
1
+ // Default react-native shim for hermes-test.
2
+ // Provides stub implementations of commonly used RN APIs.
3
+ // Users can override via hermes-test.config.json "shims" or mockModule().
4
+
5
+ var noop = function() {};
6
+ var noopReturn = function(x) { return x; };
7
+
8
+ module.exports = {
9
+ // Platform
10
+ Platform: {
11
+ OS: 'ios',
12
+ Version: 19,
13
+ select: function(obj) { return obj.ios !== undefined ? obj.ios : obj.default; },
14
+ isPad: false,
15
+ isTVOS: false,
16
+ isTV: false,
17
+ },
18
+
19
+ // StyleSheet
20
+ StyleSheet: {
21
+ create: function(styles) { return styles; },
22
+ flatten: function(style) {
23
+ if (!style) return {};
24
+ if (Array.isArray(style)) {
25
+ var result = {};
26
+ for (var i = 0; i < style.length; i++) {
27
+ if (style[i]) Object.assign(result, style[i]);
28
+ }
29
+ return result;
30
+ }
31
+ return style;
32
+ },
33
+ absoluteFill: { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 },
34
+ absoluteFillObject: { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 },
35
+ hairlineWidth: 1,
36
+ },
37
+
38
+ // Dimensions
39
+ Dimensions: {
40
+ get: function() { return { width: 375, height: 812, scale: 3, fontScale: 1 }; },
41
+ addEventListener: function() { return { remove: noop }; },
42
+ removeEventListener: noop,
43
+ },
44
+
45
+ // PixelRatio
46
+ PixelRatio: {
47
+ get: function() { return 3; },
48
+ getFontScale: function() { return 1; },
49
+ getPixelSizeForLayoutSize: function(size) { return size * 3; },
50
+ roundToNearestPixel: function(size) { return Math.round(size * 3) / 3; },
51
+ },
52
+
53
+ // AppState
54
+ AppState: {
55
+ currentState: 'active',
56
+ addEventListener: function(event, cb) {
57
+ return { remove: noop };
58
+ },
59
+ removeEventListener: noop,
60
+ },
61
+
62
+ // Alert
63
+ Alert: {
64
+ alert: noop,
65
+ prompt: noop,
66
+ },
67
+
68
+ // Linking
69
+ Linking: {
70
+ openURL: function() { return Promise.resolve(); },
71
+ canOpenURL: function() { return Promise.resolve(true); },
72
+ getInitialURL: function() { return Promise.resolve(null); },
73
+ addEventListener: function() { return { remove: noop }; },
74
+ removeEventListener: noop,
75
+ },
76
+
77
+ // Keyboard
78
+ Keyboard: {
79
+ dismiss: noop,
80
+ addListener: function() { return { remove: noop }; },
81
+ removeListener: noop,
82
+ removeAllListeners: noop,
83
+ },
84
+
85
+ // Animated
86
+ Animated: {
87
+ Value: function(val) {
88
+ this._value = val;
89
+ this.setValue = function(v) { this._value = v; };
90
+ this.interpolate = function() { return new module.exports.Animated.Value(0); };
91
+ this.addListener = function() { return { remove: noop }; };
92
+ this.removeListener = noop;
93
+ this.removeAllListeners = noop;
94
+ this.stopAnimation = function(cb) { if (cb) cb(this._value); };
95
+ },
96
+ ValueXY: function() {
97
+ this.x = new module.exports.Animated.Value(0);
98
+ this.y = new module.exports.Animated.Value(0);
99
+ this.setValue = noop;
100
+ this.getLayout = function() { return { left: this.x, top: this.y }; };
101
+ },
102
+ timing: function() { return { start: function(cb) { if (cb) cb({ finished: true }); } }; },
103
+ spring: function() { return { start: function(cb) { if (cb) cb({ finished: true }); } }; },
104
+ decay: function() { return { start: function(cb) { if (cb) cb({ finished: true }); } }; },
105
+ parallel: function() { return { start: function(cb) { if (cb) cb({ finished: true }); } }; },
106
+ sequence: function() { return { start: function(cb) { if (cb) cb({ finished: true }); } }; },
107
+ stagger: function() { return { start: function(cb) { if (cb) cb({ finished: true }); } }; },
108
+ loop: function() { return { start: noop, stop: noop }; },
109
+ event: function() { return noop; },
110
+ createAnimatedComponent: noopReturn,
111
+ View: 'Animated.View',
112
+ Text: 'Animated.Text',
113
+ Image: 'Animated.Image',
114
+ ScrollView: 'Animated.ScrollView',
115
+ FlatList: 'Animated.FlatList',
116
+ },
117
+
118
+ // Hooks
119
+ useWindowDimensions: function() { return { width: 375, height: 812, scale: 3, fontScale: 1 }; },
120
+ useColorScheme: function() { return 'light'; },
121
+
122
+ // Components (string stubs — not rendered in test)
123
+ View: 'View',
124
+ Text: 'Text',
125
+ Image: 'Image',
126
+ ScrollView: 'ScrollView',
127
+ FlatList: 'FlatList',
128
+ SectionList: 'SectionList',
129
+ TouchableOpacity: 'TouchableOpacity',
130
+ TouchableHighlight: 'TouchableHighlight',
131
+ TouchableWithoutFeedback: 'TouchableWithoutFeedback',
132
+ Pressable: 'Pressable',
133
+ TextInput: 'TextInput',
134
+ Switch: 'Switch',
135
+ ActivityIndicator: 'ActivityIndicator',
136
+ Modal: 'Modal',
137
+ SafeAreaView: 'SafeAreaView',
138
+ StatusBar: 'StatusBar',
139
+ KeyboardAvoidingView: 'KeyboardAvoidingView',
140
+
141
+ // I18nManager
142
+ I18nManager: {
143
+ isRTL: false,
144
+ allowRTL: noop,
145
+ forceRTL: noop,
146
+ },
147
+
148
+ // Appearance
149
+ Appearance: {
150
+ getColorScheme: function() { return 'light'; },
151
+ addChangeListener: function() { return { remove: noop }; },
152
+ },
153
+
154
+ // NativeModules fallback
155
+ NativeModules: {},
156
+ NativeEventEmitter: function() {
157
+ this.addListener = function() { return { remove: noop }; };
158
+ this.removeListener = noop;
159
+ this.removeAllListeners = noop;
160
+ },
161
+
162
+ // AccessibilityInfo
163
+ AccessibilityInfo: {
164
+ isScreenReaderEnabled: function() { return Promise.resolve(false); },
165
+ addEventListener: function() { return { remove: noop }; },
166
+ announceForAccessibility: noop,
167
+ },
168
+ };
@@ -0,0 +1,12 @@
1
+ // hermes-test shim for react-redux
2
+ //
3
+ // Transparent wrapper that ensures single-instance resolution.
4
+ // Re-exports Provider, useSelector, useDispatch, connect, etc.
5
+ // unchanged. Prevents dual-instance issues where React context
6
+ // from one copy can't be read by hooks from another copy.
7
+ //
8
+ // Usage in hermes-test.config.json:
9
+ // "shims": { "react-redux": "hermes-test/shims/react-redux" }
10
+
11
+ var real = require('@__ht_real_pkg/react-redux');
12
+ module.exports = real;
@@ -0,0 +1,16 @@
1
+ // Shim for react inside the harness bundle.
2
+ // react-reconciler imports 'react' — this shim delegates to globalThis.__HT_React.
3
+ // The Proxy ensures late-binding: properties are read at access time, not import time.
4
+ var handler = {
5
+ get: function(_, prop) {
6
+ if (prop === Symbol.toPrimitive || prop === 'then') return undefined;
7
+ var R = globalThis.__HT_React;
8
+ return R ? R[prop] : undefined;
9
+ },
10
+ set: function(_, prop, value) {
11
+ var R = globalThis.__HT_React;
12
+ if (R) R[prop] = value;
13
+ return true;
14
+ }
15
+ };
16
+ module.exports = new Proxy({}, handler);
@@ -0,0 +1,11 @@
1
+ // hermes-test shim for @reduxjs/toolkit
2
+ //
3
+ // Transparent wrapper that ensures single-instance resolution.
4
+ // Re-exports everything unchanged — createSlice, configureStore,
5
+ // createAsyncThunk, createSelector, etc. all work as normal.
6
+ //
7
+ // Usage in hermes-test.config.json:
8
+ // "shims": { "@reduxjs/toolkit": "hermes-test/shims/reduxjs-toolkit" }
9
+
10
+ var real = require('@__ht_real_pkg/@reduxjs/toolkit');
11
+ module.exports = real;
@@ -0,0 +1,44 @@
1
+ // hermes-test shim for @reduxjs/toolkit/query/react
2
+ //
3
+ // Fixes the dual-instance problem: when split bundles or shadow wrappers
4
+ // cause createApi() to be called twice with the same config, only one
5
+ // API instance is created. injectEndpoints() mutations always hit the
6
+ // canonical instance, so the store and endpoint modules stay in sync.
7
+ //
8
+ // Usage in hermes-test.config.json:
9
+ // "shims": { "@reduxjs/toolkit/query/react": "hermes-test/shims/rtk-query" }
10
+
11
+ var real = require('@__ht_real_pkg/@reduxjs/toolkit/query/react');
12
+
13
+ // Singleton cache keyed by reducerPath — prevents dual-instance from
14
+ // split bundles or shadow wrapper re-entrancy
15
+ var _apiCache = {};
16
+
17
+ // Proxy delegates everything to the real module with late binding.
18
+ // This avoids eagerly copying ESM live bindings that may not be initialized yet.
19
+ var handler = {
20
+ get: function(target, prop) {
21
+ if (prop === 'createApi') {
22
+ return function createApi(opts) {
23
+ var key = opts && opts.reducerPath || 'api';
24
+ if (_apiCache[key]) return _apiCache[key];
25
+ var api = real.createApi(opts);
26
+ _apiCache[key] = api;
27
+ return api;
28
+ };
29
+ }
30
+ return real[prop];
31
+ },
32
+ ownKeys: function() {
33
+ try { return Object.getOwnPropertyNames(real); } catch(e) { return []; }
34
+ },
35
+ getOwnPropertyDescriptor: function(target, prop) {
36
+ try {
37
+ var d = Object.getOwnPropertyDescriptor(real, prop);
38
+ if (d) return { configurable: true, enumerable: d.enumerable, writable: true, value: d.get ? d.get() : d.value };
39
+ } catch(e) {}
40
+ return { configurable: true, enumerable: false, writable: true, value: undefined };
41
+ }
42
+ };
43
+
44
+ module.exports = new Proxy({}, handler);