Branch data Line data Source code
1 : 240 : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
2 : : // SPDX-FileCopyrightText: 2021 Evan Welsh <contact@evanwelsh.com>
3 : :
4 : 48 : const DEFAULT_LOG_DOMAIN = 'Gjs-Console';
5 : :
6 : : // A line-by-line implementation of https://console.spec.whatwg.org/.
7 : :
8 : : // 2.2.1. Formatting specifiers
9 : : // https://console.spec.whatwg.org/#formatting-specifiers
10 : : //
11 : : // %s - string
12 : : // %d or %i - integer
13 : : // %f - float
14 : : // %o - "optimal" object formatting
15 : : // %O - "generic" object formatting
16 : : // %c - "CSS" formatting (unimplemented by GJS)
17 : :
18 : : /**
19 : : * A simple regex to capture formatting specifiers
20 : : */
21 : 48 : const specifierTest = /%(d|i|s|f|o|O|c)/;
22 : :
23 : : /**
24 : : * @param {string} str a string to check for format specifiers like %s or %i
25 : : * @returns {boolean}
26 : : */
27 : 100 : function hasFormatSpecifiers(str) {
28 : 100 : return specifierTest.test(str);
29 : : }
30 : :
31 : : /**
32 : : * @param {any} item an item to format
33 : : */
34 : 5 : function formatGenerically(item) {
35 : 5 : return JSON.stringify(item, null, 4);
36 : : }
37 : :
38 : : /**
39 : : * @param {any} item an item to format
40 : : * @returns {string}
41 : : */
42 : 11 : function formatOptimally(item) {
43 : 11 : const GLib = imports.gi.GLib;
44 : : // Handle optimal error formatting.
45 [ + + ][ + + ]: 11 : if (item instanceof Error || item instanceof GLib.Error) {
46 [ - + ][ - + ]: 5 : return `${item.toString()}${item.stack ? '\n' : ''}${item.stack
47 : 5 : ?.split('\n')
48 : : // Pad each stacktrace line.
49 : 55 : .map(line => line.padStart(2, ' '))
50 : 5 : .join('\n')}`;
51 : : }
52 : :
53 : : // TODO: Enhance 'optimal' formatting.
54 : : // There is a current work on a better object formatter for GJS in
55 : : // https://gitlab.gnome.org/GNOME/gjs/-/merge_requests/587
56 [ - + ][ - + ]: 6 : if (typeof item === 'object' && item !== null) {
57 [ + + ][ + + ]: 6 : if (item.constructor?.name !== 'Object')
58 [ + + ]: 3 : return `${item.constructor?.name} ${JSON.stringify(item, null, 4)}`;
59 [ + + ]: 3 : else if (item[Symbol.toStringTag] === 'GIRepositoryNamespace')
60 : 1 : return `[${item[Symbol.toStringTag]} ${item.__name__}]`;
61 : : }
62 : 2 : return JSON.stringify(item, null, 4);
63 : 11 : }
64 : :
65 : : /**
66 : : * Implementation of the WHATWG Console object.
67 : : */
68 : 96 : class Console {
69 : 48 : #groupIndentation = '';
70 : 48 : #countLabels = {};
71 : 48 : #timeLabels = {};
72 : 48 : #logDomain = DEFAULT_LOG_DOMAIN;
73 : :
74 : 48 : get [Symbol.toStringTag]() {
75 : 0 : return 'Console';
76 : : }
77 : :
78 : : // 1.1 Logging functions
79 : : // https://console.spec.whatwg.org/#logging
80 : :
81 : : /**
82 : : * Logs a critical message if the condition is not truthy.
83 : : * {@see console.error()} for additional information.
84 : : *
85 : : * @param {boolean} condition a boolean condition which, if false, causes
86 : : * the log to print
87 : : * @param {...any} data formatting substitutions, if applicable
88 : : * @returns {void}
89 : : */
90 : 51 : assert(condition, ...data) {
91 [ + + ]: 3 : if (condition)
92 : 1 : return;
93 : :
94 : 2 : const message = 'Assertion failed';
95 : :
96 [ + + ]: 2 : if (data.length === 0)
97 : 1 : data.push(message);
98 : :
99 [ + - ]: 2 : if (typeof data[0] !== 'string') {
100 : 0 : data.unshift(message);
101 : 2 : } else {
102 : 2 : const first = data.shift();
103 : 2 : data.unshift(`${message}: ${first}`);
104 : : }
105 : 2 : this.#logger('assert', data);
106 : 1 : }
107 : :
108 : : /**
109 : : * Resets grouping and clears the terminal on systems supporting ANSI
110 : : * terminal control sequences.
111 : : *
112 : : * In file-based stdout or systems which do not support clearing,
113 : : * console.clear() has no visual effect.
114 : : *
115 : : * @returns {void}
116 : : */
117 : 48 : clear() {
118 : 2 : this.#groupIndentation = '';
119 : 2 : imports.gi.GjsPrivate.clear_terminal();
120 : : }
121 : :
122 : : /**
123 : : * Logs a message with severity equal to {@see GLib.LogLevelFlags.DEBUG}.
124 : : *
125 : : * @param {...any} data formatting substitutions, if applicable
126 : : */
127 : 48 : debug(...data) {
128 : 0 : this.#logger('debug', data);
129 : : }
130 : :
131 : : /**
132 : : * Logs a message with severity equal to {@see GLib.LogLevelFlags.CRITICAL}.
133 : : * Does not use {@see GLib.LogLevelFlags.ERROR} to avoid asserting and
134 : : * forcibly shutting down the application.
135 : : *
136 : : * @param {...any} data formatting substitutions, if applicable
137 : : */
138 : 58 : error(...data) {
139 : 10 : this.#logger('error', data);
140 : : }
141 : :
142 : : /**
143 : : * Logs a message with severity equal to {@see GLib.LogLevelFlags.INFO}.
144 : : *
145 : : * @param {...any} data formatting substitutions, if applicable
146 : : */
147 : 59 : info(...data) {
148 : 11 : this.#logger('info', data);
149 : : }
150 : :
151 : : /**
152 : : * Logs a message with severity equal to {@see GLib.LogLevelFlags.MESSAGE}.
153 : : *
154 : : * @param {...any} data formatting substitutions, if applicable
155 : : */
156 : 67 : log(...data) {
157 : 19 : this.#logger('log', data);
158 : : }
159 : :
160 : : // 1.1.7 table(tabularData, properties)
161 : 48 : table(tabularData, _properties) {
162 : 1 : this.log(tabularData);
163 : : }
164 : :
165 : : /**
166 : : * @param {...any} data formatting substitutions, if applicable
167 : : * @returns {void}
168 : : */
169 : 60 : trace(...data) {
170 [ + + ]: 12 : if (data.length === 0)
171 : 1 : data = ['Trace'];
172 : :
173 : 12 : this.#logger('trace', data);
174 : : }
175 : :
176 : : /**
177 : : * Logs a message with severity equal to {@see GLib.LogLevelFlags.WARNING}.
178 : : *
179 : : * @param {...any} data formatting substitutions, if applicable
180 : : * @returns {void}
181 : : */
182 : 59 : warn(...data) {
183 : 11 : this.#logger('warn', data);
184 : : }
185 : :
186 : : /**
187 : : * @param {object} item an item to format generically
188 : : * @param {never} [options] any additional options for the formatter. Unused
189 : : * in our implementation.
190 : : */
191 : 48 : dir(item, options) {
192 : 0 : const object = formatGenerically(item);
193 : 0 : this.#printer('dir', [object], options);
194 : : }
195 : :
196 : : /**
197 : : * @param {...any} data formatting substitutions, if applicable
198 : : * @returns {void}
199 : : */
200 : 48 : dirxml(...data) {
201 [ # # ][ # # ]: 0 : this.log(...data);
202 : : }
203 : :
204 : : // 1.2 Counting functions
205 : : // https://console.spec.whatwg.org/#counting
206 : :
207 : : /**
208 : : * Logs how many times console.count(label) has been called with a given
209 : : * label.
210 : : * {@see console.countReset()} for resetting a count.
211 : : *
212 : : * @param {string} label unique identifier for this action
213 : : * @returns {void}
214 : : */
215 : 48 : count(label) {
216 [ # # ]: 0 : this.#countLabels[label] ??= 0;
217 : 0 : const count = ++this.#countLabels[label];
218 : 0 : const concat = `${label}: ${count}`;
219 : :
220 : 0 : this.#logger('count', [concat]);
221 : : }
222 : :
223 : : /**
224 : : * @param {string} label the unique label to reset the count for
225 : : * @returns {void}
226 : : */
227 : 48 : countReset(label) {
228 : 0 : const count = this.#countLabels[label];
229 [ # # ]: 0 : if (typeof count !== 'number')
230 : 0 : this.#printer('reportWarning', [`No count found for label: '${label}'.`]);
231 : : else
232 : 0 : this.#countLabels[label] = 0;
233 : : }
234 : :
235 : : // 1.3 Grouping functions
236 : : // https://console.spec.whatwg.org/#grouping
237 : :
238 : : /**
239 : : * @param {...any} data formatting substitutions, if applicable
240 : : * @returns {void}
241 : : */
242 : 49 : group(...data) {
243 : 1 : this.#logger('group', data);
244 : 1 : this.#groupIndentation += ' ';
245 : : }
246 : :
247 : : /**
248 : : * Alias for console.group()
249 : : *
250 : : * @param {...any} data formatting substitutions, if applicable
251 : : * @returns {void}
252 : : */
253 : 48 : groupCollapsed(...data) {
254 : : // We can't 'collapse' output in a terminal, so we alias to
255 : : // group()
256 [ # # ][ # # ]: 0 : this.group(...data);
257 : : }
258 : :
259 : : /**
260 : : * @returns {void}
261 : : */
262 : 48 : groupEnd() {
263 : 0 : this.#groupIndentation = this.#groupIndentation.slice(0, -2);
264 : : }
265 : :
266 : : // 1.4 Timing functions
267 : : // https://console.spec.whatwg.org/#timing
268 : :
269 : : /**
270 : : * @param {string} label unique identifier for this action, pass to
271 : : * console.timeEnd() to complete
272 : : * @returns {void}
273 : : */
274 : 48 : time(label) {
275 : 2 : this.#timeLabels[label] = imports.gi.GLib.get_monotonic_time();
276 : : }
277 : :
278 : : /**
279 : : * Logs the time since the last call to console.time(label) where label is
280 : : * the same.
281 : : *
282 : : * @param {string} label unique identifier for this action, pass to
283 : : * console.timeEnd() to complete
284 : : * @param {...any} data string substitutions, if applicable
285 : : * @returns {void}
286 : : */
287 : 50 : timeLog(label, ...data) {
288 : 2 : const startTime = this.#timeLabels[label];
289 : :
290 [ + + ]: 2 : if (typeof startTime !== 'number') {
291 : 2 : this.#printer('reportWarning', [
292 : 1 : `No time log found for label: '${label}'.`,
293 : : ]);
294 : 1 : } else {
295 : 1 : const durationMs = (imports.gi.GLib.get_monotonic_time() - startTime) / 1000;
296 : 1 : const concat = `${label}: ${durationMs.toFixed(3)} ms`;
297 : 1 : data.unshift(concat);
298 : :
299 : 1 : this.#printer('timeLog', data);
300 : : }
301 : : }
302 : :
303 : : /**
304 : : * Logs the time since the last call to console.time(label) and completes
305 : : * the action.
306 : : * Call console.time(label) again to re-measure.
307 : : *
308 : : * @param {string} label unique identifier for this action
309 : : * @returns {void}
310 : : */
311 : 50 : timeEnd(label) {
312 : 2 : const startTime = this.#timeLabels[label];
313 : :
314 [ + - ]: 2 : if (typeof startTime !== 'number') {
315 : 0 : this.#printer('reportWarning', [
316 : 0 : `No time log found for label: '${label}'.`,
317 : : ]);
318 : 2 : } else {
319 : 2 : delete this.#timeLabels[label];
320 : :
321 : 2 : const durationMs = (imports.gi.GLib.get_monotonic_time() - startTime) / 1000;
322 : 2 : const concat = `${label}: ${durationMs.toFixed(3)} ms`;
323 : :
324 : 2 : this.#printer('timeEnd', [concat]);
325 : : }
326 : : }
327 : :
328 : : // Non-standard functions which are de-facto standards.
329 : : // Similar to Node, we define these as no-ops for now.
330 : :
331 : : /**
332 : : * @deprecated Not implemented in GJS
333 : : *
334 : : * @param {string} _label unique identifier for this action, pass to
335 : : * console.profileEnd to complete
336 : : * @returns {void}
337 : : */
338 : 48 : profile(_label) {}
339 : :
340 : : /**
341 : : * @deprecated Not implemented in GJS
342 : : *
343 : : * @param {string} _label unique identifier for this action
344 : : * @returns {void}
345 : : */
346 : 48 : profileEnd(_label) {}
347 : :
348 : : /**
349 : : * @deprecated Not implemented in GJS
350 : : *
351 : : * @param {string} _label unique identifier for this action
352 : : * @returns {void}
353 : : */
354 : 48 : timeStamp(_label) {}
355 : :
356 : : // GJS-specific extensions for integrating with GLib structured logging
357 : :
358 : : /**
359 : : * @param {string} logDomain the GLib log domain this Console should print
360 : : * with. Defaults to 'Gjs-Console'.
361 : : * @returns {void}
362 : : */
363 : 48 : setLogDomain(logDomain) {
364 : 0 : this.#logDomain = String(logDomain);
365 : : }
366 : :
367 : : /**
368 : : * @returns {string}
369 : : */
370 : 48 : get logDomain() {
371 : 0 : return this.#logDomain;
372 : : }
373 : :
374 : : // 2. Supporting abstract operations
375 : : // https://console.spec.whatwg.org/#supporting-ops
376 : :
377 : : /**
378 : : * 2.1. Logger
379 : : * https://console.spec.whatwg.org/#logger
380 : : *
381 : : * Conditionally applies formatting based on the inputted arguments,
382 : : * and prints at the provided severity (logLevel)
383 : : *
384 : : * @param {string} logLevel the severity (log level) the args should be
385 : : * emitted with
386 : : * @param {unknown[]} args the arguments to pass to the printer
387 : : * @returns {void}
388 : : */
389 : 66 : #logger(logLevel, args) {
390 [ + - ]: 66 : if (args.length === 0)
391 : 0 : return;
392 : :
393 [ + - ][ + - ]: 66 : const [first, ...rest] = args;
[ + + ][ - + ]
394 : :
395 [ + + ]: 66 : if (rest.length === 0) {
396 : 21 : this.#printer(logLevel, [first]);
397 : 21 : return undefined;
398 : : }
399 : :
400 : : // If first does not contain any format specifiers, don't call Formatter
401 [ - + ][ + - ]: 45 : if (typeof first !== 'string' || !hasFormatSpecifiers(first)) {
402 : 0 : this.#printer(logLevel, args);
403 : 0 : return undefined;
404 : : }
405 : :
406 : : // Otherwise, perform print the result of Formatter.
407 [ + + ]: 45 : this.#printer(logLevel, this.#formatter([first, ...rest]));
408 : :
409 : 45 : return undefined;
410 : 66 : }
411 : :
412 : : /**
413 : : * 2.2. Formatter
414 : : * https://console.spec.whatwg.org/#formatter
415 : : *
416 : : * @param {[string, ...any[]]} args an array of format strings followed by
417 : : * their arguments
418 : : */
419 : 55 : #formatter(args) {
420 : : // The initial formatting string is the first arg
421 : 55 : let target = args[0];
422 : :
423 [ + - ]: 55 : if (args.length === 1)
424 : 0 : return target;
425 : :
426 : 55 : const current = args[1];
427 : :
428 : : // Find the index of the first format specifier.
429 : 55 : const specifierIndex = specifierTest.exec(target).index;
430 : 55 : const specifier = target.slice(specifierIndex, specifierIndex + 2);
431 : 55 : let converted = null;
432 [ + + ][ + + ]: 55 : switch (specifier) {
[ + + ][ + + ]
[ + + ][ + + ]
[ + - ]
433 : : case '%s':
434 : 10 : converted = String(current);
435 : : break;
436 : : case '%d':
437 : : case '%i':
438 [ + - ]: 20 : if (typeof current === 'symbol')
439 : 0 : converted = Number.NaN;
440 : : else
441 : 20 : converted = parseInt(current, 10);
442 : : break;
443 : : case '%f':
444 [ + - ]: 10 : if (typeof current === 'symbol')
445 : 0 : converted = Number.NaN;
446 : : else
447 : 10 : converted = parseFloat(current);
448 : : break;
449 : : case '%o':
450 : 5 : converted = formatOptimally(current);
451 : : break;
452 : : case '%O':
453 : 5 : converted = formatGenerically(current);
454 : : break;
455 : : case '%c':
456 : 5 : converted = '';
457 : : break;
458 : : }
459 : : // If any of the previous steps set converted, replace the specifier in
460 : : // target with the converted value.
461 [ - + ]: 55 : if (converted !== null) {
462 : : target =
463 : 55 : target.slice(0, specifierIndex) +
464 : 55 : converted +
465 : 55 : target.slice(specifierIndex + 2);
466 : : }
467 : :
468 : : /**
469 : : * Create the next format input...
470 : : *
471 : : * @type {[string, ...any[]]}
472 : : */
473 [ + + ]: 55 : const result = [target, ...args.slice(2)];
474 : :
475 [ + + ]: 55 : if (!hasFormatSpecifiers(target))
476 : 45 : return result;
477 : :
478 [ + - ]: 10 : if (result.length === 1)
479 : 0 : return result;
480 : :
481 : 10 : return this.#formatter(result);
482 : 55 : }
483 : :
484 : : /**
485 : : * @typedef {object} PrinterOptions
486 : : * @param {Array.<string[]>} [stackTrace] an error stacktrace to append
487 : : * @param {Record<string, any>} [fields] fields to include in the structured
488 : : * logging call
489 : : */
490 : :
491 : : /**
492 : : * 2.3. Printer
493 : : * https://console.spec.whatwg.org/#printer
494 : : *
495 : : * This implementation of Printer maps WHATWG log severity to
496 : : * {@see GLib.LogLevelFlags} and outputs using GLib structured logging.
497 : : *
498 : : * @param {string} logLevel the log level (log tag) the args should be
499 : : * emitted with
500 : : * @param {unknown[]} args the arguments to print, either a format string
501 : : * with replacement args or multiple strings
502 : : * @param {PrinterOptions} [options] additional options for the
503 : : * printer
504 : : * @returns {void}
505 : : */
506 : 70 : #printer(logLevel, args, options) {
507 : 70 : const GLib = imports.gi.GLib;
508 : 70 : let severity;
509 : :
510 [ + + ][ - + ]: 70 : switch (logLevel) {
[ - + ][ + + ]
[ + + ][ - + ]
[ + + ][ + + ]
[ - + ][ - + ]
[ + + ][ + + ]
[ - + ][ + + ]
[ + + ][ + - ]
511 : : case 'log':
512 : : case 'dir':
513 : : case 'dirxml':
514 : : case 'trace':
515 : : case 'group':
516 : : case 'groupCollapsed':
517 : : case 'timeLog':
518 : : case 'timeEnd':
519 : 35 : severity = GLib.LogLevelFlags.LEVEL_MESSAGE;
520 : : break;
521 : : case 'debug':
522 : 0 : severity = GLib.LogLevelFlags.LEVEL_DEBUG;
523 : : break;
524 : : case 'count':
525 : : case 'info':
526 : 11 : severity = GLib.LogLevelFlags.LEVEL_INFO;
527 : : break;
528 : : case 'warn':
529 : : case 'countReset':
530 : : case 'reportWarning':
531 : 12 : severity = GLib.LogLevelFlags.LEVEL_WARNING;
532 : : break;
533 : : case 'error':
534 : : case 'assert':
535 : 12 : severity = GLib.LogLevelFlags.LEVEL_CRITICAL;
536 : : break;
537 : : default:
538 : 0 : severity = GLib.LogLevelFlags.LEVEL_MESSAGE;
539 : : }
540 : :
541 : 70 : const output = args
542 : 70 : .map(a => {
543 [ + - ]: 70 : if (a === null)
544 : 0 : return 'null';
545 [ + + ]: 70 : else if (typeof a === 'object')
546 : 6 : return formatOptimally(a);
547 [ + - ]: 64 : else if (typeof a === 'function')
548 : 0 : return a.toString();
549 [ + - ]: 64 : else if (typeof a === 'undefined')
550 : 0 : return 'undefined';
551 [ + - ]: 64 : else if (typeof a === 'bigint')
552 : 0 : return `${a}n`;
553 : : else
554 : 64 : return String(a);
555 : : })
556 : 70 : .join(' ');
557 : :
558 : 70 : let formattedOutput = this.#groupIndentation + output;
559 : 70 : const extraFields = {};
560 : :
561 [ + - ]: 70 : let stackTrace = options?.stackTrace;
562 [ - + ]: 70 : if (!stackTrace &&
563 [ + + ][ + + ]: 70 : (logLevel === 'trace' || severity <= GLib.LogLevelFlags.LEVEL_WARNING)) {
564 : 36 : stackTrace = new Error().stack;
565 [ - + ]: 36 : const currentFile = stackTrace.match(/^[^@]*@(.*):\d+:\d+$/m)?.at(1);
566 : 36 : const index = stackTrace.lastIndexOf(currentFile) + currentFile.length;
567 : :
568 : 36 : stackTrace = stackTrace.substring(index).split('\n');
569 : : // Remove the remainder of the first line
570 : 36 : stackTrace.shift();
571 : : }
572 : :
573 [ + + ]: 70 : if (logLevel === 'trace') {
574 [ - + ][ - + ]: 12 : if (stackTrace?.length) {
575 : 12 : formattedOutput += `\n${stackTrace.map(s =>
576 : 132 : `${this.#groupIndentation}${s}`).join('\n')}`;
577 : : } else {
578 : 0 : formattedOutput +=
579 : 0 : `\n${this.#groupIndentation}No trace available`;
580 : : }
581 : : }
582 : :
583 [ + + ][ + + ]: 70 : if (stackTrace?.length) {
584 [ + - ][ + - ]: 36 : const [stackLine] = stackTrace;
585 : 36 : const match = stackLine.match(/^([^@]*)@(.*):(\d+):\d+$/);
586 : :
587 [ - + ]: 36 : if (match) {
588 [ + - ][ + - ]: 36 : const [_, func, file, line] = match;
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
589 : :
590 [ + - ]: 36 : if (func)
591 : 0 : extraFields.CODE_FUNC = func;
592 [ - + ]: 36 : if (file)
593 : 36 : extraFields.CODE_FILE = file;
594 [ - + ]: 36 : if (line)
595 : 36 : extraFields.CODE_LINE = line;
596 : : }
597 : : }
598 : :
599 : 140 : GLib.log_structured(this.#logDomain, severity, {
600 : 70 : MESSAGE: formattedOutput,
601 : 70 : ...extraFields,
602 [ + - ][ - + ]: 70 : ...options?.fields ?? {},
603 : : });
604 : : }
605 : : }
606 : :
607 : 48 : const console = new Console();
608 : :
609 : : /**
610 : : * @param {string} domain set the GLib log domain for the global console object.
611 : : */
612 : 0 : function setConsoleLogDomain(domain) {
613 : 0 : console.setLogDomain(domain);
614 : : }
615 : :
616 : : /**
617 : : * @returns {string}
618 : : */
619 : 0 : function getConsoleLogDomain() {
620 : 0 : return console.logDomain;
621 : : }
622 : :
623 : : /**
624 : : * For historical web-compatibility reasons, the namespace object for
625 : : * console must have {} as its [[Prototype]].
626 : : *
627 : : * @type {Omit<Console, 'setLogDomain' | 'logDomain'>}
628 : : */
629 : 48 : const globalConsole = Object.create({});
630 : :
631 : : const propertyNames =
632 : : /** @type {['constructor', ...Array<string & keyof Console>]} */
633 : : // eslint-disable-next-line no-extra-parens
634 : 48 : (Object.getOwnPropertyNames(Console.prototype));
635 : 48 : const propertyDescriptors = Object.getOwnPropertyDescriptors(Console.prototype);
636 [ + + ]: 1248 : for (const key of propertyNames) {
637 [ + + ]: 1200 : if (key === 'constructor')
638 : 48 : continue;
639 : :
640 : : // This non-standard function shouldn't be included.
641 [ + + ]: 1152 : if (key === 'setLogDomain')
642 : 48 : continue;
643 : :
644 : 1104 : const descriptor = propertyDescriptors[key];
645 [ + + ]: 1104 : if (typeof descriptor.value !== 'function')
646 : 48 : continue;
647 : :
648 : 2112 : Object.defineProperty(globalConsole, key, {
649 : 1056 : ...descriptor,
650 : 1056 : value: descriptor.value.bind(console),
651 : : });
652 : : }
653 : 96 : Object.defineProperties(globalConsole, {
654 : 48 : [Symbol.toStringTag]: {
655 : 48 : configurable: false,
656 : 48 : enumerable: true,
657 : 48 : get() {
658 : 1 : return 'console';
659 : : },
660 : : },
661 : : });
662 : 48 : Object.freeze(globalConsole);
663 : :
664 : 96 : Object.defineProperty(globalThis, 'console', {
665 : 48 : configurable: false,
666 : 48 : enumerable: true,
667 : 48 : writable: false,
668 : 48 : value: globalConsole,
669 : : });
670 : :
671 : : export {
672 : : getConsoleLogDomain,
673 : : setConsoleLogDomain,
674 : : DEFAULT_LOG_DOMAIN
675 : : };
676 : :
677 : 48 : export default {
678 : 48 : getConsoleLogDomain,
679 : 48 : setConsoleLogDomain,
680 : 48 : DEFAULT_LOG_DOMAIN,
681 : : };
|