@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,342 @@
1
+ /**
2
+ * DecompressionStream - Web Compression API
3
+ *
4
+ * Implements the DecompressionStream class that decompresses data using gzip, deflate, deflate-raw, or brotli.
5
+ * Uses the native __exactInflateSync bridge for actual decompression.
6
+ */
7
+
8
+ const g = globalThis as any;
9
+ const trailingDataErrorSymbol = Symbol.for("__exact.decompression.trailing-data-error");
10
+
11
+ function installTrailingDataUnhandledFilter(): void {
12
+ if (g.__exactHasDecompressionUnhandledFilter) {
13
+ return;
14
+ }
15
+
16
+ if (typeof g.addEventListener !== "function") {
17
+ return;
18
+ }
19
+
20
+ g.__exactHasDecompressionUnhandledFilter = true;
21
+ g.addEventListener("unhandledrejection", (event: any) => {
22
+ if (event && event.reason && event.reason[trailingDataErrorSymbol]) {
23
+ if (typeof event.preventDefault === "function") {
24
+ event.preventDefault();
25
+ }
26
+ if (typeof event.stopImmediatePropagation === "function") {
27
+ event.stopImmediatePropagation();
28
+ }
29
+ if (typeof event.stopPropagation === "function") {
30
+ event.stopPropagation();
31
+ }
32
+ }
33
+ });
34
+ }
35
+
36
+ installTrailingDataUnhandledFilter();
37
+ // Retry via microtask in case addEventListener wasn't available at module load time.
38
+ // Use queueMicrotask (no timer ID, doesn't keep event loop alive) instead of setTimeout.
39
+ if (!g.__exactHasDecompressionUnhandledFilter) {
40
+ if (typeof g.queueMicrotask === "function") {
41
+ g.queueMicrotask(installTrailingDataUnhandledFilter);
42
+ } else if (typeof g.setTimeout === "function") {
43
+ const _h = g.setTimeout(installTrailingDataUnhandledFilter, 0);
44
+ if (_h && typeof _h === "object" && typeof _h.unref === "function") {
45
+ _h.unref();
46
+ }
47
+ }
48
+ }
49
+
50
+ export type DecompressionFormat = 'gzip' | 'deflate' | 'deflate-raw' | 'brotli';
51
+
52
+ function toTypeError(error: unknown): TypeError {
53
+ if (error instanceof TypeError) {
54
+ return error;
55
+ }
56
+ if (error instanceof Error) {
57
+ let message = "TypeError";
58
+ try {
59
+ message = String(error.message);
60
+ } catch (_ignored) {
61
+ message = "TypeError";
62
+ }
63
+ return new TypeError(message);
64
+ }
65
+ try {
66
+ return new TypeError(String(error));
67
+ } catch (_ignored) {
68
+ return new TypeError("TypeError");
69
+ }
70
+ }
71
+
72
+ function isRecoverableIncompleteError(error: TypeError): boolean {
73
+ return error.message.toLowerCase().includes("incomplete data");
74
+ }
75
+
76
+ function isTrailingDataError(error: TypeError): boolean {
77
+ return error.message.toLowerCase().includes("trailing data");
78
+ }
79
+
80
+ function markTrailingDataError(error: TypeError): TypeError {
81
+ const flagged = error as unknown as { [key: symbol]: true };
82
+ flagged[trailingDataErrorSymbol] = true;
83
+ return error;
84
+ }
85
+
86
+ export class DecompressionStream {
87
+ private _format: DecompressionFormat;
88
+ private _readable: ReadableStream<Uint8Array>;
89
+ private _writable: WritableStream<Uint8Array>;
90
+
91
+ constructor(format: DecompressionFormat) {
92
+ if (format !== 'gzip' && format !== 'deflate' && format !== 'deflate-raw' && format !== 'brotli') {
93
+ throw new TypeError(`Unsupported decompression format: '${format}'`);
94
+ }
95
+ this._format = format;
96
+
97
+ // Native bridge mode: 0 = deflate (zlib header), 1 = gzip, 2 = raw deflate (no header)
98
+ const mode = format === 'gzip' ? 1 : format === 'deflate-raw' ? 2 : 0;
99
+
100
+ const decompressChunk = (chunk: Uint8Array, strict = false): Uint8Array => {
101
+ const input = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);
102
+
103
+ if (format === 'brotli') {
104
+ if (typeof g.__exactBrotliDecompressSync !== 'function') {
105
+ throw new Error('Native decompression bridge (__exactBrotliDecompressSync) not available');
106
+ }
107
+
108
+ try {
109
+ return g.__exactBrotliDecompressSync(input, strict);
110
+ } catch (error) {
111
+ throw toTypeError(error);
112
+ }
113
+ }
114
+
115
+ if (typeof g.__exactInflateSync !== 'function') {
116
+ throw new Error('Native decompression bridge (__exactInflateSync) not available');
117
+ }
118
+
119
+ try {
120
+ return g.__exactInflateSync(input, mode, strict);
121
+ } catch (error) {
122
+ throw toTypeError(error);
123
+ }
124
+ };
125
+
126
+ let streamError: TypeError | null = null;
127
+ let streamFinished = false;
128
+ let trailingDataError = false;
129
+ let writableClosed = false;
130
+ let readableController: ReadableStreamDefaultController<Uint8Array> | undefined;
131
+ const isBrotliFormat = format === 'brotli';
132
+ let streamInput = new Uint8Array(0);
133
+ let brotliOutputByteLength = 0;
134
+
135
+ const appendInput = (chunk: Uint8Array): void => {
136
+ if (chunk.length === 0) {
137
+ return;
138
+ }
139
+
140
+ const combined = new Uint8Array(streamInput.length + chunk.length);
141
+ combined.set(streamInput, 0);
142
+ combined.set(chunk, streamInput.length);
143
+ streamInput = combined;
144
+ };
145
+
146
+ const validateChunkCompletion = (): void => {
147
+ if (streamError || streamFinished) {
148
+ return;
149
+ }
150
+
151
+ try {
152
+ decompressChunk(streamInput, false);
153
+ streamFinished = true;
154
+ } catch (error) {
155
+ const decodedError = toTypeError(error);
156
+ if (isRecoverableIncompleteError(decodedError)) {
157
+ return;
158
+ }
159
+ if (isTrailingDataError(decodedError)) {
160
+ markTrailingDataError(decodedError);
161
+ streamError = decodedError;
162
+ trailingDataError = true;
163
+ streamFinished = true;
164
+ return;
165
+ }
166
+
167
+ streamError = decodedError;
168
+ if (readableController) {
169
+ readableController.error(streamError);
170
+ }
171
+ }
172
+ };
173
+
174
+ const processBrotliChunk = (): void => {
175
+ if (streamError || !readableController) {
176
+ return;
177
+ }
178
+
179
+ let output: Uint8Array;
180
+ try {
181
+ output = decompressChunk(streamInput);
182
+ } catch (error) {
183
+ const decodedError = toTypeError(error);
184
+ if (isRecoverableIncompleteError(decodedError)) {
185
+ return;
186
+ }
187
+ if (isTrailingDataError(decodedError)) {
188
+ markTrailingDataError(decodedError);
189
+ trailingDataError = true;
190
+ }
191
+
192
+ streamError = decodedError;
193
+ return;
194
+ }
195
+
196
+ if (output.byteLength > brotliOutputByteLength) {
197
+ readableController.enqueue(output.slice(brotliOutputByteLength));
198
+ brotliOutputByteLength = output.byteLength;
199
+ }
200
+ };
201
+
202
+ const readable = new ReadableStream<Uint8Array>({
203
+ start(controller) {
204
+ readableController = controller;
205
+ if (streamError) {
206
+ controller.error(streamError);
207
+ return;
208
+ }
209
+ },
210
+ pull(controller) {
211
+ if (!streamFinished && streamInput.length > 0 && !streamError) {
212
+ validateChunkCompletion();
213
+ }
214
+
215
+ if (streamError && trailingDataError) {
216
+ controller.error(streamError);
217
+ return Promise.resolve();
218
+ }
219
+ if (streamError) {
220
+ controller.error(streamError);
221
+ return Promise.resolve();
222
+ }
223
+ if (writableClosed && streamError === null && !streamFinished) {
224
+ streamError = new TypeError("Decompression stream failed: incomplete data");
225
+ controller.error(streamError);
226
+ return Promise.resolve();
227
+ }
228
+ if (writableClosed && readableController && streamError === null) {
229
+ controller.close();
230
+ return Promise.resolve();
231
+ }
232
+
233
+ return Promise.resolve();
234
+ },
235
+ cancel() {
236
+ writableClosed = true;
237
+ return Promise.resolve();
238
+ },
239
+ });
240
+
241
+ const writable = new WritableStream<Uint8Array>({
242
+ write(chunk) {
243
+ try {
244
+ if (streamError) {
245
+ throw streamError;
246
+ }
247
+
248
+ if (isBrotliFormat) {
249
+ if (chunk.length === 0) {
250
+ return;
251
+ }
252
+
253
+ const output = decompressChunk(chunk);
254
+ streamFinished = true;
255
+ if (output.byteLength > 0 && readableController) {
256
+ readableController.enqueue(output);
257
+ }
258
+ return;
259
+ }
260
+
261
+ appendInput(chunk);
262
+
263
+ let output: Uint8Array;
264
+ try {
265
+ output = decompressChunk(chunk);
266
+ streamFinished = true;
267
+ } catch (error) {
268
+ streamError = toTypeError(error);
269
+ if (readableController) {
270
+ readableController.error(streamError);
271
+ }
272
+ return;
273
+ }
274
+
275
+ if (output.byteLength > 0 && readableController) {
276
+ readableController.enqueue(output);
277
+ }
278
+ } catch (error) {
279
+ streamError = toTypeError(error);
280
+ if (readableController) {
281
+ readableController.error(streamError);
282
+ }
283
+ return;
284
+ }
285
+ },
286
+ async close() {
287
+ try {
288
+ writableClosed = true;
289
+
290
+ if (streamError) {
291
+ if (readableController) {
292
+ readableController.error(streamError);
293
+ }
294
+ return;
295
+ }
296
+
297
+ if (!streamFinished && streamInput.length > 0 && !streamError) {
298
+ validateChunkCompletion();
299
+ }
300
+
301
+ if (streamError === null && !streamFinished) {
302
+ streamError = new TypeError("Decompression stream failed: incomplete data");
303
+ if (readableController) {
304
+ readableController.error(streamError);
305
+ }
306
+ return;
307
+ }
308
+
309
+ if (streamError === null && readableController) {
310
+ readableController.close();
311
+ }
312
+ } catch (error) {
313
+ streamError = streamError || toTypeError(error);
314
+ if (readableController) {
315
+ readableController.error(streamError);
316
+ }
317
+ }
318
+ },
319
+ async abort(reason) {
320
+ if (streamError === null) {
321
+ streamError = reason === undefined ? new TypeError("The operation was aborted.") : toTypeError(reason);
322
+ }
323
+ writableClosed = true;
324
+ if (readableController) {
325
+ readableController.error(streamError);
326
+ }
327
+ },
328
+ });
329
+
330
+ this._readable = readable;
331
+ this._writable = writable;
332
+ return;
333
+ }
334
+
335
+ get readable(): ReadableStream<Uint8Array> {
336
+ return this._readable;
337
+ }
338
+
339
+ get writable(): WritableStream<Uint8Array> {
340
+ return this._writable;
341
+ }
342
+ }
@@ -0,0 +1,4 @@
1
+ export { CompressionStream } from './CompressionStream';
2
+ export type { CompressionFormat } from './CompressionStream';
3
+ export { DecompressionStream } from './DecompressionStream';
4
+ export type { DecompressionFormat } from './DecompressionStream';
@@ -0,0 +1,341 @@
1
+ /**
2
+ * Console - Web Standard Console Implementation
3
+ *
4
+ * @see https://console.spec.whatwg.org/
5
+ */
6
+
7
+ import { inspect } from "../inspect";
8
+
9
+ export type LogLevel = "log" | "info" | "warn" | "error" | "debug" | "trace";
10
+
11
+ export interface ConsoleOutput {
12
+ log(level: LogLevel, args: any[]): void;
13
+ group?(label: string, collapsed: boolean): void;
14
+ groupEnd?(): void;
15
+ clear?(): void;
16
+ }
17
+
18
+ // Capture the native console BEFORE we overwrite it
19
+ // The native layer (Hermes/JSC) sets up globalThis.console with native logging
20
+ const _nativeConsole = (globalThis as any).console;
21
+
22
+ // Default output sends to native console
23
+ // Note: We need to look up console dynamically because it might be
24
+ // set up AFTER our module loads but BEFORE our methods are called
25
+ let output: ConsoleOutput = {
26
+ log: (level, args) => {
27
+ // First try: use captured native console
28
+ if (_nativeConsole && typeof _nativeConsole[level] === 'function') {
29
+ _nativeConsole[level](...args);
30
+ return;
31
+ }
32
+ // Second try: use current global console (might be set up later)
33
+ const currentConsole = (globalThis as any).console;
34
+ if (currentConsole && currentConsole !== _nativeConsole && typeof currentConsole[level] === 'function') {
35
+ currentConsole[level](...args);
36
+ return;
37
+ }
38
+ },
39
+ };
40
+
41
+ export function setConsoleOutput(newOutput: ConsoleOutput): void {
42
+ output = newOutput;
43
+ }
44
+
45
+ // Counters for console.count
46
+ const counters = new Map<string, number>();
47
+
48
+ // Timers for console.time
49
+ const timers = new Map<string, number>();
50
+
51
+ // Group depth tracking
52
+ let groupDepth = 0;
53
+
54
+ /**
55
+ * Format a single value for output.
56
+ */
57
+ function formatValue(arg: any): string {
58
+ if (arg === undefined) return "undefined";
59
+ if (arg === null) return "null";
60
+ if (typeof arg === "object") {
61
+ return inspect(arg, { colors: false });
62
+ }
63
+ return String(arg);
64
+ }
65
+
66
+ /**
67
+ * Format arguments, supporting printf-style format specifiers.
68
+ * Supports: %s (string), %d/%i (integer), %f (float), %o/%O (object), %c (CSS - ignored)
69
+ *
70
+ * @see https://console.spec.whatwg.org/#formatter
71
+ */
72
+ function formatArgs(args: any[]): any[] {
73
+ if (args.length === 0) return [];
74
+
75
+ const first = args[0];
76
+
77
+ // If first arg is not a string or has no format specifiers, just format all args
78
+ if (typeof first !== 'string' || !/%[sdifoOc%]/.test(first)) {
79
+ return args.map(formatValue);
80
+ }
81
+
82
+ // Process format specifiers
83
+ let result = '';
84
+ let argIndex = 1;
85
+ let i = 0;
86
+
87
+ while (i < first.length) {
88
+ if (first[i] === '%' && i + 1 < first.length) {
89
+ const specifier = first[i + 1];
90
+
91
+ switch (specifier) {
92
+ case 's': // String
93
+ if (argIndex < args.length) {
94
+ result += String(args[argIndex++]);
95
+ } else {
96
+ result += '%s';
97
+ }
98
+ i += 2;
99
+ break;
100
+
101
+ case 'd': // Integer
102
+ case 'i':
103
+ if (argIndex < args.length) {
104
+ const num = Number(args[argIndex++]);
105
+ result += isNaN(num) ? 'NaN' : String(Math.trunc(num));
106
+ } else {
107
+ result += `%${specifier}`;
108
+ }
109
+ i += 2;
110
+ break;
111
+
112
+ case 'f': // Float
113
+ if (argIndex < args.length) {
114
+ const num = Number(args[argIndex++]);
115
+ result += isNaN(num) ? 'NaN' : String(num);
116
+ } else {
117
+ result += '%f';
118
+ }
119
+ i += 2;
120
+ break;
121
+
122
+ case 'o': // Object (compact)
123
+ case 'O': // Object (verbose)
124
+ if (argIndex < args.length) {
125
+ result += inspect(args[argIndex++], {
126
+ colors: false,
127
+ compact: specifier === 'o',
128
+ });
129
+ } else {
130
+ result += `%${specifier}`;
131
+ }
132
+ i += 2;
133
+ break;
134
+
135
+ case 'c': // CSS styling (ignored in non-browser environments)
136
+ // Skip the style argument but don't output anything
137
+ if (argIndex < args.length) {
138
+ argIndex++;
139
+ }
140
+ i += 2;
141
+ break;
142
+
143
+ case '%': // Literal %
144
+ result += '%';
145
+ i += 2;
146
+ break;
147
+
148
+ default:
149
+ // Unknown specifier, output as-is
150
+ result += first[i];
151
+ i++;
152
+ }
153
+ } else {
154
+ result += first[i];
155
+ i++;
156
+ }
157
+ }
158
+
159
+ // Append any remaining arguments
160
+ const remaining = args.slice(argIndex).map(formatValue);
161
+
162
+ if (remaining.length > 0) {
163
+ return [result, ...remaining];
164
+ }
165
+
166
+ return [result];
167
+ }
168
+
169
+ function getIndent(): string {
170
+ return " ".repeat(groupDepth);
171
+ }
172
+
173
+ export class Console {
174
+ log(...args: any[]): void {
175
+ output.log("log", formatArgs(args));
176
+ }
177
+
178
+ info(...args: any[]): void {
179
+ output.log("info", formatArgs(args));
180
+ }
181
+
182
+ warn(...args: any[]): void {
183
+ output.log("warn", formatArgs(args));
184
+ }
185
+
186
+ error(...args: any[]): void {
187
+ output.log("error", formatArgs(args));
188
+ }
189
+
190
+ debug(...args: any[]): void {
191
+ output.log("debug", formatArgs(args));
192
+ }
193
+
194
+ trace(...args: any[]): void {
195
+ // Create stack trace
196
+ const err = new Error();
197
+ const stack = err.stack?.split("\n").slice(2).join("\n") ?? "";
198
+ output.log("trace", [...formatArgs(args), "\n" + stack]);
199
+ }
200
+
201
+ assert(condition?: boolean, ...args: any[]): void {
202
+ if (!condition) {
203
+ const message = args.length > 0 ? formatArgs(args) : ["Assertion failed"];
204
+ output.log("error", ["Assertion failed:", ...message]);
205
+ }
206
+ }
207
+
208
+ table(data: any, columns?: string[]): void {
209
+ if (data === null || data === undefined) {
210
+ output.log("log", [data]);
211
+ return;
212
+ }
213
+
214
+ if (typeof data !== "object") {
215
+ output.log("log", [data]);
216
+ return;
217
+ }
218
+
219
+ // Simple table implementation
220
+ const isArray = Array.isArray(data);
221
+ const entries = isArray
222
+ ? data.map((item, index) => [index, item])
223
+ : Object.entries(data);
224
+
225
+ if (entries.length === 0) {
226
+ output.log("log", [isArray ? "[]" : "{}"]);
227
+ return;
228
+ }
229
+
230
+ // Build table string
231
+ const header = isArray ? "(index)" : "(key)";
232
+ let tableStr = `\n${header}\tValue\n`;
233
+ tableStr += "-".repeat(40) + "\n";
234
+
235
+ for (const [key, value] of entries) {
236
+ const valueStr =
237
+ typeof value === "object" ? JSON.stringify(value) : String(value);
238
+ tableStr += `${key}\t${valueStr}\n`;
239
+ }
240
+
241
+ output.log("log", [tableStr]);
242
+ }
243
+
244
+ group(label?: string): void {
245
+ const groupLabel = label ?? "console.group";
246
+ if (output.group) {
247
+ output.group(groupLabel, false);
248
+ } else {
249
+ output.log("log", [`${getIndent()}▼ ${groupLabel}`]);
250
+ }
251
+ groupDepth++;
252
+ }
253
+
254
+ groupCollapsed(label?: string): void {
255
+ const groupLabel = label ?? "console.group";
256
+ if (output.group) {
257
+ output.group(groupLabel, true);
258
+ } else {
259
+ output.log("log", [`${getIndent()}▶ ${groupLabel}`]);
260
+ }
261
+ groupDepth++;
262
+ }
263
+
264
+ groupEnd(): void {
265
+ if (groupDepth > 0) {
266
+ groupDepth--;
267
+ }
268
+ if (output.groupEnd) {
269
+ output.groupEnd();
270
+ }
271
+ }
272
+
273
+ count(label?: string): void {
274
+ const countLabel = label ?? "default";
275
+ const count = (counters.get(countLabel) ?? 0) + 1;
276
+ counters.set(countLabel, count);
277
+ output.log("log", [`${countLabel}: ${count}`]);
278
+ }
279
+
280
+ countReset(label?: string): void {
281
+ const countLabel = label ?? "default";
282
+ if (counters.has(countLabel)) {
283
+ counters.set(countLabel, 0);
284
+ } else {
285
+ output.log("warn", [`Count for '${countLabel}' does not exist`]);
286
+ }
287
+ }
288
+
289
+ time(label?: string): void {
290
+ const timerLabel = label ?? "default";
291
+ if (timers.has(timerLabel)) {
292
+ output.log("warn", [`Timer '${timerLabel}' already exists`]);
293
+ return;
294
+ }
295
+ timers.set(timerLabel, performance?.now?.() ?? Date.now());
296
+ }
297
+
298
+ timeEnd(label?: string): void {
299
+ const timerLabel = label ?? "default";
300
+ const startTime = timers.get(timerLabel);
301
+ if (startTime === undefined) {
302
+ output.log("warn", [`Timer '${timerLabel}' does not exist`]);
303
+ return;
304
+ }
305
+ const duration = (performance?.now?.() ?? Date.now()) - startTime;
306
+ timers.delete(timerLabel);
307
+ output.log("log", [`${timerLabel}: ${duration.toFixed(3)}ms`]);
308
+ }
309
+
310
+ timeLog(label?: string, ...args: any[]): void {
311
+ const timerLabel = label ?? "default";
312
+ const startTime = timers.get(timerLabel);
313
+ if (startTime === undefined) {
314
+ output.log("warn", [`Timer '${timerLabel}' does not exist`]);
315
+ return;
316
+ }
317
+ const duration = (performance?.now?.() ?? Date.now()) - startTime;
318
+ output.log("log", [`${timerLabel}: ${duration.toFixed(3)}ms`, ...formatArgs(args)]);
319
+ }
320
+
321
+ clear(): void {
322
+ if (output.clear) {
323
+ output.clear();
324
+ } else {
325
+ // Output some newlines or clear indicator
326
+ output.log("log", ["\n".repeat(50) + "--- Console cleared ---"]);
327
+ }
328
+ }
329
+
330
+ // Aliases
331
+ dir(obj: any): void {
332
+ this.log(obj);
333
+ }
334
+
335
+ dirxml(obj: any): void {
336
+ this.log(obj);
337
+ }
338
+ }
339
+
340
+ // Create singleton instance
341
+ export const console = new Console();
@@ -0,0 +1,2 @@
1
+ export { Console, console, setConsoleOutput } from "./Console";
2
+ export type { LogLevel, ConsoleOutput } from "./Console";