@gjsify/console 0.42.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 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`;function _formatArgs(...e){let t=e[0],n=e.slice(1);if(typeof t!=`string`||!/%(s|d|i|f|o|O|c)/.test(t))return e.map(e=>typeof e==`string`?e:JSON.stringify(e)).join(` `);let r=0,i=t.replace(/%([sdifOoc])/g,(e,t)=>{if(r>=n.length)return e;let i=n[r++];switch(t){case`s`:return String(i);case`d`:case`i`:return String(parseInt(String(i),10));case`f`:return String(parseFloat(String(i)));case`o`:case`O`:return JSON.stringify(i);case`c`:return``;default:return e}}),a=n.slice(r);return a.length===0?i:i+` `+a.map(e=>typeof e==`string`?e:JSON.stringify(e)).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+`
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 t=new Console,log=(...e)=>t.log(...e),info=(...e)=>t.info(...e),debug=(...e)=>t.debug(...e),warn=(...e)=>t.warn(...e),error=(...e)=>t.error(...e),dir=(e,n)=>t.dir(e,n),dirxml=(...e)=>t.dirxml(...e),table=(e,n)=>t.table(e,n),clear=()=>t.clear(),assert=(e,...n)=>t.assert(e,...n),trace=(...e)=>t.trace(...e),time=e=>t.time(e),timeEnd=e=>t.timeEnd(e),timeLog=(e,...n)=>t.timeLog(e,...n),count=e=>t.count(e),countReset=e=>t.countReset(e),group=(...e)=>t.group(...e),groupCollapsed=(...e)=>t.groupCollapsed(...e),groupEnd=()=>t.groupEnd(),profile=e=>{},profileEnd=e=>{},timeStamp=e=>{},n={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,n as default,dir,dirxml,error,group,groupCollapsed,groupEnd,info,log,profile,profileEnd,table,time,timeEnd,timeLog,timeStamp,trace,warn};
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};
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/console",
3
- "version": "0.42.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.42.0",
40
- "@gjsify/unit": "^0.42.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
  },