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