@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.
- package/package.json +63 -0
- package/src/abort/AbortController.ts +23 -0
- package/src/abort/AbortSignal.ts +152 -0
- package/src/abort/index.ts +2 -0
- package/src/accessibility.ts +12 -0
- package/src/arraybuffer-detach.ts +109 -0
- package/src/base64/base64.ts +168 -0
- package/src/base64/index.ts +1 -0
- package/src/blob/Blob.ts +259 -0
- package/src/blob/File.ts +59 -0
- package/src/blob/FormData.ts +323 -0
- package/src/blob/index.ts +3 -0
- package/src/bootstrap.ts +1946 -0
- package/src/broadcast/BroadcastChannel.ts +280 -0
- package/src/broadcast/index.ts +5 -0
- package/src/cache/Cache.ts +349 -0
- package/src/cache/CacheStorage.ts +89 -0
- package/src/cache/index.ts +27 -0
- package/src/camera/index.ts +6202 -0
- package/src/camera/processor.worker.ts +194 -0
- package/src/camera/scene.ts +195 -0
- package/src/clipboard/Clipboard.ts +129 -0
- package/src/clipboard/ClipboardItem.ts +97 -0
- package/src/clipboard/index.ts +6 -0
- package/src/clone/index.ts +1 -0
- package/src/clone/structuredClone.ts +389 -0
- package/src/clone/transferableSymbols.ts +2 -0
- package/src/compression/CompressionStream.ts +146 -0
- package/src/compression/DecompressionStream.ts +342 -0
- package/src/compression/index.ts +4 -0
- package/src/console/Console.ts +341 -0
- package/src/console/index.ts +2 -0
- package/src/core/accessibility-state.ts +263 -0
- package/src/core/accessibility.ts +184 -0
- package/src/core/agent-state.ts +37 -0
- package/src/core/diagnostics-logs.ts +144 -0
- package/src/core/host-call-bridge.ts +16 -0
- package/src/core/i18n-helpers.ts +189 -0
- package/src/core/locale-state.ts +253 -0
- package/src/core/locale.ts +95 -0
- package/src/crypto/Crypto.ts +2743 -0
- package/src/crypto/index.ts +1 -0
- package/src/diagnostics/logs.ts +7 -0
- package/src/encoding/TextDecoder.ts +1181 -0
- package/src/encoding/TextDecoderStream.ts +58 -0
- package/src/encoding/TextEncoder.ts +180 -0
- package/src/encoding/TextEncoderStream.ts +39 -0
- package/src/encoding/index.ts +8 -0
- package/src/events/CloseEvent.ts +91 -0
- package/src/events/DOMException.ts +409 -0
- package/src/events/ErrorEvent.ts +39 -0
- package/src/events/Event.ts +151 -0
- package/src/events/EventTarget.ts +280 -0
- package/src/events/FocusEvent.ts +27 -0
- package/src/events/KeyboardEvent.ts +46 -0
- package/src/events/MessageEvent.ts +61 -0
- package/src/events/ProgressEvent.ts +33 -0
- package/src/events/PromiseRejectionEvent.ts +31 -0
- package/src/events/index.ts +52 -0
- package/src/eventsource/EventSource.ts +371 -0
- package/src/eventsource/index.ts +2 -0
- package/src/fetch/Headers.ts +642 -0
- package/src/fetch/Request.ts +760 -0
- package/src/fetch/Response.ts +543 -0
- package/src/fetch/body.ts +1256 -0
- package/src/fetch/cookie-jar.ts +566 -0
- package/src/fetch/demo.ts +207 -0
- package/src/fetch/errors.ts +101 -0
- package/src/fetch/fetch.ts +2610 -0
- package/src/fetch/index.ts +101 -0
- package/src/fetch/native-bridge.ts +65 -0
- package/src/fetch/types.ts +258 -0
- package/src/filereader/FileReader.ts +236 -0
- package/src/filereader/index.ts +1 -0
- package/src/fs/Dirent.ts +39 -0
- package/src/fs/ExactFile.ts +450 -0
- package/src/fs/Stats.ts +80 -0
- package/src/fs/index.ts +944 -0
- package/src/fs/promises.ts +386 -0
- package/src/fs/shared.ts +328 -0
- package/src/http-server/index.js +697 -0
- package/src/http-server/index.ts +27 -0
- package/src/identity.generated.ts +14 -0
- package/src/index.ts +283 -0
- package/src/indexeddb/IDBCursor.ts +188 -0
- package/src/indexeddb/IDBDatabase.ts +343 -0
- package/src/indexeddb/IDBFactory.ts +269 -0
- package/src/indexeddb/IDBIndex.ts +194 -0
- package/src/indexeddb/IDBKeyRange.ts +109 -0
- package/src/indexeddb/IDBObjectStore.ts +468 -0
- package/src/indexeddb/IDBRequest.ts +163 -0
- package/src/indexeddb/IDBTransaction.ts +207 -0
- package/src/indexeddb/index.ts +34 -0
- package/src/indexeddb/utils.ts +52 -0
- package/src/inspect/index.ts +1 -0
- package/src/inspect/inspect.ts +465 -0
- package/src/internal/detect.ts +104 -0
- package/src/locale.ts +10 -0
- package/src/location/index.ts +1059 -0
- package/src/locks/LockManager.ts +460 -0
- package/src/locks/index.ts +12 -0
- package/src/media/VideoFrame.ts +58 -0
- package/src/messaging/MessageChannel.ts +31 -0
- package/src/messaging/MessagePort.ts +180 -0
- package/src/messaging/index.ts +2 -0
- package/src/messaging.ts +247 -0
- package/src/native/NativeModules.ts +354 -0
- package/src/native/index.ts +1 -0
- package/src/navigator/Navigator.ts +351 -0
- package/src/navigator/index.ts +1 -0
- package/src/node/Buffer.ts +1786 -0
- package/src/node/index.ts +4 -0
- package/src/node/path.ts +495 -0
- package/src/node/process.ts +2528 -0
- package/src/performance/Performance.ts +532 -0
- package/src/performance/index.ts +21 -0
- package/src/polyfills/array.ts +236 -0
- package/src/polyfills/arraybuffer.ts +172 -0
- package/src/polyfills/groupby.ts +85 -0
- package/src/polyfills/index.ts +85 -0
- package/src/polyfills/intl.ts +1956 -0
- package/src/polyfills/iterator.ts +479 -0
- package/src/polyfills/promise.ts +37 -0
- package/src/polyfills/set.ts +245 -0
- package/src/polyfills/string.ts +85 -0
- package/src/polyfills/typedarray.ts +110 -0
- package/src/promise-rejection-tracking.ts +464 -0
- package/src/react-native/index.ts +388 -0
- package/src/runtime-entry.ts +55 -0
- package/src/scheduling/AnimationFrame.ts +105 -0
- package/src/scheduling/IdleCallback.ts +167 -0
- package/src/scheduling/index.ts +13 -0
- package/src/security/Capabilities.ts +1146 -0
- package/src/security/Permissions.ts +392 -0
- package/src/security/capability-bits.generated.ts +63 -0
- package/src/security/index.ts +16 -0
- package/src/sqlite/Database.ts +456 -0
- package/src/sqlite/Statement.ts +206 -0
- package/src/sqlite/constants.ts +79 -0
- package/src/sqlite/errors.ts +25 -0
- package/src/sqlite/index.ts +34 -0
- package/src/sqlite/module.js +438 -0
- package/src/storage/Storage.ts +291 -0
- package/src/storage/StorageManager.ts +91 -0
- package/src/storage/index.ts +3 -0
- package/src/stream-compat.ts +47 -0
- package/src/streams/ReadableStream.ts +4131 -0
- package/src/streams/TransformStream.ts +375 -0
- package/src/streams/WritableStream.ts +866 -0
- package/src/streams/index.ts +41 -0
- package/src/timers/Timers.ts +296 -0
- package/src/timers/index.ts +11 -0
- package/src/url/URL.ts +656 -0
- package/src/url/URLPattern.ts +850 -0
- package/src/url/URLSearchParams.ts +244 -0
- package/src/url/index.ts +9 -0
- package/src/websocket/WebSocket.ts +770 -0
- package/src/websocket/WebSocketError.ts +52 -0
- package/src/websocket/WebSocketStream.ts +628 -0
- package/src/websocket/index.ts +7 -0
- package/src/window/index.ts +872 -0
package/src/bootstrap.ts
ADDED
|
@@ -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
|
+
}
|