You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP β†’
Socket
Book a DemoSign in
Socket

@ncoderz/log-m8

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ncoderz/log-m8 - npm Package Compare versions

Comparing version
1.2.5
to
1.3.0
+1
-1
dist/browser/log-m8.global.js

@@ -1,1 +0,1 @@

"use strict";var logM8=(()=>{var e=Object.defineProperty,t=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,s=Object.prototype.hasOwnProperty,i=(t,r,s)=>((t,r,s)=>r in t?e(t,r,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[r]=s)(t,"symbol"!=typeof r?r+"":r,s),n={};((t,r)=>{for(var s in r)e(t,s,{get:r[s],enumerable:!0})})(n,{LogLevel:()=>l,LogM8:()=>V,LogM8Utils:()=>y,NullLogger:()=>C,PluginKind:()=>a});var o,l={off:"off",fatal:"fatal",error:"error",warn:"warn",info:"info",debug:"debug",track:"track",trace:"trace"},a={appender:"appender",filter:"filter",formatter:"formatter"},c="console",h="1.0.0",g=a.appender,f=new Set([l.fatal,l.error,l.warn,l.info,l.debug,l.track,l.trace]),u=class{constructor(){i(this,"name",c),i(this,"version",h),i(this,"kind",g),i(this,"supportedLevels",f),i(this,"enabled",!0),i(this,"priority"),i(this,"_config"),i(this,"_formatter"),i(this,"_filters",[]),i(this,"_available",!0),i(this,"off",()=>{}),i(this,"fatal",console.error?console.error.bind(console):console.log.bind(console)),i(this,"error",console.error?console.error.bind(console):console.log.bind(console)),i(this,"warn",console.warn?console.warn.bind(console):console.log.bind(console)),i(this,"info",console.info?console.info.bind(console):console.log.bind(console)),i(this,"debug",console.debug?console.debug.bind(console):console.log.bind(console)),i(this,"trace",console.debug?console.debug.bind(console):console.log.bind(console)),i(this,"track",console.log.bind(console))}init(e,t,r){var s,i;this._config=e,this._formatter=t,this._filters=r||[],this._available="undefined"!=typeof console&&!!console.log,this.enabled=!1!==(null==(s=this._config)?void 0:s.enabled),this.priority=null==(i=this._config)?void 0:i.priority}dispose(){}write(e){if(!this._available)return;for(const t of this._filters)if(t.enabled&&!t.filter(e))return;const t=this._formatter?this._formatter.format(e):[e];this[e.level](...t)}flush(){}enableFilter(e){const t=this._getFilter(e);t&&(t.enabled=!0)}disableFilter(e){const t=this._getFilter(e);t&&(t.enabled=!1)}_getFilter(e){return this._filters.find(t=>t.name===e)}},p=class{constructor(){i(this,"name",c),i(this,"version",h),i(this,"kind",g)}create(e){const t=new u;return t.init(e),t}},_=/(yyyy|SSS|hh|mm|ss|SS|zz|z|yy|MM|dd|A|a|h|S)/g,d=/^\/(.+)\/([dgimsuvy]*)$/,m=new Set(["name","message","stack","cause"]),b=["code","errno","syscall","path"],y=class e{static isBrowser(){return"undefined"!=typeof window&&void 0!==window.document}static isString(e){return"string"==typeof e||e instanceof String}static getPropertyByPath(e,t){let r=e;const s=t.replace(/\[(\d+)\]/g,".$1").split(".");for(const e of s){if("object"!=typeof r||null===r)return;if(Array.isArray(r)){const t=Number(e);if(Number.isInteger(t)&&t>=0){r=r[t];continue}}r=r[e]}return r}static formatTimestamp(e,t){const r=null==t?void 0:t.toLowerCase();if(!t||"iso"===r||"toisostring"===r)return e.toISOString();if("locale"===r||"tolocalestring"===r)return e.toLocaleString();const s=(e,t=2)=>String(e).padStart(t,"0"),i=e.getHours(),n=i%12==0?12:i%12;return t.replace(_,t=>{switch(t){case"yyyy":return s(e.getFullYear(),4);case"yy":return s(e.getFullYear()%100);case"MM":return s(e.getMonth()+1);case"dd":return s(e.getDate());case"hh":return s(i);case"h":return s(n);case"mm":return s(e.getMinutes());case"ss":return s(e.getSeconds());case"SSS":return s(e.getMilliseconds(),3);case"SS":return s(Math.floor(e.getMilliseconds()/10),2);case"S":return s(Math.floor(e.getMilliseconds()/100),1);case"A":return i<12?"AM":"PM";case"a":return i<12?"am":"pm";case"z":case"zz":{const r=-e.getTimezoneOffset(),i=r>=0?"+":"-",n=Math.floor(Math.abs(r)/60),o=Math.abs(r)%60;return"z"===t?`${i}${s(n)}:${s(o)}`:`${i}${s(n)}${s(o)}`}default:return t}})}static stringifyLog(t,{maxDepth:r=3,maxStringLength:s=200,maxArrayLength:i=100}={},n){const o=new WeakMap;return JSON.stringify(t,function(t,n){var l;if(n&&"object"==typeof n){const e=null!=(l=o.get(this))?l:0;if(e>=r)return Array.isArray(n)?"[Array]":"[Object]";if(o.set(n,e+1),Array.isArray(n)&&n.length>i){const e=n.slice(0,i);return e.push(`... ${n.length-i} more items`),e}}return e.isString(n)&&n.length>s?n.slice(0,s)+"…":"bigint"==typeof n?n.toString():n instanceof Date?n.toISOString():n instanceof Error?e.serializeError(n):n},n)}static serializeError(e){const t=(e,r)=>{if(!e)return null;const s=e;if("object"==typeof s&&null!==s){if(r.has(s))return{name:"CircularReference",message:"Circular reference detected in error cause chain"};r.add(s)}if("function"==typeof s.toJSON)try{const e=s.toJSON();return JSON.stringify(e),e}catch(e){}const i={name:s.name||"Error",message:s.message||"",stack:s.stack};"cause"in s&&void 0!==s.cause&&(i.cause=t(s.cause,r));for(const e in s)if(Object.prototype.hasOwnProperty.call(s,e)&&!m.has(e))try{const t=s[e];JSON.stringify(t),i[e]=t}catch(e){}for(const e of b)try{const t=Object.getOwnPropertyDescriptor(s,e);t&&void 0!==t.value&&(JSON.stringify(t.value),i[e]=t.value)}catch(e){}return i};return t(e,new WeakSet)}static parseRegexFromString(e,t){try{const r=e.match(d);if(null==r)return;const[,s,i]=r;let n=i;if(Array.isArray(t))for(const e of t)i.includes(e)||(n+=e);return new RegExp(s,n)}catch(e){return}}},v=class{constructor(){i(this,"name","match-filter"),i(this,"version","1.0.0"),i(this,"kind",a.filter),i(this,"enabled",!0),i(this,"_allow"),i(this,"_deny")}init(e){const t=null!=e?e:{};this._allow=this._prepareRules(t.allow),this._deny=this._prepareRules(t.deny),this.enabled=!1!==t.enabled}dispose(){}filter(e){try{if(this._allow&&Object.keys(this._allow).length>0)for(const[t,r]of Object.entries(this._allow)){const s=y.getPropertyByPath(e,t);if(!this._matches(s,r))return!1}if(this._deny&&Object.keys(this._deny).length>0)for(const[t,r]of Object.entries(this._deny)){const s=y.getPropertyByPath(e,t);if(this._matches(s,r))return!1}return!0}catch(e){return!1}}_matches(e,t){if(t instanceof RegExp){if(null==e)return!1;const r="string"==typeof e?e:String(e);return t.test(r)}return this._isEqual(e,t)}_isEqual(e,t){if(e===t)return!0;if("number"==typeof e&&"number"==typeof t)return Number.isNaN(e)&&Number.isNaN(t);if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(!this._isEqual(e[r],t[r]))return!1;return!0}if(this._isPlainObject(e)&&this._isPlainObject(t)){const r=Object.keys(e),s=Object.keys(t);if(r.length!==s.length)return!1;for(const s of r){if(!Object.prototype.hasOwnProperty.call(t,s))return!1;if(!this._isEqual(e[s],t[s]))return!1}return!0}return!1}_isPlainObject(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)&&Object.getPrototypeOf(e)===Object.prototype}_prepareRules(e){if(!e||0===Object.keys(e).length)return;const t={};for(const[r,s]of Object.entries(e))if("string"==typeof s){const e=y.parseRegexFromString(s);t[r]=null!=e?e:s}else t[r]=s;return t}},L=class{constructor(){i(this,"name","match-filter"),i(this,"version","1.0.0"),i(this,"kind",a.filter)}create(e){const t=new v;return t.init(e),t}},w="default-formatter",O="1.0.0",S=a.formatter,M=["{timestamp} {LEVEL} [{logger}]","{message}","{data}"],x="hh:mm:ss.SSS",F=class{constructor(){i(this,"name",w),i(this,"version",O),i(this,"kind",S),i(this,"_config"),i(this,"_format"),i(this,"_timestampFormat",x),i(this,"_levelMap"),i(this,"_colorEnabled",!1),i(this,"_levelColorMap",{trace:"",track:"",debug:"",info:"",warn:"",error:"",fatal:""}),i(this,"_levelCssColorMap",{trace:"color: #bbb;",track:"color: orange;",debug:"color: grey;",info:"color: blue;",warn:"color: gold;",error:"color: red;",fatal:"background: red; color: white;"})}init(e){var t,r;const s=y.isBrowser();this._config=Object.assign({},e),this._colorEnabled=!!this._config.color,this._format=[];let i=null!=(t=this._config.format)?t:M;if("string"==typeof this._config.format&&(i=[this._config.format]),i)for(const e of i){const t=/(\{[^}]+\})|([^{}]+)/g,r=[];let s;for(;null!==(s=t.exec(e));)s[1]?r.push(s[1]):void 0!==s[2]&&r.push(s[2]);this._format.push(r)}this._timestampFormat=null!=(r=this._config.timestampFormat)?r:x;const n=Object.values(l),o=Math.max(...n.map(e=>e.length));this._levelMap=n.reduce((e,t)=>{let r=t.toUpperCase().padEnd(o," ");if(this._colorEnabled){if(s){const s=this._levelCssColorMap[t]||"";return e[t]=[`%c${r}`,s],e}r=(this._levelColorMap[t]||"")+r+""}return e[t]=r,e},{})}dispose(){}format(e){let t;const r=this._format;if(r.length>0){t=r.map(t=>1===t.length?this.resolveToken(t[0],e):t.reduce((t,r)=>{const s=this.resolveToken(r,e);return t+String(s)},""));const s=t.findIndex(e=>Array.isArray(e));if(s>=0){const e=t[s];e.length>0?t.splice(s,1,...e):t.splice(s,1)}}else t=[y.formatTimestamp(e.timestamp,this._timestampFormat),e.level,e.logger,e.message,...e.data,e.context];return t}resolveToken(e,t){var r;if(e.startsWith("{")&&e.endsWith("}")){const s=e.slice(1,-1);if("message"===s&&"string"!=typeof t.message)return t.message;if("LEVEL"===s)return null!=(r=this._levelMap[t.level])?r:t.level;if("timestamp"===s){const e=y.getPropertyByPath(t,s);return y.formatTimestamp(e,this._timestampFormat)}return y.getPropertyByPath(t,s)}return e}},P=class{constructor(){i(this,"name",w),i(this,"version",O),i(this,"kind",S)}create(e){const t=new F;return t.init(e),t}},k="json-formatter",A="1.0.0",j=a.formatter,E=["timestamp","level","logger","message","data"],N=class{constructor(){i(this,"name",k),i(this,"version",A),i(this,"kind",j),i(this,"_config"),i(this,"_format"),i(this,"_pretty"),i(this,"_maxDepth",3),i(this,"_maxStringLen",1e3),i(this,"_maxArrayLen",100),i(this,"_timestampFormat","iso")}init(e){var t,r,s,i,n;this._config=Object.assign({},e),this._pretty=!0===this._config.pretty?2:this._config.pretty?this._config.pretty:void 0,this._maxDepth=null!=(t=this._config.maxDepth)?t:3,this._maxStringLen=null!=(r=this._config.maxStringLen)?r:1e3,this._maxArrayLen=null!=(s=this._config.maxArrayLen)?s:100;let o=null!=(i=this._config.format)?i:E;"string"==typeof this._config.format&&(o=[this._config.format]),this._format=o,this._timestampFormat=null!=(n=this._config.timestampFormat)?n:"iso"}dispose(){}format(e){let t={};const r=this._format;return r.length>0?r.forEach(r=>{const s=this.resolveToken(r,e);null!=s&&(t[s.key]=s.value)}):t=e,[y.stringifyLog(t,{maxDepth:this._maxDepth,maxStringLength:this._maxStringLen,maxArrayLength:this._maxArrayLen},this._pretty)]}resolveToken(e,t){const r=e;if("LEVEL"===r)return{key:r,value:t.level};if("timestamp"===r){const e=y.getPropertyByPath(t,r);return{key:r,value:y.formatTimestamp(e,this._timestampFormat)}}return{key:r,value:y.getPropertyByPath(t,r)}}},B=class{constructor(){i(this,"name",k),i(this,"version",A),i(this,"kind",j)}create(e){const t=new N;return t.init(e),t}},$=class{constructor(){i(this,"_pluginFactories",new Map),i(this,"_plugins",[])}registerPluginFactory(e){if(this._pluginFactories.has(e.name))throw new Error(`LogM8: Plugin with name ${e.name} is already registered.`);this._pluginFactories.set(e.name,e)}createPlugin(e,t){const r="string"==typeof t?t:t.name,s="string"==typeof t?{name:r}:t,i=this.getPluginFactory(r,e);if(!i)throw new Error(`LogM8: Plugin factory kind '${e}' with name '${r}' not found.`);const n=i.create(s);return this._plugins.push(n),n}getPluginFactory(e,t){const r=this._pluginFactories.get(e);if(r&&t===r.kind)return r}disposePlugins(){this._plugins.forEach(e=>{e.dispose()}),this._plugins=[]}clearFactories(){this._pluginFactories.clear()}},z=[{name:"console",formatter:"default-formatter"}],C=class{constructor(){i(this,"isFatal",!1),i(this,"isError",!1),i(this,"isWarn",!1),i(this,"isInfo",!1),i(this,"isDebug",!1),i(this,"isTrack",!1),i(this,"isTrace",!1),i(this,"isEnabled",!1),i(this,"name","nullLogger"),i(this,"level","off"),i(this,"context",{})}fatal(e,...t){}error(e,...t){}warn(e,...t){}info(e,...t){}debug(e,...t){}track(e,...t){}trace(e,...t){}setLevel(e){}setContext(e){this.context=e}getLogger(e){return this}},V=new class{constructor(){i(this,"_initialized"),i(this,"_pluginManager"),i(this,"_loggers"),i(this,"_appenders"),i(this,"_filters",[]),i(this,"_globalLogLevel"),i(this,"_globalLogLevelNumber"),i(this,"_logLevelValues"),i(this,"_logLevelSet"),i(this,"_logBuffer"),this._initialized=!1,this._pluginManager=new $,this._loggers=new Map,this._appenders=[],this._filters=[],this._logLevelValues=Object.values(l),this._logLevelSet=new Set(this._logLevelValues),this._globalLogLevel=l.info,this._globalLogLevelNumber=this._logLevelValues.indexOf(l.info),this._logBuffer=[],this._pluginManager.registerPluginFactory(new p),this._pluginManager.registerPluginFactory(new P),this._pluginManager.registerPluginFactory(new B),this._pluginManager.registerPluginFactory(new L)}init(e){var t,r,s,i,n;e=Object.assign({},e),this._reset(),this.setLevel(null!=(t=e.level)?t:l.info);for(const[t,s]of Object.entries(null!=(r=e.loggers)?r:{})){const e=(null!=s?s:"").trim().toLowerCase(),r=this.getLogger(t),i=this._logLevelSet.has(e)?e:this._globalLogLevel;r.setLevel(i)}const o=null!=(s=e.appenders)?s:z;for(const e of o){const t=this._pluginManager.createPlugin(a.appender,e),r=y.isString(e)?{name:t.name}:e,s=(null==r?void 0:r.formatter)?this._pluginManager.createPlugin(a.formatter,r.formatter):void 0,n=[],o=r;for(const e of null!=(i=o.filters)?i:[]){const t=this._pluginManager.createPlugin(a.filter,e);t?n.push(t):console&&console.log&&console.log(`LogM8: Filter '${e}' not found for appender ${r.name}.`)}t.init(r,s,n),this._appenders.push(t)}this._sortAppenders();for(const t of null!=(n=e.filters)?n:[]){const e=this._pluginManager.createPlugin(a.filter,t);e?this._filters.push(e):console&&console.log&&console.log(`LogM8: Filter '${t}' not found (global).`)}this._initialized=!0}dispose(){this._reset(),this._logBuffer=[],this._pluginManager.clearFactories(),this._initialized=!1}isInitialized(){return this._initialized}getLogger(e){let t=e;Array.isArray(e)&&(t=e.join("."));const r=this._loggers.get(t);if(r)return r;const s={name:t,level:this._globalLogLevel,context:{}};return s.fatal=this._log.bind(this,s,l.fatal),s.error=this._log.bind(this,s,l.error),s.warn=this._log.bind(this,s,l.warn),s.info=this._log.bind(this,s,l.info),s.debug=this._log.bind(this,s,l.debug),s.trace=this._log.bind(this,s,l.trace),s.track=this._log.bind(this,s,l.track),s.setLevel=this._setLevel.bind(this,s),s.setContext=this._setContext.bind(this,s),s.getLogger=e=>this.getLogger([s.name,e]),this._setLevel(s,this._globalLogLevel),this._loggers.set(s.name,s),s}setLevel(e,t){const r=(null!=e?e:"").trim().toLowerCase();t?this.getLogger(t).setLevel(r):(this._globalLogLevel=this._logLevelSet.has(r)?r:this._globalLogLevel,this._globalLogLevelNumber=this._logLevelValues.indexOf(this._globalLogLevel))}enableAppender(e){const t=this._getAppender(e);t&&(t.enabled=!0)}disableAppender(e){const t=this._getAppender(e);t&&(t.enabled=!1)}flushAppender(e){const t=this._getAppender(e);if(t)try{t.flush()}catch(e){console&&console.error&&console.error(`LogM8: Failed to flush appender: ${t.name}:`,e)}}flushAppenders(){for(const e of this._appenders)this.flushAppender(e.name)}enableFilter(e,t){var r;if(t)return void(null==(r=this._getAppender(t))||r.enableFilter(e));const s=this._getFilter(e);s&&(s.enabled=!0)}disableFilter(e,t){var r;if(t)return void(null==(r=this._getAppender(t))||r.disableFilter(e));const s=this._getFilter(e);s&&(s.enabled=!1)}registerPluginFactory(e){this._pluginManager.registerPluginFactory(e)}_log(e,t,r,...s){const i=this._logLevelValues.indexOf(t);if(i>e._levelNumber||i>this._globalLogLevelNumber)return;const n={logger:e.name,level:t,message:r,data:s,context:e.context,timestamp:new Date};if(this._initialized){if(this._logBuffer.length>0){for(const e of this._logBuffer)this._processLogEvent(e);this._logBuffer=[]}this._processLogEvent(n)}else this._logBuffer.length>=100&&this._logBuffer.shift(),this._logBuffer.push(n)}_setLevel(e,t){const r=(null!=t?t:"").trim().toLowerCase();e.level=this._logLevelSet.has(r)?r:e.level,e._levelNumber=this._logLevelValues.indexOf(e.level),e.isEnabled=e.level!==l.off;const s=e._levelNumber;e.isFatal=this._logLevelValues.indexOf(l.fatal)<=s,e.isError=this._logLevelValues.indexOf(l.error)<=s,e.isWarn=this._logLevelValues.indexOf(l.warn)<=s,e.isInfo=this._logLevelValues.indexOf(l.info)<=s,e.isDebug=this._logLevelValues.indexOf(l.debug)<=s,e.isTrack=this._logLevelValues.indexOf(l.track)<=s,e.isTrace=this._logLevelValues.indexOf(l.trace)<=s}_setContext(e,t){e.context=null!=t?t:{}}_processLogEvent(e){for(const t of this._filters)if(t.enabled&&!t.filter(e))return;for(const t of this._appenders)try{if(!t.enabled)continue;if(!t.supportedLevels.has(e.level))continue;t.write(e)}catch(e){console&&console.log&&console.log(`LogM8: Failed to append log with '${t.name}':`,e)}}_getAppender(e){return this._appenders.find(t=>t.name===e)}_sortAppenders(){this._appenders.sort((e,t)=>{var r,s;const i=null!=(r=null==e?void 0:e.priority)?r:0;return(null!=(s=null==t?void 0:t.priority)?s:0)-i})}_getFilter(e){return this._filters.find(t=>t.name===e)}_reset(){this.flushAppenders(),this._appenders=[],this._loggers.clear(),this._globalLogLevel=l.info,this._pluginManager.disposePlugins()}};return o=n,((i,n,o,l)=>{if(n&&"object"==typeof n||"function"==typeof n)for(let a of r(n))s.call(i,a)||a===o||e(i,a,{get:()=>n[a],enumerable:!(l=t(n,a))||l.enumerable});return i})(e({},"__esModule",{value:!0}),o)})();//# sourceMappingURL=log-m8.global.js.map
"use strict";var logM8=(()=>{var e=Object.defineProperty,t=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,s=Object.prototype.hasOwnProperty,i=(t,r,s)=>((t,r,s)=>r in t?e(t,r,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[r]=s)(t,"symbol"!=typeof r?r+"":r,s),n={};((t,r)=>{for(var s in r)e(t,s,{get:r[s],enumerable:!0})})(n,{ConsoleAppender:()=>u,DefaultFormatter:()=>x,JsonFormatter:()=>N,LogLevel:()=>l,LogM8:()=>D,LogM8Utils:()=>b,MatchFilter:()=>v,NullLogger:()=>B,PACKAGE_INFO:()=>$,PluginKind:()=>a});var o,l={off:"off",fatal:"fatal",error:"error",warn:"warn",info:"info",debug:"debug",track:"track",trace:"trace"},a={appender:"appender",filter:"filter",formatter:"formatter"},c="console",h="1.0.0",g=a.appender,f=new Set([l.fatal,l.error,l.warn,l.info,l.debug,l.track,l.trace]),u=class{constructor(){i(this,"name",c),i(this,"version",h),i(this,"kind",g),i(this,"supportedLevels",f),i(this,"enabled",!0),i(this,"priority"),i(this,"_config"),i(this,"_formatter"),i(this,"_filters",[]),i(this,"_available",!0),i(this,"off",()=>{}),i(this,"fatal",console.error?console.error.bind(console):console.log.bind(console)),i(this,"error",console.error?console.error.bind(console):console.log.bind(console)),i(this,"warn",console.warn?console.warn.bind(console):console.log.bind(console)),i(this,"info",console.info?console.info.bind(console):console.log.bind(console)),i(this,"debug",console.debug?console.debug.bind(console):console.log.bind(console)),i(this,"trace",console.debug?console.debug.bind(console):console.log.bind(console)),i(this,"track",console.log.bind(console))}init(e,t,r){var s,i;this._config=e,this._formatter=t,this._filters=r||[],this._available="undefined"!=typeof console&&!!console.log,this.enabled=!1!==(null==(s=this._config)?void 0:s.enabled),this.priority=null==(i=this._config)?void 0:i.priority}dispose(){}write(e){if(!this._available)return;for(const t of this._filters)if(t.enabled&&!t.filter(e))return;const t=this._formatter?this._formatter.format(e):[e];this[e.level](...t)}flush(){}enableFilter(e){const t=this._getFilter(e);t&&(t.enabled=!0)}disableFilter(e){const t=this._getFilter(e);t&&(t.enabled=!1)}_getFilter(e){return this._filters.find(t=>t.name===e)}},p=class{constructor(){i(this,"name",c),i(this,"version",h),i(this,"kind",g)}create(e){const t=new u;return t.init(e),t}},d=/(yyyy|SSS|hh|mm|ss|SS|zz|z|yy|MM|dd|A|a|h|S)/g,_=/^\/(.+)\/([dgimsuvy]*)$/,m=new Set(["name","message","stack","cause"]),y=["code","errno","syscall","path"],b=class e{static isBrowser(){return"undefined"!=typeof window&&void 0!==window.document}static isString(e){return"string"==typeof e||e instanceof String}static getPropertyByPath(e,t){let r=e;const s=t.replace(/\[(\d+)\]/g,".$1").split(".");for(const e of s){if("object"!=typeof r||null===r)return;if(Array.isArray(r)){const t=Number(e);if(Number.isInteger(t)&&t>=0){r=r[t];continue}}r=r[e]}return r}static formatTimestamp(e,t){const r=null==t?void 0:t.toLowerCase();if(!t||"iso"===r||"toisostring"===r)return e.toISOString();if("locale"===r||"tolocalestring"===r)return e.toLocaleString();const s=(e,t=2)=>String(e).padStart(t,"0"),i=e.getHours(),n=i%12==0?12:i%12;return t.replace(d,t=>{switch(t){case"yyyy":return s(e.getFullYear(),4);case"yy":return s(e.getFullYear()%100);case"MM":return s(e.getMonth()+1);case"dd":return s(e.getDate());case"hh":return s(i);case"h":return s(n);case"mm":return s(e.getMinutes());case"ss":return s(e.getSeconds());case"SSS":return s(e.getMilliseconds(),3);case"SS":return s(Math.floor(e.getMilliseconds()/10),2);case"S":return s(Math.floor(e.getMilliseconds()/100),1);case"A":return i<12?"AM":"PM";case"a":return i<12?"am":"pm";case"z":case"zz":{const r=-e.getTimezoneOffset(),i=r>=0?"+":"-",n=Math.floor(Math.abs(r)/60),o=Math.abs(r)%60;return"z"===t?`${i}${s(n)}:${s(o)}`:`${i}${s(n)}${s(o)}`}default:return t}})}static stringifyLog(t,{maxDepth:r=3,maxStringLength:s=200,maxArrayLength:i=100}={},n){const o=new WeakMap;return JSON.stringify(t,function(t,n){var l;if(n&&"object"==typeof n){const e=null!=(l=o.get(this))?l:0;if(e>=r)return Array.isArray(n)?"[Array]":"[Object]";if(o.set(n,e+1),Array.isArray(n)&&n.length>i){const e=n.slice(0,i);return e.push(`... ${n.length-i} more items`),e}}return e.isString(n)&&n.length>s?n.slice(0,s)+"…":"bigint"==typeof n?n.toString():n instanceof Date?n.toISOString():n instanceof Error?e.serializeError(n):n},n)}static serializeError(e){const t=(e,r)=>{if(!e)return null;const s=e;if("object"==typeof s&&null!==s){if(r.has(s))return{name:"CircularReference",message:"Circular reference detected in error cause chain"};r.add(s)}if("function"==typeof s.toJSON)try{const e=s.toJSON();return JSON.stringify(e),e}catch(e){}const i={name:s.name||"Error",message:s.message||"",stack:s.stack};"cause"in s&&void 0!==s.cause&&(i.cause=t(s.cause,r));for(const e in s)if(Object.prototype.hasOwnProperty.call(s,e)&&!m.has(e))try{const t=s[e];JSON.stringify(t),i[e]=t}catch(e){}for(const e of y)try{const t=Object.getOwnPropertyDescriptor(s,e);t&&void 0!==t.value&&(JSON.stringify(t.value),i[e]=t.value)}catch(e){}return i};return t(e,new WeakSet)}static parseRegexFromString(e,t){try{const r=e.match(_);if(null==r)return;const[,s,i]=r;let n=i;if(Array.isArray(t))for(const e of t)i.includes(e)||(n+=e);return new RegExp(s,n)}catch(e){return}}},v=class{constructor(){i(this,"name","match-filter"),i(this,"version","1.0.0"),i(this,"kind",a.filter),i(this,"enabled",!0),i(this,"_allow"),i(this,"_deny")}init(e){const t=null!=e?e:{};this._allow=this._prepareRules(t.allow),this._deny=this._prepareRules(t.deny),this.enabled=!1!==t.enabled}dispose(){}filter(e){try{if(this._allow&&Object.keys(this._allow).length>0)for(const[t,r]of Object.entries(this._allow)){const s=b.getPropertyByPath(e,t);if(!this._matches(s,r))return!1}if(this._deny&&Object.keys(this._deny).length>0)for(const[t,r]of Object.entries(this._deny)){const s=b.getPropertyByPath(e,t);if(this._matches(s,r))return!1}return!0}catch(e){return!1}}_matches(e,t){if(t instanceof RegExp){if(null==e)return!1;const r="string"==typeof e?e:String(e);return t.test(r)}return this._isEqual(e,t)}_isEqual(e,t){if(e===t)return!0;if("number"==typeof e&&"number"==typeof t)return Number.isNaN(e)&&Number.isNaN(t);if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(!this._isEqual(e[r],t[r]))return!1;return!0}if(this._isPlainObject(e)&&this._isPlainObject(t)){const r=Object.keys(e),s=Object.keys(t);if(r.length!==s.length)return!1;for(const s of r){if(!Object.prototype.hasOwnProperty.call(t,s))return!1;if(!this._isEqual(e[s],t[s]))return!1}return!0}return!1}_isPlainObject(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)&&Object.getPrototypeOf(e)===Object.prototype}_prepareRules(e){if(!e||0===Object.keys(e).length)return;const t={};for(const[r,s]of Object.entries(e))if("string"==typeof s){const e=b.parseRegexFromString(s);t[r]=null!=e?e:s}else t[r]=s;return t}},L=class{constructor(){i(this,"name","match-filter"),i(this,"version","1.0.0"),i(this,"kind",a.filter)}create(e){const t=new v;return t.init(e),t}},w="default-formatter",M="1.0.0",A=a.formatter,S=["{timestamp} {LEVEL} [{logger}]","{message}","{data}"],F="hh:mm:ss.SSS",x=class{constructor(){i(this,"name",w),i(this,"version",M),i(this,"kind",A),i(this,"_config"),i(this,"_format"),i(this,"_timestampFormat",F),i(this,"_levelMap"),i(this,"_colorEnabled",!1),i(this,"_levelColorMap",{trace:"",track:"",debug:"",info:"",warn:"",error:"",fatal:""}),i(this,"_levelCssColorMap",{trace:"color: #bbb;",track:"color: orange;",debug:"color: grey;",info:"color: blue;",warn:"color: gold;",error:"color: red;",fatal:"background: red; color: white;"})}init(e){var t,r;const s=b.isBrowser();this._config=Object.assign({},e),this._colorEnabled=!!this._config.color,this._format=[];let i=null!=(t=this._config.format)?t:S;if("string"==typeof this._config.format&&(i=[this._config.format]),i)for(const e of i){const t=/(\{[^}]+\})|([^{}]+)/g,r=[];let s;for(;null!==(s=t.exec(e));)s[1]?r.push(s[1]):void 0!==s[2]&&r.push(s[2]);this._format.push(r)}this._timestampFormat=null!=(r=this._config.timestampFormat)?r:F;const n=Object.values(l),o=Math.max(...n.map(e=>e.length));this._levelMap=n.reduce((e,t)=>{let r=t.toUpperCase().padEnd(o," ");if(this._colorEnabled){if(s){const s=this._levelCssColorMap[t]||"";return e[t]=[`%c${r}`,s],e}r=(this._levelColorMap[t]||"")+r+""}return e[t]=r,e},{})}dispose(){}format(e){let t;const r=this._format;if(r.length>0){t=r.map(t=>1===t.length?this.resolveToken(t[0],e):t.reduce((t,r)=>{const s=this.resolveToken(r,e);return t+String(s)},""));const s=t.findIndex(e=>Array.isArray(e));if(s>=0){const e=t[s];e.length>0?t.splice(s,1,...e):t.splice(s,1)}}else t=[b.formatTimestamp(e.timestamp,this._timestampFormat),e.level,e.logger,e.message,...e.data,e.context];return t}resolveToken(e,t){var r;if(e.startsWith("{")&&e.endsWith("}")){const s=e.slice(1,-1);if("message"===s&&"string"!=typeof t.message)return t.message;if("LEVEL"===s)return null!=(r=this._levelMap[t.level])?r:t.level;if("timestamp"===s){const e=b.getPropertyByPath(t,s);return b.formatTimestamp(e,this._timestampFormat)}return b.getPropertyByPath(t,s)}return e}},P=class{constructor(){i(this,"name",w),i(this,"version",M),i(this,"kind",A)}create(e){const t=new x;return t.init(e),t}},O="json-formatter",k="1.0.0",j=a.formatter,E=["timestamp","level","logger","message","data"],N=class{constructor(){i(this,"name",O),i(this,"version",k),i(this,"kind",j),i(this,"_config"),i(this,"_format"),i(this,"_pretty"),i(this,"_maxDepth",3),i(this,"_maxStringLen",1e3),i(this,"_maxArrayLen",100),i(this,"_timestampFormat","iso")}init(e){var t,r,s,i,n;this._config=Object.assign({},e),this._pretty=!0===this._config.pretty?2:this._config.pretty?this._config.pretty:void 0,this._maxDepth=null!=(t=this._config.maxDepth)?t:3,this._maxStringLen=null!=(r=this._config.maxStringLen)?r:1e3,this._maxArrayLen=null!=(s=this._config.maxArrayLen)?s:100;let o=null!=(i=this._config.format)?i:E;"string"==typeof this._config.format&&(o=[this._config.format]),this._format=o,this._timestampFormat=null!=(n=this._config.timestampFormat)?n:"iso"}dispose(){}format(e){let t={};const r=this._format;return r.length>0?r.forEach(r=>{const s=this.resolveToken(r,e);null!=s&&(t[s.key]=s.value)}):t=e,[b.stringifyLog(t,{maxDepth:this._maxDepth,maxStringLength:this._maxStringLen,maxArrayLength:this._maxArrayLen},this._pretty)]}resolveToken(e,t){const r=e;if("LEVEL"===r)return{key:r,value:t.level};if("timestamp"===r){const e=b.getPropertyByPath(t,r);return{key:r,value:b.formatTimestamp(e,this._timestampFormat)}}return{key:r,value:b.getPropertyByPath(t,r)}}},I=class{constructor(){i(this,"name",O),i(this,"version",k),i(this,"kind",j)}create(e){const t=new N;return t.init(e),t}},C=class{constructor(){i(this,"_pluginFactories",new Map),i(this,"_plugins",[])}registerPluginFactory(e){if(this._pluginFactories.has(e.name))throw new Error(`LogM8: Plugin with name ${e.name} is already registered.`);this._pluginFactories.set(e.name,e)}createPlugin(e,t){const r="string"==typeof t?t:t.name,s="string"==typeof t?{name:r}:t,i=this.getPluginFactory(r,e);if(!i)throw new Error(`LogM8: Plugin factory kind '${e}' with name '${r}' not found.`);const n=i.create(s);return this._plugins.push(n),n}getPluginFactory(e,t){const r=this._pluginFactories.get(e);if(r&&t===r.kind)return r}disposePlugins(){this._plugins.forEach(e=>{e.dispose()}),this._plugins=[]}clearFactories(){this._pluginFactories.clear()}},z=[{name:"console",formatter:"default-formatter"}],$={name:"@ncoderz/log-m8",version:"1.3.0",author:"RA Sewell <richard.sewell@ncoderz.com>",license:"BSD-2-Clause",description:"Logging system for TypeScript / JavaScript"},B=class{constructor(){i(this,"isFatal",!1),i(this,"isError",!1),i(this,"isWarn",!1),i(this,"isInfo",!1),i(this,"isDebug",!1),i(this,"isTrack",!1),i(this,"isTrace",!1),i(this,"isEnabled",!1),i(this,"name","nullLogger"),i(this,"level","off"),i(this,"context",{})}fatal(e,...t){}error(e,...t){}warn(e,...t){}info(e,...t){}debug(e,...t){}track(e,...t){}trace(e,...t){}setLevel(e){}setContext(e){this.context=e}getLogger(e){return this}},D=new class{constructor(){i(this,"_initialized"),i(this,"_pluginManager"),i(this,"_loggers"),i(this,"_appenders"),i(this,"_filters",[]),i(this,"_globalLogLevel"),i(this,"_globalLogLevelNumber"),i(this,"_logLevelValues"),i(this,"_logLevelSet"),i(this,"_logLevelIndexMap"),i(this,"_logBuffer"),this._initialized=!1,this._pluginManager=new C,this._loggers=new Map,this._appenders=[],this._filters=[],this._logLevelValues=Object.values(l),this._logLevelSet=new Set(this._logLevelValues),this._logLevelIndexMap=new Map(this._logLevelValues.map((e,t)=>[e,t])),this._globalLogLevel=l.info,this._globalLogLevelNumber=this._logLevelIndexMap.get(l.info),this._logBuffer=[],this._pluginManager.registerPluginFactory(new p),this._pluginManager.registerPluginFactory(new P),this._pluginManager.registerPluginFactory(new I),this._pluginManager.registerPluginFactory(new L)}init(e){var t,r,s,i;e=Object.assign({},e),this._reset(),this.setLevel(null!=(t=e.level)?t:l.info);for(const[t,s]of Object.entries(null!=(r=e.loggers)?r:{})){const e=(null!=s?s:"").trim().toLowerCase(),r=this.getLogger(t),i=this._logLevelSet.has(e)?e:this._globalLogLevel;r.setLevel(i)}const n=null!=(s=e.appenders)?s:z;for(const e of n){const t=this._createAndInitAppender(e);this._appenders.push(t)}this._sortAppenders();for(const t of null!=(i=e.filters)?i:[]){const e=this._pluginManager.createPlugin(a.filter,t);e?this._filters.push(e):console&&console.log&&console.log(`LogM8: Filter '${t}' not found (global).`)}this._initialized=!0}dispose(){this._reset(),this._logBuffer=[],this._pluginManager.clearFactories(),this._initialized=!1}isInitialized(){return this._initialized}getLogger(e){let t=e;Array.isArray(e)&&(t=e.join("."));const r=this._loggers.get(t);if(r)return r;const s={name:t,level:this._globalLogLevel,context:{}};return s.fatal=this._log.bind(this,s,l.fatal),s.error=this._log.bind(this,s,l.error),s.warn=this._log.bind(this,s,l.warn),s.info=this._log.bind(this,s,l.info),s.debug=this._log.bind(this,s,l.debug),s.trace=this._log.bind(this,s,l.trace),s.track=this._log.bind(this,s,l.track),s.setLevel=this._setLevel.bind(this,s),s.setContext=this._setContext.bind(this,s),s.getLogger=e=>this.getLogger([s.name,e]),this._setLevel(s,this._globalLogLevel),this._loggers.set(s.name,s),s}setLevel(e,t){const r=(null!=e?e:"").trim().toLowerCase();t?this.getLogger(t).setLevel(r):(this._globalLogLevel=this._logLevelSet.has(r)?r:this._globalLogLevel,this._globalLogLevelNumber=this._logLevelIndexMap.get(this._globalLogLevel))}enableAppender(e){const t=this._getAppender(e);t&&(t.enabled=!0)}disableAppender(e){const t=this._getAppender(e);t&&(t.enabled=!1)}addAppender(e){if(!this._initialized)throw new Error("LogM8: Cannot add appender before init()");const t="string"==typeof e?e:e.name;if(this._getAppender(t))throw new Error(`LogM8: Appender '${t}' already exists`);const r=this._createAndInitAppender(e);this._appenders.push(r),this._sortAppenders()}removeAppender(e){const t=this._getAppender(e);if(!t)return!1;try{t.flush()}catch(e){console&&console.error&&console.error(`LogM8: Failed to flush appender during removal: ${t.name}:`,e)}return t.dispose(),this._appenders=this._appenders.filter(e=>e!==t),!0}getAppenderNames(){return this._appenders.map(e=>e.name)}flushAppender(e){const t=this._getAppender(e);if(t)try{t.flush()}catch(e){console&&console.error&&console.error(`LogM8: Failed to flush appender: ${t.name}:`,e)}}flushAppenders(){for(const e of this._appenders)this.flushAppender(e.name)}enableFilter(e,t){var r;if(t)return void(null==(r=this._getAppender(t))||r.enableFilter(e));const s=this._getFilter(e);s&&(s.enabled=!0)}disableFilter(e,t){var r;if(t)return void(null==(r=this._getAppender(t))||r.disableFilter(e));const s=this._getFilter(e);s&&(s.enabled=!1)}registerPluginFactory(e){this._pluginManager.registerPluginFactory(e)}_log(e,t,r){const s=this._logLevelIndexMap.get(t);if(s>e._levelNumber||s>this._globalLogLevelNumber)return;const i=arguments.length>3?Array.prototype.slice.call(arguments,3):[],n={logger:e.name,level:t,message:r,data:i,context:e.context,timestamp:new Date};if(this._initialized){if(this._logBuffer.length>0){for(const e of this._logBuffer)this._processLogEvent(e);this._logBuffer=[]}this._processLogEvent(n)}else this._logBuffer.length>=100&&this._logBuffer.shift(),this._logBuffer.push(n)}_setLevel(e,t){const r=(null!=t?t:"").trim().toLowerCase();e.level=this._logLevelSet.has(r)?r:e.level,e._levelNumber=this._logLevelIndexMap.get(e.level),e.isEnabled=e.level!==l.off;const s=e._levelNumber;e.isFatal=this._logLevelIndexMap.get(l.fatal)<=s,e.isError=this._logLevelIndexMap.get(l.error)<=s,e.isWarn=this._logLevelIndexMap.get(l.warn)<=s,e.isInfo=this._logLevelIndexMap.get(l.info)<=s,e.isDebug=this._logLevelIndexMap.get(l.debug)<=s,e.isTrack=this._logLevelIndexMap.get(l.track)<=s,e.isTrace=this._logLevelIndexMap.get(l.trace)<=s}_setContext(e,t){e.context=null!=t?t:{}}_processLogEvent(e){for(const t of this._filters)if(t.enabled&&!t.filter(e))return;for(const t of this._appenders)try{if(!t.enabled)continue;if(!t.supportedLevels.has(e.level))continue;t.write(e)}catch(e){console&&console.log&&console.log(`LogM8: Failed to append log with '${t.name}':`,e)}}_createAndInitAppender(e){var t;const r=this._pluginManager.createPlugin(a.appender,e),s=b.isString(e)?{name:r.name}:e,i=(null==s?void 0:s.formatter)?this._pluginManager.createPlugin(a.formatter,s.formatter):void 0,n=[],o=s;for(const e of null!=(t=o.filters)?t:[]){const t=this._pluginManager.createPlugin(a.filter,e);t?n.push(t):console&&console.log&&console.log(`LogM8: Filter '${e}' not found for appender ${s.name}.`)}return r.init(s,i,n),r}_getAppender(e){return this._appenders.find(t=>t.name===e)}_sortAppenders(){this._appenders=[...this._appenders].sort((e,t)=>{var r,s;const i=null!=(r=null==e?void 0:e.priority)?r:0;return(null!=(s=null==t?void 0:t.priority)?s:0)-i})}_getFilter(e){return this._filters.find(t=>t.name===e)}_reset(){this.flushAppenders(),this._appenders=[],this._loggers.clear(),this._globalLogLevel=l.info,this._pluginManager.disposePlugins()}};return o=n,((i,n,o,l)=>{if(n&&"object"==typeof n||"function"==typeof n)for(let a of r(n))s.call(i,a)||a===o||e(i,a,{get:()=>n[a],enumerable:!(l=t(n,a))||l.enumerable});return i})(e({},"__esModule",{value:!0}),o)})();//# sourceMappingURL=log-m8.global.js.map

@@ -25,6 +25,12 @@ "use strict";

__export(index_exports, {
ConsoleAppender: () => ConsoleAppender,
DefaultFormatter: () => DefaultFormatter,
FileAppender: () => FileAppender,
JsonFormatter: () => JsonFormatter,
LogLevel: () => LogLevel,
LogM8: () => LogM82,
LogM8Utils: () => LogM8Utils,
MatchFilter: () => MatchFilter,
NullLogger: () => NullLogger,
PACKAGE_INFO: () => PACKAGE_INFO,
PluginKind: () => PluginKind

@@ -1110,2 +1116,3 @@ });

__publicField(this, "_logLevelSet");
__publicField(this, "_logLevelIndexMap");
// Buffer for log events before the system is initialized

@@ -1120,4 +1127,7 @@ __publicField(this, "_logBuffer");

this._logLevelSet = new Set(this._logLevelValues);
this._logLevelIndexMap = new Map(
this._logLevelValues.map((level, index) => [level, index])
);
this._globalLogLevel = LogLevel.info;
this._globalLogLevelNumber = this._logLevelValues.indexOf(LogLevel.info);
this._globalLogLevelNumber = this._logLevelIndexMap.get(LogLevel.info);
this._logBuffer = [];

@@ -1169,28 +1179,3 @@ this._pluginManager.registerPluginFactory(new ConsoleAppenderFactory());

for (const appenderConfigOrName of appenderConfigs) {
const appender = this._pluginManager.createPlugin(
PluginKind.appender,
appenderConfigOrName
);
const appenderConfig = LogM8Utils.isString(appenderConfigOrName) ? {
name: appender.name
} : appenderConfigOrName;
const formatter = appenderConfig?.formatter ? this._pluginManager.createPlugin(
PluginKind.formatter,
appenderConfig.formatter
) : void 0;
const filters = [];
const ac = appenderConfig;
for (const filterConfig of ac.filters ?? []) {
const filter = this._pluginManager.createPlugin(PluginKind.filter, filterConfig);
if (filter) {
filters.push(filter);
} else {
if (console && console.log) {
console.log(
`LogM8: Filter '${filterConfig}' not found for appender ${appenderConfig.name}.`
);
}
}
}
appender.init(appenderConfig, formatter, filters);
const appender = this._createAndInitAppender(appenderConfigOrName);
this._appenders.push(appender);

@@ -1302,3 +1287,3 @@ }

this._globalLogLevel = this._logLevelSet.has(levelStr) ? levelStr : this._globalLogLevel;
this._globalLogLevelNumber = this._logLevelValues.indexOf(this._globalLogLevel);
this._globalLogLevelNumber = this._logLevelIndexMap.get(this._globalLogLevel);
}

@@ -1327,2 +1312,97 @@ }

/**
* Adds an appender to the logging system at runtime.
*
* Creates and initializes the appender using the plugin factory system,
* including any configured formatter and filters. The appender is inserted
* into the priority-sorted appender list and immediately begins receiving
* log events.
*
* Must be called after init(). The appender name must not already be in use.
*
* @param config - Appender name (string) or full configuration object
*
* @throws {Error} When called before init()
* @throws {Error} When an appender with the same name already exists
* @throws {Error} When the referenced plugin factory is not registered
*
* @example
* ```typescript
* // Add by name (uses factory defaults)
* Logging.addAppender('console');
*
* // Add with full configuration
* Logging.addAppender({
* name: 'file',
* filename: 'debug.log',
* formatter: 'json-formatter',
* filters: ['sensitive-data'],
* priority: 10
* });
* ```
*/
addAppender(config) {
if (!this._initialized) {
throw new Error("LogM8: Cannot add appender before init()");
}
const name = typeof config === "string" ? config : config.name;
if (this._getAppender(name)) {
throw new Error(`LogM8: Appender '${name}' already exists`);
}
const appender = this._createAndInitAppender(config);
this._appenders.push(appender);
this._sortAppenders();
}
/**
* Removes an appender from the logging system at runtime.
*
* Flushes any buffered output, disposes the appender, and removes it
* from the active appender list. The appender will no longer receive
* log events after removal.
*
* @param name - Name of the appender to remove
* @returns True if the appender was found and removed, false otherwise
*
* @example
* ```typescript
* // Remove a dynamically added appender
* Logging.removeAppender('file');
*
* // Check if removal succeeded
* if (!Logging.removeAppender('unknown')) {
* console.warn('Appender not found');
* }
* ```
*/
removeAppender(name) {
const appender = this._getAppender(name);
if (!appender) return false;
try {
appender.flush();
} catch (err) {
if (console && console.error) {
console.error(`LogM8: Failed to flush appender during removal: ${appender.name}:`, err);
}
}
appender.dispose();
this._appenders = this._appenders.filter((a) => a !== appender);
return true;
}
/**
* Returns the names of all currently registered appenders.
*
* The returned array is a snapshot; modifications to it do not affect
* the logging system. Names are in priority-sorted order (highest first).
*
* @returns Array of appender names
*
* @example
* ```typescript
* const names = Logging.getAppenderNames();
* console.log('Active appenders:', names); // ['console', 'file']
* ```
*/
getAppenderNames() {
return this._appenders.map((a) => a.name);
}
/**
* Forces an appender to flush any buffered output.

@@ -1435,5 +1515,6 @@ *

}
_log(logger, level, message, ...data) {
const levelNumber = this._logLevelValues.indexOf(level);
_log(logger, level, message) {
const levelNumber = this._logLevelIndexMap.get(level);
if (levelNumber > logger._levelNumber || levelNumber > this._globalLogLevelNumber) return;
const data = arguments.length > 3 ? Array.prototype.slice.call(arguments, 3) : [];
const logEvent = {

@@ -1465,12 +1546,12 @@ logger: logger.name,

logger.level = this._logLevelSet.has(levelStr) ? levelStr : logger.level;
logger._levelNumber = this._logLevelValues.indexOf(logger.level);
logger._levelNumber = this._logLevelIndexMap.get(logger.level);
logger.isEnabled = logger.level !== LogLevel.off;
const levelNumber = logger._levelNumber;
logger.isFatal = this._logLevelValues.indexOf(LogLevel.fatal) <= levelNumber;
logger.isError = this._logLevelValues.indexOf(LogLevel.error) <= levelNumber;
logger.isWarn = this._logLevelValues.indexOf(LogLevel.warn) <= levelNumber;
logger.isInfo = this._logLevelValues.indexOf(LogLevel.info) <= levelNumber;
logger.isDebug = this._logLevelValues.indexOf(LogLevel.debug) <= levelNumber;
logger.isTrack = this._logLevelValues.indexOf(LogLevel.track) <= levelNumber;
logger.isTrace = this._logLevelValues.indexOf(LogLevel.trace) <= levelNumber;
logger.isFatal = this._logLevelIndexMap.get(LogLevel.fatal) <= levelNumber;
logger.isError = this._logLevelIndexMap.get(LogLevel.error) <= levelNumber;
logger.isWarn = this._logLevelIndexMap.get(LogLevel.warn) <= levelNumber;
logger.isInfo = this._logLevelIndexMap.get(LogLevel.info) <= levelNumber;
logger.isDebug = this._logLevelIndexMap.get(LogLevel.debug) <= levelNumber;
logger.isTrack = this._logLevelIndexMap.get(LogLevel.track) <= levelNumber;
logger.isTrace = this._logLevelIndexMap.get(LogLevel.trace) <= levelNumber;
}

@@ -1498,2 +1579,31 @@ _setContext(logger, context) {

}
_createAndInitAppender(appenderConfigOrName) {
const appender = this._pluginManager.createPlugin(
PluginKind.appender,
appenderConfigOrName
);
const appenderConfig = LogM8Utils.isString(appenderConfigOrName) ? {
name: appender.name
} : appenderConfigOrName;
const formatter = appenderConfig?.formatter ? this._pluginManager.createPlugin(
PluginKind.formatter,
appenderConfig.formatter
) : void 0;
const filters = [];
const ac = appenderConfig;
for (const filterConfig of ac.filters ?? []) {
const filter = this._pluginManager.createPlugin(PluginKind.filter, filterConfig);
if (filter) {
filters.push(filter);
} else {
if (console && console.log) {
console.log(
`LogM8: Filter '${filterConfig}' not found for appender ${appenderConfig.name}.`
);
}
}
}
appender.init(appenderConfig, formatter, filters);
return appender;
}
_getAppender(name) {

@@ -1503,3 +1613,3 @@ return this._appenders.find((a) => a.name === name);

_sortAppenders() {
this._appenders.sort((a, b) => {
this._appenders = [...this._appenders].sort((a, b) => {
const aPriority = a?.priority ?? 0;

@@ -1522,2 +1632,11 @@ const bPriority = b?.priority ?? 0;

// src/_generated/package_info.ts
var PACKAGE_INFO = {
"name": "@ncoderz/log-m8",
"version": "1.3.0",
"author": "RA Sewell <richard.sewell@ncoderz.com>",
"license": "BSD-2-Clause",
"description": "Logging system for TypeScript / JavaScript"
};
// src/NullLogger.ts

@@ -1566,8 +1685,14 @@ var NullLogger = class {

0 && (module.exports = {
ConsoleAppender,
DefaultFormatter,
FileAppender,
JsonFormatter,
LogLevel,
LogM8,
LogM8Utils,
MatchFilter,
NullLogger,
PACKAGE_INFO,
PluginKind
});
//# sourceMappingURL=index.cjs.map
/**
* Configuration options for initializing a plugin.
*/
interface PluginConfig {
/**
* The plugin's unique name.
*/
name: string;
/**
* Additional plugin-specific settings.
*/
[key: string]: unknown;
}
/**
* Defines configuration options for a filter plugin.
* @extends PluginConfig
*/
interface FilterConfig extends PluginConfig {
/**
* Whether the filter is enabled.
*/
enabled?: boolean;
}
/**
* Defines configuration options for a formatter plugin.
* @extends PluginConfig
*/
interface FormatterConfig extends PluginConfig {
}
/**
* Defines the configuration options for a log appender plugin.
*/
interface AppenderConfig extends PluginConfig {
/**
* Whether the appender is enabled.
*/
enabled?: boolean;
/**
* Priority determining execution order; higher values run first (descending order).
*/
priority?: number;
/**
* The formatter to apply to log events, specified by name or config object.
*/
formatter?: string | FormatterConfig;
/**
* Filters to apply to log events, specified by name or config objects.
*/
filters?: (string | FilterConfig)[];
}
/**
* Contextual metadata automatically included with all log events from a logger.

@@ -272,56 +326,2 @@ *

/**
* Configuration options for initializing a plugin.
*/
interface PluginConfig {
/**
* The plugin's unique name.
*/
name: string;
/**
* Additional plugin-specific settings.
*/
[key: string]: unknown;
}
/**
* Defines configuration options for a filter plugin.
* @extends PluginConfig
*/
interface FilterConfig extends PluginConfig {
/**
* Whether the filter is enabled.
*/
enabled?: boolean;
}
/**
* Defines configuration options for a formatter plugin.
* @extends PluginConfig
*/
interface FormatterConfig extends PluginConfig {
}
/**
* Defines the configuration options for a log appender plugin.
*/
interface AppenderConfig extends PluginConfig {
/**
* Whether the appender is enabled.
*/
enabled?: boolean;
/**
* Priority determining execution order; higher values run first (descending order).
*/
priority?: number;
/**
* The formatter to apply to log events, specified by name or config object.
*/
formatter?: string | FormatterConfig;
/**
* Filters to apply to log events, specified by name or config objects.
*/
filters?: (string | FilterConfig)[];
}
/**
* Primary configuration object for initializing the LogM8 logging system.

@@ -540,2 +540,3 @@ *

private _logLevelSet;
private _logLevelIndexMap;
private _logBuffer;

@@ -638,2 +639,71 @@ constructor();

/**
* Adds an appender to the logging system at runtime.
*
* Creates and initializes the appender using the plugin factory system,
* including any configured formatter and filters. The appender is inserted
* into the priority-sorted appender list and immediately begins receiving
* log events.
*
* Must be called after init(). The appender name must not already be in use.
*
* @param config - Appender name (string) or full configuration object
*
* @throws {Error} When called before init()
* @throws {Error} When an appender with the same name already exists
* @throws {Error} When the referenced plugin factory is not registered
*
* @example
* ```typescript
* // Add by name (uses factory defaults)
* Logging.addAppender('console');
*
* // Add with full configuration
* Logging.addAppender({
* name: 'file',
* filename: 'debug.log',
* formatter: 'json-formatter',
* filters: ['sensitive-data'],
* priority: 10
* });
* ```
*/
addAppender(config: string | AppenderConfig): void;
/**
* Removes an appender from the logging system at runtime.
*
* Flushes any buffered output, disposes the appender, and removes it
* from the active appender list. The appender will no longer receive
* log events after removal.
*
* @param name - Name of the appender to remove
* @returns True if the appender was found and removed, false otherwise
*
* @example
* ```typescript
* // Remove a dynamically added appender
* Logging.removeAppender('file');
*
* // Check if removal succeeded
* if (!Logging.removeAppender('unknown')) {
* console.warn('Appender not found');
* }
* ```
*/
removeAppender(name: string): boolean;
/**
* Returns the names of all currently registered appenders.
*
* The returned array is a snapshot; modifications to it do not affect
* the logging system. Names are in priority-sorted order (highest first).
*
* @returns Array of appender names
*
* @example
* ```typescript
* const names = Logging.getAppenderNames();
* console.log('Active appenders:', names); // ['console', 'file']
* ```
*/
getAppenderNames(): string[];
/**
* Forces an appender to flush any buffered output.

@@ -718,2 +788,3 @@ *

private _processLogEvent;
private _createAndInitAppender;
private _getAppender;

@@ -725,2 +796,10 @@ private _sortAppenders;

declare const PACKAGE_INFO: {
name: string;
version: string;
author: string;
license: string;
description: string;
};
/**

@@ -942,2 +1021,52 @@ * Structured representation of a single log entry containing all event data.

}
/**
* Built-in appender that outputs log events to the global console object.
*
* Maps log levels to appropriate console methods (error, warn, info, debug, etc.)
* with fallback to console.log when specific methods are unavailable.
* Automatically detects console availability and gracefully handles environments
* where console is not available.
*
* Features:
* - Zero-configuration operation
* - Automatic console method mapping by log level
* - Graceful degradation when console methods are missing
* - No-op flush operation (console output is immediate)
* - Environment detection for console availability
*
* @example
* ```typescript
* // Automatic registration - no manual setup needed
* Logging.init({
* appenders: [{ name: 'console', formatter: 'default-formatter' }]
* });
* ```
*/
declare class ConsoleAppender implements Appender {
name: string;
version: string;
kind: "appender";
readonly supportedLevels: Set<LogLevelType>;
enabled: boolean;
priority?: number;
private _config?;
private _formatter?;
private _filters;
private _available;
private off;
private fatal;
private error;
private warn;
private info;
private debug;
private trace;
private track;
init(config: AppenderConfig, formatter?: Formatter, filters?: Filter[]): void;
dispose(): void;
write(event: LogEvent): void;
flush(): void;
enableFilter(name: string): void;
disableFilter(name: string): void;
private _getFilter;
}

@@ -956,2 +1085,33 @@ /**

}
/**
* Appender that writes each formatted log event to a file (one line per event).
*
* Behavior
* - Initializes a WriteStream on init() using the provided filename.
* - Joins formatted tokens with a single space and appends a trailing newline.
* - If no formatter is configured, writes the raw LogEvent via String() coercion of tokens.
* - Respects per-appender filters before writing.
* - flush() is a no-op; data is flushed by the stream implementation. dispose() ends the stream.
*/
declare class FileAppender implements Appender {
name: string;
version: string;
kind: "appender";
readonly supportedLevels: Set<LogLevelType>;
enabled: boolean;
priority?: number;
private _config?;
private _formatter?;
private _filters;
private _stream?;
private _streamCreationFailed;
init(config: AppenderConfig, formatter?: Formatter, filters?: Filter[]): void;
dispose(): void;
write(event: LogEvent): void;
flush(): void;
enableFilter(name: string): void;
disableFilter(name: string): void;
private _getFilter;
private _createStream;
}

@@ -993,2 +1153,38 @@ /**

}
/**
* Built-in filter providing straightforward allow/deny path-based matching.
*
* Use this when you want quick, declarative filtering without writing code. Rules are
* evaluated against the LogEvent using robust dot-path resolution (with support for
* `array[index]` notation). Comparisons use deep equality for objects/arrays and strict
* equality for primitives.
*/
declare class MatchFilter implements Filter {
name: string;
version: string;
kind: "filter";
enabled: boolean;
private _allow?;
private _deny?;
/**
* Initializes allow/deny rule maps. Values are compared using deep equality for
* arrays/objects and strict equality for primitives. Missing maps are treated as empty.
* @param config - Filter configuration with optional allow/deny maps
*/
init(config: FilterConfig): void;
dispose(): void;
/**
* Evaluates the given event against configured rules.
* - allow: if provided and non-empty, ALL rules must match (AND)
* - deny: if provided, ANY match denies (OR); deny takes precedence over allow
* Returns false on unexpected errors to fail-safe.
* @param logEvent - Event to evaluate
* @returns true when the event should be logged; false to drop
*/
filter(logEvent: LogEvent): boolean;
private _matches;
private _isEqual;
private _isPlainObject;
private _prepareRules;
}

@@ -1024,3 +1220,160 @@ /**

}
/**
* Built-in text formatter with token-based templates and optional colorized levels.
*
* Features
* - Customizable templates using curly-brace tokens mixed with literal text.
* - Timestamp formatting via presets or custom patterns.
* - Optional colorization of the {LEVEL} token (ANSI in Node.js, CSS tuple in browsers).
*
* Notes
* - This formatter outputs text (not JSON). It returns an array of strings/values that
* appenders can pass to console/file outputs.
* - The {data} token resolves to the `logEvent.data` array. If present as its own
* line entry, items are expanded in-place; if the array is empty, the token is removed.
* - {message} is passed through as-is when it’s not a string (e.g., objects or errors).
* - Any other token (including nested paths like {context.userId}) is resolved using
* dot-path access on the LogEvent.
*
* Supported tokens
* - {timestamp}: Formatted with `timestampFormat`.
* - {LEVEL}: Uppercase level label (with optional colorization/padding).
* - {level}: Lowercase level name.
* - {logger}: Logger name.
* - {message}: Primary log message (string or non-string value).
* - {data}: Additional data arguments array (expanded inline when present alone in a line).
* - {context.*}: Nested context properties.
*
* @example
* // Text with colors
* formatter.init({
* format: '{timestamp} {LEVEL} [{logger}] {message}',
* timestampFormat: 'hh:mm:ss.SSS',
* color: true,
* });
*
* // Multi-line text output with expanded data
* formatter.init({
* format: [
* '{timestamp} {LEVEL} [{logger}] {message}',
* 'Context: {context}',
* 'Data: {data}',
* ],
* });
*/
declare class DefaultFormatter implements Formatter {
name: string;
version: string;
kind: "formatter";
private _config;
private _format;
private _timestampFormat;
private _levelMap;
private _colorEnabled;
private _levelColorMap;
private _levelCssColorMap;
init(config: DefaultFormatterConfig): void;
dispose(): void;
format(logEvent: LogEvent): unknown[];
private resolveToken;
}
/**
* Configuration for the JSON formatter.
*
* Extends the base FormatterConfig with options for selecting which fields to
* include, pretty printing, timestamp formatting, and output size limits.
*/
interface JsonFormatterConfig extends FormatterConfig {
/**
* Fields to include in the output object.
* Accepts a single field or an array of fields. Defaults to
* ['timestamp', 'level', 'logger', 'message', 'data'].
*
* Each entry is used as the object key and resolved via dot-path on the LogEvent.
* Special handling:
* - 'LEVEL' returns the raw level string (e.g., 'info').
* - 'timestamp' is formatted with `timestampFormat`.
*
* If the list is empty, the entire LogEvent object is used.
*/
format?: string | string[];
/**
* Pretty-print JSON output.
* - true: default indentation of 2 spaces.
* - number: use the provided number of spaces.
* - false/undefined: minified JSON with no extra whitespace.
*/
pretty?: boolean | number;
/**
* Timestamp format pattern or preset.
* Supports 'iso', 'locale', or custom token patterns (yyyy-MM-dd hh:mm:ss).
*/
timestampFormat?: string;
/**
* Maximum depth for nested objects in JSON output.
* Defaults to 3.
*/
maxDepth?: number;
/**
* Maximum length for string values in JSON output.
* Strings longer than this may be truncated by `stringifyLog`. Defaults to 1000.
*/
maxStringLen?: number;
/**
* Maximum length for array values in JSON output.
* Arrays longer than this may be truncated by `stringifyLog`. Defaults to 100.
*/
maxArrayLen?: number;
}
/**
* JSON formatter that emits a single JSON string per log event.
*
* Features
* - Select fields via `format` (e.g., ['timestamp','level','logger','message','data']).
* - Formats timestamps using `timestampFormat` ('iso', 'locale', or custom pattern).
* - Pretty printing with fixed (2 spaces) or custom indentation.
* - Output size controls via `maxDepth`, `maxStringLen`, and `maxArrayLen` (passed to `LogM8Utils.stringifyLog`).
*
* Behavior
* - Returns an array with a single element: the JSON string representation of the selected fields.
* - Field resolution uses dot-path access on the LogEvent (e.g., 'context.userId').
* - Special tokens:
* - 'timestamp': formatted per `timestampFormat`.
* - 'LEVEL': raw level string (lowercase; no color or padding).
* - If `format` is empty, the entire LogEvent object is serialized.
*
* Examples
*
* // Default fields, minified JSON
* formatter.init({});
* // => ["{\"timestamp\":\"...\",\"level\":\"info\",\"logger\":\"app\",\"message\":\"...\",\"data\":[]}"]
*
* // Pretty printed output (2 spaces)
* formatter.init({ pretty: true });
*
* // Custom fields with a nested context property
* formatter.init({
* format: ['timestamp', 'LEVEL', 'logger', 'context.userId', 'message'],
* timestampFormat: 'hh:mm:ss.SSS',
* pretty: 2,
* });
*/
declare class JsonFormatter implements Formatter {
name: string;
version: string;
kind: "formatter";
private _config;
private _format;
private _pretty;
private _maxDepth;
private _maxStringLen;
private _maxArrayLen;
private _timestampFormat;
init(config: JsonFormatterConfig): void;
dispose(): void;
format(logEvent: LogEvent): unknown[];
private resolveToken;
}
interface StringifyLogOptions {

@@ -1336,2 +1689,2 @@ /** Max object/array nesting depth to descend into (default 3). */

export { type Appender, type AppenderConfig, type ConsoleAppenderConfig, type DefaultFormatterConfig, type FileAppenderConfig, type Filter, type FilterConfig, type Formatter, type FormatterConfig, type Log, type LogContext, LogLevel, type LogLevelType, LogM8, LogM8Utils, type LoggingConfig, type MatchFilterConfig, NullLogger, type Plugin, type PluginConfig, type PluginFactory, PluginKind, type PluginKindType };
export { type Appender, type AppenderConfig, ConsoleAppender, type ConsoleAppenderConfig, DefaultFormatter, type DefaultFormatterConfig, FileAppender, type FileAppenderConfig, type Filter, type FilterConfig, type Formatter, type FormatterConfig, JsonFormatter, type JsonFormatterConfig, type Log, type LogContext, type LogEvent, LogLevel, type LogLevelType, LogM8, LogM8Utils, type LoggingConfig, MatchFilter, type MatchFilterConfig, NullLogger, PACKAGE_INFO, type Plugin, type PluginConfig, type PluginFactory, PluginKind, type PluginKindType };
/**
* Configuration options for initializing a plugin.
*/
interface PluginConfig {
/**
* The plugin's unique name.
*/
name: string;
/**
* Additional plugin-specific settings.
*/
[key: string]: unknown;
}
/**
* Defines configuration options for a filter plugin.
* @extends PluginConfig
*/
interface FilterConfig extends PluginConfig {
/**
* Whether the filter is enabled.
*/
enabled?: boolean;
}
/**
* Defines configuration options for a formatter plugin.
* @extends PluginConfig
*/
interface FormatterConfig extends PluginConfig {
}
/**
* Defines the configuration options for a log appender plugin.
*/
interface AppenderConfig extends PluginConfig {
/**
* Whether the appender is enabled.
*/
enabled?: boolean;
/**
* Priority determining execution order; higher values run first (descending order).
*/
priority?: number;
/**
* The formatter to apply to log events, specified by name or config object.
*/
formatter?: string | FormatterConfig;
/**
* Filters to apply to log events, specified by name or config objects.
*/
filters?: (string | FilterConfig)[];
}
/**
* Contextual metadata automatically included with all log events from a logger.

@@ -272,56 +326,2 @@ *

/**
* Configuration options for initializing a plugin.
*/
interface PluginConfig {
/**
* The plugin's unique name.
*/
name: string;
/**
* Additional plugin-specific settings.
*/
[key: string]: unknown;
}
/**
* Defines configuration options for a filter plugin.
* @extends PluginConfig
*/
interface FilterConfig extends PluginConfig {
/**
* Whether the filter is enabled.
*/
enabled?: boolean;
}
/**
* Defines configuration options for a formatter plugin.
* @extends PluginConfig
*/
interface FormatterConfig extends PluginConfig {
}
/**
* Defines the configuration options for a log appender plugin.
*/
interface AppenderConfig extends PluginConfig {
/**
* Whether the appender is enabled.
*/
enabled?: boolean;
/**
* Priority determining execution order; higher values run first (descending order).
*/
priority?: number;
/**
* The formatter to apply to log events, specified by name or config object.
*/
formatter?: string | FormatterConfig;
/**
* Filters to apply to log events, specified by name or config objects.
*/
filters?: (string | FilterConfig)[];
}
/**
* Primary configuration object for initializing the LogM8 logging system.

@@ -540,2 +540,3 @@ *

private _logLevelSet;
private _logLevelIndexMap;
private _logBuffer;

@@ -638,2 +639,71 @@ constructor();

/**
* Adds an appender to the logging system at runtime.
*
* Creates and initializes the appender using the plugin factory system,
* including any configured formatter and filters. The appender is inserted
* into the priority-sorted appender list and immediately begins receiving
* log events.
*
* Must be called after init(). The appender name must not already be in use.
*
* @param config - Appender name (string) or full configuration object
*
* @throws {Error} When called before init()
* @throws {Error} When an appender with the same name already exists
* @throws {Error} When the referenced plugin factory is not registered
*
* @example
* ```typescript
* // Add by name (uses factory defaults)
* Logging.addAppender('console');
*
* // Add with full configuration
* Logging.addAppender({
* name: 'file',
* filename: 'debug.log',
* formatter: 'json-formatter',
* filters: ['sensitive-data'],
* priority: 10
* });
* ```
*/
addAppender(config: string | AppenderConfig): void;
/**
* Removes an appender from the logging system at runtime.
*
* Flushes any buffered output, disposes the appender, and removes it
* from the active appender list. The appender will no longer receive
* log events after removal.
*
* @param name - Name of the appender to remove
* @returns True if the appender was found and removed, false otherwise
*
* @example
* ```typescript
* // Remove a dynamically added appender
* Logging.removeAppender('file');
*
* // Check if removal succeeded
* if (!Logging.removeAppender('unknown')) {
* console.warn('Appender not found');
* }
* ```
*/
removeAppender(name: string): boolean;
/**
* Returns the names of all currently registered appenders.
*
* The returned array is a snapshot; modifications to it do not affect
* the logging system. Names are in priority-sorted order (highest first).
*
* @returns Array of appender names
*
* @example
* ```typescript
* const names = Logging.getAppenderNames();
* console.log('Active appenders:', names); // ['console', 'file']
* ```
*/
getAppenderNames(): string[];
/**
* Forces an appender to flush any buffered output.

@@ -718,2 +788,3 @@ *

private _processLogEvent;
private _createAndInitAppender;
private _getAppender;

@@ -725,2 +796,10 @@ private _sortAppenders;

declare const PACKAGE_INFO: {
name: string;
version: string;
author: string;
license: string;
description: string;
};
/**

@@ -942,2 +1021,52 @@ * Structured representation of a single log entry containing all event data.

}
/**
* Built-in appender that outputs log events to the global console object.
*
* Maps log levels to appropriate console methods (error, warn, info, debug, etc.)
* with fallback to console.log when specific methods are unavailable.
* Automatically detects console availability and gracefully handles environments
* where console is not available.
*
* Features:
* - Zero-configuration operation
* - Automatic console method mapping by log level
* - Graceful degradation when console methods are missing
* - No-op flush operation (console output is immediate)
* - Environment detection for console availability
*
* @example
* ```typescript
* // Automatic registration - no manual setup needed
* Logging.init({
* appenders: [{ name: 'console', formatter: 'default-formatter' }]
* });
* ```
*/
declare class ConsoleAppender implements Appender {
name: string;
version: string;
kind: "appender";
readonly supportedLevels: Set<LogLevelType>;
enabled: boolean;
priority?: number;
private _config?;
private _formatter?;
private _filters;
private _available;
private off;
private fatal;
private error;
private warn;
private info;
private debug;
private trace;
private track;
init(config: AppenderConfig, formatter?: Formatter, filters?: Filter[]): void;
dispose(): void;
write(event: LogEvent): void;
flush(): void;
enableFilter(name: string): void;
disableFilter(name: string): void;
private _getFilter;
}

@@ -956,2 +1085,33 @@ /**

}
/**
* Appender that writes each formatted log event to a file (one line per event).
*
* Behavior
* - Initializes a WriteStream on init() using the provided filename.
* - Joins formatted tokens with a single space and appends a trailing newline.
* - If no formatter is configured, writes the raw LogEvent via String() coercion of tokens.
* - Respects per-appender filters before writing.
* - flush() is a no-op; data is flushed by the stream implementation. dispose() ends the stream.
*/
declare class FileAppender implements Appender {
name: string;
version: string;
kind: "appender";
readonly supportedLevels: Set<LogLevelType>;
enabled: boolean;
priority?: number;
private _config?;
private _formatter?;
private _filters;
private _stream?;
private _streamCreationFailed;
init(config: AppenderConfig, formatter?: Formatter, filters?: Filter[]): void;
dispose(): void;
write(event: LogEvent): void;
flush(): void;
enableFilter(name: string): void;
disableFilter(name: string): void;
private _getFilter;
private _createStream;
}

@@ -993,2 +1153,38 @@ /**

}
/**
* Built-in filter providing straightforward allow/deny path-based matching.
*
* Use this when you want quick, declarative filtering without writing code. Rules are
* evaluated against the LogEvent using robust dot-path resolution (with support for
* `array[index]` notation). Comparisons use deep equality for objects/arrays and strict
* equality for primitives.
*/
declare class MatchFilter implements Filter {
name: string;
version: string;
kind: "filter";
enabled: boolean;
private _allow?;
private _deny?;
/**
* Initializes allow/deny rule maps. Values are compared using deep equality for
* arrays/objects and strict equality for primitives. Missing maps are treated as empty.
* @param config - Filter configuration with optional allow/deny maps
*/
init(config: FilterConfig): void;
dispose(): void;
/**
* Evaluates the given event against configured rules.
* - allow: if provided and non-empty, ALL rules must match (AND)
* - deny: if provided, ANY match denies (OR); deny takes precedence over allow
* Returns false on unexpected errors to fail-safe.
* @param logEvent - Event to evaluate
* @returns true when the event should be logged; false to drop
*/
filter(logEvent: LogEvent): boolean;
private _matches;
private _isEqual;
private _isPlainObject;
private _prepareRules;
}

@@ -1024,3 +1220,160 @@ /**

}
/**
* Built-in text formatter with token-based templates and optional colorized levels.
*
* Features
* - Customizable templates using curly-brace tokens mixed with literal text.
* - Timestamp formatting via presets or custom patterns.
* - Optional colorization of the {LEVEL} token (ANSI in Node.js, CSS tuple in browsers).
*
* Notes
* - This formatter outputs text (not JSON). It returns an array of strings/values that
* appenders can pass to console/file outputs.
* - The {data} token resolves to the `logEvent.data` array. If present as its own
* line entry, items are expanded in-place; if the array is empty, the token is removed.
* - {message} is passed through as-is when it’s not a string (e.g., objects or errors).
* - Any other token (including nested paths like {context.userId}) is resolved using
* dot-path access on the LogEvent.
*
* Supported tokens
* - {timestamp}: Formatted with `timestampFormat`.
* - {LEVEL}: Uppercase level label (with optional colorization/padding).
* - {level}: Lowercase level name.
* - {logger}: Logger name.
* - {message}: Primary log message (string or non-string value).
* - {data}: Additional data arguments array (expanded inline when present alone in a line).
* - {context.*}: Nested context properties.
*
* @example
* // Text with colors
* formatter.init({
* format: '{timestamp} {LEVEL} [{logger}] {message}',
* timestampFormat: 'hh:mm:ss.SSS',
* color: true,
* });
*
* // Multi-line text output with expanded data
* formatter.init({
* format: [
* '{timestamp} {LEVEL} [{logger}] {message}',
* 'Context: {context}',
* 'Data: {data}',
* ],
* });
*/
declare class DefaultFormatter implements Formatter {
name: string;
version: string;
kind: "formatter";
private _config;
private _format;
private _timestampFormat;
private _levelMap;
private _colorEnabled;
private _levelColorMap;
private _levelCssColorMap;
init(config: DefaultFormatterConfig): void;
dispose(): void;
format(logEvent: LogEvent): unknown[];
private resolveToken;
}
/**
* Configuration for the JSON formatter.
*
* Extends the base FormatterConfig with options for selecting which fields to
* include, pretty printing, timestamp formatting, and output size limits.
*/
interface JsonFormatterConfig extends FormatterConfig {
/**
* Fields to include in the output object.
* Accepts a single field or an array of fields. Defaults to
* ['timestamp', 'level', 'logger', 'message', 'data'].
*
* Each entry is used as the object key and resolved via dot-path on the LogEvent.
* Special handling:
* - 'LEVEL' returns the raw level string (e.g., 'info').
* - 'timestamp' is formatted with `timestampFormat`.
*
* If the list is empty, the entire LogEvent object is used.
*/
format?: string | string[];
/**
* Pretty-print JSON output.
* - true: default indentation of 2 spaces.
* - number: use the provided number of spaces.
* - false/undefined: minified JSON with no extra whitespace.
*/
pretty?: boolean | number;
/**
* Timestamp format pattern or preset.
* Supports 'iso', 'locale', or custom token patterns (yyyy-MM-dd hh:mm:ss).
*/
timestampFormat?: string;
/**
* Maximum depth for nested objects in JSON output.
* Defaults to 3.
*/
maxDepth?: number;
/**
* Maximum length for string values in JSON output.
* Strings longer than this may be truncated by `stringifyLog`. Defaults to 1000.
*/
maxStringLen?: number;
/**
* Maximum length for array values in JSON output.
* Arrays longer than this may be truncated by `stringifyLog`. Defaults to 100.
*/
maxArrayLen?: number;
}
/**
* JSON formatter that emits a single JSON string per log event.
*
* Features
* - Select fields via `format` (e.g., ['timestamp','level','logger','message','data']).
* - Formats timestamps using `timestampFormat` ('iso', 'locale', or custom pattern).
* - Pretty printing with fixed (2 spaces) or custom indentation.
* - Output size controls via `maxDepth`, `maxStringLen`, and `maxArrayLen` (passed to `LogM8Utils.stringifyLog`).
*
* Behavior
* - Returns an array with a single element: the JSON string representation of the selected fields.
* - Field resolution uses dot-path access on the LogEvent (e.g., 'context.userId').
* - Special tokens:
* - 'timestamp': formatted per `timestampFormat`.
* - 'LEVEL': raw level string (lowercase; no color or padding).
* - If `format` is empty, the entire LogEvent object is serialized.
*
* Examples
*
* // Default fields, minified JSON
* formatter.init({});
* // => ["{\"timestamp\":\"...\",\"level\":\"info\",\"logger\":\"app\",\"message\":\"...\",\"data\":[]}"]
*
* // Pretty printed output (2 spaces)
* formatter.init({ pretty: true });
*
* // Custom fields with a nested context property
* formatter.init({
* format: ['timestamp', 'LEVEL', 'logger', 'context.userId', 'message'],
* timestampFormat: 'hh:mm:ss.SSS',
* pretty: 2,
* });
*/
declare class JsonFormatter implements Formatter {
name: string;
version: string;
kind: "formatter";
private _config;
private _format;
private _pretty;
private _maxDepth;
private _maxStringLen;
private _maxArrayLen;
private _timestampFormat;
init(config: JsonFormatterConfig): void;
dispose(): void;
format(logEvent: LogEvent): unknown[];
private resolveToken;
}
interface StringifyLogOptions {

@@ -1336,2 +1689,2 @@ /** Max object/array nesting depth to descend into (default 3). */

export { type Appender, type AppenderConfig, type ConsoleAppenderConfig, type DefaultFormatterConfig, type FileAppenderConfig, type Filter, type FilterConfig, type Formatter, type FormatterConfig, type Log, type LogContext, LogLevel, type LogLevelType, LogM8, LogM8Utils, type LoggingConfig, type MatchFilterConfig, NullLogger, type Plugin, type PluginConfig, type PluginFactory, PluginKind, type PluginKindType };
export { type Appender, type AppenderConfig, ConsoleAppender, type ConsoleAppenderConfig, DefaultFormatter, type DefaultFormatterConfig, FileAppender, type FileAppenderConfig, type Filter, type FilterConfig, type Formatter, type FormatterConfig, JsonFormatter, type JsonFormatterConfig, type Log, type LogContext, type LogEvent, LogLevel, type LogLevelType, LogM8, LogM8Utils, type LoggingConfig, MatchFilter, type MatchFilterConfig, NullLogger, PACKAGE_INFO, type Plugin, type PluginConfig, type PluginFactory, PluginKind, type PluginKindType };

@@ -1081,2 +1081,3 @@ var __defProp = Object.defineProperty;

__publicField(this, "_logLevelSet");
__publicField(this, "_logLevelIndexMap");
// Buffer for log events before the system is initialized

@@ -1091,4 +1092,7 @@ __publicField(this, "_logBuffer");

this._logLevelSet = new Set(this._logLevelValues);
this._logLevelIndexMap = new Map(
this._logLevelValues.map((level, index) => [level, index])
);
this._globalLogLevel = LogLevel.info;
this._globalLogLevelNumber = this._logLevelValues.indexOf(LogLevel.info);
this._globalLogLevelNumber = this._logLevelIndexMap.get(LogLevel.info);
this._logBuffer = [];

@@ -1140,28 +1144,3 @@ this._pluginManager.registerPluginFactory(new ConsoleAppenderFactory());

for (const appenderConfigOrName of appenderConfigs) {
const appender = this._pluginManager.createPlugin(
PluginKind.appender,
appenderConfigOrName
);
const appenderConfig = LogM8Utils.isString(appenderConfigOrName) ? {
name: appender.name
} : appenderConfigOrName;
const formatter = appenderConfig?.formatter ? this._pluginManager.createPlugin(
PluginKind.formatter,
appenderConfig.formatter
) : void 0;
const filters = [];
const ac = appenderConfig;
for (const filterConfig of ac.filters ?? []) {
const filter = this._pluginManager.createPlugin(PluginKind.filter, filterConfig);
if (filter) {
filters.push(filter);
} else {
if (console && console.log) {
console.log(
`LogM8: Filter '${filterConfig}' not found for appender ${appenderConfig.name}.`
);
}
}
}
appender.init(appenderConfig, formatter, filters);
const appender = this._createAndInitAppender(appenderConfigOrName);
this._appenders.push(appender);

@@ -1273,3 +1252,3 @@ }

this._globalLogLevel = this._logLevelSet.has(levelStr) ? levelStr : this._globalLogLevel;
this._globalLogLevelNumber = this._logLevelValues.indexOf(this._globalLogLevel);
this._globalLogLevelNumber = this._logLevelIndexMap.get(this._globalLogLevel);
}

@@ -1298,2 +1277,97 @@ }

/**
* Adds an appender to the logging system at runtime.
*
* Creates and initializes the appender using the plugin factory system,
* including any configured formatter and filters. The appender is inserted
* into the priority-sorted appender list and immediately begins receiving
* log events.
*
* Must be called after init(). The appender name must not already be in use.
*
* @param config - Appender name (string) or full configuration object
*
* @throws {Error} When called before init()
* @throws {Error} When an appender with the same name already exists
* @throws {Error} When the referenced plugin factory is not registered
*
* @example
* ```typescript
* // Add by name (uses factory defaults)
* Logging.addAppender('console');
*
* // Add with full configuration
* Logging.addAppender({
* name: 'file',
* filename: 'debug.log',
* formatter: 'json-formatter',
* filters: ['sensitive-data'],
* priority: 10
* });
* ```
*/
addAppender(config) {
if (!this._initialized) {
throw new Error("LogM8: Cannot add appender before init()");
}
const name = typeof config === "string" ? config : config.name;
if (this._getAppender(name)) {
throw new Error(`LogM8: Appender '${name}' already exists`);
}
const appender = this._createAndInitAppender(config);
this._appenders.push(appender);
this._sortAppenders();
}
/**
* Removes an appender from the logging system at runtime.
*
* Flushes any buffered output, disposes the appender, and removes it
* from the active appender list. The appender will no longer receive
* log events after removal.
*
* @param name - Name of the appender to remove
* @returns True if the appender was found and removed, false otherwise
*
* @example
* ```typescript
* // Remove a dynamically added appender
* Logging.removeAppender('file');
*
* // Check if removal succeeded
* if (!Logging.removeAppender('unknown')) {
* console.warn('Appender not found');
* }
* ```
*/
removeAppender(name) {
const appender = this._getAppender(name);
if (!appender) return false;
try {
appender.flush();
} catch (err) {
if (console && console.error) {
console.error(`LogM8: Failed to flush appender during removal: ${appender.name}:`, err);
}
}
appender.dispose();
this._appenders = this._appenders.filter((a) => a !== appender);
return true;
}
/**
* Returns the names of all currently registered appenders.
*
* The returned array is a snapshot; modifications to it do not affect
* the logging system. Names are in priority-sorted order (highest first).
*
* @returns Array of appender names
*
* @example
* ```typescript
* const names = Logging.getAppenderNames();
* console.log('Active appenders:', names); // ['console', 'file']
* ```
*/
getAppenderNames() {
return this._appenders.map((a) => a.name);
}
/**
* Forces an appender to flush any buffered output.

@@ -1406,5 +1480,6 @@ *

}
_log(logger, level, message, ...data) {
const levelNumber = this._logLevelValues.indexOf(level);
_log(logger, level, message) {
const levelNumber = this._logLevelIndexMap.get(level);
if (levelNumber > logger._levelNumber || levelNumber > this._globalLogLevelNumber) return;
const data = arguments.length > 3 ? Array.prototype.slice.call(arguments, 3) : [];
const logEvent = {

@@ -1436,12 +1511,12 @@ logger: logger.name,

logger.level = this._logLevelSet.has(levelStr) ? levelStr : logger.level;
logger._levelNumber = this._logLevelValues.indexOf(logger.level);
logger._levelNumber = this._logLevelIndexMap.get(logger.level);
logger.isEnabled = logger.level !== LogLevel.off;
const levelNumber = logger._levelNumber;
logger.isFatal = this._logLevelValues.indexOf(LogLevel.fatal) <= levelNumber;
logger.isError = this._logLevelValues.indexOf(LogLevel.error) <= levelNumber;
logger.isWarn = this._logLevelValues.indexOf(LogLevel.warn) <= levelNumber;
logger.isInfo = this._logLevelValues.indexOf(LogLevel.info) <= levelNumber;
logger.isDebug = this._logLevelValues.indexOf(LogLevel.debug) <= levelNumber;
logger.isTrack = this._logLevelValues.indexOf(LogLevel.track) <= levelNumber;
logger.isTrace = this._logLevelValues.indexOf(LogLevel.trace) <= levelNumber;
logger.isFatal = this._logLevelIndexMap.get(LogLevel.fatal) <= levelNumber;
logger.isError = this._logLevelIndexMap.get(LogLevel.error) <= levelNumber;
logger.isWarn = this._logLevelIndexMap.get(LogLevel.warn) <= levelNumber;
logger.isInfo = this._logLevelIndexMap.get(LogLevel.info) <= levelNumber;
logger.isDebug = this._logLevelIndexMap.get(LogLevel.debug) <= levelNumber;
logger.isTrack = this._logLevelIndexMap.get(LogLevel.track) <= levelNumber;
logger.isTrace = this._logLevelIndexMap.get(LogLevel.trace) <= levelNumber;
}

@@ -1469,2 +1544,31 @@ _setContext(logger, context) {

}
_createAndInitAppender(appenderConfigOrName) {
const appender = this._pluginManager.createPlugin(
PluginKind.appender,
appenderConfigOrName
);
const appenderConfig = LogM8Utils.isString(appenderConfigOrName) ? {
name: appender.name
} : appenderConfigOrName;
const formatter = appenderConfig?.formatter ? this._pluginManager.createPlugin(
PluginKind.formatter,
appenderConfig.formatter
) : void 0;
const filters = [];
const ac = appenderConfig;
for (const filterConfig of ac.filters ?? []) {
const filter = this._pluginManager.createPlugin(PluginKind.filter, filterConfig);
if (filter) {
filters.push(filter);
} else {
if (console && console.log) {
console.log(
`LogM8: Filter '${filterConfig}' not found for appender ${appenderConfig.name}.`
);
}
}
}
appender.init(appenderConfig, formatter, filters);
return appender;
}
_getAppender(name) {

@@ -1474,3 +1578,3 @@ return this._appenders.find((a) => a.name === name);

_sortAppenders() {
this._appenders.sort((a, b) => {
this._appenders = [...this._appenders].sort((a, b) => {
const aPriority = a?.priority ?? 0;

@@ -1493,2 +1597,11 @@ const bPriority = b?.priority ?? 0;

// src/_generated/package_info.ts
var PACKAGE_INFO = {
"name": "@ncoderz/log-m8",
"version": "1.3.0",
"author": "RA Sewell <richard.sewell@ncoderz.com>",
"license": "BSD-2-Clause",
"description": "Logging system for TypeScript / JavaScript"
};
// src/NullLogger.ts

@@ -1536,8 +1649,14 @@ var NullLogger = class {

export {
ConsoleAppender,
DefaultFormatter,
FileAppender,
JsonFormatter,
LogLevel,
LogM82 as LogM8,
LogM8Utils,
MatchFilter,
NullLogger,
PACKAGE_INFO,
PluginKind
};
//# sourceMappingURL=index.js.map
{
"name": "@ncoderz/log-m8",
"version": "1.2.5",
"version": "1.3.0",
"description": "Logging system for TypeScript / JavaScript",

@@ -5,0 +5,0 @@ "author": "RA Sewell <richard.sewell@ncoderz.com>",

# log-m8
![Build & Test](https://github.com/ncoderz/superenum/actions/workflows/build-test.yml/badge.svg?branch=main)
![Build & Test](https://github.com/ncoderz/log-m8/actions/workflows/build-test.yml/badge.svg?branch=main)
![npm version](https://img.shields.io/npm/v/@ncoderz/log-m8)

@@ -15,2 +15,3 @@ ![License](https://img.shields.io/badge/license-BSD--2--Clause-blue)

- πŸ”Œ **Plugin architecture** - extensible with custom appenders, formatters, and filters
- πŸ”„ **Dynamic appenders** - add and remove appenders at runtime without re-initialization
- 🎨 **Multiple output targets** - console, file, and more with custom formatters

@@ -268,2 +269,33 @@ - πŸ” **Configurable filters** - control what gets logged and where

### Dynamic Appender Management
Appenders can be added and removed after initialization.
This is useful for diagnostic injection, feature-flag-driven logging, or attaching
test spies at runtime.
```typescript
// Add an appender at runtime
LogM8.addAppender({
name: 'file',
filename: 'debug.log',
formatter: 'json-formatter',
priority: 10
});
// Add by name (uses factory defaults)
LogM8.addAppender('console');
// Check which appenders are active (returns names in priority order)
console.log(LogM8.getAppenderNames()); // ['file', 'console']
// Remove an appender (flushes and disposes it)
LogM8.removeAppender('file');
// Returns false if the appender doesn't exist
LogM8.removeAppender('unknown'); // false
```
Note: `addAppender` must be called after `init()` and the appender name must not
already be in use. To swap an appender's configuration, remove then re-add it.
### Adjusting log levels at runtime

@@ -270,0 +302,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display