@ccheever/exact-ibex-runtime 0.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 (161) hide show
  1. package/package.json +63 -0
  2. package/src/abort/AbortController.ts +23 -0
  3. package/src/abort/AbortSignal.ts +152 -0
  4. package/src/abort/index.ts +2 -0
  5. package/src/accessibility.ts +12 -0
  6. package/src/arraybuffer-detach.ts +109 -0
  7. package/src/base64/base64.ts +168 -0
  8. package/src/base64/index.ts +1 -0
  9. package/src/blob/Blob.ts +259 -0
  10. package/src/blob/File.ts +59 -0
  11. package/src/blob/FormData.ts +323 -0
  12. package/src/blob/index.ts +3 -0
  13. package/src/bootstrap.ts +1946 -0
  14. package/src/broadcast/BroadcastChannel.ts +280 -0
  15. package/src/broadcast/index.ts +5 -0
  16. package/src/cache/Cache.ts +349 -0
  17. package/src/cache/CacheStorage.ts +89 -0
  18. package/src/cache/index.ts +27 -0
  19. package/src/camera/index.ts +6202 -0
  20. package/src/camera/processor.worker.ts +194 -0
  21. package/src/camera/scene.ts +195 -0
  22. package/src/clipboard/Clipboard.ts +129 -0
  23. package/src/clipboard/ClipboardItem.ts +97 -0
  24. package/src/clipboard/index.ts +6 -0
  25. package/src/clone/index.ts +1 -0
  26. package/src/clone/structuredClone.ts +389 -0
  27. package/src/clone/transferableSymbols.ts +2 -0
  28. package/src/compression/CompressionStream.ts +146 -0
  29. package/src/compression/DecompressionStream.ts +342 -0
  30. package/src/compression/index.ts +4 -0
  31. package/src/console/Console.ts +341 -0
  32. package/src/console/index.ts +2 -0
  33. package/src/core/accessibility-state.ts +263 -0
  34. package/src/core/accessibility.ts +184 -0
  35. package/src/core/agent-state.ts +37 -0
  36. package/src/core/diagnostics-logs.ts +144 -0
  37. package/src/core/host-call-bridge.ts +16 -0
  38. package/src/core/i18n-helpers.ts +189 -0
  39. package/src/core/locale-state.ts +253 -0
  40. package/src/core/locale.ts +95 -0
  41. package/src/crypto/Crypto.ts +2743 -0
  42. package/src/crypto/index.ts +1 -0
  43. package/src/diagnostics/logs.ts +7 -0
  44. package/src/encoding/TextDecoder.ts +1181 -0
  45. package/src/encoding/TextDecoderStream.ts +58 -0
  46. package/src/encoding/TextEncoder.ts +180 -0
  47. package/src/encoding/TextEncoderStream.ts +39 -0
  48. package/src/encoding/index.ts +8 -0
  49. package/src/events/CloseEvent.ts +91 -0
  50. package/src/events/DOMException.ts +409 -0
  51. package/src/events/ErrorEvent.ts +39 -0
  52. package/src/events/Event.ts +151 -0
  53. package/src/events/EventTarget.ts +280 -0
  54. package/src/events/FocusEvent.ts +27 -0
  55. package/src/events/KeyboardEvent.ts +46 -0
  56. package/src/events/MessageEvent.ts +61 -0
  57. package/src/events/ProgressEvent.ts +33 -0
  58. package/src/events/PromiseRejectionEvent.ts +31 -0
  59. package/src/events/index.ts +52 -0
  60. package/src/eventsource/EventSource.ts +371 -0
  61. package/src/eventsource/index.ts +2 -0
  62. package/src/fetch/Headers.ts +642 -0
  63. package/src/fetch/Request.ts +760 -0
  64. package/src/fetch/Response.ts +543 -0
  65. package/src/fetch/body.ts +1256 -0
  66. package/src/fetch/cookie-jar.ts +566 -0
  67. package/src/fetch/demo.ts +207 -0
  68. package/src/fetch/errors.ts +101 -0
  69. package/src/fetch/fetch.ts +2610 -0
  70. package/src/fetch/index.ts +101 -0
  71. package/src/fetch/native-bridge.ts +65 -0
  72. package/src/fetch/types.ts +258 -0
  73. package/src/filereader/FileReader.ts +236 -0
  74. package/src/filereader/index.ts +1 -0
  75. package/src/fs/Dirent.ts +39 -0
  76. package/src/fs/ExactFile.ts +450 -0
  77. package/src/fs/Stats.ts +80 -0
  78. package/src/fs/index.ts +944 -0
  79. package/src/fs/promises.ts +386 -0
  80. package/src/fs/shared.ts +328 -0
  81. package/src/http-server/index.js +697 -0
  82. package/src/http-server/index.ts +27 -0
  83. package/src/identity.generated.ts +14 -0
  84. package/src/index.ts +283 -0
  85. package/src/indexeddb/IDBCursor.ts +188 -0
  86. package/src/indexeddb/IDBDatabase.ts +343 -0
  87. package/src/indexeddb/IDBFactory.ts +269 -0
  88. package/src/indexeddb/IDBIndex.ts +194 -0
  89. package/src/indexeddb/IDBKeyRange.ts +109 -0
  90. package/src/indexeddb/IDBObjectStore.ts +468 -0
  91. package/src/indexeddb/IDBRequest.ts +163 -0
  92. package/src/indexeddb/IDBTransaction.ts +207 -0
  93. package/src/indexeddb/index.ts +34 -0
  94. package/src/indexeddb/utils.ts +52 -0
  95. package/src/inspect/index.ts +1 -0
  96. package/src/inspect/inspect.ts +465 -0
  97. package/src/internal/detect.ts +104 -0
  98. package/src/locale.ts +10 -0
  99. package/src/location/index.ts +1059 -0
  100. package/src/locks/LockManager.ts +460 -0
  101. package/src/locks/index.ts +12 -0
  102. package/src/media/VideoFrame.ts +58 -0
  103. package/src/messaging/MessageChannel.ts +31 -0
  104. package/src/messaging/MessagePort.ts +180 -0
  105. package/src/messaging/index.ts +2 -0
  106. package/src/messaging.ts +247 -0
  107. package/src/native/NativeModules.ts +354 -0
  108. package/src/native/index.ts +1 -0
  109. package/src/navigator/Navigator.ts +351 -0
  110. package/src/navigator/index.ts +1 -0
  111. package/src/node/Buffer.ts +1786 -0
  112. package/src/node/index.ts +4 -0
  113. package/src/node/path.ts +495 -0
  114. package/src/node/process.ts +2528 -0
  115. package/src/performance/Performance.ts +532 -0
  116. package/src/performance/index.ts +21 -0
  117. package/src/polyfills/array.ts +236 -0
  118. package/src/polyfills/arraybuffer.ts +172 -0
  119. package/src/polyfills/groupby.ts +85 -0
  120. package/src/polyfills/index.ts +85 -0
  121. package/src/polyfills/intl.ts +1956 -0
  122. package/src/polyfills/iterator.ts +479 -0
  123. package/src/polyfills/promise.ts +37 -0
  124. package/src/polyfills/set.ts +245 -0
  125. package/src/polyfills/string.ts +85 -0
  126. package/src/polyfills/typedarray.ts +110 -0
  127. package/src/promise-rejection-tracking.ts +464 -0
  128. package/src/react-native/index.ts +388 -0
  129. package/src/runtime-entry.ts +55 -0
  130. package/src/scheduling/AnimationFrame.ts +105 -0
  131. package/src/scheduling/IdleCallback.ts +167 -0
  132. package/src/scheduling/index.ts +13 -0
  133. package/src/security/Capabilities.ts +1146 -0
  134. package/src/security/Permissions.ts +392 -0
  135. package/src/security/capability-bits.generated.ts +63 -0
  136. package/src/security/index.ts +16 -0
  137. package/src/sqlite/Database.ts +456 -0
  138. package/src/sqlite/Statement.ts +206 -0
  139. package/src/sqlite/constants.ts +79 -0
  140. package/src/sqlite/errors.ts +25 -0
  141. package/src/sqlite/index.ts +34 -0
  142. package/src/sqlite/module.js +438 -0
  143. package/src/storage/Storage.ts +291 -0
  144. package/src/storage/StorageManager.ts +91 -0
  145. package/src/storage/index.ts +3 -0
  146. package/src/stream-compat.ts +47 -0
  147. package/src/streams/ReadableStream.ts +4131 -0
  148. package/src/streams/TransformStream.ts +375 -0
  149. package/src/streams/WritableStream.ts +866 -0
  150. package/src/streams/index.ts +41 -0
  151. package/src/timers/Timers.ts +296 -0
  152. package/src/timers/index.ts +11 -0
  153. package/src/url/URL.ts +656 -0
  154. package/src/url/URLPattern.ts +850 -0
  155. package/src/url/URLSearchParams.ts +244 -0
  156. package/src/url/index.ts +9 -0
  157. package/src/websocket/WebSocket.ts +770 -0
  158. package/src/websocket/WebSocketError.ts +52 -0
  159. package/src/websocket/WebSocketStream.ts +628 -0
  160. package/src/websocket/index.ts +7 -0
  161. package/src/window/index.ts +872 -0
@@ -0,0 +1,1946 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Ibex Runtime Bootstrap
4
+ *
5
+ * Installs web-standard globals on demand using lazy loading.
6
+ * Essential APIs (console, timers, process, Buffer, require) load eagerly.
7
+ * Non-essential APIs (streams, crypto, fetch, inspect) use lazy getters
8
+ * that self-replace on first access, reducing startup GC pressure.
9
+ */
10
+
11
+ // ---- Essential imports (always needed at startup) ----
12
+ import { setNativeCryptoModule, setNativeFileSystemModule, setNativeSchedulingModule, setNativeStorageModule, setNativeWebSocketModule } from "./native";
13
+ import { setNativeCapabilityModule, enableStrictMode } from "./security";
14
+
15
+ // ---- Lazy-loaded module imports ----
16
+ // These are still bundled, but the globals are installed via lazy getters
17
+ // so their constructors/prototypes don't allocate until first access.
18
+ import { initializeNativeFetch } from "./fetch/native-bridge";
19
+ import { installFetchGlobals } from "./fetch/fetch";
20
+ import { Headers as FetchHeaders } from "./fetch/Headers";
21
+ import { Request as FetchRequest } from "./fetch/Request";
22
+ import { Response as FetchResponse } from "./fetch/Response";
23
+ import { inspect } from "./inspect";
24
+ import { crypto as exactCrypto, Crypto, SubtleCrypto, CryptoKey } from "./crypto";
25
+ import {
26
+ ReadableStream,
27
+ ReadableStreamDefaultReader,
28
+ ReadableStreamDefaultController,
29
+ ReadableStreamBYOBReader,
30
+ ReadableByteStreamController,
31
+ ReadableStreamBYOBRequest,
32
+ isReadableStream,
33
+ WritableStream,
34
+ WritableStreamDefaultWriter,
35
+ WritableStreamDefaultController,
36
+ TransformStream,
37
+ TransformStreamDefaultController,
38
+ ByteLengthQueuingStrategy,
39
+ CountQueuingStrategy,
40
+ } from "./streams";
41
+
42
+ // ---- Lazy-loaded web API imports ----
43
+ // Static imports so bundler can tree-shake; values only used in lazy getters.
44
+ import { Blob, File, FormData } from "./blob";
45
+ import {
46
+ Event, CustomEvent, EventTarget, DOMException,
47
+ MessageEvent, CloseEvent, ErrorEvent, ProgressEvent,
48
+ KeyboardEvent, FocusEvent,
49
+ PromiseRejectionEvent,
50
+ } from "./events";
51
+ import { AbortController, AbortSignal } from "./abort";
52
+ import { TextEncoder, TextDecoder, TextEncoderStream, TextDecoderStream } from "./encoding";
53
+ import { URL, URLSearchParams, URLPattern } from "./url";
54
+ import { MessageChannel, MessagePort } from "./messaging";
55
+ import { VideoFrame } from "./media/VideoFrame";
56
+ import { BroadcastChannel } from "./broadcast";
57
+ import { WebSocket, WebSocketError, WebSocketStream } from "./websocket";
58
+ import { EventSource } from "./eventsource";
59
+ import { FileReader } from "./filereader";
60
+ import { navigator } from "./navigator";
61
+ import { localStorage, sessionStorage } from "./storage";
62
+ import { CacheStorage } from "./cache";
63
+ import { ClipboardItem } from "./clipboard";
64
+ import { performance as perfInstance, PerformanceObserver, PerformanceEntry, PerformanceMark, PerformanceMeasure, setNativePerformanceModule } from "./performance";
65
+ import { structuredClone as structuredCloneFn } from "./clone";
66
+ import { atob as atobFn, btoa as btoaFn } from "./base64";
67
+ import {
68
+ requestAnimationFrame as rafFn, cancelAnimationFrame as cafFn,
69
+ requestIdleCallback as ricFn, cancelIdleCallback as cicFn,
70
+ } from "./scheduling";
71
+ import { CompressionStream, DecompressionStream } from "./compression";
72
+ import { IDBFactory, IDBKeyRange, IDBDatabase, IDBObjectStore, IDBTransaction, IDBRequest, IDBOpenDBRequest, IDBCursor, IDBCursorWithValue, IDBIndex } from "./indexeddb";
73
+ import { Database } from "./sqlite";
74
+ import { installPolyfills } from "./polyfills";
75
+ import { installPromiseRejectionTracking } from "./promise-rejection-tracking";
76
+ import { installExactGlobal } from "./fs/ExactFile";
77
+ import { installExactAccessibilityGlobal } from "./accessibility";
78
+ import { installExactLocaleGlobal } from "./locale";
79
+ import {
80
+ setImmediate as exactSetImmediate,
81
+ clearImmediate as exactClearImmediate,
82
+ } from "./timers";
83
+ import { Buffer as ExactBuffer } from "./node/Buffer";
84
+ import { window as exactWindow, MediaQueryList, MediaQueryListEvent } from "./window";
85
+ import { process as exactProcess } from "./node/process";
86
+
87
+ function createGlobalBufferConstructor(BufferCtor: typeof ExactBuffer): typeof ExactBuffer {
88
+ function Buffer(value?: unknown, encodingOrOffset?: unknown, length?: number): unknown {
89
+ if (typeof value === 'number') {
90
+ if (arguments.length > 1) {
91
+ const err = new TypeError(
92
+ `The "string" argument must be of type string. Received type number (${value})`,
93
+ ) as TypeError & { code?: string };
94
+ err.code = 'ERR_INVALID_ARG_TYPE';
95
+ throw err;
96
+ }
97
+ return BufferCtor.allocUnsafe(value);
98
+ }
99
+ return (BufferCtor as any).from(value, encodingOrOffset, length);
100
+ }
101
+
102
+ Object.setPrototypeOf(Buffer, BufferCtor);
103
+ Object.defineProperty(Buffer, 'prototype', {
104
+ value: BufferCtor.prototype,
105
+ writable: false,
106
+ configurable: false,
107
+ });
108
+ return Buffer as unknown as typeof ExactBuffer;
109
+ }
110
+
111
+ /**
112
+ * Define a lazy global property that self-replaces with the real value on first access.
113
+ * This avoids eagerly assigning heavy objects to globalThis during bootstrap,
114
+ * reducing GC registration and startup cost for unused APIs.
115
+ */
116
+ function defineLazyGlobal(g: any, name: string, factory: () => any): void {
117
+ // Skip if already defined (e.g. by native layer)
118
+ if (typeof g[name] !== 'undefined') return;
119
+
120
+ Object.defineProperty(g, name, {
121
+ configurable: true,
122
+ enumerable: true,
123
+ get() {
124
+ const value = factory();
125
+ // Replace getter with direct value property for subsequent accesses
126
+ Object.defineProperty(g, name, {
127
+ value,
128
+ writable: true,
129
+ configurable: true,
130
+ enumerable: true,
131
+ });
132
+ return value;
133
+ },
134
+ });
135
+ }
136
+
137
+ function preserveConstructorName<T>(value: T, expectedName: string): T {
138
+ if (typeof value === 'function' && (value as Function).name !== expectedName) {
139
+ try {
140
+ Object.defineProperty(value, 'name', {
141
+ value: expectedName,
142
+ writable: false,
143
+ configurable: true,
144
+ enumerable: false,
145
+ });
146
+ } catch (_) {}
147
+ }
148
+ return value;
149
+ }
150
+
151
+ // Track whether fetch has been lazily initialized
152
+ let _fetchInitialized = false;
153
+
154
+ // Track whether filesystem module has been lazily initialized
155
+ let _fsModuleInitialized = false;
156
+
157
+ const WEB_STORAGE_QUOTA_BYTES = 10 * 1024 * 1024;
158
+
159
+ function trimTrailingSlash(path: string): string {
160
+ return path.replace(/\/+$/, '') || '/';
161
+ }
162
+
163
+ function getNativeStorageRoot(g: any): string {
164
+ const androidFilesDir = g.__exactAndroidStoragePaths?.filesDir;
165
+ if (typeof androidFilesDir === 'string' && androidFilesDir.length > 0) {
166
+ return trimTrailingSlash(androidFilesDir);
167
+ }
168
+
169
+ const env = g.process?.env;
170
+ const envFilesDir = env?.EXACT_ANDROID_FILES_DIR ?? env?.HOME;
171
+ if (typeof envFilesDir === 'string' && envFilesDir.length > 0) {
172
+ return trimTrailingSlash(envFilesDir);
173
+ }
174
+
175
+ return '/tmp';
176
+ }
177
+
178
+ function ensureStorageDirectory(g: any, directory: string): void {
179
+ try {
180
+ if (typeof g.__exactMkdir !== 'function' && typeof g.__exactEnsureFs === 'function') {
181
+ g.__exactEnsureFs();
182
+ }
183
+ if (typeof g.__exactMkdir === 'function') {
184
+ g.__exactMkdir(directory, true);
185
+ }
186
+ } catch (_) {}
187
+ }
188
+
189
+ function installSQLiteStorageModule(g: any): boolean {
190
+ if (
191
+ typeof g.__exactSqliteOpen !== 'function' &&
192
+ typeof g.__exactEnsureSqlite !== 'function'
193
+ ) {
194
+ return false;
195
+ }
196
+
197
+ let db: Database | null = null;
198
+
199
+ const ensureDb = (): Database => {
200
+ if (db) return db;
201
+ if (typeof g.__exactSqliteOpen !== 'function' && typeof g.__exactEnsureSqlite === 'function') {
202
+ g.__exactEnsureSqlite();
203
+ }
204
+ if (typeof g.__exactSqliteOpen !== 'function') {
205
+ throw new Error('SQLite native bridge is not available for Web Storage');
206
+ }
207
+
208
+ // @ref LLP 0008#android-backend-matrix — Android localStorage persists under app filesDir.
209
+ const directory = `${getNativeStorageRoot(g)}/.ibex`;
210
+ ensureStorageDirectory(g, directory);
211
+ db = new Database(`${directory}/web-storage.sqlite`, {
212
+ create: true,
213
+ readwrite: true,
214
+ });
215
+ db.run(
216
+ 'CREATE TABLE IF NOT EXISTS exact_web_storage (' +
217
+ 'storage_key TEXT PRIMARY KEY NOT NULL, ' +
218
+ 'storage_value TEXT NOT NULL' +
219
+ ')',
220
+ );
221
+ return db;
222
+ };
223
+
224
+ const storageUsage = (): number => {
225
+ const row = ensureDb()
226
+ .query<{ usage: number | bigint }>('SELECT COALESCE(SUM(length(storage_key) + length(storage_value)), 0) AS usage FROM exact_web_storage')
227
+ .get();
228
+ return Number(row?.usage ?? 0);
229
+ };
230
+
231
+ const quotaExceeded = (): never => {
232
+ throw new DOMException('Storage quota exceeded', 'QuotaExceededError');
233
+ };
234
+
235
+ setNativeStorageModule({
236
+ getItem(key: string): string | null {
237
+ const row = ensureDb()
238
+ .query<{ storage_value: string }, [string]>('SELECT storage_value FROM exact_web_storage WHERE storage_key = ?')
239
+ .get(String(key));
240
+ return typeof row?.storage_value === 'string' ? row.storage_value : null;
241
+ },
242
+ setItem(key: string, value: string): void {
243
+ const keyStr = String(key);
244
+ const valueStr = String(value);
245
+ const oldValue = this.getItem(keyStr);
246
+ const oldSize = oldValue === null ? 0 : keyStr.length + oldValue.length;
247
+ const nextSize = storageUsage() - oldSize + keyStr.length + valueStr.length;
248
+ if (nextSize > WEB_STORAGE_QUOTA_BYTES) {
249
+ quotaExceeded();
250
+ }
251
+ ensureDb().run(
252
+ 'INSERT INTO exact_web_storage(storage_key, storage_value) VALUES (?, ?) ' +
253
+ 'ON CONFLICT(storage_key) DO UPDATE SET storage_value = excluded.storage_value',
254
+ keyStr,
255
+ valueStr,
256
+ );
257
+ },
258
+ removeItem(key: string): void {
259
+ ensureDb().run('DELETE FROM exact_web_storage WHERE storage_key = ?', String(key));
260
+ },
261
+ clear(): void {
262
+ ensureDb().run('DELETE FROM exact_web_storage');
263
+ },
264
+ key(index: number): string | null {
265
+ const offset = Math.trunc(Number(index));
266
+ if (!Number.isFinite(offset) || offset < 0) return null;
267
+ const row = ensureDb()
268
+ .query<{ storage_key: string }, [number]>('SELECT storage_key FROM exact_web_storage ORDER BY rowid LIMIT 1 OFFSET ?')
269
+ .get(offset);
270
+ return typeof row?.storage_key === 'string' ? row.storage_key : null;
271
+ },
272
+ length(): number {
273
+ const row = ensureDb()
274
+ .query<{ count: number | bigint }>('SELECT COUNT(*) AS count FROM exact_web_storage')
275
+ .get();
276
+ return Number(row?.count ?? 0);
277
+ },
278
+ estimate() {
279
+ return {
280
+ usage: storageUsage(),
281
+ quota: WEB_STORAGE_QUOTA_BYTES,
282
+ };
283
+ },
284
+ persist() {
285
+ return true;
286
+ },
287
+ persisted() {
288
+ return true;
289
+ },
290
+ });
291
+
292
+ return true;
293
+ }
294
+
295
+ /**
296
+ * Ensure fetch globals are initialized (called lazily on first access)
297
+ */
298
+ function ensureFetchInitialized(): void {
299
+ if (_fetchInitialized) return;
300
+ _fetchInitialized = true;
301
+ const g = globalThis as any;
302
+ if (typeof g.__nativeFetch === 'function') {
303
+ initializeNativeFetch();
304
+ installFetchGlobals(g);
305
+ }
306
+ }
307
+
308
+ // Track whether crypto has been lazily initialized
309
+ let _cryptoInitialized = false;
310
+
311
+ /**
312
+ * Get the crypto object, initializing native crypto module if needed.
313
+ * Called lazily on first access of globalThis.crypto.
314
+ */
315
+ function ensureCryptoInitialized(): any {
316
+ if (_cryptoInitialized) return exactCrypto;
317
+ _cryptoInitialized = true;
318
+ return exactCrypto;
319
+ }
320
+
321
+ /**
322
+ * Install runtime globals with lazy loading for non-essential APIs.
323
+ *
324
+ * Essential (immediate): process, __exactRuntimeLoaded, capabilities, fs bridge
325
+ * Lazy (on first access): crypto, streams, fetch, inspect
326
+ */
327
+ export function installGlobals(): void {
328
+ const g = globalThis as any;
329
+ if (g.__exactLoadTimings) g.__exactLoadTimings.installGlobalsStart = Date.now();
330
+
331
+ if (typeof g.__exactHostNavigator === 'undefined' && typeof g.navigator === 'object' && g.navigator) {
332
+ g.__exactHostNavigator = g.navigator;
333
+ }
334
+
335
+ installExactLocaleGlobal();
336
+ installExactAccessibilityGlobal();
337
+
338
+ // ========================================
339
+ // ESSENTIAL: Runtime marker
340
+ // ========================================
341
+ g.__exactRuntimeLoaded = true;
342
+
343
+ // ========================================
344
+ // ESSENTIAL: process object (Node.js compat)
345
+ // Install the full Process class with real event emitter support.
346
+ // Preserve key env vars from the host runtime's process (e.g. NODE_ENV=test in Bun).
347
+ // ========================================
348
+ const _prevEnv: Record<string, string | undefined> = {};
349
+ const _oldProcess = g.process;
350
+ if (
351
+ g.__exactRuntimeContext !== 'shell' &&
352
+ g.process?.env &&
353
+ typeof g.process.env === 'object'
354
+ ) {
355
+ const hostEnv = g.process.env;
356
+ for (const key of ['NODE_ENV', 'EXACT_ALLOW_INSECURE_CRYPTO', 'CI', 'TEST']) {
357
+ const val = hostEnv[key];
358
+ if (typeof val === 'string') _prevEnv[key] = val;
359
+ }
360
+ }
361
+ g.process = exactProcess;
362
+ // Copy over properties from the old (native-patched) process object that
363
+ // the new Process class doesn't have (e.g. emitWarning, features, etc.)
364
+ if (_oldProcess && typeof _oldProcess === 'object') {
365
+ const skip = new Set(['env', 'version', 'versions', 'platform', 'arch',
366
+ 'pid', 'ppid', 'argv', 'argv0', 'execPath', 'execArgv', 'title',
367
+ 'stdin', 'stdout', 'stderr', 'config', 'release', 'browser',
368
+ 'on', 'off', 'once', 'emit', 'removeListener', 'removeAllListeners',
369
+ 'listeners', 'listenerCount', 'addListener', '_events']);
370
+ for (const key of Object.getOwnPropertyNames(_oldProcess)) {
371
+ if (!skip.has(key) && (exactProcess as any)[key] === undefined) {
372
+ try {
373
+ (exactProcess as any)[key] = (_oldProcess as any)[key];
374
+ } catch (_e) { /* skip read-only */ }
375
+ }
376
+ }
377
+ // Also check the prototype chain for methods added via addEventEmitter
378
+ const proto = Object.getPrototypeOf(_oldProcess);
379
+ if (proto && proto !== Object.prototype) {
380
+ for (const key of Object.getOwnPropertyNames(proto)) {
381
+ if (!skip.has(key) && (exactProcess as any)[key] === undefined) {
382
+ try {
383
+ (exactProcess as any)[key] = proto[key];
384
+ } catch (_e) { /* skip */ }
385
+ }
386
+ }
387
+ }
388
+ }
389
+ // Re-apply host env vars so existing env checks (e.g. NODE_ENV === 'test') still work
390
+ for (const [key, val] of Object.entries(_prevEnv)) {
391
+ if (exactProcess.env[key] === undefined || exactProcess.env[key] === 'development') {
392
+ exactProcess.env[key] = val;
393
+ }
394
+ }
395
+
396
+ // ========================================
397
+ // ESSENTIAL: Buffer (Node.js compat)
398
+ // Shared runtime bootstrap must install this explicitly because the legacy
399
+ // bootstrap-globals path is skipped once the runtime bundle loads.
400
+ // ========================================
401
+ if (typeof g.Buffer === 'undefined') {
402
+ Object.defineProperty(g, 'Buffer', {
403
+ value: createGlobalBufferConstructor(ExactBuffer),
404
+ writable: true,
405
+ configurable: true,
406
+ enumerable: true,
407
+ });
408
+ }
409
+
410
+ Object.defineProperty(g, 'setImmediate', {
411
+ value: exactSetImmediate,
412
+ writable: true,
413
+ configurable: true,
414
+ enumerable: true,
415
+ });
416
+
417
+ Object.defineProperty(g, 'clearImmediate', {
418
+ value: exactClearImmediate,
419
+ writable: true,
420
+ configurable: true,
421
+ enumerable: true,
422
+ });
423
+
424
+ const exactFormatBytes = (value: unknown): string => {
425
+ let bytes = typeof value === 'number' ? value : Number(value);
426
+ if (!Number.isFinite(bytes) || bytes <= 0) {
427
+ return '0B';
428
+ }
429
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
430
+ let unitIndex = 0;
431
+ while (bytes >= 1024 && unitIndex < units.length - 1) {
432
+ bytes /= 1024;
433
+ unitIndex += 1;
434
+ }
435
+ const formatted = bytes >= 100 || unitIndex === 0
436
+ ? bytes.toFixed(0)
437
+ : bytes.toFixed(1).replace(/\.0$/, '');
438
+ return `${formatted}${units[unitIndex]}`;
439
+ };
440
+
441
+ const exactEstimateStringBytes = (value: unknown): number => {
442
+ if (typeof value !== 'string' || value.length === 0) {
443
+ return 0;
444
+ }
445
+ if (typeof TextEncoder === 'function') {
446
+ try {
447
+ return new TextEncoder().encode(value).length;
448
+ } catch {
449
+ return value.length * 2;
450
+ }
451
+ }
452
+ return value.length * 2;
453
+ };
454
+
455
+ const exactNormalizeMemoryOptions = (input: unknown) => {
456
+ if (input && typeof input === 'object') {
457
+ const candidate = input as {
458
+ includeExpensive?: unknown;
459
+ includeGCStats?: unknown;
460
+ };
461
+ return {
462
+ includeExpensive: candidate.includeExpensive === true,
463
+ includeGCStats: candidate.includeGCStats === true,
464
+ };
465
+ }
466
+ return {
467
+ includeExpensive: input === true,
468
+ includeGCStats: false,
469
+ };
470
+ };
471
+
472
+ const exactGetDebugModuleSourcesSummary = () => {
473
+ const entries = Array.isArray(g.__exactDebugModuleSources)
474
+ ? g.__exactDebugModuleSources
475
+ : [];
476
+ let totalBytes = 0;
477
+ for (const entry of entries) {
478
+ if (entry && typeof entry.source === 'string') {
479
+ totalBytes += exactEstimateStringBytes(entry.source);
480
+ }
481
+ }
482
+ const configuredLimit = Number(g.__exactDebugModuleSourceLimit);
483
+ return {
484
+ count: entries.length,
485
+ totalBytes,
486
+ currentSourceBytes: exactEstimateStringBytes(
487
+ typeof g.__exactDebugModuleSource === 'string'
488
+ ? g.__exactDebugModuleSource
489
+ : '',
490
+ ),
491
+ limit:
492
+ Number.isFinite(configuredLimit) && configuredLimit > 0
493
+ ? Math.floor(configuredLimit)
494
+ : 256,
495
+ };
496
+ };
497
+
498
+ const exactGetPerformanceTimelineSummary = () => {
499
+ const performanceObject =
500
+ typeof g.performance === 'object' && g.performance !== null
501
+ ? g.performance as {
502
+ _entries?: unknown;
503
+ _marks?: { size?: unknown } | null;
504
+ _measures?: { size?: unknown } | null;
505
+ }
506
+ : null;
507
+ if (!performanceObject) {
508
+ return {
509
+ entries: 0,
510
+ markNames: 0,
511
+ measureNames: 0,
512
+ };
513
+ }
514
+ const entries = Array.isArray(performanceObject._entries)
515
+ ? performanceObject._entries.length
516
+ : 0;
517
+ const markNames =
518
+ typeof performanceObject._marks?.size === 'number'
519
+ ? performanceObject._marks.size
520
+ : 0;
521
+ const measureNames =
522
+ typeof performanceObject._measures?.size === 'number'
523
+ ? performanceObject._measures.size
524
+ : 0;
525
+ return {
526
+ entries,
527
+ markNames,
528
+ measureNames,
529
+ };
530
+ };
531
+
532
+ const exactMemorySnapshot = (input: unknown) => {
533
+ const options = exactNormalizeMemoryOptions(input);
534
+ let usage: unknown = null;
535
+ if (typeof exactProcess.memoryUsage === 'function') {
536
+ try {
537
+ usage = exactProcess.memoryUsage();
538
+ } catch {
539
+ usage = null;
540
+ }
541
+ }
542
+
543
+ let heapInfo: unknown = null;
544
+ if (typeof g.__exactGetHeapInfo === 'function') {
545
+ try {
546
+ heapInfo = g.__exactGetHeapInfo(options.includeExpensive);
547
+ } catch {
548
+ heapInfo = null;
549
+ }
550
+ }
551
+
552
+ let gcStats: unknown = null;
553
+ if (options.includeGCStats && typeof g.__exactGetGCStats === 'function') {
554
+ try {
555
+ const rawGcStats = g.__exactGetGCStats();
556
+ gcStats = typeof rawGcStats === 'string' ? JSON.parse(rawGcStats) : null;
557
+ } catch {
558
+ gcStats = null;
559
+ }
560
+ }
561
+
562
+ let sourceCache: unknown = null;
563
+ if (typeof g.__exactGetSourceCacheStats === 'function') {
564
+ try {
565
+ sourceCache = g.__exactGetSourceCacheStats();
566
+ } catch {
567
+ sourceCache = null;
568
+ }
569
+ }
570
+
571
+ const loader = typeof g.__exactRequire === 'function' ? g.__exactRequire : g.require;
572
+ const requireCacheCount =
573
+ loader && loader.cache && typeof loader.cache === 'object'
574
+ ? Object.keys(loader.cache).length
575
+ : 0;
576
+
577
+ return {
578
+ timestamp: Date.now(),
579
+ usage,
580
+ heapInfo,
581
+ gcStats,
582
+ sourceCache,
583
+ performanceTimeline: exactGetPerformanceTimelineSummary(),
584
+ requireCacheCount,
585
+ debugModuleSources: exactGetDebugModuleSourcesSummary(),
586
+ };
587
+ };
588
+
589
+ const exactMemorySummary = (snapshotInput: unknown) => {
590
+ const snapshot =
591
+ snapshotInput && typeof snapshotInput === 'object'
592
+ ? snapshotInput as Record<string, any>
593
+ : exactMemorySnapshot(snapshotInput);
594
+ const usage = snapshot.usage && typeof snapshot.usage === 'object'
595
+ ? snapshot.usage
596
+ : {};
597
+ const heapInfo = snapshot.heapInfo && typeof snapshot.heapInfo === 'object'
598
+ ? snapshot.heapInfo
599
+ : {};
600
+ const sourceCache = snapshot.sourceCache && typeof snapshot.sourceCache === 'object'
601
+ ? snapshot.sourceCache
602
+ : {};
603
+ const performanceTimeline =
604
+ snapshot.performanceTimeline && typeof snapshot.performanceTimeline === 'object'
605
+ ? snapshot.performanceTimeline
606
+ : {};
607
+ const debugModuleSources =
608
+ snapshot.debugModuleSources && typeof snapshot.debugModuleSources === 'object'
609
+ ? snapshot.debugModuleSources
610
+ : {};
611
+ const heapUsed =
612
+ typeof usage.heapUsed === 'number'
613
+ ? usage.heapUsed
614
+ : typeof heapInfo.hermes_allocatedBytes === 'number'
615
+ ? heapInfo.hermes_allocatedBytes
616
+ : 0;
617
+ const heapTotal =
618
+ typeof usage.heapTotal === 'number'
619
+ ? usage.heapTotal
620
+ : typeof heapInfo.hermes_heapSize === 'number'
621
+ ? heapInfo.hermes_heapSize
622
+ : 0;
623
+ return {
624
+ timestamp: typeof snapshot.timestamp === 'number' ? snapshot.timestamp : Date.now(),
625
+ heapUsed,
626
+ heapTotal,
627
+ rss: typeof usage.rss === 'number' ? usage.rss : 0,
628
+ external:
629
+ typeof usage.external === 'number'
630
+ ? usage.external
631
+ : typeof heapInfo.hermes_externalBytes === 'number'
632
+ ? heapInfo.hermes_externalBytes
633
+ : 0,
634
+ requireCacheCount:
635
+ typeof snapshot.requireCacheCount === 'number' ? snapshot.requireCacheCount : 0,
636
+ performanceEntriesCount:
637
+ typeof performanceTimeline.entries === 'number' ? performanceTimeline.entries : 0,
638
+ debugModuleSourcesCount:
639
+ typeof debugModuleSources.count === 'number' ? debugModuleSources.count : 0,
640
+ debugModuleSourcesBytes:
641
+ typeof debugModuleSources.totalBytes === 'number' ? debugModuleSources.totalBytes : 0,
642
+ sourceCacheCount:
643
+ typeof sourceCache.count === 'number' ? sourceCache.count : 0,
644
+ sourceCacheBytes:
645
+ typeof sourceCache.totalBytes === 'number' ? sourceCache.totalBytes : 0,
646
+ numCollections:
647
+ typeof heapInfo.hermes_numCollections === 'number'
648
+ ? heapInfo.hermes_numCollections
649
+ : typeof heapInfo.numCollections === 'number'
650
+ ? heapInfo.numCollections
651
+ : 0,
652
+ };
653
+ };
654
+
655
+ const exactLogMemorySample = (snapshot: unknown) => {
656
+ const summary = exactMemorySummary(snapshot);
657
+ console.info(
658
+ '[exact:memory]',
659
+ `heap=${exactFormatBytes(summary.heapUsed)}/${exactFormatBytes(summary.heapTotal)}`,
660
+ `rss=${exactFormatBytes(summary.rss)}`,
661
+ `external=${exactFormatBytes(summary.external)}`,
662
+ `require=${summary.requireCacheCount}`,
663
+ `perf=${summary.performanceEntriesCount}`,
664
+ `debugSources=${summary.debugModuleSourcesCount}/${exactFormatBytes(summary.debugModuleSourcesBytes)}`,
665
+ `sourceCache=${summary.sourceCacheCount}/${exactFormatBytes(summary.sourceCacheBytes)}`,
666
+ `gc=${summary.numCollections}`,
667
+ );
668
+ };
669
+
670
+ if (!g.__exactMemoryDebugState || typeof g.__exactMemoryDebugState !== 'object') {
671
+ g.__exactMemoryDebugState = {
672
+ timer: null,
673
+ samples: [],
674
+ nextSampleId: 0,
675
+ options: null,
676
+ sampleCount: 0,
677
+ lastLoggedHeapUsed: 0,
678
+ };
679
+ }
680
+
681
+ const exactMemoryDebugState = g.__exactMemoryDebugState;
682
+ const exactStopMemorySampler = () => {
683
+ if (exactMemoryDebugState.timer != null) {
684
+ clearInterval(exactMemoryDebugState.timer);
685
+ exactMemoryDebugState.timer = null;
686
+ }
687
+ return exactMemoryDebugState.samples.slice();
688
+ };
689
+
690
+ g.__exactMemoryDebug = {
691
+ snapshot: (options?: unknown) => exactMemorySnapshot(options),
692
+ summary: (options?: unknown) => exactMemorySummary(exactMemorySnapshot(options)),
693
+ samples: () => exactMemoryDebugState.samples.slice(),
694
+ state: () => ({
695
+ running: exactMemoryDebugState.timer != null,
696
+ options: exactMemoryDebugState.options,
697
+ sampleCount: exactMemoryDebugState.sampleCount,
698
+ retainedSamples: exactMemoryDebugState.samples.length,
699
+ }),
700
+ clearModuleDebugSources: () => {
701
+ if (Array.isArray(g.__exactDebugModuleSources)) {
702
+ g.__exactDebugModuleSources.length = 0;
703
+ }
704
+ g.__exactDebugModuleSource = undefined;
705
+ return exactGetDebugModuleSourcesSummary();
706
+ },
707
+ stop: () => exactStopMemorySampler(),
708
+ start: (options?: {
709
+ intervalMs?: number;
710
+ maxSamples?: number;
711
+ logEvery?: number;
712
+ logOnGrowthBytes?: number;
713
+ includeExpensive?: boolean;
714
+ includeGCStats?: boolean;
715
+ }) => {
716
+ exactStopMemorySampler();
717
+ const normalizedOptions = {
718
+ intervalMs: Math.max(200, Number(options?.intervalMs) || 1000),
719
+ maxSamples: Math.max(10, Number(options?.maxSamples) || 240),
720
+ logEvery: Math.max(0, Math.floor(Number(options?.logEvery) || 0)),
721
+ logOnGrowthBytes: Math.max(
722
+ 0,
723
+ Math.floor(Number(options?.logOnGrowthBytes) || (32 * 1024 * 1024)),
724
+ ),
725
+ includeExpensive: options?.includeExpensive === true,
726
+ includeGCStats: options?.includeGCStats === true,
727
+ };
728
+ exactMemoryDebugState.samples = [];
729
+ exactMemoryDebugState.sampleCount = 0;
730
+ exactMemoryDebugState.lastLoggedHeapUsed = 0;
731
+ exactMemoryDebugState.options = normalizedOptions;
732
+ const takeSample = () => {
733
+ const sample = exactMemorySnapshot(normalizedOptions);
734
+ exactMemoryDebugState.nextSampleId += 1;
735
+ sample.sampleId = exactMemoryDebugState.nextSampleId;
736
+ exactMemoryDebugState.samples.push(sample);
737
+ if (exactMemoryDebugState.samples.length > normalizedOptions.maxSamples) {
738
+ exactMemoryDebugState.samples.splice(
739
+ 0,
740
+ exactMemoryDebugState.samples.length - normalizedOptions.maxSamples,
741
+ );
742
+ }
743
+ exactMemoryDebugState.sampleCount += 1;
744
+ const summary = exactMemorySummary(sample);
745
+ const shouldLog =
746
+ exactMemoryDebugState.sampleCount === 1 ||
747
+ (normalizedOptions.logEvery > 0 &&
748
+ exactMemoryDebugState.sampleCount % normalizedOptions.logEvery === 0) ||
749
+ (normalizedOptions.logOnGrowthBytes > 0 &&
750
+ summary.heapUsed - exactMemoryDebugState.lastLoggedHeapUsed >=
751
+ normalizedOptions.logOnGrowthBytes);
752
+ if (shouldLog) {
753
+ exactMemoryDebugState.lastLoggedHeapUsed = summary.heapUsed;
754
+ exactLogMemorySample(sample);
755
+ }
756
+ return sample;
757
+ };
758
+ const first = takeSample();
759
+ exactMemoryDebugState.timer = setInterval(takeSample, normalizedOptions.intervalMs);
760
+ if (typeof exactMemoryDebugState.timer?.unref === 'function') {
761
+ exactMemoryDebugState.timer.unref();
762
+ }
763
+ return first;
764
+ },
765
+ formatBytes: exactFormatBytes,
766
+ };
767
+
768
+ // ========================================
769
+ // ESSENTIAL: Capability security bridge
770
+ // ========================================
771
+ if (typeof g.__exactCapabilityCheck === 'function') {
772
+ setNativeCapabilityModule({
773
+ checkCapability: (capability: string, moduleId?: number) =>
774
+ !!g.__exactCapabilityCheck(capability, moduleId),
775
+ requestCapability: async (capability: string) =>
776
+ !!g.__exactCapabilityCheck(capability),
777
+ getGrantedCapabilities: () => [],
778
+ });
779
+ enableStrictMode();
780
+ }
781
+
782
+ // ========================================
783
+ // ESSENTIAL: Native crypto bridge registration
784
+ // (registers the native module but doesn't create the global crypto object)
785
+ // ========================================
786
+ if (typeof g.__exactRandomBytes === 'function') {
787
+ setNativeCryptoModule({
788
+ getRandomValues: (array: Uint8Array) => {
789
+ const bytes: Uint8Array = g.__exactRandomBytes(array.byteLength);
790
+ array.set(bytes);
791
+ return array;
792
+ },
793
+ randomUUID: () => {
794
+ const bytes: Uint8Array = g.__exactRandomBytes(16);
795
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
796
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
797
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0'));
798
+ return (
799
+ hex.slice(0, 4).join('') + '-' +
800
+ hex.slice(4, 6).join('') + '-' +
801
+ hex.slice(6, 8).join('') + '-' +
802
+ hex.slice(8, 10).join('') + '-' +
803
+ hex.slice(10).join('')
804
+ );
805
+ },
806
+ sha256: async () => {
807
+ throw new DOMException('sha256 not available', 'NotSupportedError');
808
+ },
809
+ sha384: async () => {
810
+ throw new DOMException('sha384 not available', 'NotSupportedError');
811
+ },
812
+ sha512: async () => {
813
+ throw new DOMException('sha512 not available', 'NotSupportedError');
814
+ },
815
+ sha1: async () => {
816
+ throw new DOMException('sha1 not available', 'NotSupportedError');
817
+ },
818
+ digest: typeof g.__exactCryptoSubtleDigest === 'function'
819
+ ? async (hash: string, data: Uint8Array) => Promise.resolve(g.__exactCryptoSubtleDigest(hash, data))
820
+ : undefined,
821
+ hmacSign: typeof g.__exactCryptoSubtleSign === 'function'
822
+ ? async (hash: string, key: Uint8Array, data: Uint8Array) => Promise.resolve(
823
+ g.__exactCryptoSubtleSign(hash, key, data)
824
+ )
825
+ : undefined,
826
+ hmacVerify: typeof g.__exactCryptoSubtleVerify === 'function'
827
+ ? async (
828
+ hash: string,
829
+ key: Uint8Array,
830
+ signature: Uint8Array,
831
+ data: Uint8Array
832
+ ) => Promise.resolve(g.__exactCryptoSubtleVerify(hash, key, signature, data))
833
+ : undefined,
834
+ });
835
+ }
836
+
837
+ // ========================================
838
+ // LAZY: crypto global (deferred until first access)
839
+ // ========================================
840
+ if (!g.crypto) {
841
+ Object.defineProperty(g, 'crypto', {
842
+ configurable: true,
843
+ enumerable: true,
844
+ get() {
845
+ const value = ensureCryptoInitialized();
846
+ Object.defineProperty(g, 'crypto', {
847
+ value,
848
+ writable: true,
849
+ configurable: true,
850
+ enumerable: true,
851
+ });
852
+ return value;
853
+ },
854
+ });
855
+ } else {
856
+ // crypto object may already exist (e.g., from native/bootstrap layer).
857
+ // Replace subtle with our full SubtleCrypto implementation as a read-only
858
+ // getter property. In browsers/Bun, crypto.subtle is a getter-only property —
859
+ // assigning to it is silently ignored. This prevents test code from
860
+ // accidentally clobbering subtle (e.g. `globalThis.crypto.subtle = 123`).
861
+ const fullSubtle = exactCrypto.subtle;
862
+ const subtleDescriptor = Object.getOwnPropertyDescriptor(g.crypto, 'subtle');
863
+
864
+ // If subtle already exists and is non-configurable (common in Bun), we
865
+ // should not redefine it; simply leave the existing descriptor in place.
866
+ if (!subtleDescriptor || subtleDescriptor.configurable) {
867
+ Object.defineProperty(g.crypto, 'subtle', {
868
+ get() { return fullSubtle; },
869
+ set() { /* no-op, matching browser behavior */ },
870
+ enumerable: true,
871
+ configurable: true,
872
+ });
873
+ }
874
+ }
875
+
876
+ // Mark that the full SubtleCrypto is installed so the bootstrap shim
877
+ // (web-crypto.js) doesn't overwrite it when loaded later via
878
+ // __exactEnsureWebCrypto() triggered by require("node:crypto").
879
+ if (g.crypto && g.crypto.subtle) {
880
+ try {
881
+ Object.defineProperty(g.crypto.subtle, '__exactFullSubtle', {
882
+ value: true,
883
+ writable: false,
884
+ enumerable: false,
885
+ configurable: false,
886
+ });
887
+ } catch (_) {}
888
+ }
889
+
890
+ if (typeof g.Crypto === 'undefined') {
891
+ Object.defineProperty(g, 'Crypto', {
892
+ value: Crypto,
893
+ writable: true,
894
+ configurable: true,
895
+ enumerable: true,
896
+ });
897
+ }
898
+
899
+ if (typeof g.SubtleCrypto === 'undefined') {
900
+ Object.defineProperty(g, 'SubtleCrypto', {
901
+ value: SubtleCrypto,
902
+ writable: true,
903
+ configurable: true,
904
+ enumerable: true,
905
+ });
906
+ }
907
+
908
+ if (typeof g.CryptoKey === 'undefined') {
909
+ Object.defineProperty(g, 'CryptoKey', {
910
+ value: CryptoKey,
911
+ writable: true,
912
+ configurable: true,
913
+ enumerable: true,
914
+ });
915
+ }
916
+
917
+ // ========================================
918
+ // LAZY: Filesystem bridge
919
+ // Deferred until first fs operation to avoid triggering macOS sandbox
920
+ // permission prompts at startup.
921
+ // ========================================
922
+ if (typeof g.__exactReadFile === 'function' || typeof g.__exactEnsureFs === 'function') {
923
+ g.__exactEnsureFilesystemModule = () => {
924
+ if (_fsModuleInitialized) return;
925
+ if (typeof g.__exactReadFile !== 'function' && typeof g.__exactEnsureFs === 'function') {
926
+ g.__exactEnsureFs();
927
+ }
928
+ if (typeof g.__exactReadFile !== 'function') {
929
+ return;
930
+ }
931
+ _fsModuleInitialized = true;
932
+
933
+ const toBytes = (data: Uint8Array): Uint8Array => {
934
+ if (data instanceof Uint8Array) return data;
935
+ return new Uint8Array(data);
936
+ };
937
+
938
+ setNativeFileSystemModule({
939
+ readFile: async (path: string) => g.__exactReadFile(path),
940
+ writeFile: async (path: string, data: Uint8Array) => g.__exactWriteFile(path, toBytes(data)),
941
+ appendFile: async (path: string, data: Uint8Array) => g.__exactAppendFile(path, toBytes(data)),
942
+ exists: async (path: string) => {
943
+ try { g.__exactAccess(path, 0); return true; } catch { return false; }
944
+ },
945
+ unlink: async (path: string) => g.__exactUnlink(path),
946
+ stat: async (path: string) => JSON.parse(g.__exactStat(path)),
947
+ lstat: async (path: string) => JSON.parse(g.__exactLstat(path)),
948
+ readdir: async (path: string) => JSON.parse(g.__exactReaddir(path)),
949
+ mkdir: async (path: string, options?: { recursive?: boolean }) =>
950
+ g.__exactMkdir(path, !!(options && options.recursive)),
951
+ rmdir: async (path: string) => g.__exactRmdir(path),
952
+ rename: async (oldPath: string, newPath: string) => g.__exactRename(oldPath, newPath),
953
+ copyFile: async (src: string, dest: string) => g.__exactCopyFile(src, dest),
954
+ access: async (path: string, mode?: number) => g.__exactAccess(path, mode ?? 0),
955
+ chmod: async (path: string, mode: number) => g.__exactChmod(path, mode),
956
+ realpath: async (path: string) => g.__exactRealpath(path),
957
+ mkdtemp: async (prefix: string) => g.__exactMkdtemp(prefix),
958
+ getDocumentsDir: () => '/',
959
+ getCacheDir: () => '/',
960
+ getTempDir: () => '/',
961
+ });
962
+ };
963
+ }
964
+
965
+ // ========================================
966
+ // Fetch API
967
+ // ========================================
968
+ // Headers, Request, Response are always available (they work without native fetch).
969
+ // The fetch() function itself requires the native bridge.
970
+ Object.defineProperty(g, 'Headers', {
971
+ value: FetchHeaders,
972
+ writable: true,
973
+ configurable: true,
974
+ enumerable: true,
975
+ });
976
+ Object.defineProperty(g, 'Request', {
977
+ value: FetchRequest,
978
+ writable: true,
979
+ configurable: true,
980
+ enumerable: true,
981
+ });
982
+ Object.defineProperty(g, 'Response', {
983
+ value: FetchResponse,
984
+ writable: true,
985
+ configurable: true,
986
+ enumerable: true,
987
+ });
988
+
989
+ if (typeof g.__nativeFetch === 'function') {
990
+ if (typeof g.fetch === 'undefined') {
991
+ // Install a lazy getter for 'fetch' that triggers full fetch initialization.
992
+ // We must delete the getter first to avoid infinite recursion, since
993
+ // installFetchGlobals will redefine g.fetch as a data property.
994
+ Object.defineProperty(g, 'fetch', {
995
+ configurable: true,
996
+ enumerable: true,
997
+ get() {
998
+ // Remove the getter before initializing to prevent recursion
999
+ delete g.fetch;
1000
+ ensureFetchInitialized();
1001
+ return g.fetch;
1002
+ },
1003
+ });
1004
+ }
1005
+ }
1006
+
1007
+ // ========================================
1008
+ // ESSENTIAL: WebSocket native bridge registration
1009
+ // Wire up C++ __exactWsConnect/__exactWsSend/__exactWsClose as the native WebSocket module.
1010
+ // The create() method passes the WebSocket instance so C++ callbacks can call
1011
+ // _handleOpen/_handleMessage/_handleClose/_handleError/_handleBytesSent on it.
1012
+ // ========================================
1013
+ if (typeof g.__exactWsConnect === 'function') {
1014
+ setNativeWebSocketModule({
1015
+ create(url: string, protocols?: string[], instance?: any): number {
1016
+ const protocolStr = (protocols || []).join(',');
1017
+ return g.__exactWsConnect(url, protocolStr, instance);
1018
+ },
1019
+ send(id: number, data: string | Uint8Array): void {
1020
+ g.__exactWsSend(id, data);
1021
+ },
1022
+ close(id: number, code?: number, reason?: string): void {
1023
+ g.__exactWsClose(id, code ?? 1005, reason ?? '');
1024
+ },
1025
+ pause(id: number): void {
1026
+ if (typeof g.__exactWsPause === 'function') {
1027
+ g.__exactWsPause(id);
1028
+ }
1029
+ },
1030
+ resume(id: number): void {
1031
+ if (typeof g.__exactWsResume === 'function') {
1032
+ g.__exactWsResume(id);
1033
+ }
1034
+ },
1035
+ setFlowControlled(id: number, enabled: boolean): void {
1036
+ if (typeof g.__exactWsSetFlowControlled === 'function') {
1037
+ g.__exactWsSetFlowControlled(id, !!enabled);
1038
+ }
1039
+ },
1040
+ });
1041
+ }
1042
+
1043
+ // ========================================
1044
+ // LAZY: Exact.inspect (pretty printing) & setModuleCapabilities
1045
+ // ========================================
1046
+ g.Exact = g.Exact || ({} as any);
1047
+ // Lazy-load inspect: only resolve when actually called
1048
+ Object.defineProperty(g.Exact, 'inspect', {
1049
+ configurable: true,
1050
+ enumerable: true,
1051
+ get() {
1052
+ Object.defineProperty(g.Exact, 'inspect', {
1053
+ value: inspect,
1054
+ writable: true,
1055
+ configurable: true,
1056
+ enumerable: true,
1057
+ });
1058
+ return inspect;
1059
+ },
1060
+ });
1061
+ g.Exact.setModuleCapabilities = function(moduleId: number, capabilities: string[]) {
1062
+ // Grant capabilities through native layer
1063
+ const grantFn = (g as any).__exactGrantCapability;
1064
+ if (typeof grantFn === 'function') {
1065
+ for (let i = 0; i < capabilities.length; i++) {
1066
+ grantFn(moduleId, capabilities[i]);
1067
+ }
1068
+ }
1069
+ };
1070
+
1071
+ // ========================================
1072
+ // LAZY: Web Streams API (deferred until first access)
1073
+ // ========================================
1074
+ defineLazyGlobal(g, 'ReadableStream', () => preserveConstructorName(ReadableStream, 'ReadableStream'));
1075
+ defineLazyGlobal(g, 'ReadableStreamDefaultReader', () => preserveConstructorName(ReadableStreamDefaultReader, 'ReadableStreamDefaultReader'));
1076
+ defineLazyGlobal(g, 'ReadableStreamDefaultController', () => preserveConstructorName(ReadableStreamDefaultController, 'ReadableStreamDefaultController'));
1077
+ defineLazyGlobal(g, 'ReadableByteStreamController', () => preserveConstructorName(ReadableByteStreamController, 'ReadableByteStreamController'));
1078
+ defineLazyGlobal(g, 'ReadableStreamBYOBReader', () => preserveConstructorName(ReadableStreamBYOBReader, 'ReadableStreamBYOBReader'));
1079
+ defineLazyGlobal(g, 'ReadableStreamBYOBRequest', () => preserveConstructorName(ReadableStreamBYOBRequest, 'ReadableStreamBYOBRequest'));
1080
+ defineLazyGlobal(g, 'WritableStream', () => preserveConstructorName(WritableStream, 'WritableStream'));
1081
+ defineLazyGlobal(g, 'WritableStreamDefaultWriter', () => preserveConstructorName(WritableStreamDefaultWriter, 'WritableStreamDefaultWriter'));
1082
+ defineLazyGlobal(g, 'WritableStreamDefaultController', () => preserveConstructorName(WritableStreamDefaultController, 'WritableStreamDefaultController'));
1083
+ defineLazyGlobal(g, 'TransformStream', () => preserveConstructorName(TransformStream, 'TransformStream'));
1084
+ defineLazyGlobal(g, 'TransformStreamDefaultController', () => preserveConstructorName(TransformStreamDefaultController, 'TransformStreamDefaultController'));
1085
+ defineLazyGlobal(g, 'ByteLengthQueuingStrategy', () => preserveConstructorName(ByteLengthQueuingStrategy, 'ByteLengthQueuingStrategy'));
1086
+ defineLazyGlobal(g, 'CountQueuingStrategy', () => preserveConstructorName(CountQueuingStrategy, 'CountQueuingStrategy'));
1087
+ if (typeof g.__exactIsReadableStream !== 'function') {
1088
+ g.__exactIsReadableStream = isReadableStream;
1089
+ }
1090
+
1091
+ // ========================================
1092
+ // LAZY: Blob, File, FormData
1093
+ // ========================================
1094
+ defineLazyGlobal(g, 'Blob', () => Blob);
1095
+ defineLazyGlobal(g, 'File', () => File);
1096
+ defineLazyGlobal(g, 'FormData', () => FormData);
1097
+
1098
+
1099
+ // ========================================
1100
+ // LAZY: Event, CustomEvent, EventTarget, DOMException
1101
+ // ========================================
1102
+ defineLazyGlobal(g, 'Event', () => Event);
1103
+ defineLazyGlobal(g, 'CustomEvent', () => CustomEvent);
1104
+ defineLazyGlobal(g, 'EventTarget', () => EventTarget);
1105
+ defineLazyGlobal(g, 'DOMException', () => DOMException);
1106
+ defineLazyGlobal(g, 'MessageEvent', () => MessageEvent);
1107
+ defineLazyGlobal(g, 'CloseEvent', () => {
1108
+ const globalEvent = g.Event || Event;
1109
+ if (globalEvent && Object.getPrototypeOf(CloseEvent) !== globalEvent) {
1110
+ Object.setPrototypeOf(CloseEvent, globalEvent);
1111
+ }
1112
+ if (
1113
+ globalEvent &&
1114
+ globalEvent.prototype &&
1115
+ Object.getPrototypeOf(CloseEvent.prototype) !== globalEvent.prototype
1116
+ ) {
1117
+ Object.setPrototypeOf(CloseEvent.prototype, globalEvent.prototype);
1118
+ }
1119
+ return CloseEvent;
1120
+ });
1121
+ defineLazyGlobal(g, 'ErrorEvent', () => ErrorEvent);
1122
+ defineLazyGlobal(g, 'ProgressEvent', () => ProgressEvent);
1123
+ defineLazyGlobal(g, 'KeyboardEvent', () => KeyboardEvent);
1124
+ defineLazyGlobal(g, 'FocusEvent', () => FocusEvent);
1125
+ defineLazyGlobal(g, 'PromiseRejectionEvent', () => PromiseRejectionEvent);
1126
+
1127
+ // ========================================
1128
+ // LAZY: AbortController, AbortSignal
1129
+ // ========================================
1130
+ defineLazyGlobal(g, 'AbortController', () => AbortController);
1131
+ defineLazyGlobal(g, 'AbortSignal', () => AbortSignal);
1132
+
1133
+ // ========================================
1134
+ // TextEncoder, TextDecoder
1135
+ // ========================================
1136
+ // Install TextEncoder/TextDecoder lazily — the full TextDecoder is ~4K
1137
+ // lines and initializing it eagerly adds measurable startup cost.
1138
+ defineLazyGlobal(g, 'TextEncoder', () => TextEncoder);
1139
+ defineLazyGlobal(g, 'TextDecoder', () => TextDecoder);
1140
+ defineLazyGlobal(g, 'TextEncoderStream', () => TextEncoderStream);
1141
+ defineLazyGlobal(g, 'TextDecoderStream', () => TextDecoderStream);
1142
+
1143
+ // ========================================
1144
+ // LAZY: URL, URLSearchParams
1145
+ // ========================================
1146
+ // Prefer the host's WHATWG URL implementation when it exists. Our TypeScript
1147
+ // fallback stays available for runtimes that do not provide URL globals.
1148
+ if (typeof g.URL === 'undefined') {
1149
+ Object.defineProperty(g, 'URL', {
1150
+ value: URL,
1151
+ writable: true,
1152
+ configurable: true,
1153
+ enumerable: true,
1154
+ });
1155
+ }
1156
+ if (typeof g.URLSearchParams === 'undefined') {
1157
+ Object.defineProperty(g, 'URLSearchParams', {
1158
+ value: URLSearchParams,
1159
+ writable: true,
1160
+ configurable: true,
1161
+ enumerable: true,
1162
+ });
1163
+ }
1164
+ if (typeof g.URL === 'function') {
1165
+ const urlCtor = g.URL as typeof URL;
1166
+ if (typeof (urlCtor as any).createObjectURL !== 'function') {
1167
+ Object.defineProperty(urlCtor, 'createObjectURL', {
1168
+ value: URL.createObjectURL,
1169
+ writable: true,
1170
+ configurable: true,
1171
+ enumerable: true,
1172
+ });
1173
+ }
1174
+ if (typeof (urlCtor as any).revokeObjectURL !== 'function') {
1175
+ Object.defineProperty(urlCtor, 'revokeObjectURL', {
1176
+ value: URL.revokeObjectURL,
1177
+ writable: true,
1178
+ configurable: true,
1179
+ enumerable: true,
1180
+ });
1181
+ }
1182
+ }
1183
+ defineLazyGlobal(g, 'URLPattern', () => URLPattern);
1184
+
1185
+ // ========================================
1186
+ // LAZY: MessageChannel, MessagePort, BroadcastChannel
1187
+ // ========================================
1188
+ defineLazyGlobal(g, 'MessageChannel', () => MessageChannel);
1189
+ defineLazyGlobal(g, 'MessagePort', () => MessagePort);
1190
+ defineLazyGlobal(g, 'VideoFrame', () => VideoFrame);
1191
+ defineLazyGlobal(g, 'BroadcastChannel', () => BroadcastChannel);
1192
+
1193
+ // ========================================
1194
+ // LAZY: WebSocket
1195
+ // ========================================
1196
+ defineLazyGlobal(g, 'WebSocket', () => {
1197
+ const globalEventTarget = g.EventTarget || EventTarget;
1198
+ if (globalEventTarget && Object.getPrototypeOf(WebSocket) !== globalEventTarget) {
1199
+ Object.setPrototypeOf(WebSocket, globalEventTarget);
1200
+ }
1201
+ if (
1202
+ globalEventTarget &&
1203
+ globalEventTarget.prototype &&
1204
+ Object.getPrototypeOf(WebSocket.prototype) !== globalEventTarget.prototype
1205
+ ) {
1206
+ Object.setPrototypeOf(WebSocket.prototype, globalEventTarget.prototype);
1207
+ }
1208
+ return WebSocket;
1209
+ });
1210
+ defineLazyGlobal(g, 'WebSocketError', () => WebSocketError);
1211
+ defineLazyGlobal(g, 'WebSocketStream', () => WebSocketStream);
1212
+
1213
+ // ========================================
1214
+ // LAZY: EventSource (Server-Sent Events)
1215
+ // ========================================
1216
+ defineLazyGlobal(g, 'EventSource', () => EventSource);
1217
+
1218
+ // ========================================
1219
+ // LAZY: FileReader
1220
+ // ========================================
1221
+ defineLazyGlobal(g, 'FileReader', () => FileReader);
1222
+
1223
+ // ========================================
1224
+ // navigator - always install ours (even if Node/native provides one)
1225
+ // Our navigator includes locks, storage, and other web-standard properties
1226
+ // that the host navigator may lack.
1227
+ // ========================================
1228
+ Object.defineProperty(g, 'navigator', {
1229
+ value: navigator,
1230
+ writable: true,
1231
+ configurable: true,
1232
+ enumerable: true,
1233
+ });
1234
+
1235
+ // ========================================
1236
+ // LAZY: localStorage, sessionStorage
1237
+ // ========================================
1238
+ const hasNativeStorage = installSQLiteStorageModule(g);
1239
+ if (hasNativeStorage) {
1240
+ Object.defineProperty(g, 'localStorage', {
1241
+ value: localStorage,
1242
+ writable: true,
1243
+ configurable: true,
1244
+ enumerable: true,
1245
+ });
1246
+ Object.defineProperty(g, 'sessionStorage', {
1247
+ value: sessionStorage,
1248
+ writable: true,
1249
+ configurable: true,
1250
+ enumerable: true,
1251
+ });
1252
+ } else {
1253
+ defineLazyGlobal(g, 'localStorage', () => localStorage);
1254
+ defineLazyGlobal(g, 'sessionStorage', () => sessionStorage);
1255
+ }
1256
+
1257
+ // ========================================
1258
+ // Scheduling bridge
1259
+ // ========================================
1260
+ if (typeof g.__exactRequestAnimationFrame === 'function') {
1261
+ setNativeSchedulingModule({
1262
+ requestAnimationFrame(callback: () => void): void {
1263
+ try {
1264
+ const scheduled = g.__exactRequestAnimationFrame(callback);
1265
+ if (scheduled !== false) {
1266
+ return;
1267
+ }
1268
+ } catch (_) {}
1269
+ setTimeout(callback, 16);
1270
+ },
1271
+ });
1272
+ }
1273
+
1274
+ // ========================================
1275
+ // performance, PerformanceObserver
1276
+ // Always override: the C++ bootstrap installs a stub with no-op mark/measure
1277
+ // that blocks the real implementation via defineLazyGlobal.
1278
+ // ========================================
1279
+ if (typeof g.__exactPerformanceNow === 'function') {
1280
+ try {
1281
+ const nativeTimeOrigin =
1282
+ typeof g.__exactPerformanceTimeOrigin === 'function'
1283
+ ? Number(g.__exactPerformanceTimeOrigin())
1284
+ : Date.now();
1285
+ const timeOrigin = Number.isFinite(nativeTimeOrigin) ? nativeTimeOrigin : Date.now();
1286
+ setNativePerformanceModule({
1287
+ now: () => {
1288
+ const value = Number(g.__exactPerformanceNow());
1289
+ return Number.isFinite(value) ? value : Date.now() - timeOrigin;
1290
+ },
1291
+ timeOrigin,
1292
+ });
1293
+ } catch (_) {}
1294
+ }
1295
+ Object.defineProperty(g, 'performance', {
1296
+ value: perfInstance,
1297
+ writable: true,
1298
+ configurable: true,
1299
+ enumerable: true,
1300
+ });
1301
+ defineLazyGlobal(g, 'PerformanceObserver', () => PerformanceObserver);
1302
+ defineLazyGlobal(g, 'PerformanceEntry', () => PerformanceEntry);
1303
+ defineLazyGlobal(g, 'PerformanceMark', () => PerformanceMark);
1304
+ defineLazyGlobal(g, 'PerformanceMeasure', () => PerformanceMeasure);
1305
+
1306
+ // ========================================
1307
+ // structuredClone
1308
+ // Always override: the C++ bootstrap polyfill may not handle all types
1309
+ // (e.g. Map, Set, TypedArrays) correctly.
1310
+ // ========================================
1311
+ Object.defineProperty(g, 'structuredClone', {
1312
+ value: structuredCloneFn,
1313
+ writable: true,
1314
+ configurable: true,
1315
+ enumerable: true,
1316
+ });
1317
+
1318
+ // ========================================
1319
+ // LAZY: atob, btoa
1320
+ // ========================================
1321
+ defineLazyGlobal(g, 'atob', () => atobFn);
1322
+ defineLazyGlobal(g, 'btoa', () => btoaFn);
1323
+
1324
+ // ========================================
1325
+ // LAZY: requestAnimationFrame, cancelAnimationFrame,
1326
+ // requestIdleCallback, cancelIdleCallback
1327
+ // ========================================
1328
+ defineLazyGlobal(g, 'requestAnimationFrame', () => rafFn);
1329
+ defineLazyGlobal(g, 'cancelAnimationFrame', () => cafFn);
1330
+ defineLazyGlobal(g, 'requestIdleCallback', () => ricFn);
1331
+ defineLazyGlobal(g, 'cancelIdleCallback', () => cicFn);
1332
+
1333
+ // ========================================
1334
+ // LAZY: CompressionStream, DecompressionStream
1335
+ // ========================================
1336
+ defineLazyGlobal(g, 'CompressionStream', () => CompressionStream);
1337
+ defineLazyGlobal(g, 'DecompressionStream', () => DecompressionStream);
1338
+
1339
+ // ========================================
1340
+ // LAZY: Cache API (caches global)
1341
+ // ========================================
1342
+ defineLazyGlobal(g, 'caches', () => new CacheStorage());
1343
+
1344
+ // ========================================
1345
+ // LAZY: ClipboardItem (Clipboard API)
1346
+ // ========================================
1347
+ defineLazyGlobal(g, 'ClipboardItem', () => ClipboardItem);
1348
+
1349
+ // ========================================
1350
+ // LAZY: IndexedDB API (backed by exact:sqlite)
1351
+ // ========================================
1352
+ defineLazyGlobal(g, 'indexedDB', () => new IDBFactory());
1353
+ defineLazyGlobal(g, 'IDBKeyRange', () => IDBKeyRange);
1354
+ defineLazyGlobal(g, 'IDBDatabase', () => IDBDatabase);
1355
+ defineLazyGlobal(g, 'IDBObjectStore', () => IDBObjectStore);
1356
+ defineLazyGlobal(g, 'IDBTransaction', () => IDBTransaction);
1357
+ defineLazyGlobal(g, 'IDBRequest', () => IDBRequest);
1358
+ defineLazyGlobal(g, 'IDBOpenDBRequest', () => IDBOpenDBRequest);
1359
+ defineLazyGlobal(g, 'IDBCursor', () => IDBCursor);
1360
+ defineLazyGlobal(g, 'IDBCursorWithValue', () => IDBCursorWithValue);
1361
+ defineLazyGlobal(g, 'IDBIndex', () => IDBIndex);
1362
+
1363
+ // ========================================
1364
+ // LAZY: window (for library compat)
1365
+ // ========================================
1366
+ if (typeof g.window === 'undefined') {
1367
+ // Point window at globalThis for libraries that check `typeof window !== 'undefined'`
1368
+ g.window = g;
1369
+ }
1370
+ // self alias (used by some web workers and libraries)
1371
+ if (typeof g.self === 'undefined') {
1372
+ g.self = g;
1373
+ }
1374
+ // Node.js-style global alias
1375
+ if (typeof g.global === 'undefined') {
1376
+ g.global = g;
1377
+ }
1378
+ defineLazyGlobal(g, 'matchMedia', () => exactWindow.matchMedia.bind(exactWindow));
1379
+ defineLazyGlobal(g, 'MediaQueryList', () => MediaQueryList);
1380
+ defineLazyGlobal(g, 'MediaQueryListEvent', () => MediaQueryListEvent);
1381
+
1382
+ // ========================================
1383
+ // Bun / Exact file globals (Bun.file, Bun.write, Bun.env, etc.)
1384
+ // Lazy: installExactGlobal() only runs when Exact.file or Bun.file is first accessed,
1385
+ // to avoid triggering macOS filesystem permission prompts at startup.
1386
+ // ========================================
1387
+ g.Exact = g.Exact || ({} as any);
1388
+ if (typeof g.__exactReadFile === 'function' || typeof g.__exactEnsureFs === 'function') {
1389
+ const exactFileDescriptor = Object.getOwnPropertyDescriptor(g.Exact, 'file');
1390
+ const exactWriteDescriptor = Object.getOwnPropertyDescriptor(g.Exact, 'write');
1391
+ const hasConcreteExactFile =
1392
+ !!exactFileDescriptor && !exactFileDescriptor.get && typeof exactFileDescriptor.value === 'function';
1393
+ const hasConcreteExactWrite =
1394
+ !!exactWriteDescriptor && !exactWriteDescriptor.get && typeof exactWriteDescriptor.value === 'function';
1395
+
1396
+ if (!hasConcreteExactFile || !hasConcreteExactWrite) {
1397
+ let _exactGlobalInstalled = false;
1398
+ let _exactFileFactory: ((path: string, options?: any) => any) | undefined;
1399
+ let _exactWriteFactory: typeof import('./fs/ExactFile').exactWrite | undefined;
1400
+ const ensureExactGlobal = () => {
1401
+ if (_exactGlobalInstalled) {
1402
+ return {
1403
+ file: _exactFileFactory!,
1404
+ write: _exactWriteFactory!,
1405
+ };
1406
+ }
1407
+ _exactGlobalInstalled = true;
1408
+ const installed = installExactGlobal();
1409
+ _exactFileFactory = installed.file;
1410
+ _exactWriteFactory = installed.write;
1411
+ return installed;
1412
+ };
1413
+ Object.defineProperty(g.Exact, 'file', {
1414
+ configurable: true,
1415
+ enumerable: true,
1416
+ get() {
1417
+ return ensureExactGlobal().file;
1418
+ },
1419
+ });
1420
+ Object.defineProperty(g.Exact, 'write', {
1421
+ configurable: true,
1422
+ enumerable: true,
1423
+ get() {
1424
+ return ensureExactGlobal().write;
1425
+ },
1426
+ });
1427
+ }
1428
+ }
1429
+ // The Bun-shaped facade is opt-in (`ibex --compat=bun` or
1430
+ // EXACT_COMPAT_BUN=1): an ambient 16-key Bun global made `typeof Bun`
1431
+ // detection lie to ecosystem packages (LLP 0012#decision). Implementations
1432
+ // stay reachable under `Exact`.
1433
+ const compatModes = (g as any).__exactCompatModes;
1434
+ const bunCompatEnabled =
1435
+ (Array.isArray(compatModes) && compatModes.indexOf('bun') !== -1) ||
1436
+ g.process?.env?.EXACT_COMPAT_BUN === '1';
1437
+ if (bunCompatEnabled && !g.Bun) g.Bun = g.Exact;
1438
+ // Wire Bun.env to process.env so packages can use either
1439
+ if (g.Exact && !g.Exact.env) {
1440
+ Object.defineProperty(g.Exact, 'env', {
1441
+ get() { return g.process?.env; },
1442
+ configurable: true,
1443
+ enumerable: true,
1444
+ });
1445
+ }
1446
+ // Bun.sleep(ms) - Promise-based delay
1447
+ if (g.Exact && !g.Exact.sleep) {
1448
+ g.Exact.sleep = (ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms));
1449
+ g.Exact.sleepSync = (ms: number) => {
1450
+ const end = Date.now() + ms;
1451
+ while (Date.now() < end) { /* spin */ }
1452
+ };
1453
+ }
1454
+ // Bun.which(cmd) - find executable in PATH (stub - returns null on mobile)
1455
+ if (g.Exact && !g.Exact.which) {
1456
+ g.Exact.which = (_cmd: string) => null;
1457
+ }
1458
+ // Exact.unsafe (aliased as Bun.unsafe under --compat=bun) - shims expected
1459
+ // by the CLI test harness.
1460
+ if (g.Exact) {
1461
+ const bunUnsafe = g.Exact.unsafe && typeof g.Exact.unsafe === 'object'
1462
+ ? g.Exact.unsafe
1463
+ : {};
1464
+ if (typeof bunUnsafe.gcAggressionLevel !== 'function') {
1465
+ bunUnsafe.gcAggressionLevel = (level?: number) => level !== undefined ? level : 0;
1466
+ }
1467
+ if (typeof bunUnsafe.arrayBufferToString !== 'function') {
1468
+ bunUnsafe.arrayBufferToString = (buf: ArrayBuffer | ArrayBufferView) => {
1469
+ const bytes = buf instanceof ArrayBuffer
1470
+ ? new Uint8Array(buf)
1471
+ : buf instanceof Uint8Array
1472
+ ? buf
1473
+ : new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
1474
+ return new TextDecoder().decode(bytes);
1475
+ };
1476
+ }
1477
+ if (typeof bunUnsafe.segfault !== 'function') {
1478
+ bunUnsafe.segfault = () => {
1479
+ throw new Error('Bun.unsafe.segfault is not implemented');
1480
+ };
1481
+ }
1482
+ g.Exact.unsafe = bunUnsafe;
1483
+ }
1484
+ // Bun.peek.status - promise state probe shim used by compat tests.
1485
+ if (g.Exact) {
1486
+ if (typeof g.Exact.peek !== 'function') {
1487
+ g.Exact.peek = <T>(value: T) => value;
1488
+ }
1489
+ if (g.Exact.peek && typeof g.Exact.peek.status !== 'function') {
1490
+ g.Exact.peek.status = (_promise: Promise<unknown>) => 'pending';
1491
+ }
1492
+ }
1493
+ // Bun.deepMatch(obj, subset) - check if subset is a subset of obj
1494
+ if (g.Exact && !g.Exact.deepMatch) {
1495
+ g.Exact.deepMatch = function deepMatch(obj: any, subset: any): boolean {
1496
+ if (subset === obj) return true;
1497
+ if (subset === null || typeof subset !== 'object') return subset === obj;
1498
+ if (obj === null || typeof obj !== 'object') return false;
1499
+ for (const key of Object.keys(subset)) {
1500
+ if (!deepMatch((obj as any)[key], (subset as any)[key])) return false;
1501
+ }
1502
+ return true;
1503
+ };
1504
+ }
1505
+ // Bun.password - argon2/bcrypt (stub - rejects since we lack native support)
1506
+ if (g.Exact && !g.Exact.password) {
1507
+ g.Exact.password = {
1508
+ hash: () => Promise.reject(new Error('Bun.password not available in Ibex runtime')),
1509
+ verify: () => Promise.reject(new Error('Bun.password not available in Ibex runtime')),
1510
+ hashSync: () => { throw new Error('Bun.password not available in Ibex runtime'); },
1511
+ verifySync: () => { throw new Error('Bun.password not available in Ibex runtime'); },
1512
+ };
1513
+ }
1514
+ // Bun.semver - semver comparison utilities
1515
+ if (g.Exact && !g.Exact.semver) {
1516
+ g.Exact.semver = {
1517
+ satisfies: (_version: string, _range: string) => false,
1518
+ order: (a: string, b: string) => {
1519
+ const parse = (v: string) => v.replace(/^[^0-9]*/, '').split('.').map(Number);
1520
+ const [aMajor = 0, aMinor = 0, aPatch = 0] = parse(a);
1521
+ const [bMajor = 0, bMinor = 0, bPatch = 0] = parse(b);
1522
+ if (aMajor !== bMajor) return aMajor - bMajor;
1523
+ if (aMinor !== bMinor) return aMinor - bMinor;
1524
+ return aPatch - bPatch;
1525
+ },
1526
+ };
1527
+ }
1528
+ // Bun.CryptoHasher - wraps node:crypto hash API
1529
+ if (g.Exact && !g.Exact.CryptoHasher) {
1530
+ g.Exact.CryptoHasher = class CryptoHasher {
1531
+ private _algo: string;
1532
+ private _chunks: string[] = [];
1533
+ static readonly algorithms = ['md5', 'sha1', 'sha256', 'sha384', 'sha512', 'sha224'];
1534
+ constructor(algorithm: string) {
1535
+ this._algo = algorithm.toLowerCase().replace(/-/g, '');
1536
+ }
1537
+ update(data: string | ArrayBuffer | Uint8Array): this {
1538
+ if (typeof data === 'string') {
1539
+ this._chunks.push(data);
1540
+ } else {
1541
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
1542
+ let s = '';
1543
+ for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
1544
+ this._chunks.push(s);
1545
+ }
1546
+ return this;
1547
+ }
1548
+ digest(encoding?: 'hex' | 'base64' | 'base64url'): string | Uint8Array {
1549
+ const joined = this._chunks.join('');
1550
+ const gx = globalThis as any;
1551
+ if (typeof gx.__exactHashSync === 'function') {
1552
+ const hex = gx.__exactHashSync(this._algo, joined);
1553
+ if (!encoding || encoding === 'hex') return hex;
1554
+ if (encoding === 'base64' || encoding === 'base64url') {
1555
+ const bytes: number[] = [];
1556
+ for (let i = 0; i < hex.length; i += 2) bytes.push(parseInt(hex.substr(i, 2), 16));
1557
+ const b64 = btoa(bytes.map(b => String.fromCharCode(b)).join(''));
1558
+ return encoding === 'base64url' ? b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') : b64;
1559
+ }
1560
+ }
1561
+ throw new Error('CryptoHasher: native hash not available');
1562
+ }
1563
+ };
1564
+ }
1565
+
1566
+ // ========================================
1567
+ // SharedArrayBuffer stub (prevents ReferenceError in Hermes)
1568
+ // Many compat tests reference SharedArrayBuffer at file scope.
1569
+ // This stub allows files to load; actual shared memory semantics are not supported.
1570
+ // ========================================
1571
+ if (typeof g.SharedArrayBuffer === 'undefined') {
1572
+ g.SharedArrayBuffer = class SharedArrayBuffer {
1573
+ _buffer: ArrayBuffer;
1574
+
1575
+ constructor(byteLength: number) {
1576
+ const length = Math.floor(Number(byteLength));
1577
+ if (!Number.isFinite(length) || length < 0) {
1578
+ throw new RangeError('Invalid SharedArrayBuffer length');
1579
+ }
1580
+ this._buffer = new ArrayBuffer(length);
1581
+ }
1582
+
1583
+ get byteLength(): number {
1584
+ return this._buffer.byteLength;
1585
+ }
1586
+
1587
+ slice(begin?: number, end?: number): SharedArrayBuffer {
1588
+ const sliced = this._buffer.slice(begin, end);
1589
+ const result = new (g.SharedArrayBuffer as typeof SharedArrayBuffer)(sliced.byteLength);
1590
+ new Uint8Array((result as SharedArrayBuffer)._buffer).set(new Uint8Array(sliced));
1591
+ return result;
1592
+ }
1593
+
1594
+ get [Symbol.toStringTag]() {
1595
+ return 'SharedArrayBuffer';
1596
+ }
1597
+ } as any;
1598
+ }
1599
+
1600
+ const patchBrokenSharedArrayBufferViews = () => {
1601
+ if (typeof g.SharedArrayBuffer !== 'function' || typeof g.Uint8Array !== 'function') {
1602
+ return;
1603
+ }
1604
+
1605
+ let needsViewPatch = false;
1606
+ try {
1607
+ const probeBuffer = new g.SharedArrayBuffer(1);
1608
+ const probeView = new g.Uint8Array(probeBuffer);
1609
+ needsViewPatch =
1610
+ probeView.length === 0 ||
1611
+ probeView.byteLength === 0 ||
1612
+ Object.prototype.toString.call(probeView.buffer) !== '[object SharedArrayBuffer]';
1613
+ } catch {
1614
+ return;
1615
+ }
1616
+
1617
+ if (!needsViewPatch) {
1618
+ return;
1619
+ }
1620
+
1621
+ const getSharedArrayBufferBacking = (buffer: any): ArrayBuffer | null => {
1622
+ if (!buffer || typeof buffer !== 'object') return null;
1623
+ if (Object.prototype.toString.call(buffer) !== '[object SharedArrayBuffer]') return null;
1624
+ if (!buffer._buffer || Object.prototype.toString.call(buffer._buffer) !== '[object ArrayBuffer]') {
1625
+ return null;
1626
+ }
1627
+ return buffer._buffer;
1628
+ };
1629
+
1630
+ const exposeSharedArrayBufferView = <T extends object>(view: T, originalBuffer: any): T => {
1631
+ try {
1632
+ Object.defineProperty(view, 'buffer', {
1633
+ configurable: true,
1634
+ enumerable: false,
1635
+ get() {
1636
+ return originalBuffer;
1637
+ },
1638
+ });
1639
+ } catch {}
1640
+ try {
1641
+ Object.defineProperty(view, '__exactSharedArrayBuffer', {
1642
+ value: originalBuffer,
1643
+ writable: false,
1644
+ configurable: true,
1645
+ enumerable: false,
1646
+ });
1647
+ } catch {}
1648
+ return view;
1649
+ };
1650
+
1651
+ const wrapSharedArrayBufferViewCtor = (name: string) => {
1652
+ const NativeCtor = (g as any)[name];
1653
+ if (typeof NativeCtor !== 'function' || NativeCtor.__exactSharedArrayBufferWrapped) {
1654
+ return;
1655
+ }
1656
+
1657
+ const WrappedCtor = function(this: any, buffer?: any, byteOffset?: number, length?: number) {
1658
+ if (!(this instanceof WrappedCtor)) {
1659
+ throw new TypeError(`Constructor ${name} requires "new"`);
1660
+ }
1661
+
1662
+ const backing = getSharedArrayBufferBacking(buffer);
1663
+ if (backing) {
1664
+ let view;
1665
+ if (arguments.length <= 1) {
1666
+ view = new NativeCtor(backing);
1667
+ } else if (arguments.length === 2) {
1668
+ view = new NativeCtor(backing, byteOffset);
1669
+ } else {
1670
+ view = new NativeCtor(backing, byteOffset, length);
1671
+ }
1672
+ return exposeSharedArrayBufferView(view, buffer);
1673
+ }
1674
+
1675
+ if (arguments.length === 0) return new NativeCtor();
1676
+ if (arguments.length === 1) return new NativeCtor(buffer);
1677
+ if (arguments.length === 2) return new NativeCtor(buffer, byteOffset);
1678
+ return new NativeCtor(buffer, byteOffset, length);
1679
+ } as any;
1680
+
1681
+ WrappedCtor.prototype = NativeCtor.prototype;
1682
+ try {
1683
+ Object.defineProperty(WrappedCtor.prototype, 'constructor', {
1684
+ value: WrappedCtor,
1685
+ writable: true,
1686
+ configurable: true,
1687
+ enumerable: false,
1688
+ });
1689
+ } catch {}
1690
+ try {
1691
+ Object.setPrototypeOf(WrappedCtor, NativeCtor);
1692
+ } catch {}
1693
+ try {
1694
+ Object.defineProperty(WrappedCtor, 'name', {
1695
+ value: name,
1696
+ configurable: true,
1697
+ });
1698
+ } catch {}
1699
+ try {
1700
+ Object.defineProperty(WrappedCtor, '__exactSharedArrayBufferWrapped', {
1701
+ value: true,
1702
+ writable: false,
1703
+ configurable: true,
1704
+ enumerable: false,
1705
+ });
1706
+ } catch {}
1707
+ try {
1708
+ Object.defineProperty(g, name, {
1709
+ value: WrappedCtor,
1710
+ writable: true,
1711
+ configurable: true,
1712
+ enumerable: false,
1713
+ });
1714
+ } catch {
1715
+ (g as any)[name] = WrappedCtor;
1716
+ }
1717
+ };
1718
+
1719
+ const typedArrayNames = [
1720
+ 'Int8Array',
1721
+ 'Uint8Array',
1722
+ 'Uint8ClampedArray',
1723
+ 'Int16Array',
1724
+ 'Uint16Array',
1725
+ 'Int32Array',
1726
+ 'Uint32Array',
1727
+ 'Float32Array',
1728
+ 'Float64Array',
1729
+ ];
1730
+ if (typeof (g as any).Float16Array === 'function') {
1731
+ typedArrayNames.push('Float16Array');
1732
+ }
1733
+ if (typeof (g as any).BigInt64Array === 'function') {
1734
+ typedArrayNames.push('BigInt64Array');
1735
+ }
1736
+ if (typeof (g as any).BigUint64Array === 'function') {
1737
+ typedArrayNames.push('BigUint64Array');
1738
+ }
1739
+
1740
+ for (const name of typedArrayNames) {
1741
+ wrapSharedArrayBufferViewCtor(name);
1742
+ }
1743
+ wrapSharedArrayBufferViewCtor('DataView');
1744
+ };
1745
+
1746
+ patchBrokenSharedArrayBufferViews();
1747
+
1748
+ const ensureSharedArrayBufferUint8ArrayWorks = () => {
1749
+ if (typeof g.SharedArrayBuffer !== 'function' || typeof g.Uint8Array !== 'function') {
1750
+ return;
1751
+ }
1752
+
1753
+ const exposeUint8ArrayBuffer = <T extends object>(view: T, originalBuffer: any): T => {
1754
+ try {
1755
+ Object.defineProperty(view, 'buffer', {
1756
+ configurable: true,
1757
+ enumerable: false,
1758
+ get() {
1759
+ return originalBuffer;
1760
+ },
1761
+ });
1762
+ } catch {}
1763
+ try {
1764
+ Object.defineProperty(view, '__exactSharedArrayBuffer', {
1765
+ value: originalBuffer,
1766
+ writable: false,
1767
+ configurable: true,
1768
+ enumerable: false,
1769
+ });
1770
+ } catch {}
1771
+ return view;
1772
+ };
1773
+
1774
+ try {
1775
+ const probeBuffer = new g.SharedArrayBuffer(1);
1776
+ const probeView = new g.Uint8Array(probeBuffer);
1777
+ if (
1778
+ probeView.length === 1 &&
1779
+ probeView.byteLength === 1 &&
1780
+ Object.prototype.toString.call(probeView.buffer) === '[object SharedArrayBuffer]'
1781
+ ) {
1782
+ return;
1783
+ }
1784
+ } catch {}
1785
+
1786
+ const NativeUint8Array = g.Uint8Array;
1787
+ const WrappedUint8Array = function Uint8Array(
1788
+ this: any,
1789
+ buffer?: any,
1790
+ byteOffset?: number,
1791
+ length?: number
1792
+ ) {
1793
+ if (!(this instanceof WrappedUint8Array)) {
1794
+ throw new TypeError('Constructor Uint8Array requires "new"');
1795
+ }
1796
+
1797
+ const backing = buffer && buffer._buffer;
1798
+ if (backing && Object.prototype.toString.call(backing) === '[object ArrayBuffer]') {
1799
+ let view;
1800
+ if (arguments.length <= 1) {
1801
+ view = new NativeUint8Array(backing);
1802
+ } else if (arguments.length === 2) {
1803
+ view = new NativeUint8Array(backing, byteOffset);
1804
+ } else {
1805
+ view = new NativeUint8Array(backing, byteOffset, length);
1806
+ }
1807
+ return exposeUint8ArrayBuffer(view, buffer);
1808
+ }
1809
+
1810
+ if (arguments.length === 0) return new NativeUint8Array();
1811
+ if (arguments.length === 1) return new NativeUint8Array(buffer);
1812
+ if (arguments.length === 2) return new NativeUint8Array(buffer, byteOffset);
1813
+ return new NativeUint8Array(buffer, byteOffset, length);
1814
+ } as any;
1815
+
1816
+ WrappedUint8Array.prototype = NativeUint8Array.prototype;
1817
+ try {
1818
+ Object.setPrototypeOf(WrappedUint8Array, NativeUint8Array);
1819
+ } catch {}
1820
+ try {
1821
+ Object.defineProperty(WrappedUint8Array.prototype, 'constructor', {
1822
+ value: WrappedUint8Array,
1823
+ writable: true,
1824
+ configurable: true,
1825
+ });
1826
+ } catch {}
1827
+ try {
1828
+ Object.defineProperty(WrappedUint8Array, '__exactSharedArrayBufferWrapped', {
1829
+ value: true,
1830
+ writable: false,
1831
+ configurable: true,
1832
+ });
1833
+ } catch {}
1834
+ try {
1835
+ Object.defineProperty(g, 'Uint8Array', {
1836
+ value: WrappedUint8Array,
1837
+ writable: true,
1838
+ configurable: true,
1839
+ enumerable: false,
1840
+ });
1841
+ } catch {
1842
+ g.Uint8Array = WrappedUint8Array;
1843
+ }
1844
+ };
1845
+
1846
+ ensureSharedArrayBufferUint8ArrayWorks();
1847
+
1848
+ // ========================================
1849
+ // Atomics stub (companion to SharedArrayBuffer stub)
1850
+ // ========================================
1851
+ if (typeof g.Atomics === 'undefined') {
1852
+ g.Atomics = {
1853
+ add(ta: any, index: number, value: number) { const old = ta[index]; ta[index] += value; return old; },
1854
+ and(ta: any, index: number, value: number) { const old = ta[index]; ta[index] &= value; return old; },
1855
+ compareExchange(ta: any, index: number, expected: number, replacement: number) {
1856
+ const old = ta[index]; if (old === expected) ta[index] = replacement; return old;
1857
+ },
1858
+ exchange(ta: any, index: number, value: number) { const old = ta[index]; ta[index] = value; return old; },
1859
+ isLockFree(size: number) { return size === 1 || size === 2 || size === 4; },
1860
+ load(ta: any, index: number) { return ta[index]; },
1861
+ or(ta: any, index: number, value: number) { const old = ta[index]; ta[index] |= value; return old; },
1862
+ store(ta: any, index: number, value: number) { ta[index] = value; return value; },
1863
+ sub(ta: any, index: number, value: number) { const old = ta[index]; ta[index] -= value; return old; },
1864
+ wait() { return 'not-equal' as const; },
1865
+ notify() { return 0; },
1866
+ xor(ta: any, index: number, value: number) { const old = ta[index]; ta[index] ^= value; return old; },
1867
+ [Symbol.toStringTag]: 'Atomics',
1868
+ };
1869
+ }
1870
+
1871
+ // ========================================
1872
+ // Float16Array stub (TC39 Stage 3, not yet in Hermes)
1873
+ // Prevents ReferenceError in tests that reference it at file scope.
1874
+ // ========================================
1875
+ if (typeof g.Float16Array === 'undefined') {
1876
+ g.Float16Array = class Float16Array extends Uint16Array {
1877
+ static get BYTES_PER_ELEMENT() { return 2; }
1878
+ get [Symbol.toStringTag]() { return 'Float16Array'; }
1879
+ };
1880
+ }
1881
+
1882
+ // ========================================
1883
+ // ES2023+ Polyfills (Array, Set, Object.groupBy, Promise.withResolvers, Iterator helpers)
1884
+ // ========================================
1885
+ installPolyfills();
1886
+
1887
+ // ========================================
1888
+ // Promise rejection tracking (unhandledrejection / rejectionhandled)
1889
+ // Must come after Event, EventTarget, and PromiseRejectionEvent globals
1890
+ // ========================================
1891
+ installPromiseRejectionTracking();
1892
+ if (g.__exactLoadTimings) g.__exactLoadTimings.installGlobalsEnd = Date.now();
1893
+ }
1894
+
1895
+ // Stub exports to prevent import errors
1896
+ export function getRuntimeVersion() { return '0.1.0'; }
1897
+ export function detectEngine() { return 'hermes'; }
1898
+ export function detectPlatform() {
1899
+ const g: any = globalThis as any;
1900
+ if (typeof g.__exactPlatform === 'string' && g.__exactPlatform.length > 0) return g.__exactPlatform;
1901
+ const hostProcess = g.process;
1902
+ if (hostProcess) {
1903
+ let isExactProcess = false;
1904
+ try {
1905
+ const versions = hostProcess.versions;
1906
+ isExactProcess = !!(versions && typeof versions === 'object' && (typeof versions.ibex === 'string' || typeof versions.exact === 'string'));
1907
+ } catch {
1908
+ isExactProcess = false;
1909
+ }
1910
+ if (!isExactProcess) {
1911
+ try {
1912
+ const platform = hostProcess.platform;
1913
+ if (typeof platform === 'string' && platform.length > 0) {
1914
+ return platform;
1915
+ }
1916
+ } catch {
1917
+ // Ignore host process platform lookup errors and keep falling back.
1918
+ }
1919
+ }
1920
+ }
1921
+ return 'ios';
1922
+ }
1923
+ export const runtimeInfo = {
1924
+ version: '0.1.0',
1925
+ engine: 'hermes',
1926
+ get platform() { return detectPlatform(); },
1927
+ };
1928
+
1929
+ export function areGlobalsInstalled(): boolean {
1930
+ return (globalThis as any).__exactRuntimeLoaded === true;
1931
+ }
1932
+
1933
+ const runtimeBundle = {
1934
+ installGlobals,
1935
+ areGlobalsInstalled,
1936
+ getRuntimeVersion,
1937
+ detectEngine,
1938
+ detectPlatform,
1939
+ runtimeInfo,
1940
+ };
1941
+
1942
+ (globalThis as any).ExactBundle = (globalThis as any).ExactBundle || runtimeBundle;
1943
+
1944
+ if ((globalThis as any).ExactBundle && (globalThis as any).ExactBundle !== runtimeBundle) {
1945
+ Object.assign((globalThis as any).ExactBundle, runtimeBundle);
1946
+ }