@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,388 @@
|
|
|
1
|
+
import { detectPlatform } from '../internal/detect';
|
|
2
|
+
import { window as exactWindow } from '../window';
|
|
3
|
+
|
|
4
|
+
type AppStateStatus = 'active' | 'background' | 'inactive' | 'unknown';
|
|
5
|
+
type ColorSchemeName = 'light' | 'dark' | null | undefined;
|
|
6
|
+
type PlatformOS =
|
|
7
|
+
| 'ios'
|
|
8
|
+
| 'android'
|
|
9
|
+
| 'macos'
|
|
10
|
+
| 'windows'
|
|
11
|
+
| 'linux'
|
|
12
|
+
| 'web'
|
|
13
|
+
| 'native'
|
|
14
|
+
| 'unknown';
|
|
15
|
+
|
|
16
|
+
interface EmitterSubscription {
|
|
17
|
+
remove(): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ScaledSize {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
scale: number;
|
|
24
|
+
fontScale: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface DimensionsValue {
|
|
28
|
+
window: ScaledSize;
|
|
29
|
+
screen: ScaledSize;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface DimensionsChangePayload {
|
|
33
|
+
window: ScaledSize;
|
|
34
|
+
screen: ScaledSize;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface AppearancePreferences {
|
|
38
|
+
colorScheme: ColorSchemeName;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface AppearanceChangePayload {
|
|
42
|
+
colorScheme: ColorSchemeName;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface LinkingUrlEvent {
|
|
46
|
+
url: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type DimensionsChangeHandler = (payload: DimensionsChangePayload) => void;
|
|
50
|
+
type AppearanceChangeHandler = (payload: AppearanceChangePayload) => void;
|
|
51
|
+
type AppStateChangeHandler = (state: AppStateStatus) => void;
|
|
52
|
+
type AppStateEventHandler = () => void;
|
|
53
|
+
type LinkingUrlHandler = (event: LinkingUrlEvent) => void;
|
|
54
|
+
|
|
55
|
+
type PlatformSelectSpec<T> = Partial<Record<PlatformOS | 'default', T>>;
|
|
56
|
+
|
|
57
|
+
declare global {
|
|
58
|
+
var __exactInitialURL: string | null | undefined;
|
|
59
|
+
var __exactOpenURL:
|
|
60
|
+
| ((url: string) => boolean | void | Promise<boolean | void>)
|
|
61
|
+
| undefined;
|
|
62
|
+
var __exactCanOpenURL:
|
|
63
|
+
| ((url: string) => boolean | Promise<boolean>)
|
|
64
|
+
| undefined;
|
|
65
|
+
var __exactReactNativeNotifyURL:
|
|
66
|
+
| ((url: string) => void)
|
|
67
|
+
| undefined;
|
|
68
|
+
var __exactReactNativeNotifyAppState:
|
|
69
|
+
| ((state: AppStateStatus) => void)
|
|
70
|
+
| undefined;
|
|
71
|
+
var __exactReactNativeNotifyMemoryWarning:
|
|
72
|
+
| (() => void)
|
|
73
|
+
| undefined;
|
|
74
|
+
var __exactReactNativeNotifyDimensionsChange:
|
|
75
|
+
| ((next?: Partial<DimensionsValue>) => void)
|
|
76
|
+
| undefined;
|
|
77
|
+
var __exactAppState: AppStateStatus | undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function normalizePlatform(value: string): PlatformOS {
|
|
81
|
+
const normalized = value.toLowerCase();
|
|
82
|
+
if (normalized === 'darwin' || normalized === 'mac') {
|
|
83
|
+
return 'macos';
|
|
84
|
+
}
|
|
85
|
+
if (normalized === 'win32') {
|
|
86
|
+
return 'windows';
|
|
87
|
+
}
|
|
88
|
+
if (
|
|
89
|
+
normalized === 'ios' ||
|
|
90
|
+
normalized === 'android' ||
|
|
91
|
+
normalized === 'macos' ||
|
|
92
|
+
normalized === 'windows' ||
|
|
93
|
+
normalized === 'linux' ||
|
|
94
|
+
normalized === 'web' ||
|
|
95
|
+
normalized === 'native'
|
|
96
|
+
) {
|
|
97
|
+
return normalized;
|
|
98
|
+
}
|
|
99
|
+
return 'unknown';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function currentPlatform(): PlatformOS {
|
|
103
|
+
return normalizePlatform(detectPlatform());
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function screenInfo(): ScaledSize {
|
|
107
|
+
const nativeInfo =
|
|
108
|
+
typeof globalThis.__exactGetScreenInfo === 'function'
|
|
109
|
+
? globalThis.__exactGetScreenInfo()
|
|
110
|
+
: null;
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
width: nativeInfo?.width ?? exactWindow.innerWidth,
|
|
114
|
+
height: nativeInfo?.height ?? exactWindow.innerHeight,
|
|
115
|
+
scale: nativeInfo?.scale ?? exactWindow.devicePixelRatio,
|
|
116
|
+
fontScale: nativeInfo?.fontScale ?? 1,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function currentDimensions(): DimensionsValue {
|
|
121
|
+
const current = screenInfo();
|
|
122
|
+
return {
|
|
123
|
+
window: { ...current },
|
|
124
|
+
screen: { ...current },
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function normalizeDimensions(next?: Partial<DimensionsValue>): DimensionsValue {
|
|
129
|
+
const fallback = currentDimensions();
|
|
130
|
+
return {
|
|
131
|
+
window: { ...fallback.window, ...next?.window },
|
|
132
|
+
screen: { ...fallback.screen, ...next?.screen },
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const dimensionsListeners = new Set<DimensionsChangeHandler>();
|
|
137
|
+
let lastDimensions = currentDimensions();
|
|
138
|
+
|
|
139
|
+
function notifyDimensions(next?: Partial<DimensionsValue>): void {
|
|
140
|
+
lastDimensions = normalizeDimensions(next);
|
|
141
|
+
const payload: DimensionsChangePayload = {
|
|
142
|
+
window: { ...lastDimensions.window },
|
|
143
|
+
screen: { ...lastDimensions.screen },
|
|
144
|
+
};
|
|
145
|
+
for (const listener of [...dimensionsListeners]) {
|
|
146
|
+
listener(payload);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
globalThis.__exactReactNativeNotifyDimensionsChange = notifyDimensions;
|
|
151
|
+
|
|
152
|
+
export const Dimensions = {
|
|
153
|
+
get(dimension: 'window' | 'screen'): ScaledSize {
|
|
154
|
+
const dimensions = currentDimensions();
|
|
155
|
+
return { ...dimensions[dimension] };
|
|
156
|
+
},
|
|
157
|
+
addEventListener(
|
|
158
|
+
eventType: 'change',
|
|
159
|
+
handler: DimensionsChangeHandler,
|
|
160
|
+
): EmitterSubscription {
|
|
161
|
+
if (eventType !== 'change') {
|
|
162
|
+
return { remove() {} };
|
|
163
|
+
}
|
|
164
|
+
dimensionsListeners.add(handler);
|
|
165
|
+
return {
|
|
166
|
+
remove() {
|
|
167
|
+
dimensionsListeners.delete(handler);
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
removeEventListener(
|
|
172
|
+
eventType: 'change',
|
|
173
|
+
handler: DimensionsChangeHandler,
|
|
174
|
+
): void {
|
|
175
|
+
if (eventType === 'change') {
|
|
176
|
+
dimensionsListeners.delete(handler);
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export const Platform = {
|
|
182
|
+
get OS(): PlatformOS {
|
|
183
|
+
return currentPlatform();
|
|
184
|
+
},
|
|
185
|
+
get Version(): string {
|
|
186
|
+
const runtime = globalThis as { __exactPlatformVersion?: unknown };
|
|
187
|
+
return typeof runtime.__exactPlatformVersion === 'string'
|
|
188
|
+
? runtime.__exactPlatformVersion
|
|
189
|
+
: '0';
|
|
190
|
+
},
|
|
191
|
+
get isPad(): boolean {
|
|
192
|
+
const dimensions = Dimensions.get('screen');
|
|
193
|
+
return Platform.OS === 'ios' && Math.min(dimensions.width, dimensions.height) >= 768;
|
|
194
|
+
},
|
|
195
|
+
get isTV(): boolean {
|
|
196
|
+
return false;
|
|
197
|
+
},
|
|
198
|
+
get isTesting(): boolean {
|
|
199
|
+
return Boolean((globalThis as { __exactIsTesting?: unknown }).__exactIsTesting);
|
|
200
|
+
},
|
|
201
|
+
select<T>(spec: PlatformSelectSpec<T>): T | undefined {
|
|
202
|
+
const os = Platform.OS;
|
|
203
|
+
return spec[os] ?? spec.default;
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
function currentColorScheme(): ColorSchemeName {
|
|
208
|
+
const state = globalThis.__exactAppearanceState;
|
|
209
|
+
return state?.colorScheme === 'dark' ? 'dark' : 'light';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const appearanceListeners = new Set<AppearanceChangeHandler>();
|
|
213
|
+
const previousAppearanceNotifier = globalThis.__exactWindowNotifyMediaChange;
|
|
214
|
+
|
|
215
|
+
function notifyAppearance(next: AppearancePreferences): void {
|
|
216
|
+
previousAppearanceNotifier?.({
|
|
217
|
+
colorScheme: next.colorScheme === 'dark' ? 'dark' : 'light',
|
|
218
|
+
});
|
|
219
|
+
const payload = { colorScheme: currentColorScheme() };
|
|
220
|
+
for (const listener of [...appearanceListeners]) {
|
|
221
|
+
listener(payload);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
globalThis.__exactWindowNotifyMediaChange = (next) => {
|
|
226
|
+
previousAppearanceNotifier?.(next);
|
|
227
|
+
const payload = { colorScheme: currentColorScheme() };
|
|
228
|
+
for (const listener of [...appearanceListeners]) {
|
|
229
|
+
listener(payload);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export const Appearance = {
|
|
234
|
+
getColorScheme(): ColorSchemeName {
|
|
235
|
+
return currentColorScheme();
|
|
236
|
+
},
|
|
237
|
+
addChangeListener(listener: AppearanceChangeHandler): EmitterSubscription {
|
|
238
|
+
appearanceListeners.add(listener);
|
|
239
|
+
return {
|
|
240
|
+
remove() {
|
|
241
|
+
appearanceListeners.delete(listener);
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
},
|
|
245
|
+
removeChangeListener(listener: AppearanceChangeHandler): void {
|
|
246
|
+
appearanceListeners.delete(listener);
|
|
247
|
+
},
|
|
248
|
+
setColorScheme(colorScheme: ColorSchemeName): void {
|
|
249
|
+
notifyAppearance({ colorScheme });
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const appStateChangeListeners = new Set<AppStateChangeHandler>();
|
|
254
|
+
const appStateFocusListeners = new Set<AppStateEventHandler>();
|
|
255
|
+
const appStateBlurListeners = new Set<AppStateEventHandler>();
|
|
256
|
+
const appStateMemoryWarningListeners = new Set<AppStateEventHandler>();
|
|
257
|
+
|
|
258
|
+
function normalizeAppState(state: unknown): AppStateStatus {
|
|
259
|
+
return state === 'active' || state === 'background' || state === 'inactive'
|
|
260
|
+
? state
|
|
261
|
+
: 'unknown';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
let currentAppState: AppStateStatus = normalizeAppState(globalThis.__exactAppState) === 'unknown'
|
|
265
|
+
? 'active'
|
|
266
|
+
: normalizeAppState(globalThis.__exactAppState);
|
|
267
|
+
|
|
268
|
+
function notifyAppState(state: AppStateStatus): void {
|
|
269
|
+
const previous = currentAppState;
|
|
270
|
+
currentAppState = normalizeAppState(state);
|
|
271
|
+
globalThis.__exactAppState = currentAppState;
|
|
272
|
+
|
|
273
|
+
if (previous !== currentAppState) {
|
|
274
|
+
for (const listener of [...appStateChangeListeners]) {
|
|
275
|
+
listener(currentAppState);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (currentAppState === 'active' && previous !== 'active') {
|
|
279
|
+
for (const listener of [...appStateFocusListeners]) {
|
|
280
|
+
listener();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (currentAppState !== 'active' && previous === 'active') {
|
|
284
|
+
for (const listener of [...appStateBlurListeners]) {
|
|
285
|
+
listener();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
globalThis.__exactReactNativeNotifyAppState = notifyAppState;
|
|
291
|
+
globalThis.__exactReactNativeNotifyMemoryWarning = () => {
|
|
292
|
+
for (const listener of [...appStateMemoryWarningListeners]) {
|
|
293
|
+
listener();
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
export const AppState = {
|
|
298
|
+
get currentState(): AppStateStatus {
|
|
299
|
+
return currentAppState;
|
|
300
|
+
},
|
|
301
|
+
addEventListener(
|
|
302
|
+
type: 'change' | 'focus' | 'blur' | 'memoryWarning',
|
|
303
|
+
listener: AppStateChangeHandler | AppStateEventHandler,
|
|
304
|
+
): EmitterSubscription {
|
|
305
|
+
const target =
|
|
306
|
+
type === 'change'
|
|
307
|
+
? appStateChangeListeners
|
|
308
|
+
: type === 'focus'
|
|
309
|
+
? appStateFocusListeners
|
|
310
|
+
: type === 'blur'
|
|
311
|
+
? appStateBlurListeners
|
|
312
|
+
: appStateMemoryWarningListeners;
|
|
313
|
+
target.add(listener as never);
|
|
314
|
+
return {
|
|
315
|
+
remove() {
|
|
316
|
+
target.delete(listener as never);
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const linkingListeners = new Set<LinkingUrlHandler>();
|
|
323
|
+
|
|
324
|
+
function notifyURL(url: string): void {
|
|
325
|
+
for (const listener of [...linkingListeners]) {
|
|
326
|
+
listener({ url });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
globalThis.__exactReactNativeNotifyURL = notifyURL;
|
|
331
|
+
|
|
332
|
+
function normalizeURL(url: string): string {
|
|
333
|
+
if (url.length === 0) {
|
|
334
|
+
return exactWindow.location.href;
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
return new URL(url, exactWindow.location.href).href;
|
|
338
|
+
} catch {
|
|
339
|
+
return url;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export const Linking = {
|
|
344
|
+
async canOpenURL(url: string): Promise<boolean> {
|
|
345
|
+
if (typeof globalThis.__exactCanOpenURL === 'function') {
|
|
346
|
+
return Boolean(await globalThis.__exactCanOpenURL(url));
|
|
347
|
+
}
|
|
348
|
+
return /^(exact|https?|mailto|tel|sms):/i.test(url);
|
|
349
|
+
},
|
|
350
|
+
async openURL(url: string): Promise<void> {
|
|
351
|
+
if (typeof globalThis.__exactOpenURL === 'function') {
|
|
352
|
+
await globalThis.__exactOpenURL(url);
|
|
353
|
+
} else {
|
|
354
|
+
exactWindow.location.assign(normalizeURL(url));
|
|
355
|
+
}
|
|
356
|
+
notifyURL(normalizeURL(url));
|
|
357
|
+
},
|
|
358
|
+
async getInitialURL(): Promise<string | null> {
|
|
359
|
+
return globalThis.__exactInitialURL ?? null;
|
|
360
|
+
},
|
|
361
|
+
addEventListener(
|
|
362
|
+
type: 'url',
|
|
363
|
+
handler: LinkingUrlHandler,
|
|
364
|
+
): EmitterSubscription {
|
|
365
|
+
if (type !== 'url') {
|
|
366
|
+
return { remove() {} };
|
|
367
|
+
}
|
|
368
|
+
linkingListeners.add(handler);
|
|
369
|
+
return {
|
|
370
|
+
remove() {
|
|
371
|
+
linkingListeners.delete(handler);
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
},
|
|
375
|
+
removeEventListener(type: 'url', handler: LinkingUrlHandler): void {
|
|
376
|
+
if (type === 'url') {
|
|
377
|
+
linkingListeners.delete(handler);
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
export default {
|
|
383
|
+
AppState,
|
|
384
|
+
Appearance,
|
|
385
|
+
Dimensions,
|
|
386
|
+
Linking,
|
|
387
|
+
Platform,
|
|
388
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ibex Runtime Entry Point
|
|
3
|
+
*
|
|
4
|
+
* This file is the entry point for building the standalone runtime bundle.
|
|
5
|
+
* It installs all web-standard globals and exposes runtime utilities.
|
|
6
|
+
*
|
|
7
|
+
* Build: vite build --config vite.config.runtime.ts
|
|
8
|
+
* Output: dist/runtime.js -> hermesc -> dist/runtime.hbc
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { installGlobals, areGlobalsInstalled, getRuntimeVersion, detectEngine, detectPlatform, runtimeInfo } from './bootstrap.js';
|
|
12
|
+
|
|
13
|
+
// Agent runtime is loaded on demand (when EXACT_AGENT_BOOT=1), not during
|
|
14
|
+
// core runtime startup. Importing it here pulled in the renderer/inspector
|
|
15
|
+
// module graph (React, Solid, Vue, etc.) and inflated the runtime bundle
|
|
16
|
+
// from ~100 KB to >2 MB, adding >10 ms of HBC evaluation on every startup.
|
|
17
|
+
// The agent bootstrap is now loaded lazily by the CLI when agent mode is
|
|
18
|
+
// explicitly requested.
|
|
19
|
+
// import '../agent/runtime-bootstrap.js';
|
|
20
|
+
|
|
21
|
+
// Install globals immediately when the runtime loads
|
|
22
|
+
installGlobals();
|
|
23
|
+
|
|
24
|
+
// Mark the runtime as installed with version info
|
|
25
|
+
(globalThis as any).__exactRuntime = {
|
|
26
|
+
version: getRuntimeVersion(),
|
|
27
|
+
installed: true,
|
|
28
|
+
installedAt: Date.now(),
|
|
29
|
+
engine: detectEngine(),
|
|
30
|
+
platform: detectPlatform(),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Expose runtime utilities on the exact global (created by native layer)
|
|
34
|
+
const g = globalThis as any;
|
|
35
|
+
if (!g.exact) {
|
|
36
|
+
g.exact = {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
g.exact.runtime = {
|
|
40
|
+
version: getRuntimeVersion(),
|
|
41
|
+
info: runtimeInfo,
|
|
42
|
+
isInstalled: areGlobalsInstalled,
|
|
43
|
+
detectEngine,
|
|
44
|
+
detectPlatform,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Log that the runtime was installed (will be visible if console is set up)
|
|
48
|
+
// Using try-catch in case console isn't ready yet
|
|
49
|
+
try {
|
|
50
|
+
if (!(globalThis as any).__exactSuppressRuntimeBanner) {
|
|
51
|
+
console.log(`[Ibex Runtime v${getRuntimeVersion()}] Installed (${detectEngine()})`);
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Console not ready yet, that's fine
|
|
55
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* requestAnimationFrame / cancelAnimationFrame implementation for Ibex runtime
|
|
3
|
+
*
|
|
4
|
+
* Schedules callbacks to run before the next repaint.
|
|
5
|
+
* @see https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#dom-animationframeprovider-requestanimationframe
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getNativeModule } from '../native/NativeModules.js';
|
|
9
|
+
import {
|
|
10
|
+
trackAnimationFrameCancelled,
|
|
11
|
+
trackAnimationFrameExecuted,
|
|
12
|
+
trackAnimationFrameRequested,
|
|
13
|
+
} from '../core/agent-state.js';
|
|
14
|
+
|
|
15
|
+
export type FrameRequestCallback = (time: DOMHighResTimeStamp) => void;
|
|
16
|
+
type DOMHighResTimeStamp = number;
|
|
17
|
+
|
|
18
|
+
// Callback storage
|
|
19
|
+
const callbacks = new Map<number, FrameRequestCallback>();
|
|
20
|
+
let nextId = 1;
|
|
21
|
+
let isScheduled = false;
|
|
22
|
+
|
|
23
|
+
// Target ~60fps = 16.67ms for runtimes that are not already display-linked.
|
|
24
|
+
const FRAME_TIME = 16;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Requests that a callback be called before the next repaint.
|
|
28
|
+
* Returns an ID that can be used to cancel the request.
|
|
29
|
+
*/
|
|
30
|
+
export function requestAnimationFrame(callback: FrameRequestCallback): number {
|
|
31
|
+
if (typeof callback !== 'function') {
|
|
32
|
+
throw new TypeError('Failed to execute \'requestAnimationFrame\': parameter 1 is not of type \'Function\'.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const id = nextId++;
|
|
36
|
+
callbacks.set(id, callback);
|
|
37
|
+
trackAnimationFrameRequested(id);
|
|
38
|
+
|
|
39
|
+
// Check for native implementation
|
|
40
|
+
const nativeScheduling = getNativeModule('scheduling');
|
|
41
|
+
if (nativeScheduling?.requestAnimationFrame) {
|
|
42
|
+
// Use native RAF which is tied to the display refresh rate
|
|
43
|
+
if (!isScheduled) {
|
|
44
|
+
isScheduled = true;
|
|
45
|
+
nativeScheduling.requestAnimationFrame(() => {
|
|
46
|
+
isScheduled = false;
|
|
47
|
+
runCallbacks();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return id;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Fallback: use setTimeout to approximate 60fps
|
|
54
|
+
if (!isScheduled) {
|
|
55
|
+
isScheduled = true;
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
isScheduled = false;
|
|
58
|
+
runCallbacks();
|
|
59
|
+
}, fallbackFrameDelayMs());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return id;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Cancels a previously scheduled animation frame request.
|
|
67
|
+
*/
|
|
68
|
+
export function cancelAnimationFrame(id: number): void {
|
|
69
|
+
if (callbacks.delete(id)) {
|
|
70
|
+
trackAnimationFrameCancelled(id);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Run all pending callbacks with the current timestamp.
|
|
76
|
+
*/
|
|
77
|
+
function runCallbacks(): void {
|
|
78
|
+
// Get current time
|
|
79
|
+
const timestamp = typeof performance !== 'undefined' && performance.now
|
|
80
|
+
? performance.now()
|
|
81
|
+
: Date.now();
|
|
82
|
+
|
|
83
|
+
// Copy callbacks to allow adding new ones during iteration
|
|
84
|
+
const pending = new Map(callbacks);
|
|
85
|
+
callbacks.clear();
|
|
86
|
+
trackAnimationFrameExecuted(Array.from(pending.keys()));
|
|
87
|
+
|
|
88
|
+
for (const [, callback] of pending) {
|
|
89
|
+
try {
|
|
90
|
+
callback(timestamp);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
// Report error but continue with other callbacks
|
|
93
|
+
console.error('Error in requestAnimationFrame callback:', error);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function fallbackFrameDelayMs(): number {
|
|
99
|
+
return (globalThis as { __exactDisplayLinkedEventLoop?: boolean })
|
|
100
|
+
.__exactDisplayLinkedEventLoop === true
|
|
101
|
+
? 0
|
|
102
|
+
: FRAME_TIME;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default { requestAnimationFrame, cancelAnimationFrame };
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* requestIdleCallback / cancelIdleCallback implementation for Ibex runtime
|
|
3
|
+
*
|
|
4
|
+
* Schedules callbacks to run during idle periods.
|
|
5
|
+
* @see https://w3c.github.io/requestidlecallback/
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getNativeModule } from '../native/NativeModules';
|
|
9
|
+
|
|
10
|
+
export interface IdleRequestOptions {
|
|
11
|
+
timeout?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface IdleDeadline {
|
|
15
|
+
readonly didTimeout: boolean;
|
|
16
|
+
timeRemaining(): number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type IdleRequestCallback = (deadline: IdleDeadline) => void;
|
|
20
|
+
|
|
21
|
+
// Callback storage
|
|
22
|
+
interface PendingCallback {
|
|
23
|
+
callback: IdleRequestCallback;
|
|
24
|
+
timeout?: number;
|
|
25
|
+
scheduledTime: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const callbacks = new Map<number, PendingCallback>();
|
|
29
|
+
let nextId = 1;
|
|
30
|
+
let isScheduled = false;
|
|
31
|
+
|
|
32
|
+
// Idle period is typically 50ms max per spec recommendation
|
|
33
|
+
const MAX_IDLE_PERIOD = 50;
|
|
34
|
+
// How often to check for idle time
|
|
35
|
+
const IDLE_CHECK_INTERVAL = 100;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Requests that a callback be called during an idle period.
|
|
39
|
+
* Returns an ID that can be used to cancel the request.
|
|
40
|
+
*/
|
|
41
|
+
export function requestIdleCallback(
|
|
42
|
+
callback: IdleRequestCallback,
|
|
43
|
+
options?: IdleRequestOptions
|
|
44
|
+
): number {
|
|
45
|
+
if (typeof callback !== 'function') {
|
|
46
|
+
throw new TypeError('Failed to execute \'requestIdleCallback\': parameter 1 is not of type \'Function\'.');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const id = nextId++;
|
|
50
|
+
const timeout = options?.timeout;
|
|
51
|
+
const scheduledTime = performance?.now?.() ?? Date.now();
|
|
52
|
+
|
|
53
|
+
callbacks.set(id, {
|
|
54
|
+
callback,
|
|
55
|
+
timeout,
|
|
56
|
+
scheduledTime,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Check for native implementation
|
|
60
|
+
const nativeScheduling = getNativeModule('scheduling');
|
|
61
|
+
if (nativeScheduling?.requestIdleCallback) {
|
|
62
|
+
nativeScheduling.requestIdleCallback((deadline: IdleDeadline) => {
|
|
63
|
+
runCallback(id, deadline);
|
|
64
|
+
}, options);
|
|
65
|
+
return id;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Fallback: simulate idle callback using setTimeout
|
|
69
|
+
scheduleIdleCheck();
|
|
70
|
+
|
|
71
|
+
// If timeout is specified, ensure callback runs within that time
|
|
72
|
+
if (timeout !== undefined && timeout > 0) {
|
|
73
|
+
setTimeout(() => {
|
|
74
|
+
const pending = callbacks.get(id);
|
|
75
|
+
if (pending) {
|
|
76
|
+
runCallback(id, createDeadline(true, 0));
|
|
77
|
+
}
|
|
78
|
+
}, timeout);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return id;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Cancels a previously scheduled idle callback.
|
|
86
|
+
*/
|
|
87
|
+
export function cancelIdleCallback(id: number): void {
|
|
88
|
+
callbacks.delete(id);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Schedule a check for idle time.
|
|
93
|
+
*/
|
|
94
|
+
function scheduleIdleCheck(): void {
|
|
95
|
+
if (isScheduled || callbacks.size === 0) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
isScheduled = true;
|
|
100
|
+
|
|
101
|
+
// Use setTimeout as a simple idle heuristic
|
|
102
|
+
// In practice, if we're running this callback, the main thread is likely idle
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
isScheduled = false;
|
|
105
|
+
runIdleCallbacks();
|
|
106
|
+
}, IDLE_CHECK_INTERVAL);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Run callbacks that can fit in the current idle period.
|
|
111
|
+
*/
|
|
112
|
+
function runIdleCallbacks(): void {
|
|
113
|
+
const now = performance?.now?.() ?? Date.now();
|
|
114
|
+
const idleDeadline = now + MAX_IDLE_PERIOD;
|
|
115
|
+
|
|
116
|
+
// Process callbacks until we run out of time or callbacks
|
|
117
|
+
for (const [id, pending] of callbacks) {
|
|
118
|
+
const remaining = idleDeadline - (performance?.now?.() ?? Date.now());
|
|
119
|
+
|
|
120
|
+
if (remaining <= 0) {
|
|
121
|
+
// No more idle time, schedule another check
|
|
122
|
+
scheduleIdleCheck();
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check if this callback has timed out
|
|
127
|
+
const elapsed = now - pending.scheduledTime;
|
|
128
|
+
const didTimeout = pending.timeout !== undefined && elapsed >= pending.timeout;
|
|
129
|
+
|
|
130
|
+
runCallback(id, createDeadline(didTimeout, remaining));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Run a specific callback and remove it from the pending set.
|
|
136
|
+
*/
|
|
137
|
+
function runCallback(id: number, deadline: IdleDeadline): void {
|
|
138
|
+
const pending = callbacks.get(id);
|
|
139
|
+
if (!pending) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
callbacks.delete(id);
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
pending.callback(deadline);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('Error in requestIdleCallback:', error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Create an IdleDeadline object.
|
|
154
|
+
*/
|
|
155
|
+
function createDeadline(didTimeout: boolean, remainingTime: number): IdleDeadline {
|
|
156
|
+
const deadlineEnd = (performance?.now?.() ?? Date.now()) + remainingTime;
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
didTimeout,
|
|
160
|
+
timeRemaining(): number {
|
|
161
|
+
const now = performance?.now?.() ?? Date.now();
|
|
162
|
+
return Math.max(0, deadlineEnd - now);
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export default { requestIdleCallback, cancelIdleCallback };
|