@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
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Promise Rejection Tracking
|
|
4
|
+
*
|
|
5
|
+
* Provides `unhandledrejection` and `rejectionhandled` event dispatch on globalThis.
|
|
6
|
+
*
|
|
7
|
+
* Strategy:
|
|
8
|
+
* 1. If the native bridge exposes `__exactOnUnhandledRejection`, use it directly.
|
|
9
|
+
* 2. Otherwise, install a userland polyfill that:
|
|
10
|
+
* a. Wraps `Promise.prototype.then` to track which promises have rejection handlers.
|
|
11
|
+
* b. Uses a Map to track unhandled rejections.
|
|
12
|
+
* c. Uses `queueMicrotask` to defer detection until after synchronous handler attachment.
|
|
13
|
+
* d. Dispatches `unhandledrejection` on globalThis when a rejection is unhandled.
|
|
14
|
+
* e. Dispatches `rejectionhandled` when a previously-unhandled rejection gets a handler.
|
|
15
|
+
*
|
|
16
|
+
* @see https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { EventTarget } from "./events/EventTarget";
|
|
20
|
+
import { PromiseRejectionEvent } from "./events/PromiseRejectionEvent";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Map of currently-unhandled rejections: Promise -> reason.
|
|
24
|
+
* Exposed for testing purposes.
|
|
25
|
+
*/
|
|
26
|
+
const _unhandledRejections: Map<Promise<any>, any> = new Map();
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Internal EventTarget used to dispatch promise rejection events.
|
|
30
|
+
* We always use our own EventTarget instance to ensure compatibility with
|
|
31
|
+
* our custom Event classes (avoids issues with native EventTarget implementations
|
|
32
|
+
* in environments like Bun/Node that reject non-native Event subclasses).
|
|
33
|
+
*/
|
|
34
|
+
let _eventTarget: EventTarget | null = null;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Whether globalThis.addEventListener has been wrapped to intercept
|
|
38
|
+
* unhandledrejection/rejectionhandled listeners.
|
|
39
|
+
*/
|
|
40
|
+
let _globalListenersInstalled = false;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Promise rejection event types that we intercept.
|
|
44
|
+
*/
|
|
45
|
+
const REJECTION_EVENT_TYPES = new Set(['unhandledrejection', 'rejectionhandled']);
|
|
46
|
+
const trailingDataErrorSymbol = Symbol.for("__exact.decompression.trailing-data-error");
|
|
47
|
+
const _synchronouslyHandledPromises = new WeakSet<Promise<any>>();
|
|
48
|
+
let _nativeHandledTrackingInstalled = false;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get or create the internal EventTarget for promise rejection events.
|
|
52
|
+
*/
|
|
53
|
+
function getEventTarget(): EventTarget {
|
|
54
|
+
if (_eventTarget) return _eventTarget;
|
|
55
|
+
_eventTarget = new EventTarget();
|
|
56
|
+
return _eventTarget;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Install interception of addEventListener/removeEventListener on globalThis
|
|
61
|
+
* so that listeners for 'unhandledrejection' and 'rejectionhandled' are
|
|
62
|
+
* forwarded to our internal EventTarget.
|
|
63
|
+
*/
|
|
64
|
+
function installGlobalListenerForwarding(): void {
|
|
65
|
+
if (_globalListenersInstalled) return;
|
|
66
|
+
_globalListenersInstalled = true;
|
|
67
|
+
|
|
68
|
+
const g = globalThis as any;
|
|
69
|
+
const et = getEventTarget();
|
|
70
|
+
|
|
71
|
+
const origAddEventListener = g.addEventListener;
|
|
72
|
+
const origRemoveEventListener = g.removeEventListener;
|
|
73
|
+
|
|
74
|
+
if (typeof origAddEventListener === 'function') {
|
|
75
|
+
// Wrap existing addEventListener to intercept rejection events
|
|
76
|
+
g.addEventListener = function wrappedAddEventListener(
|
|
77
|
+
type: string,
|
|
78
|
+
callback: any,
|
|
79
|
+
options?: any
|
|
80
|
+
): void {
|
|
81
|
+
if (REJECTION_EVENT_TYPES.has(type)) {
|
|
82
|
+
et.addEventListener(type, callback, options);
|
|
83
|
+
} else {
|
|
84
|
+
origAddEventListener.call(this, type, callback, options);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
} else {
|
|
88
|
+
// No existing addEventListener, install ours directly
|
|
89
|
+
g.addEventListener = et.addEventListener.bind(et);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (typeof origRemoveEventListener === 'function') {
|
|
93
|
+
g.removeEventListener = function wrappedRemoveEventListener(
|
|
94
|
+
type: string,
|
|
95
|
+
callback: any,
|
|
96
|
+
options?: any
|
|
97
|
+
): void {
|
|
98
|
+
if (REJECTION_EVENT_TYPES.has(type)) {
|
|
99
|
+
et.removeEventListener(type, callback, options);
|
|
100
|
+
} else {
|
|
101
|
+
origRemoveEventListener.call(this, type, callback, options);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
} else {
|
|
105
|
+
g.removeEventListener = et.removeEventListener.bind(et);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (typeof g.dispatchEvent !== 'function') {
|
|
109
|
+
g.dispatchEvent = et.dispatchEvent.bind(et);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Track a promise rejection. Called when a promise is rejected without a handler.
|
|
115
|
+
* After a microtask, if no handler has been attached, dispatches `unhandledrejection`.
|
|
116
|
+
*/
|
|
117
|
+
export function trackPromiseRejection(promise: Promise<any>, reason: any): void {
|
|
118
|
+
if (_synchronouslyHandledPromises.has(promise)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const suppressUnhandledRejection = (globalThis as any).__exactShouldSuppressUnhandledRejection;
|
|
123
|
+
if (typeof suppressUnhandledRejection === "function") {
|
|
124
|
+
try {
|
|
125
|
+
if (suppressUnhandledRejection(reason, promise)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
} catch (_) {}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (reason && reason[trailingDataErrorSymbol]) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_unhandledRejections.set(promise, reason);
|
|
136
|
+
|
|
137
|
+
// Defer the check to after the current microtask queue drains,
|
|
138
|
+
// giving synchronous .catch() calls a chance to attach.
|
|
139
|
+
queueMicrotask(() => {
|
|
140
|
+
if (!_unhandledRejections.has(promise)) {
|
|
141
|
+
// Handler was attached synchronously -- nothing to report
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const target = getEventTarget();
|
|
146
|
+
const event = new PromiseRejectionEvent('unhandledrejection', {
|
|
147
|
+
promise,
|
|
148
|
+
reason,
|
|
149
|
+
cancelable: true,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const notPrevented = target.dispatchEvent(event);
|
|
153
|
+
|
|
154
|
+
if (notPrevented) {
|
|
155
|
+
// Default behavior: log the unhandled rejection to console
|
|
156
|
+
// (browsers typically report to console unless preventDefault is called)
|
|
157
|
+
console.error('Unhandled promise rejection:', reason);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function installNativeHandledPromiseTracking(OriginalPromise: PromiseConstructor): void {
|
|
163
|
+
if (_nativeHandledTrackingInstalled) return;
|
|
164
|
+
_nativeHandledTrackingInstalled = true;
|
|
165
|
+
|
|
166
|
+
const originalThen = OriginalPromise.prototype.then;
|
|
167
|
+
const originalCatch = OriginalPromise.prototype.catch;
|
|
168
|
+
const originalFinally = OriginalPromise.prototype.finally;
|
|
169
|
+
|
|
170
|
+
function markHandled(promise: Promise<any>): void {
|
|
171
|
+
_synchronouslyHandledPromises.add(promise);
|
|
172
|
+
if (_unhandledRejections.has(promise)) {
|
|
173
|
+
trackPromiseRejectionHandled(promise);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
OriginalPromise.prototype.then = function wrappedThen(
|
|
178
|
+
onFulfilled?: any,
|
|
179
|
+
onRejected?: any
|
|
180
|
+
): Promise<any> {
|
|
181
|
+
if (this && (typeof this === 'object' || typeof this === 'function')) {
|
|
182
|
+
markHandled(this as Promise<any>);
|
|
183
|
+
}
|
|
184
|
+
return originalThen.call(this, onFulfilled, onRejected);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
OriginalPromise.prototype.catch = function wrappedCatch(
|
|
188
|
+
onRejected?: any
|
|
189
|
+
): Promise<any> {
|
|
190
|
+
if (typeof onRejected === 'function' && this && (typeof this === 'object' || typeof this === 'function')) {
|
|
191
|
+
markHandled(this as Promise<any>);
|
|
192
|
+
}
|
|
193
|
+
return originalCatch.call(this, onRejected);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
if (typeof originalFinally === 'function') {
|
|
197
|
+
OriginalPromise.prototype.finally = function wrappedFinally(
|
|
198
|
+
onFinally?: any
|
|
199
|
+
): Promise<any> {
|
|
200
|
+
if (this && (typeof this === 'object' || typeof this === 'function')) {
|
|
201
|
+
markHandled(this as Promise<any>);
|
|
202
|
+
}
|
|
203
|
+
return originalFinally.call(this, onFinally);
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Track that a previously-unhandled promise rejection has been handled.
|
|
210
|
+
* Dispatches `rejectionhandled` on globalThis.
|
|
211
|
+
*/
|
|
212
|
+
export function trackPromiseRejectionHandled(promise: Promise<any>): void {
|
|
213
|
+
const reason = _unhandledRejections.get(promise);
|
|
214
|
+
if (!_unhandledRejections.has(promise)) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
_unhandledRejections.delete(promise);
|
|
219
|
+
|
|
220
|
+
// Dispatch rejectionhandled asynchronously (per spec, fires in a later task)
|
|
221
|
+
queueMicrotask(() => {
|
|
222
|
+
const target = getEventTarget();
|
|
223
|
+
const event = new PromiseRejectionEvent('rejectionhandled', {
|
|
224
|
+
promise,
|
|
225
|
+
reason,
|
|
226
|
+
cancelable: false,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
target.dispatchEvent(event);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Install promise rejection tracking on the global Promise.
|
|
235
|
+
*
|
|
236
|
+
* This wraps Promise.prototype.then to intercept rejection handler attachment
|
|
237
|
+
* and hooks into the Promise constructor to detect unhandled rejections.
|
|
238
|
+
*/
|
|
239
|
+
export function installPromiseRejectionTracking(): void {
|
|
240
|
+
const g = globalThis as any;
|
|
241
|
+
|
|
242
|
+
// Set up event listener forwarding on globalThis
|
|
243
|
+
installGlobalListenerForwarding();
|
|
244
|
+
|
|
245
|
+
const OriginalPromise = g.Promise;
|
|
246
|
+
|
|
247
|
+
// If native bridge provides promise rejection hooks, use those
|
|
248
|
+
if (typeof g.__exactOnUnhandledRejection === 'function') {
|
|
249
|
+
installNativeHandledPromiseTracking(OriginalPromise);
|
|
250
|
+
g.__exactOnUnhandledRejection((promise: Promise<any>, reason: any) => {
|
|
251
|
+
trackPromiseRejection(promise, reason);
|
|
252
|
+
});
|
|
253
|
+
if (typeof g.__exactOnRejectionHandled === 'function') {
|
|
254
|
+
g.__exactOnRejectionHandled((promise: Promise<any>) => {
|
|
255
|
+
trackPromiseRejectionHandled(promise);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Polyfill approach: wrap Promise.prototype.then to detect handler attachment
|
|
262
|
+
// and wrap the Promise constructor to detect rejections without handlers.
|
|
263
|
+
const originalThen = OriginalPromise.prototype.then;
|
|
264
|
+
const originalCatch = OriginalPromise.prototype.catch;
|
|
265
|
+
|
|
266
|
+
// WeakSet to track promises that have had rejection handlers attached
|
|
267
|
+
const _handledPromises = new WeakSet<Promise<any>>();
|
|
268
|
+
// WeakSet to track promises that have been reported as rejected
|
|
269
|
+
const _rejectedPromises = new WeakSet<Promise<any>>();
|
|
270
|
+
// WeakMap to map child promises back to parent for handler tracking
|
|
271
|
+
const _promiseParents = new WeakMap<Promise<any>, Promise<any>>();
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Mark a promise (and its ancestor chain) as handled.
|
|
275
|
+
*/
|
|
276
|
+
function markHandled(promise: Promise<any>): void {
|
|
277
|
+
let current: unknown = promise;
|
|
278
|
+
while (current && (typeof current === 'object' || typeof current === 'function')) {
|
|
279
|
+
if (_handledPromises.has(current)) break;
|
|
280
|
+
_handledPromises.add(current);
|
|
281
|
+
|
|
282
|
+
// If this promise was in the unhandled set, dispatch rejectionhandled
|
|
283
|
+
if (_rejectedPromises.has(current)) {
|
|
284
|
+
_rejectedPromises.delete(current);
|
|
285
|
+
trackPromiseRejectionHandled(current);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
current = _promiseParents.get(current);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Wrapped .then() that tracks rejection handler attachment.
|
|
294
|
+
*/
|
|
295
|
+
OriginalPromise.prototype.then = function wrappedThen(
|
|
296
|
+
onFulfilled?: any,
|
|
297
|
+
onRejected?: any
|
|
298
|
+
): Promise<any> {
|
|
299
|
+
const child = originalThen.call(this, onFulfilled, onRejected);
|
|
300
|
+
|
|
301
|
+
// Track the parent-child relationship
|
|
302
|
+
if (child && (typeof child === 'object' || typeof child === 'function')) {
|
|
303
|
+
_promiseParents.set(child, this);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// If a rejection handler is provided, mark the promise as handled
|
|
307
|
+
if (typeof onRejected === 'function' && this && (typeof this === 'object' || typeof this === 'function')) {
|
|
308
|
+
markHandled(this);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return child;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Wrapped .catch() that tracks rejection handler attachment.
|
|
316
|
+
*/
|
|
317
|
+
OriginalPromise.prototype.catch = function wrappedCatch(
|
|
318
|
+
onRejected?: any
|
|
319
|
+
): Promise<any> {
|
|
320
|
+
const child = originalCatch.call(this, onRejected);
|
|
321
|
+
|
|
322
|
+
// Track the parent-child relationship
|
|
323
|
+
if (child && (typeof child === 'object' || typeof child === 'function')) {
|
|
324
|
+
_promiseParents.set(child, this);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// A .catch() always provides a rejection handler
|
|
328
|
+
if (typeof onRejected === 'function' && this && (typeof this === 'object' || typeof this === 'function')) {
|
|
329
|
+
markHandled(this);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return child;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// Preserve .finally if it exists
|
|
336
|
+
const originalFinally = OriginalPromise.prototype.finally;
|
|
337
|
+
if (originalFinally) {
|
|
338
|
+
OriginalPromise.prototype.finally = function wrappedFinally(
|
|
339
|
+
onFinally?: any
|
|
340
|
+
): Promise<any> {
|
|
341
|
+
const child = originalFinally.call(this, onFinally);
|
|
342
|
+
if (child && (typeof child === 'object' || typeof child === 'function')) {
|
|
343
|
+
_promiseParents.set(child, this);
|
|
344
|
+
}
|
|
345
|
+
return child;
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Create a wrapper around Promise.reject to detect immediate rejections.
|
|
351
|
+
*/
|
|
352
|
+
const originalReject = OriginalPromise.reject;
|
|
353
|
+
OriginalPromise.reject = function wrappedReject(reason?: any): Promise<any> {
|
|
354
|
+
const promise = originalReject.call(this, reason);
|
|
355
|
+
if (promise && (typeof promise === 'object' || typeof promise === 'function')) {
|
|
356
|
+
_rejectedPromises.add(promise);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Schedule a microtask to check if a handler was attached
|
|
360
|
+
queueMicrotask(() => {
|
|
361
|
+
if (promise && (typeof promise === 'object' || typeof promise === 'function') && !_handledPromises.has(promise)) {
|
|
362
|
+
trackPromiseRejection(promise, reason);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
return promise;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Wrap the Promise constructor to detect rejections in executor functions.
|
|
371
|
+
*/
|
|
372
|
+
const ExactPromise = function ExactPromise(
|
|
373
|
+
this: any,
|
|
374
|
+
executor: (
|
|
375
|
+
resolve: (value?: any) => void,
|
|
376
|
+
reject: (reason?: any) => void
|
|
377
|
+
) => void
|
|
378
|
+
): Promise<any> {
|
|
379
|
+
let promiseRef: Promise<any> | null = null;
|
|
380
|
+
let pendingReject: any = null;
|
|
381
|
+
|
|
382
|
+
const wrappedExecutor = (
|
|
383
|
+
resolve: (value?: any) => void,
|
|
384
|
+
reject: (reason?: any) => void
|
|
385
|
+
) => {
|
|
386
|
+
const wrappedReject = (reason?: any) => {
|
|
387
|
+
if (promiseRef && (typeof promiseRef === 'object' || typeof promiseRef === 'function')) {
|
|
388
|
+
_rejectedPromises.add(promiseRef);
|
|
389
|
+
queueMicrotask(() => {
|
|
390
|
+
if (!_handledPromises.has(promiseRef as any)) {
|
|
391
|
+
trackPromiseRejection(promiseRef as any, reason);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
} else {
|
|
395
|
+
pendingReject = reason;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return reject(reason);
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
return executor(resolve, wrappedReject);
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const promise = new OriginalPromise(wrappedExecutor);
|
|
405
|
+
promiseRef = promise;
|
|
406
|
+
if (pendingReject !== null) {
|
|
407
|
+
const reason = pendingReject;
|
|
408
|
+
pendingReject = null;
|
|
409
|
+
_rejectedPromises.add(promiseRef);
|
|
410
|
+
queueMicrotask(() => {
|
|
411
|
+
if (!_handledPromises.has(promiseRef as any)) {
|
|
412
|
+
trackPromiseRejection(promiseRef as any, reason);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return promise;
|
|
418
|
+
} as any;
|
|
419
|
+
|
|
420
|
+
// Copy static methods and properties
|
|
421
|
+
const bindStatic = (name: string): void => {
|
|
422
|
+
const value = (OriginalPromise as any)[name];
|
|
423
|
+
if (typeof value === 'function') {
|
|
424
|
+
ExactPromise[name] = value.bind(OriginalPromise);
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
bindStatic('resolve');
|
|
429
|
+
ExactPromise.reject = OriginalPromise.reject; // Already wrapped above
|
|
430
|
+
bindStatic('all');
|
|
431
|
+
bindStatic('allSettled');
|
|
432
|
+
bindStatic('race');
|
|
433
|
+
bindStatic('any');
|
|
434
|
+
bindStatic('withResolvers');
|
|
435
|
+
ExactPromise.prototype = OriginalPromise.prototype;
|
|
436
|
+
ExactPromise[Symbol.species] = OriginalPromise;
|
|
437
|
+
|
|
438
|
+
// Make instanceof checks work
|
|
439
|
+
Object.defineProperty(ExactPromise, Symbol.hasInstance, {
|
|
440
|
+
value: (instance: any) => instance instanceof OriginalPromise,
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// Install as the global Promise
|
|
444
|
+
g.Promise = ExactPromise;
|
|
445
|
+
|
|
446
|
+
// Store original for internal use
|
|
447
|
+
g.__OriginalPromise = OriginalPromise;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Get the current set of unhandled rejections (for testing).
|
|
452
|
+
*/
|
|
453
|
+
export function getUnhandledRejections(): Map<Promise<any>, any> {
|
|
454
|
+
return _unhandledRejections;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Reset tracking state (for testing).
|
|
459
|
+
*/
|
|
460
|
+
export function resetPromiseRejectionTracking(): void {
|
|
461
|
+
_unhandledRejections.clear();
|
|
462
|
+
_eventTarget = null;
|
|
463
|
+
_globalListenersInstalled = false;
|
|
464
|
+
}
|