@ccheever/exact-ibex-runtime 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/package.json +63 -0
  2. package/src/abort/AbortController.ts +23 -0
  3. package/src/abort/AbortSignal.ts +152 -0
  4. package/src/abort/index.ts +2 -0
  5. package/src/accessibility.ts +12 -0
  6. package/src/arraybuffer-detach.ts +109 -0
  7. package/src/base64/base64.ts +168 -0
  8. package/src/base64/index.ts +1 -0
  9. package/src/blob/Blob.ts +259 -0
  10. package/src/blob/File.ts +59 -0
  11. package/src/blob/FormData.ts +323 -0
  12. package/src/blob/index.ts +3 -0
  13. package/src/bootstrap.ts +1946 -0
  14. package/src/broadcast/BroadcastChannel.ts +280 -0
  15. package/src/broadcast/index.ts +5 -0
  16. package/src/cache/Cache.ts +349 -0
  17. package/src/cache/CacheStorage.ts +89 -0
  18. package/src/cache/index.ts +27 -0
  19. package/src/camera/index.ts +6202 -0
  20. package/src/camera/processor.worker.ts +194 -0
  21. package/src/camera/scene.ts +195 -0
  22. package/src/clipboard/Clipboard.ts +129 -0
  23. package/src/clipboard/ClipboardItem.ts +97 -0
  24. package/src/clipboard/index.ts +6 -0
  25. package/src/clone/index.ts +1 -0
  26. package/src/clone/structuredClone.ts +389 -0
  27. package/src/clone/transferableSymbols.ts +2 -0
  28. package/src/compression/CompressionStream.ts +146 -0
  29. package/src/compression/DecompressionStream.ts +342 -0
  30. package/src/compression/index.ts +4 -0
  31. package/src/console/Console.ts +341 -0
  32. package/src/console/index.ts +2 -0
  33. package/src/core/accessibility-state.ts +263 -0
  34. package/src/core/accessibility.ts +184 -0
  35. package/src/core/agent-state.ts +37 -0
  36. package/src/core/diagnostics-logs.ts +144 -0
  37. package/src/core/host-call-bridge.ts +16 -0
  38. package/src/core/i18n-helpers.ts +189 -0
  39. package/src/core/locale-state.ts +253 -0
  40. package/src/core/locale.ts +95 -0
  41. package/src/crypto/Crypto.ts +2743 -0
  42. package/src/crypto/index.ts +1 -0
  43. package/src/diagnostics/logs.ts +7 -0
  44. package/src/encoding/TextDecoder.ts +1181 -0
  45. package/src/encoding/TextDecoderStream.ts +58 -0
  46. package/src/encoding/TextEncoder.ts +180 -0
  47. package/src/encoding/TextEncoderStream.ts +39 -0
  48. package/src/encoding/index.ts +8 -0
  49. package/src/events/CloseEvent.ts +91 -0
  50. package/src/events/DOMException.ts +409 -0
  51. package/src/events/ErrorEvent.ts +39 -0
  52. package/src/events/Event.ts +151 -0
  53. package/src/events/EventTarget.ts +280 -0
  54. package/src/events/FocusEvent.ts +27 -0
  55. package/src/events/KeyboardEvent.ts +46 -0
  56. package/src/events/MessageEvent.ts +61 -0
  57. package/src/events/ProgressEvent.ts +33 -0
  58. package/src/events/PromiseRejectionEvent.ts +31 -0
  59. package/src/events/index.ts +52 -0
  60. package/src/eventsource/EventSource.ts +371 -0
  61. package/src/eventsource/index.ts +2 -0
  62. package/src/fetch/Headers.ts +642 -0
  63. package/src/fetch/Request.ts +760 -0
  64. package/src/fetch/Response.ts +543 -0
  65. package/src/fetch/body.ts +1256 -0
  66. package/src/fetch/cookie-jar.ts +566 -0
  67. package/src/fetch/demo.ts +207 -0
  68. package/src/fetch/errors.ts +101 -0
  69. package/src/fetch/fetch.ts +2610 -0
  70. package/src/fetch/index.ts +101 -0
  71. package/src/fetch/native-bridge.ts +65 -0
  72. package/src/fetch/types.ts +258 -0
  73. package/src/filereader/FileReader.ts +236 -0
  74. package/src/filereader/index.ts +1 -0
  75. package/src/fs/Dirent.ts +39 -0
  76. package/src/fs/ExactFile.ts +450 -0
  77. package/src/fs/Stats.ts +80 -0
  78. package/src/fs/index.ts +944 -0
  79. package/src/fs/promises.ts +386 -0
  80. package/src/fs/shared.ts +328 -0
  81. package/src/http-server/index.js +697 -0
  82. package/src/http-server/index.ts +27 -0
  83. package/src/identity.generated.ts +14 -0
  84. package/src/index.ts +283 -0
  85. package/src/indexeddb/IDBCursor.ts +188 -0
  86. package/src/indexeddb/IDBDatabase.ts +343 -0
  87. package/src/indexeddb/IDBFactory.ts +269 -0
  88. package/src/indexeddb/IDBIndex.ts +194 -0
  89. package/src/indexeddb/IDBKeyRange.ts +109 -0
  90. package/src/indexeddb/IDBObjectStore.ts +468 -0
  91. package/src/indexeddb/IDBRequest.ts +163 -0
  92. package/src/indexeddb/IDBTransaction.ts +207 -0
  93. package/src/indexeddb/index.ts +34 -0
  94. package/src/indexeddb/utils.ts +52 -0
  95. package/src/inspect/index.ts +1 -0
  96. package/src/inspect/inspect.ts +465 -0
  97. package/src/internal/detect.ts +104 -0
  98. package/src/locale.ts +10 -0
  99. package/src/location/index.ts +1059 -0
  100. package/src/locks/LockManager.ts +460 -0
  101. package/src/locks/index.ts +12 -0
  102. package/src/media/VideoFrame.ts +58 -0
  103. package/src/messaging/MessageChannel.ts +31 -0
  104. package/src/messaging/MessagePort.ts +180 -0
  105. package/src/messaging/index.ts +2 -0
  106. package/src/messaging.ts +247 -0
  107. package/src/native/NativeModules.ts +354 -0
  108. package/src/native/index.ts +1 -0
  109. package/src/navigator/Navigator.ts +351 -0
  110. package/src/navigator/index.ts +1 -0
  111. package/src/node/Buffer.ts +1786 -0
  112. package/src/node/index.ts +4 -0
  113. package/src/node/path.ts +495 -0
  114. package/src/node/process.ts +2528 -0
  115. package/src/performance/Performance.ts +532 -0
  116. package/src/performance/index.ts +21 -0
  117. package/src/polyfills/array.ts +236 -0
  118. package/src/polyfills/arraybuffer.ts +172 -0
  119. package/src/polyfills/groupby.ts +85 -0
  120. package/src/polyfills/index.ts +85 -0
  121. package/src/polyfills/intl.ts +1956 -0
  122. package/src/polyfills/iterator.ts +479 -0
  123. package/src/polyfills/promise.ts +37 -0
  124. package/src/polyfills/set.ts +245 -0
  125. package/src/polyfills/string.ts +85 -0
  126. package/src/polyfills/typedarray.ts +110 -0
  127. package/src/promise-rejection-tracking.ts +464 -0
  128. package/src/react-native/index.ts +388 -0
  129. package/src/runtime-entry.ts +55 -0
  130. package/src/scheduling/AnimationFrame.ts +105 -0
  131. package/src/scheduling/IdleCallback.ts +167 -0
  132. package/src/scheduling/index.ts +13 -0
  133. package/src/security/Capabilities.ts +1146 -0
  134. package/src/security/Permissions.ts +392 -0
  135. package/src/security/capability-bits.generated.ts +63 -0
  136. package/src/security/index.ts +16 -0
  137. package/src/sqlite/Database.ts +456 -0
  138. package/src/sqlite/Statement.ts +206 -0
  139. package/src/sqlite/constants.ts +79 -0
  140. package/src/sqlite/errors.ts +25 -0
  141. package/src/sqlite/index.ts +34 -0
  142. package/src/sqlite/module.js +438 -0
  143. package/src/storage/Storage.ts +291 -0
  144. package/src/storage/StorageManager.ts +91 -0
  145. package/src/storage/index.ts +3 -0
  146. package/src/stream-compat.ts +47 -0
  147. package/src/streams/ReadableStream.ts +4131 -0
  148. package/src/streams/TransformStream.ts +375 -0
  149. package/src/streams/WritableStream.ts +866 -0
  150. package/src/streams/index.ts +41 -0
  151. package/src/timers/Timers.ts +296 -0
  152. package/src/timers/index.ts +11 -0
  153. package/src/url/URL.ts +656 -0
  154. package/src/url/URLPattern.ts +850 -0
  155. package/src/url/URLSearchParams.ts +244 -0
  156. package/src/url/index.ts +9 -0
  157. package/src/websocket/WebSocket.ts +770 -0
  158. package/src/websocket/WebSocketError.ts +52 -0
  159. package/src/websocket/WebSocketStream.ts +628 -0
  160. package/src/websocket/index.ts +7 -0
  161. package/src/window/index.ts +872 -0
@@ -0,0 +1,450 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * ExactFile - Bun.file() compatible lazy file handle.
4
+ *
5
+ * Like Bun.file(), this returns a lazy reference to a file on disk.
6
+ * Nothing is read until you call .text(), .json(), .arrayBuffer(), .bytes(), etc.
7
+ *
8
+ * Usage:
9
+ * const f = Exact.file("package.json");
10
+ * const text = await f.text();
11
+ * const data = await f.json();
12
+ * const exists = await f.exists();
13
+ * console.log(f.name, f.size, f.type);
14
+ */
15
+
16
+ declare const __exactReadFile: (path: string) => Uint8Array;
17
+ declare const __exactWriteFile: (path: string, data: Uint8Array) => void;
18
+ declare const __exactStat: (path: string) => string;
19
+ declare const __exactAccess: (path: string, mode: number) => void;
20
+ declare const __exactEnsureFs: (() => void) | undefined;
21
+
22
+ // Common extension → MIME type map
23
+ const MIME_TYPES: Record<string, string> = {
24
+ '.txt': 'text/plain;charset=utf-8',
25
+ '.html': 'text/html;charset=utf-8',
26
+ '.htm': 'text/html;charset=utf-8',
27
+ '.css': 'text/css;charset=utf-8',
28
+ '.js': 'text/javascript;charset=utf-8',
29
+ '.mjs': 'text/javascript;charset=utf-8',
30
+ '.cjs': 'text/javascript;charset=utf-8',
31
+ '.ts': 'text/typescript;charset=utf-8',
32
+ '.tsx': 'text/typescript;charset=utf-8',
33
+ '.jsx': 'text/javascript;charset=utf-8',
34
+ '.json': 'application/json;charset=utf-8',
35
+ '.jsonl': 'application/x-ndjson;charset=utf-8',
36
+ '.xml': 'application/xml;charset=utf-8',
37
+ '.svg': 'image/svg+xml;charset=utf-8',
38
+ '.png': 'image/png',
39
+ '.jpg': 'image/jpeg',
40
+ '.jpeg': 'image/jpeg',
41
+ '.gif': 'image/gif',
42
+ '.webp': 'image/webp',
43
+ '.avif': 'image/avif',
44
+ '.ico': 'image/x-icon',
45
+ '.bmp': 'image/bmp',
46
+ '.tiff': 'image/tiff',
47
+ '.tif': 'image/tiff',
48
+ '.pdf': 'application/pdf',
49
+ '.zip': 'application/zip',
50
+ '.gz': 'application/gzip',
51
+ '.tar': 'application/x-tar',
52
+ '.wasm': 'application/wasm',
53
+ '.mp3': 'audio/mpeg',
54
+ '.mp4': 'video/mp4',
55
+ '.webm': 'video/webm',
56
+ '.ogg': 'audio/ogg',
57
+ '.wav': 'audio/wav',
58
+ '.woff': 'font/woff',
59
+ '.woff2': 'font/woff2',
60
+ '.ttf': 'font/ttf',
61
+ '.otf': 'font/otf',
62
+ '.md': 'text/markdown;charset=utf-8',
63
+ '.yaml': 'text/yaml;charset=utf-8',
64
+ '.yml': 'text/yaml;charset=utf-8',
65
+ '.toml': 'text/toml;charset=utf-8',
66
+ '.csv': 'text/csv;charset=utf-8',
67
+ '.sh': 'application/x-sh',
68
+ '.sql': 'application/sql',
69
+ '.wgsl': 'text/wgsl;charset=utf-8',
70
+ '.glsl': 'text/plain;charset=utf-8',
71
+ };
72
+
73
+ function getMimeType(path: string): string {
74
+ const dot = path.lastIndexOf('.');
75
+ if (dot === -1) return 'application/octet-stream';
76
+ const ext = path.slice(dot).toLowerCase();
77
+ return MIME_TYPES[ext] || 'application/octet-stream';
78
+ }
79
+
80
+ function decodeBytes(bytes: Uint8Array): string {
81
+ if (typeof TextDecoder !== 'undefined') {
82
+ const decoded = new TextDecoder('utf-8').decode(bytes);
83
+ if (
84
+ bytes.length >= 3 &&
85
+ bytes[0] === 0xef &&
86
+ bytes[1] === 0xbb &&
87
+ bytes[2] === 0xbf &&
88
+ decoded.charCodeAt(0) !== 0xfeff
89
+ ) {
90
+ return `\uFEFF${decoded}`;
91
+ }
92
+ return decoded;
93
+ }
94
+ let result = '';
95
+ for (let i = 0; i < bytes.length; i++) {
96
+ result += String.fromCharCode(bytes[i]);
97
+ }
98
+ return result;
99
+ }
100
+
101
+ function toUint8Array(data: string | Uint8Array | ArrayBuffer | ArrayBufferView): Uint8Array {
102
+ if (typeof data === 'string') {
103
+ if (typeof TextEncoder !== 'undefined') {
104
+ return new TextEncoder().encode(data);
105
+ }
106
+ const buf = new Uint8Array(data.length * 3);
107
+ let offset = 0;
108
+ for (let i = 0; i < data.length; i++) {
109
+ const code = data.charCodeAt(i);
110
+ if (code < 0x80) {
111
+ buf[offset++] = code;
112
+ } else if (code < 0x800) {
113
+ buf[offset++] = 0xc0 | (code >> 6);
114
+ buf[offset++] = 0x80 | (code & 0x3f);
115
+ } else {
116
+ buf[offset++] = 0xe0 | (code >> 12);
117
+ buf[offset++] = 0x80 | ((code >> 6) & 0x3f);
118
+ buf[offset++] = 0x80 | (code & 0x3f);
119
+ }
120
+ }
121
+ return buf.slice(0, offset);
122
+ }
123
+ if (data instanceof Uint8Array) return data;
124
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
125
+ if (ArrayBuffer.isView(data)) return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
126
+ return new Uint8Array(data as any);
127
+ }
128
+
129
+ let _exactFsInitialized = false;
130
+
131
+ function ensureExactFs(): void {
132
+ if (_exactFsInitialized) {
133
+ return;
134
+ }
135
+
136
+ const g = globalThis as any;
137
+ const hasFsPrimitives =
138
+ typeof g.__exactReadFile === 'function' &&
139
+ typeof g.__exactWriteFile === 'function' &&
140
+ typeof g.__exactStat === 'function' &&
141
+ typeof g.__exactAccess === 'function';
142
+
143
+ if (!hasFsPrimitives && typeof g.__exactEnsureFs === 'function') {
144
+ g.__exactEnsureFs();
145
+ }
146
+
147
+ _exactFsInitialized =
148
+ typeof g.__exactReadFile === 'function' ||
149
+ typeof g.__exactWriteFile === 'function' ||
150
+ typeof g.__exactStat === 'function' ||
151
+ typeof g.__exactAccess === 'function';
152
+ }
153
+
154
+ export interface ExactFileOptions {
155
+ type?: string;
156
+ }
157
+
158
+ type ExactFilePathLike =
159
+ | string
160
+ | {
161
+ pathname?: string;
162
+ href?: string;
163
+ };
164
+
165
+ function normalizeExactFilePath(path: ExactFilePathLike): string {
166
+ if (typeof path === 'string') {
167
+ return path;
168
+ }
169
+
170
+ if (path && typeof path === 'object') {
171
+ if (typeof path.pathname === 'string' && path.pathname) {
172
+ return decodeURIComponent(path.pathname);
173
+ }
174
+ if (typeof path.href === 'string' && path.href.startsWith('file:')) {
175
+ return decodeURIComponent(path.href.replace(/^file:\/\//, ''));
176
+ }
177
+ }
178
+
179
+ return String(path);
180
+ }
181
+
182
+ /**
183
+ * Lazy file reference compatible with Bun.file().
184
+ *
185
+ * Properties:
186
+ * - name: string (the file path)
187
+ * - size: number (file size in bytes, 0 if not found)
188
+ * - type: string (MIME type inferred from extension)
189
+ * - lastModified: number (mtime in ms since epoch)
190
+ *
191
+ * Methods:
192
+ * - text(): Promise<string>
193
+ * - json(): Promise<any>
194
+ * - arrayBuffer(): Promise<ArrayBuffer>
195
+ * - bytes(): Promise<Uint8Array>
196
+ * - slice(begin?, end?): Blob
197
+ * - exists(): Promise<boolean>
198
+ * - stat(): Promise<{size, mtime_ms, ...} | null>
199
+ */
200
+ export class ExactFile {
201
+ readonly name: string;
202
+ readonly type: string;
203
+
204
+ constructor(path: ExactFilePathLike, options?: ExactFileOptions) {
205
+ const normalizedPath = normalizeExactFilePath(path);
206
+ this.name = normalizedPath;
207
+ this.type = options?.type || getMimeType(normalizedPath);
208
+ }
209
+
210
+ get size(): number {
211
+ ensureExactFs();
212
+ try {
213
+ const json = __exactStat(this.name);
214
+ const info = JSON.parse(json);
215
+ return info.size;
216
+ } catch {
217
+ return 0;
218
+ }
219
+ }
220
+
221
+ get lastModified(): number {
222
+ ensureExactFs();
223
+ try {
224
+ const json = __exactStat(this.name);
225
+ const info = JSON.parse(json);
226
+ return info.mtime_ms;
227
+ } catch {
228
+ return 0;
229
+ }
230
+ }
231
+
232
+ async text(): Promise<string> {
233
+ ensureExactFs();
234
+ const bytes = __exactReadFile(this.name);
235
+ return decodeBytes(bytes);
236
+ }
237
+
238
+ async json(): Promise<any> {
239
+ const text = await this.text();
240
+ return JSON.parse(text);
241
+ }
242
+
243
+ async arrayBuffer(): Promise<ArrayBuffer> {
244
+ ensureExactFs();
245
+ const bytes = __exactReadFile(this.name);
246
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
247
+ }
248
+
249
+ async bytes(): Promise<Uint8Array> {
250
+ ensureExactFs();
251
+ return __exactReadFile(this.name);
252
+ }
253
+
254
+ _getBytes(): Uint8Array {
255
+ ensureExactFs();
256
+ return __exactReadFile(this.name);
257
+ }
258
+
259
+ async exists(): Promise<boolean> {
260
+ ensureExactFs();
261
+ try {
262
+ __exactAccess(this.name, 0);
263
+ return true;
264
+ } catch {
265
+ return false;
266
+ }
267
+ }
268
+
269
+ async stat(): Promise<{
270
+ size: number;
271
+ mtime_ms: number;
272
+ is_dir: boolean;
273
+ is_file: boolean;
274
+ mode: number;
275
+ } | null> {
276
+ ensureExactFs();
277
+ try {
278
+ const json = __exactStat(this.name);
279
+ return JSON.parse(json);
280
+ } catch {
281
+ return null;
282
+ }
283
+ }
284
+
285
+ slice(begin?: number, end?: number, contentType?: string): Blob {
286
+ ensureExactFs();
287
+ const bytes = __exactReadFile(this.name);
288
+ const sliced = bytes.slice(begin ?? 0, end ?? bytes.length);
289
+ return new Blob([sliced], { type: contentType || this.type });
290
+ }
291
+
292
+ // stream() returns a ReadableStream if available
293
+ stream(): ReadableStream<Uint8Array> {
294
+ ensureExactFs();
295
+ const name = this.name;
296
+ return new ReadableStream<Uint8Array>({
297
+ start(controller) {
298
+ try {
299
+ const bytes = __exactReadFile(name);
300
+ controller.enqueue(bytes);
301
+ controller.close();
302
+ } catch (err) {
303
+ controller.error(err);
304
+ }
305
+ },
306
+ });
307
+ }
308
+
309
+ // writer() returns a simple FileSink-like object
310
+ writer(): { write(data: string | Uint8Array | ArrayBuffer): number; end(): void; flush(): void } {
311
+ const name = this.name;
312
+ let started = false;
313
+ return {
314
+ write(data: string | Uint8Array | ArrayBuffer): number {
315
+ ensureExactFs();
316
+ const bytes = toUint8Array(data);
317
+ if (!started) {
318
+ __exactWriteFile(name, bytes);
319
+ started = true;
320
+ } else {
321
+ // Append after first write
322
+ const g = globalThis as any;
323
+ if (typeof g.__exactAppendFile === 'function') {
324
+ g.__exactAppendFile(name, bytes);
325
+ } else {
326
+ // Fallback: read + concat + write
327
+ const existing = __exactReadFile(name);
328
+ const combined = new Uint8Array(existing.length + bytes.length);
329
+ combined.set(existing);
330
+ combined.set(bytes, existing.length);
331
+ __exactWriteFile(name, combined);
332
+ }
333
+ }
334
+ return bytes.length;
335
+ },
336
+ end() { /* no-op, writes are synchronous */ },
337
+ flush() { /* no-op, writes are synchronous */ },
338
+ };
339
+ }
340
+
341
+ toString(): string {
342
+ return `ExactFile("${this.name}")`;
343
+ }
344
+
345
+ // Make it work with JSON.stringify
346
+ toJSON(): { name: string; type: string; size: number; lastModified: number } {
347
+ return {
348
+ name: this.name,
349
+ type: this.type,
350
+ size: this.size,
351
+ lastModified: this.lastModified,
352
+ };
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Exact.write(dest, data) - Write data to a file.
358
+ * Compatible with Bun.write().
359
+ *
360
+ * @param dest - File path or ExactFile object
361
+ * @param data - String, Uint8Array, ArrayBuffer, Blob, or Response
362
+ * @returns Promise<number> - bytes written
363
+ */
364
+ export async function exactWrite(
365
+ dest: string | ExactFile,
366
+ data: string | Uint8Array | ArrayBuffer | ArrayBufferView | Blob | Response,
367
+ ): Promise<number> {
368
+ ensureExactFs();
369
+ const path = typeof dest === 'string' ? dest : dest.name;
370
+
371
+ let bytes: Uint8Array;
372
+
373
+ if (typeof data === 'string') {
374
+ bytes = toUint8Array(data);
375
+ } else if (data instanceof Uint8Array) {
376
+ bytes = data;
377
+ } else if (data instanceof ArrayBuffer) {
378
+ bytes = new Uint8Array(data);
379
+ } else if (ArrayBuffer.isView(data)) {
380
+ bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
381
+ } else if (typeof Blob !== 'undefined' && data instanceof Blob) {
382
+ const ab = await data.arrayBuffer();
383
+ bytes = new Uint8Array(ab);
384
+ } else if (typeof Response !== 'undefined' && data instanceof Response) {
385
+ const ab = await data.arrayBuffer();
386
+ bytes = new Uint8Array(ab);
387
+ } else {
388
+ bytes = toUint8Array(String(data));
389
+ }
390
+
391
+ __exactWriteFile(path, bytes);
392
+ return bytes.length;
393
+ }
394
+
395
+ function defineGlobalValue(target: any, key: string, value: unknown): void {
396
+ Object.defineProperty(target, key, {
397
+ value,
398
+ configurable: true,
399
+ enumerable: true,
400
+ writable: true,
401
+ });
402
+ }
403
+
404
+ export interface ExactGlobalInstallResult {
405
+ file: (path: ExactFilePathLike, options?: ExactFileOptions) => ExactFile;
406
+ write: typeof exactWrite;
407
+ }
408
+
409
+ /**
410
+ * Install Exact and Bun globals with file() and write() methods.
411
+ */
412
+ export function installExactGlobal(): ExactGlobalInstallResult {
413
+ const g = globalThis as any;
414
+
415
+ // Create or extend Exact object
416
+ if (!g.Exact) {
417
+ g.Exact = {};
418
+ }
419
+
420
+ const file = (path: ExactFilePathLike, options?: ExactFileOptions) => new ExactFile(path, options);
421
+ const write = exactWrite;
422
+
423
+ defineGlobalValue(g.Exact, 'file', file);
424
+ defineGlobalValue(g.Exact, 'write', write);
425
+
426
+ // Version info
427
+ if (!g.Exact.version) g.Exact.version = '0.1.0';
428
+ if (!g.Exact.platform) g.Exact.platform = 'cli';
429
+
430
+ // Alias Bun to Exact (Bun may be read-only in Bun's own test runner)
431
+ try {
432
+ if (!g.Bun) {
433
+ g.Bun = g.Exact;
434
+ } else {
435
+ // Augment existing Bun object with Exact.file and Exact.write
436
+ const bunFileDescriptor = Object.getOwnPropertyDescriptor(g.Bun, 'file');
437
+ if (!bunFileDescriptor || bunFileDescriptor.get || bunFileDescriptor.value == null) {
438
+ defineGlobalValue(g.Bun, 'file', file);
439
+ }
440
+ const bunWriteDescriptor = Object.getOwnPropertyDescriptor(g.Bun, 'write');
441
+ if (!bunWriteDescriptor || bunWriteDescriptor.get || bunWriteDescriptor.value == null) {
442
+ defineGlobalValue(g.Bun, 'write', write);
443
+ }
444
+ }
445
+ } catch (_e) {
446
+ // Bun is read-only (e.g. in Bun's native test runner) — skip aliasing
447
+ }
448
+
449
+ return { file, write };
450
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Node.js-compatible Stats class.
3
+ */
4
+
5
+ export class Stats {
6
+ dev: number = 0;
7
+ ino: number = 0;
8
+ mode: number;
9
+ nlink: number = 1;
10
+ uid: number = 0;
11
+ gid: number = 0;
12
+ rdev: number = 0;
13
+ size: number;
14
+ blksize: number = 4096;
15
+ blocks: number;
16
+ atimeMs: number;
17
+ mtimeMs: number;
18
+ ctimeMs: number;
19
+ birthtimeMs: number;
20
+ atime: Date;
21
+ mtime: Date;
22
+ ctime: Date;
23
+ birthtime: Date;
24
+
25
+ private _isFile: boolean;
26
+ private _isDirectory: boolean;
27
+ private _isSymlink: boolean;
28
+
29
+ constructor(raw: {
30
+ size: number;
31
+ mtime_ms: number;
32
+ is_dir: boolean;
33
+ is_file: boolean;
34
+ is_symlink?: boolean;
35
+ mode: number;
36
+ }) {
37
+ this.size = raw.size;
38
+ this.mode = raw.mode;
39
+ this.blocks = Math.ceil(raw.size / 512);
40
+ this.mtimeMs = raw.mtime_ms;
41
+ this.atimeMs = raw.mtime_ms;
42
+ this.ctimeMs = raw.mtime_ms;
43
+ this.birthtimeMs = raw.mtime_ms;
44
+ this.mtime = new Date(raw.mtime_ms);
45
+ this.atime = new Date(raw.mtime_ms);
46
+ this.ctime = new Date(raw.mtime_ms);
47
+ this.birthtime = new Date(raw.mtime_ms);
48
+ this._isFile = raw.is_file;
49
+ this._isDirectory = raw.is_dir;
50
+ this._isSymlink = raw.is_symlink ?? false;
51
+ }
52
+
53
+ isFile(): boolean {
54
+ return this._isFile;
55
+ }
56
+
57
+ isDirectory(): boolean {
58
+ return this._isDirectory;
59
+ }
60
+
61
+ isBlockDevice(): boolean {
62
+ return false;
63
+ }
64
+
65
+ isCharacterDevice(): boolean {
66
+ return false;
67
+ }
68
+
69
+ isSymbolicLink(): boolean {
70
+ return this._isSymlink;
71
+ }
72
+
73
+ isFIFO(): boolean {
74
+ return false;
75
+ }
76
+
77
+ isSocket(): boolean {
78
+ return false;
79
+ }
80
+ }