@gjsify/console 0.41.0 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/lib/esm/index.js +5 -2
- package/lib/types/gerror.gjs.spec.d.ts +2 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -24,6 +24,52 @@ logger.log('hello');
|
|
|
24
24
|
logger.error('something went wrong');
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
## Value formatting
|
|
28
|
+
|
|
29
|
+
Arguments are rendered with `JSON.stringify`, not `util.inspect` — a deliberate
|
|
30
|
+
simplification, so object output is `{"a":1}` where Node prints `{ a: 1 }`.
|
|
31
|
+
|
|
32
|
+
An **Error** is the one value that cannot go through `JSON.stringify`: it keeps only
|
|
33
|
+
own *enumerable* properties, and `message`/`stack` are neither, so
|
|
34
|
+
`console.error(new Error('boom'))` used to print `{}` and an error subclass carrying a
|
|
35
|
+
code printed `{"name":"GtkHostError","code":"unknown-tag"}` — the code and nothing else.
|
|
36
|
+
Errors are therefore rendered as `Name: message`, the stack, and the remaining own
|
|
37
|
+
enumerable properties as JSON, plus `[cause]` and `[errors]` (an `AggregateError`'s
|
|
38
|
+
members), recursively and cycle-safe. Nested errors inside an array or object are
|
|
39
|
+
rendered too.
|
|
40
|
+
|
|
41
|
+
The header is built from `name`/`message` rather than taken from `err.stack`, because
|
|
42
|
+
the engines disagree: V8 prefixes the stack with `Name: message`, SpiderMonkey does not
|
|
43
|
+
(frames only, measured on gjs 1.88.1).
|
|
44
|
+
|
|
45
|
+
### GErrors
|
|
46
|
+
|
|
47
|
+
A **GJS GError** — `new GLib.Error(Gio.IOErrorEnum, …)`, and everything a GI call
|
|
48
|
+
throws — is not an `Error` instance (`instanceof` is false; only the `Error.isError`
|
|
49
|
+
brand check sees it) and has **no `name` at all**: `Error.prototype` is not on its
|
|
50
|
+
prototype chain. Its identity lives in `domain`/`code`, which are accessors on the
|
|
51
|
+
prototype and therefore invisible to `Object.keys`. So the header comes from the
|
|
52
|
+
GError's own `toString` — `Gio.IOErrorEnum: no such file`, which names the domain —
|
|
53
|
+
and `domain`/`code` are appended:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Gio.IOErrorEnum: no such file
|
|
57
|
+
@file:///app/main.js:12:12 {"domain":198,"code":1}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`stack`, `fileName`, `lineNumber` and `columnNumber` are never appended as properties.
|
|
61
|
+
They are non-enumerable on a plain `new Error()` but own-*enumerable* on a GError, and
|
|
62
|
+
they hold exactly the file, line and column the printed stack already shows.
|
|
63
|
+
|
|
64
|
+
### Reporting never throws
|
|
65
|
+
|
|
66
|
+
Every slot the renderer reads off an Error — `name`, `message`, `stack`, `cause`,
|
|
67
|
+
`errors`, each own property — can be an accessor, and an accessor can throw. Each is
|
|
68
|
+
read defensively, so a poisoned slot costs that slot and not the log line, and the
|
|
69
|
+
whole render is wrapped as a last resort: an Error that cannot be rendered at all
|
|
70
|
+
degrades to `Name: message [unformattable: <why>]`. A console that throws while
|
|
71
|
+
reporting a failure replaces the report with a second failure.
|
|
72
|
+
|
|
27
73
|
## Inspirations and credits
|
|
28
74
|
|
|
29
75
|
- https://github.com/denoland/deno/tree/main/ext/console
|
package/lib/esm/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";const e=typeof print==`function`&&typeof printerr==`function
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";const e=typeof print==`function`&&typeof printerr==`function`,t=[`name`,`message`,`stack`,`cause`,`errors`,`fileName`,`lineNumber`,`columnNumber`];function _isError(e){return typeof Error.isError==`function`?Error.isError(e):e instanceof Error}function _safeRead(e,t){try{return e[t]}catch{return}}function _safeString(e){try{let t=String(e);return t.length>0&&!t.startsWith(`[object `)?t:void 0}catch{return}}function _thrownText(e){let t=_isError(e)?_safeRead(e,`message`):void 0;return typeof t==`string`?t:_safeString(e)??`unknown`}function _gerrorIdentity(e){let t=Object.prototype.hasOwnProperty;if(t.call(e,`domain`)||t.call(e,`code`))return;let n=_safeRead(e,`domain`),r=_safeRead(e,`code`);if(!(typeof n!=`number`||typeof r!=`number`))return{domain:n,code:r}}function _errorHeader(e){let t=_safeRead(e,`name`),n=_safeRead(e,`message`),r=typeof n==`string`?n:``;if(typeof t==`string`&&t.length>0)return r.length>0?`${t}: ${r}`:t;let i=_safeString(e);return i===void 0?r.length>0?`Error: ${r}`:`Error`:i}function _indentContinuation(e,t){return e.split(`
|
|
2
|
+
`).join(`
|
|
3
|
+
`+t)}function _formatError(e,t){if(t.has(e))return`[Circular ${_errorHeader(e)}]`;t.add(e);try{return _renderError(e,t)}catch(t){return`${_errorHeader(e)} [unformattable: ${_thrownText(t)}]`}finally{t.delete(e)}}function _renderError(e,n){let r=_errorHeader(e),i=_safeRead(e,`stack`),a=typeof i==`string`?i.replace(/\n+$/,``):``,o=a.length===0?r:a.startsWith(r)?a:`${r}\n${a}`,s={..._gerrorIdentity(e)};for(let n of Object.keys(e))t.includes(n)||(s[n]=_safeRead(e,n));let c=_stringify(s,n);c!==`{}`&&(o+=` `+c);let l=_safeRead(e,`cause`);l!==void 0&&(o+=`
|
|
4
|
+
[cause]: `+_indentContinuation(_formatValue(l,n),` `));let u=_safeRead(e,`errors`);if(Array.isArray(u))for(let e=0;e<u.length;e++)o+=`\n [errors][${e}]: `+_indentContinuation(_formatValue(u[e],n),` `);return o}function _stringify(e,t){try{return JSON.stringify(e,(e,n)=>_isError(n)?_formatError(n,t):n)}catch(e){return`[unserializable: ${_thrownText(e)}]`}}function _formatValue(e,t){return _isError(e)?_formatError(e,t):_stringify(e,t)}function _formatArgs(...e){let t=new Set,n=e[0],r=e.slice(1);if(typeof n!=`string`||!/%(s|d|i|f|o|O|c)/.test(n))return e.map(e=>typeof e==`string`?e:_formatValue(e,t)).join(` `);let i=0,a=n.replace(/%([sdifOoc])/g,(e,n)=>{if(i>=r.length)return e;let a=r[i++];switch(n){case`s`:return _isError(a)?_formatError(a,t):String(a);case`d`:case`i`:return String(parseInt(String(a),10));case`f`:return String(parseFloat(String(a)));case`o`:case`O`:return _formatValue(a,t);case`c`:return``;default:return e}}),o=r.slice(i);return o.length===0?a:a+` `+o.map(e=>typeof e==`string`?e:_formatValue(e,t)).join(` `)}var Console=class{_stdout;_stderr;_groupDepth=0;_groupIndentation;_timers=new Map;_counters=new Map;constructor(e,t){if(e&&typeof e.write==`function`)this._stdout=e,this._stderr=t||this._stdout;else if(e&&typeof e==`object`){let t=e;this._stdout=t.stdout,this._stderr=t.stderr||t.stdout}this._groupIndentation=2}_write(t,...n){let r=t===`stderr`&&this._stderr||this._stdout;if(r){let e=` `.repeat(this._groupDepth*this._groupIndentation),t=_formatArgs(...n);r.write(e+t+`
|
|
2
5
|
`)}else if(e){let e=` `.repeat(this._groupDepth*this._groupIndentation)+_formatArgs(...n);t===`stderr`?printerr(e):print(e)}else{let e=globalThis.console;t===`stderr`?e.error(...n):e.log(...n)}}log(...e){this._write(`stdout`,...e)}info(...e){this._write(`stdout`,...e)}debug(...e){this._write(`stdout`,...e)}warn(...e){this._write(`stderr`,...e)}error(...e){this._write(`stderr`,...e)}dir(e,t){this._write(`stdout`,e)}dirxml(...e){this.log(...e)}assert(e,...t){e||this.error(`Assertion failed:`,...t)}clear(){this._stdout?this._stdout.write(`\x1Bc`):e?print(`\x1Bc`):globalThis.console.clear()}count(e=`default`){let t=(this._counters.get(e)||0)+1;this._counters.set(e,t),this.log(`${e}: ${t}`)}countReset(e=`default`){this._counters.delete(e)}group(...e){e.length>0&&this.log(...e),this._groupDepth++}groupCollapsed(...e){this.group(...e)}groupEnd(){this._groupDepth>0&&this._groupDepth--}table(t,n){this._stdout?this._write(`stdout`,t):e?print(JSON.stringify(t,null,2)):globalThis.console.table(t,n)}time(e=`default`){this._timers.set(e,Date.now())}timeEnd(e=`default`){let t=this._timers.get(e);t===void 0?this.warn(`Warning: No such label '${e}' for console.timeEnd()`):(this.log(`${e}: ${Date.now()-t}ms`),this._timers.delete(e))}timeLog(e=`default`,...t){let n=this._timers.get(e);n===void 0?this.warn(`Warning: No such label '${e}' for console.timeLog()`):this.log(`${e}: ${Date.now()-n}ms`,...t)}trace(...e){let t=Error().stack?.split(`
|
|
3
6
|
`).slice(1).join(`
|
|
4
7
|
`)||``;this._write(`stderr`,`Trace:`,...e,`
|
|
5
|
-
`+t)}profile(e){}profileEnd(e){}timeStamp(e){}};const
|
|
8
|
+
`+t)}profile(e){}profileEnd(e){}timeStamp(e){}};const n=new Console,log=(...e)=>n.log(...e),info=(...e)=>n.info(...e),debug=(...e)=>n.debug(...e),warn=(...e)=>n.warn(...e),error=(...e)=>n.error(...e),dir=(e,t)=>n.dir(e,t),dirxml=(...e)=>n.dirxml(...e),table=(e,t)=>n.table(e,t),clear=()=>n.clear(),assert=(e,...t)=>n.assert(e,...t),trace=(...e)=>n.trace(...e),time=e=>n.time(e),timeEnd=e=>n.timeEnd(e),timeLog=(e,...t)=>n.timeLog(e,...t),count=e=>n.count(e),countReset=e=>n.countReset(e),group=(...e)=>n.group(...e),groupCollapsed=(...e)=>n.groupCollapsed(...e),groupEnd=()=>n.groupEnd(),profile=e=>{},profileEnd=e=>{},timeStamp=e=>{},r={Console,log,info,debug,warn,error,dir,dirxml,table,time,timeEnd,timeLog,trace,assert,clear,count,countReset,group,groupCollapsed,groupEnd,profile,profileEnd,timeStamp};export{Console,assert,clear,count,countReset,debug,r as default,dir,dirxml,error,group,groupCollapsed,groupEnd,info,log,profile,profileEnd,table,time,timeEnd,timeLog,timeStamp,trace,warn};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/console",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "Node.js console module for Gjs",
|
|
5
5
|
"module": "lib/esm/index.js",
|
|
6
6
|
"types": "lib/types/index.d.ts",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"console"
|
|
37
37
|
],
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@gjsify/cli": "^0.
|
|
40
|
-
"@gjsify/unit": "^0.
|
|
39
|
+
"@gjsify/cli": "^0.43.0",
|
|
40
|
+
"@gjsify/unit": "^0.43.0",
|
|
41
41
|
"@types/node": "^25.9.2",
|
|
42
42
|
"typescript": "^6.0.3"
|
|
43
43
|
},
|