axios-interceptor-logger 1.0.1 → 1.1.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
@@ -32,9 +32,10 @@ import { AxiosLoggerSingleton } from "axios-interceptor-logger";
32
32
  // 1. Initialize the Logger Configuration
33
33
  const logger = AxiosLoggerSingleton.getInstance({
34
34
  verbose: true, // Prints ECS logs to the console
35
- maxPayloadBytes: 5000, // Truncates massive JSON bodies
36
- redactKeys: ["password", "secret", "token", "authorization"],
35
+ maxPayloadBytes: 5000, // Caps how many characters of each string field are logged (hard ceiling: 10000)
36
+ redactKeys: ["password", "secret", "token", "authorization", "apikey", "api_key"],
37
37
  ignoreDomains: ["localhost"],
38
+ sourceDomain: "my-app-host.internal", // Populates the `source.domain` field in the ECS log
38
39
  });
39
40
 
40
41
  // 2. Create an Axios Instance
@@ -52,9 +53,80 @@ await apiClient.post("/login", {
52
53
  });
53
54
  ```
54
55
 
55
- ## Environment Variables
56
+ ### Default Redacted Keys
56
57
 
57
- - `APP_HOST`: If set, the logger will populate the `source.domain` field in the ECS log with this value. If missing, it defaults to `localhost`.
58
+ If you don't pass `redactKeys`, any header/body key whose name *contains* one of the following (case-insensitive) is replaced with `[REDACTED]`:
59
+
60
+ ```
61
+ authorization, password, token, secret, cookie, apikey, api_key, api-key,
62
+ privatekey, private_key, private-key
63
+ ```
64
+
65
+ This is a substring match against common secret names — it is **not** a guarantee that every secret-shaped field in your API traffic will be caught. Review the [Responsibility & Disclaimer](#responsibility--disclaimer) section below and pass your own `redactKeys` for anything app-specific (custom auth headers, PII fields, etc.).
66
+
67
+ ### Attaching to `axios.create()`'d Instances
68
+
69
+ `attach()` only registers interceptors on the exact instance you pass it. By default, if that instance also exposes its own `.create()` — the top-level `axios` default export, for example — the library wraps `.create()` so every instance it produces afterwards (`AeremAuth`, `OcrClient`, or anything else built with `axios.create()`) is automatically attached too, with no per-integration wiring needed:
70
+
71
+ ```typescript
72
+ import axios from "axios";
73
+ import { AxiosLoggerSingleton } from "axios-interceptor-logger";
74
+
75
+ const logger = AxiosLoggerSingleton.getInstance({ filePath: "./logs/axios.log" });
76
+ logger.attach(axios); // also patches axios.create() from here on
77
+
78
+ const apiClient = axios.create({ baseURL: "https://api.example.com" }); // attached automatically
79
+ ```
80
+
81
+ Set `autoPatchCreate: false` in the initializer if you'd rather call `logger.attach()` yourself on each instance you create.
82
+
83
+ **Limitations:** `autoPatchCreate` only wraps `.create()` on the exact module/instance you pass to `attach()`. Two things it can't reach:
84
+ - An instance built with `new (require("axios").Axios)(config)` instead of `.create()` — bypasses the patch entirely.
85
+ - A dependency that bundles its **own** copy of `axios` (a duplicate/nested install, common with mismatched versions in a monorepo) — patching your copy's `.create()` has no effect on a different module instance in memory.
86
+
87
+ In both cases, call `logger.attach()` directly on that instance instead.
88
+
89
+ Axios itself has no Express-style `next()` — interceptors chain via promises in FIFO registration order (request and response alike, in current axios versions). Because `autoPatchCreate` attaches synchronously inside the patched `create()`, this library's interceptors are always registered before anything the calling code adds afterward on that instance, so they always run first outbound and first inbound. This is safe by design: `handleRequest` returns the (possibly-mutated) config for downstream interceptors to see, and `handleError` always re-rejects rather than swallowing the error, so interceptors added later (retry logic, token refresh, etc.) still run normally.
90
+
91
+ ### Environment Variables (Deprecated)
92
+
93
+ - `APP_HOST` — **deprecated, will be removed in a future release.** If set (and `sourceDomain` isn't passed to `getInstance()`), it still populates the `source.domain` field for now, but using it logs a startup deprecation warning. Pass `sourceDomain` to the initializer instead (see [Usage](#usage) above).
94
+
95
+ ### Multi-Instance / Shared Volume Deployments
96
+
97
+ If several replicas of the same service (Docker Swarm, ECS, Kubernetes, PM2 cluster mode) share one mounted log directory, pointing every replica at the same `filePath` means they're all appending to one file concurrently — writes can interleave and corrupt individual JSON lines, and nothing here coordinates that across processes.
98
+
99
+ Give each replica its own file instead: pick a stable per-instance id available at startup — the container hostname is usually good enough, since Docker/Swarm/Kubernetes each set one per container — and derive both `filePath` and `sourceDomain` from it before calling `getInstance()`. Using the same id for both means the file a log entry landed in and the `source.domain` inside that entry agree with each other.
100
+
101
+ ```typescript
102
+ import os from "node:os";
103
+ import path from "node:path";
104
+ import axios from "axios";
105
+ import { AxiosLoggerSingleton } from "axios-interceptor-logger";
106
+
107
+ // "/var/log/my-service/axios-outbound.log" -> "/var/log/my-service/axios-outbound-<instanceId>.log"
108
+ // Keeps the configured base path and extension exactly as given — only the
109
+ // filename gets the per-instance suffix inserted before it.
110
+ function instancedLogPath(basePath: string, instanceId: string = os.hostname()): string {
111
+ const ext = path.extname(basePath);
112
+ const dir = path.dirname(basePath);
113
+ const base = path.basename(basePath, ext);
114
+ return path.join(dir, `${base}-${instanceId}${ext}`);
115
+ }
116
+
117
+ const instanceId = os.hostname();
118
+
119
+ const logger = AxiosLoggerSingleton.getInstance({
120
+ filePath: instancedLogPath(process.env.AXIOS_LOG_PATH!, instanceId),
121
+ sourceDomain: instanceId,
122
+ });
123
+
124
+ logger.attach(axios);
125
+ ```
126
+
127
+ Your app's own log-path configuration (env var, config file, whatever you call it) stays a single literal path, same as a single-instance deployment — only the file this library actually opens gets the suffix. A log shipper (Grafana Alloy, Filebeat, etc.) watching the directory with a glob — `axios-outbound-*.log` — picks up every replica's file automatically as they come and go, with no per-replica configuration on the shipper's side.
128
+
129
+ By default, a container's hostname is a random id that changes every time the container is recreated (redeploy, scale, crash restart) — old files are never reused or cleaned up, so pair this with your own log rotation ([see Responsibility & Disclaimer](#responsibility--disclaimer)). If your orchestrator supports naming replicas predictably (e.g. Docker Swarm's `hostname: "my-service-{{.Task.Slot}}"` service field), using that instead of the raw hostname gives you a bounded, reused set of files instead of an ever-growing one.
58
130
 
59
131
  ## Output Example
60
132
 
@@ -67,7 +139,7 @@ The logger intercepts the traffic and outputs standard Packetbeat ECS JSON:
67
139
  "agent": {
68
140
  "name": "axios-interceptor-logger",
69
141
  "type": "axios-logger",
70
- "version": "1.0.1"
142
+ "version": "1.1.0"
71
143
  },
72
144
  "event": {
73
145
  "start": "2026-09-11T09:34:31.799Z",
@@ -97,3 +169,27 @@ The logger intercepts the traffic and outputs standard Packetbeat ECS JSON:
97
169
  }
98
170
  }
99
171
  ```
172
+
173
+ ## Requirements
174
+
175
+ - Node.js >= 16 (uses `crypto.randomUUID` and Node's `fs`/streams APIs).
176
+ - **Server-side only.** This package imports Node core modules directly and is not meant to be bundled into a browser build — using it there will throw or fail to bundle.
177
+
178
+ ## Responsibility & Disclaimer
179
+
180
+ This library sits between your code and the network, so "is my logging safe/reliable" depends on more than just this package. To be explicit about where each concern is handled:
181
+
182
+ **This library (`axios-interceptor-logger`) is responsible for:**
183
+ - Not crashing or leaking memory under normal or adversarial payload shapes (bounded recursion depth/keys/array items, backpressure-aware file writes, a circuit breaker on disk errors).
184
+ - Redacting the key names listed in [Default Redacted Keys](#default-redacted-keys), or any additional keys you pass via `redactKeys`.
185
+ - Falling back to `console` output if the configured log file becomes unwritable, so log entries aren't silently dropped.
186
+
187
+ **`axios` (the peer dependency) is responsible for:**
188
+ - Actually performing the HTTP request/response lifecycle this library hooks into — bugs in header normalization, redirects, or request/response shape are upstream of this package. Pin an `axios@^1.x` version you trust and keep it patched.
189
+
190
+ **Your application (the dependent) is responsible for:**
191
+ - **Log rotation and disk space.** This library appends to a single file forever; it does not rotate or cap file size. Point `filePath` at a location managed by `logrotate`, your process manager, or a log-shipping sidecar — otherwise the log file will grow until the disk fills up.
192
+ - **Writable storage.** On read-only filesystems (some serverless/container platforms), file writes will fail and the logger falls back to `console`; set `filePath` to a writable path (e.g. `/tmp`) explicitly rather than relying on the fallback.
193
+ - **What actually counts as sensitive for your API.** The default `redactKeys` only catches common secret *names*. Fields like SSNs, card numbers, or app-specific PII in the body will be logged in cleartext (subject to the per-string truncation cap) unless you add them to `redactKeys` or route those endpoints through `ignoreDomains`.
194
+ - **Compliance requirements** (GDPR, PCI-DSS, etc.) for what you're allowed to persist to disk — this library logs request/response bodies by design; it's your call which traffic should go through it.
195
+ - **Throughput at scale.** Sanitizing and serializing very large request/response bodies is synchronous CPU work (deferred one tick via `setImmediate`, not offloaded to a worker). For endpoints with large payloads, consider `ignoreDomains` or trimming what you send/receive.
package/dist/index.d.mts CHANGED
@@ -77,6 +77,16 @@ interface AxiosLoggerConfig {
77
77
  otlpEndpoint?: string;
78
78
  customLogger?: LogTransport;
79
79
  verbose?: boolean;
80
+ sourceDomain?: string;
81
+ /**
82
+ * When `attach()` is called on an axios module/instance that exposes its own
83
+ * `.create()` (e.g. the default `axios` export), automatically wrap that
84
+ * `.create()` so every instance it produces afterwards is attach()'d too —
85
+ * axios.create()'d instances otherwise have an independent interceptor
86
+ * stack that attach() never sees. Defaults to `true`; set `false` to manage
87
+ * attaching to created instances yourself.
88
+ */
89
+ autoPatchCreate?: boolean;
80
90
  }
81
91
 
82
92
  declare class AxiosLoggerSingleton {
@@ -88,6 +98,7 @@ declare class AxiosLoggerSingleton {
88
98
  attach(axiosInstance: AxiosInstance): {
89
99
  eject: () => void;
90
100
  };
101
+ private patchCreateIfPresent;
91
102
  private isDomainIgnored;
92
103
  private handleRequest;
93
104
  private handleResponse;
package/dist/index.d.ts CHANGED
@@ -77,6 +77,16 @@ interface AxiosLoggerConfig {
77
77
  otlpEndpoint?: string;
78
78
  customLogger?: LogTransport;
79
79
  verbose?: boolean;
80
+ sourceDomain?: string;
81
+ /**
82
+ * When `attach()` is called on an axios module/instance that exposes its own
83
+ * `.create()` (e.g. the default `axios` export), automatically wrap that
84
+ * `.create()` so every instance it produces afterwards is attach()'d too —
85
+ * axios.create()'d instances otherwise have an independent interceptor
86
+ * stack that attach() never sees. Defaults to `true`; set `false` to manage
87
+ * attaching to created instances yourself.
88
+ */
89
+ autoPatchCreate?: boolean;
80
90
  }
81
91
 
82
92
  declare class AxiosLoggerSingleton {
@@ -88,6 +98,7 @@ declare class AxiosLoggerSingleton {
88
98
  attach(axiosInstance: AxiosInstance): {
89
99
  eject: () => void;
90
100
  };
101
+ private patchCreateIfPresent;
91
102
  private isDomainIgnored;
92
103
  private handleRequest;
93
104
  private handleResponse;
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
- "use strict";var k=Object.create;var h=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var q=Object.getPrototypeOf,b=Object.prototype.hasOwnProperty;var N=(s,e)=>{for(var t in e)h(s,t,{get:e[t],enumerable:!0})},C=(s,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of U(e))!b.call(s,i)&&i!==t&&h(s,i,{get:()=>e[i],enumerable:!(r=P(e,i))||r.enumerable});return s};var D=(s,e,t)=>(t=s!=null?k(q(s)):{},C(e||!s||!s.__esModule?h(t,"default",{value:s,enumerable:!0}):t,s)),O=s=>C(h({},"__esModule",{value:!0}),s);var M={};N(M,{AxiosLoggerSingleton:()=>T});module.exports=O(M);var v=D(require("crypto"));function g(s,e=["authorization","password","token","secret","cookie"],t=10240){let r=new WeakSet;function i(n,u=0){if(n==null)return n;if(typeof n!="object"&&typeof n!="function")return typeof n=="string"&&Buffer.byteLength(n,"utf8")>t?n.substring(0,t/2)+`... [TRUNCATED ${Buffer.byteLength(n,"utf8")-t/2} BYTES]`:n;if(Buffer.isBuffer(n)||n instanceof ArrayBuffer||typeof Blob<"u"&&n instanceof Blob){let o=Buffer.isBuffer(n)?"Buffer":n instanceof ArrayBuffer?"ArrayBuffer":"Blob",f=n.byteLength||n.size||0;return{"[BINARY_DATA]":`<Type: ${o}, Size: ${f} bytes>`}}if(r.has(n))return"[CIRCULAR_REFERENCE]";if(r.add(n),Array.isArray(n))return n.map(o=>i(o,u+1));let a={};for(let o of Object.keys(n))e.some(f=>o.toLowerCase().includes(f.toLowerCase()))?a[o]="[REDACTED]":a[o]=i(n[o],u+1);return r.delete(n),a}try{let n=JSON.stringify(s);if(n&&Buffer.byteLength(n,"utf8")>t*2)return{"[TRUNCATED_NESTED_DEPTH]":!0}}catch{}return i(s)}var m=class{log(e){console.log(JSON.stringify(e,null,2))}};var l=D(require("fs")),y=D(require("path")),c=class{filePath;constructor(e){this.filePath=e||y.join(process.cwd(),"logs","http-transactions.log"),this.ensureDirectoryExists(this.filePath)}ensureDirectoryExists(e){let t=y.dirname(e);l.existsSync(t)||l.mkdirSync(t,{recursive:!0})}log(e){let t=JSON.stringify(e)+`
2
- `;l.appendFile(this.filePath,t,r=>{r&&console.error(`Failed to write to log file: ${this.filePath}`,r)})}};var L="_axios_logging_metadata";function w(s){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s)}var T=class s{static instance;config;transport;constructor(e){this.config=e||{},this.config.customLogger?this.transport=this.config.customLogger:this.config.transportMode==="otlp"?(console.warn("OTLP transport is marked as future scope. Falling back to FileTransport."),this.transport=new c(this.config.filePath)):this.config.transportMode==="file"||!this.config.transportMode?this.transport=new c(this.config.filePath):this.transport=new c(this.config.filePath)}static getInstance(e){return s.instance||(s.instance=new s(e)),s.instance}attach(e){let t=e.interceptors.request.use(i=>this.handleRequest(i),i=>Promise.reject(i)),r=e.interceptors.response.use(i=>this.handleResponse(i),i=>this.handleError(i));return{eject:()=>{e.interceptors.request.eject(t),e.interceptors.response.eject(r)}}}isDomainIgnored(e){if(!e||!this.config.ignoreDomains)return!1;let t=e.url||"";e.baseURL&&!t.startsWith("http")&&(t=e.baseURL.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""));try{let r=new URL(t,t.startsWith("/")?"http://localhost":void 0);return this.config.ignoreDomains.includes(r.hostname)}catch{return!1}}handleRequest(e){if(this.isDomainIgnored(e))return e;let t=e.headers["X-Correlation-ID"];return(!t||!w(t))&&(t=v.default.randomUUID()),e.headers["X-Correlation-ID"]=t,e[L]={id:t,startTime:Date.now(),logged:!1},e}handleResponse(e){return this.isDomainIgnored(e.config)||this.logTransaction(e.config,e,null),e}handleError(e){if(e.config&&this.isDomainIgnored(e.config))return Promise.reject(e);let t=e.config,r,i=!1;if(t&&(r=t[L]),!r){let n;t&&t.headers&&t.headers["X-Correlation-ID"]?n=t.headers["X-Correlation-ID"]:e.response&&e.response.config&&e.response.config.headers["X-Correlation-ID"]?n=e.response.config.headers["X-Correlation-ID"]:e.request&&typeof e.request.getHeader=="function"&&(n=e.request.getHeader("X-Correlation-ID")),(!n||!w(n))&&(n=v.default.randomUUID(),i=!0),r={id:n,startTime:Date.now(),logged:!1},t&&(t[L]=r)}return this.logTransaction(t,e.response,e,i),Promise.reject(e)}logTransaction(e,t,r,i=!1){if(!e)return;let n=e[L];if(!n||n.logged)return;n.logged=!0;let u=Date.now()-n.startTime,a=e.url||"";e.baseURL&&!a.startsWith("http")&&(a=e.baseURL.replace(/\/+$/,"")+"/"+a.replace(/^\/+/,""));let o,f;try{o=new URL(a,a.startsWith("/")?"http://localhost":void 0),o.port&&(f=parseInt(o.port,10))}catch{}let A=e.headers?g({...e.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},I;A["content-length"]?I=parseInt(A["content-length"],10):e.data&&typeof e.data=="string"&&(I=Buffer.byteLength(e.data,"utf8"));let E=new Date().toISOString(),B=new Date(n.startTime).toISOString(),S=u*1e6,R=o?o.hostname:"unknown",p={"@timestamp":E,ecs:{version:"8.0.0"},agent:{name:"axios-interceptor-logger",type:"axios-logger",version:"1.0.1"},event:{start:B,end:E,duration:S,kind:"event",category:["network"],type:["connection","protocol"],dataset:"http"},http:{request:{method:(e.method||"GET").toUpperCase(),bytes:I,headers:A,body:e.data?{content:g(e.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0},version:"1.1"},url:{full:a,path:o?o.pathname:a,query:o&&o.search?o.search.replace("?",""):void 0,scheme:o?o.protocol.replace(":",""):"unknown",domain:R,port:f},source:{domain:typeof process<"u"&&process.env&&process.env.APP_HOST?process.env.APP_HOST:"localhost"},destination:{domain:R},server:{domain:R},network:{protocol:"http"},status:r?"Error":"OK","trace.id":n.id};if(t){let d=t.headers?g({...t.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},x;d["content-length"]?x=parseInt(d["content-length"],10):t.data&&typeof t.data=="string"&&(x=Buffer.byteLength(t.data,"utf8")),p.http.response={status_code:t.status,headers:d,body:t.data?{content:g(t.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0,bytes:x}}r&&(p.error={message:r.message,code:r.code||r.code}),this.config.verbose&&new m().log(p),this.transport.log(p)}};0&&(module.exports={AxiosLoggerSingleton});
1
+ "use strict";var b=Object.create;var A=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var O=Object.getPrototypeOf,q=Object.prototype.hasOwnProperty;var X=(i,e)=>{for(var t in e)A(i,t,{get:e[t],enumerable:!0})},_=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of U(e))!q.call(i,n)&&n!==t&&A(i,n,{get:()=>e[n],enumerable:!(r=B(e,n))||r.enumerable});return i};var L=(i,e,t)=>(t=i!=null?b(O(i)):{},_(e||!i||!i.__esModule?A(t,"default",{value:i,enumerable:!0}):t,i)),N=i=>_(A({},"__esModule",{value:!0}),i);var W={};X(W,{AxiosLoggerSingleton:()=>S});module.exports=N(W);var I=L(require("crypto"));var Y=["authorization","password","token","secret","cookie","apikey","api_key","api-key","privatekey","private_key","private-key"];function m(i,e=Y,t=10240){let r=Math.min(Math.max(0,t||0),1e4),n=new WeakSet;function a(s,l=0){if(s==null)return s;if(l>=10)return"[MAX_DEPTH_EXCEEDED]";if(typeof s!="object"&&typeof s!="function")return typeof s=="string"?s.length>r?s.substring(0,r)+`... [TRUNCATED ${s.length-r} CHARACTERS]`:s:typeof s=="bigint"?s.toString():s;if(Buffer.isBuffer(s)||s instanceof ArrayBuffer||typeof Blob<"u"&&s instanceof Blob){let c=Buffer.isBuffer(s)?"Buffer":s instanceof ArrayBuffer?"ArrayBuffer":"Blob",f=s.byteLength||s.size||0;return{"[BINARY_DATA]":`<Type: ${c}, Size: ${f} bytes>`}}if(n.has(s))return"[CIRCULAR_REFERENCE]";n.add(s);let o;if(Array.isArray(s)){let c=s.length>100;o=s.slice(0,100).map(f=>a(f,l+1)),c&&o.push(`[... ${s.length-100} MORE ITEMS TRUNCATED]`)}else{o={};let c=Object.keys(s),f=c.length>50;for(let u of c.slice(0,50))if(e.some(g=>u.toLowerCase().includes(g.toLowerCase())))o[u]="[REDACTED]";else try{o[u]=a(s[u],l+1)}catch{o[u]="[UNREADABLE_PROPERTY]"}f&&(o["[TRUNCATED_KEYS]"]=`${c.length-50} more keys omitted`)}return n.delete(s),o}return a(i)}var d=class{log(e){console.log(JSON.stringify(e,null,2))}};var E=L(require("fs")),T=L(require("path"));var h=class{filePath;stream=null;fallbackTransport=null;initializing=!1;backpressured=!1;queue=[];queueBytes=0;MAX_QUEUE_BYTES=20*1024*1024;lastErrorTime=0;COOLDOWN_MS=6e4;constructor(e){this.filePath=e||T.join(process.cwd(),"logs","http-transactions.log")}async initStream(){if(!(this.initializing||this.stream)&&!(Date.now()-this.lastErrorTime<this.COOLDOWN_MS)){this.initializing=!0;try{let e=T.dirname(this.filePath);await E.promises.mkdir(e,{recursive:!0}),this.stream=E.createWriteStream(this.filePath,{flags:"a"}),this.stream.on("error",t=>{this.handleStreamError(t)}),this.stream.on("drain",()=>{this.backpressured=!1,this.flushQueue()}),this.flushQueue()}catch(e){this.handleStreamError(e)}finally{this.initializing=!1}}}handleStreamError(e){let t=!this.fallbackTransport;this.stream&&(this.stream.destroy(),this.stream=null),this.lastErrorTime=Date.now(),t&&(this.fallbackTransport=new d,console.error(`[Axios Logger] Critical failure creating file transport at ${this.filePath}. Falling back to console. Error:`,e)),this.flushQueue()}flushQueue(){if(this.queue.length!==0){if(!this.stream||this.backpressured){if(this.fallbackTransport)for(;this.queue.length>0;){let e=this.queue.shift();e&&(this.queueBytes-=e.length*2,process.stdout.write(e))}return}for(;this.queue.length>0;){let e=this.queue.shift();if(e&&(this.queueBytes-=e.length*2,!this.stream.write(e))){this.backpressured=!0;break}}}}log(e){if(!this.stream&&this.lastErrorTime>0){if(Date.now()-this.lastErrorTime>this.COOLDOWN_MS)this.initStream();else if(this.fallbackTransport){this.fallbackTransport.log(e);return}}!this.stream&&!this.initializing&&this.lastErrorTime===0&&this.initStream();let t;try{t=JSON.stringify(e)+`
2
+ `}catch(r){t=JSON.stringify({error:"Failed to stringify log payload",message:r.message})+`
3
+ `}if(this.stream&&!this.backpressured)this.stream.write(t)||(this.backpressured=!0);else{let r=t.length*2,n=e.status==="Error"||!!e.error,a=this.MAX_QUEUE_BYTES*.8;if(this.queueBytes+r<(n?this.MAX_QUEUE_BYTES:a))this.queue.push(t),this.queueBytes+=r;else{let s=Date.now();(s-this.lastDropWarningTime>1e4||!this.lastDropWarningTime)&&(this.lastDropWarningTime=s,console.warn(`[Axios Logger] Dropping ${n?"ERROR":"INFO"} logs due to disk backpressure. Queue memory limit reached.`))}}}};var C="1.1.0";var R=Symbol("axios_logging_metadata"),k=new WeakSet;function w(i){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(i)}function H(){if(!(typeof process<"u"&&!!process.versions&&!!process.versions.node))throw new Error("[axios-interceptor-logger] This library uses Node.js core modules (fs, crypto) and is not supported outside a Node.js runtime (e.g. browser bundles).")}var S=class i{static instance;config;transport;constructor(e){H(),this.config=e||{},this.config.customLogger?this.transport=this.config.customLogger:this.config.transportMode==="otlp"?(console.warn("OTLP transport is marked as future scope. Falling back to FileTransport."),this.transport=new h(this.config.filePath)):this.config.transportMode==="file"||!this.config.transportMode?this.transport=new h(this.config.filePath):this.transport=new h(this.config.filePath),!this.config.sourceDomain&&typeof process<"u"&&process.env&&process.env.APP_HOST&&console.warn("[axios-interceptor-logger] The APP_HOST environment variable is deprecated and will be removed in a future release. Pass `sourceDomain` to AxiosLoggerSingleton.getInstance() instead.")}static getInstance(e){return i.instance||(i.instance=new i(e)),i.instance}attach(e){let t=e.interceptors.request.use(n=>this.handleRequest(n),n=>Promise.reject(n)),r=e.interceptors.response.use(n=>this.handleResponse(n),n=>this.handleError(n));return this.config.autoPatchCreate!==!1&&this.patchCreateIfPresent(e),{eject:()=>{e.interceptors.request.eject(t),e.interceptors.response.eject(r)}}}patchCreateIfPresent(e){let t=e;if(typeof t.create!="function"||k.has(t))return;k.add(t);let r=t.create.bind(t);t.create=(n=>{let a=r(n);return this.attach(a),a})}isDomainIgnored(e){if(!e||!this.config.ignoreDomains)return!1;let t=e.url||"";e.baseURL&&!t.startsWith("http")&&(t=e.baseURL.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""));try{let r=new URL(t,t.startsWith("/")?"http://localhost":void 0);return this.config.ignoreDomains.includes(r.hostname)}catch{return!1}}handleRequest(e){if(this.isDomainIgnored(e))return e;let t=e.headers["X-Correlation-ID"];return(!t||!w(t))&&(t=I.default.randomUUID()),e.headers["X-Correlation-ID"]=t,e[R]={id:t,startTime:Date.now(),logged:!1},e}handleResponse(e){return this.isDomainIgnored(e.config)||setImmediate(()=>this.logTransaction(e.config,e,null)),e}handleError(e){if(e.config&&this.isDomainIgnored(e.config))return Promise.reject(e);let t=e.config,r,n=!1;if(t&&(r=t[R]),!r){let a;t&&t.headers&&t.headers["X-Correlation-ID"]?a=t.headers["X-Correlation-ID"]:e.response&&e.response.config&&e.response.config.headers["X-Correlation-ID"]?a=e.response.config.headers["X-Correlation-ID"]:e.request&&typeof e.request.getHeader=="function"&&(a=e.request.getHeader("X-Correlation-ID")),(!a||!w(a))&&(a=I.default.randomUUID(),n=!0),r={id:a,startTime:Date.now(),logged:!1},t&&(t[R]=r)}return setImmediate(()=>this.logTransaction(t,e.response,e,n)),Promise.reject(e)}logTransaction(e,t,r,n=!1){if(!e)return;let a=e[R];if(!a||a.logged)return;a.logged=!0;let s=Date.now()-a.startTime,l=e.url||"";e.baseURL&&!l.startsWith("http")&&(l=e.baseURL.replace(/\/+$/,"")+"/"+l.replace(/^\/+/,""));let o,c;try{o=new URL(l,l.startsWith("/")?"http://localhost":void 0),o.port&&(c=parseInt(o.port,10))}catch{}let f=e.headers?m({...e.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},u;f["content-length"]?u=parseInt(f["content-length"],10):e.data&&typeof e.data=="string"&&(u=Buffer.byteLength(e.data,"utf8"));let g=new Date().toISOString(),x=new Date(a.startTime).toISOString(),M=s*1e6,v=o?o.hostname:"unknown",P=this.config.sourceDomain||typeof process<"u"&&process.env&&process.env.APP_HOST||"localhost",y={"@timestamp":g,ecs:{version:"8.0.0"},agent:{name:"axios-interceptor-logger",type:"axios-logger",version:C},event:{start:x,end:g,duration:M,kind:"event",category:["network"],type:["connection","protocol"],dataset:"http"},http:{request:{method:(e.method||"GET").toUpperCase(),bytes:u,headers:f,body:e.data?{content:m(e.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0},version:"1.1"},url:{full:l,path:o?o.pathname:l,query:o&&o.search?o.search.replace("?",""):void 0,scheme:o?o.protocol.replace(":",""):"unknown",domain:v,port:c},source:{domain:P},destination:{domain:v},server:{domain:v},network:{protocol:"http"},status:r?"Error":"OK","trace.id":a.id};if(t){let p=t.headers?m({...t.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},D;p["content-length"]?D=parseInt(p["content-length"],10):t.data&&typeof t.data=="string"&&(D=Buffer.byteLength(t.data,"utf8")),y.http.response={status_code:t.status,headers:p,body:t.data?{content:m(t.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0,bytes:D}}r&&(y.error={message:r.message?String(r.message):"Unknown error",code:r.code||r.code?String(r.code||r.code):void 0}),this.config.verbose&&new d().log(y),this.transport.log(y)}};0&&(module.exports={AxiosLoggerSingleton});
package/dist/index.mjs CHANGED
@@ -1,2 +1,3 @@
1
- import D from"crypto";function g(o,e=["authorization","password","token","secret","cookie"],t=10240){let s=new WeakSet;function i(n,u=0){if(n==null)return n;if(typeof n!="object"&&typeof n!="function")return typeof n=="string"&&Buffer.byteLength(n,"utf8")>t?n.substring(0,t/2)+`... [TRUNCATED ${Buffer.byteLength(n,"utf8")-t/2} BYTES]`:n;if(Buffer.isBuffer(n)||n instanceof ArrayBuffer||typeof Blob<"u"&&n instanceof Blob){let r=Buffer.isBuffer(n)?"Buffer":n instanceof ArrayBuffer?"ArrayBuffer":"Blob",f=n.byteLength||n.size||0;return{"[BINARY_DATA]":`<Type: ${r}, Size: ${f} bytes>`}}if(s.has(n))return"[CIRCULAR_REFERENCE]";if(s.add(n),Array.isArray(n))return n.map(r=>i(r,u+1));let a={};for(let r of Object.keys(n))e.some(f=>r.toLowerCase().includes(f.toLowerCase()))?a[r]="[REDACTED]":a[r]=i(n[r],u+1);return s.delete(n),a}try{let n=JSON.stringify(o);if(n&&Buffer.byteLength(n,"utf8")>t*2)return{"[TRUNCATED_NESTED_DEPTH]":!0}}catch{}return i(o)}var h=class{log(e){console.log(JSON.stringify(e,null,2))}};import*as l from"fs";import*as m from"path";var c=class{filePath;constructor(e){this.filePath=e||m.join(process.cwd(),"logs","http-transactions.log"),this.ensureDirectoryExists(this.filePath)}ensureDirectoryExists(e){let t=m.dirname(e);l.existsSync(t)||l.mkdirSync(t,{recursive:!0})}log(e){let t=JSON.stringify(e)+`
2
- `;l.appendFile(this.filePath,t,s=>{s&&console.error(`Failed to write to log file: ${this.filePath}`,s)})}};var y="_axios_logging_metadata";function v(o){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(o)}var R=class o{static instance;config;transport;constructor(e){this.config=e||{},this.config.customLogger?this.transport=this.config.customLogger:this.config.transportMode==="otlp"?(console.warn("OTLP transport is marked as future scope. Falling back to FileTransport."),this.transport=new c(this.config.filePath)):this.config.transportMode==="file"||!this.config.transportMode?this.transport=new c(this.config.filePath):this.transport=new c(this.config.filePath)}static getInstance(e){return o.instance||(o.instance=new o(e)),o.instance}attach(e){let t=e.interceptors.request.use(i=>this.handleRequest(i),i=>Promise.reject(i)),s=e.interceptors.response.use(i=>this.handleResponse(i),i=>this.handleError(i));return{eject:()=>{e.interceptors.request.eject(t),e.interceptors.response.eject(s)}}}isDomainIgnored(e){if(!e||!this.config.ignoreDomains)return!1;let t=e.url||"";e.baseURL&&!t.startsWith("http")&&(t=e.baseURL.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""));try{let s=new URL(t,t.startsWith("/")?"http://localhost":void 0);return this.config.ignoreDomains.includes(s.hostname)}catch{return!1}}handleRequest(e){if(this.isDomainIgnored(e))return e;let t=e.headers["X-Correlation-ID"];return(!t||!v(t))&&(t=D.randomUUID()),e.headers["X-Correlation-ID"]=t,e[y]={id:t,startTime:Date.now(),logged:!1},e}handleResponse(e){return this.isDomainIgnored(e.config)||this.logTransaction(e.config,e,null),e}handleError(e){if(e.config&&this.isDomainIgnored(e.config))return Promise.reject(e);let t=e.config,s,i=!1;if(t&&(s=t[y]),!s){let n;t&&t.headers&&t.headers["X-Correlation-ID"]?n=t.headers["X-Correlation-ID"]:e.response&&e.response.config&&e.response.config.headers["X-Correlation-ID"]?n=e.response.config.headers["X-Correlation-ID"]:e.request&&typeof e.request.getHeader=="function"&&(n=e.request.getHeader("X-Correlation-ID")),(!n||!v(n))&&(n=D.randomUUID(),i=!0),s={id:n,startTime:Date.now(),logged:!1},t&&(t[y]=s)}return this.logTransaction(t,e.response,e,i),Promise.reject(e)}logTransaction(e,t,s,i=!1){if(!e)return;let n=e[y];if(!n||n.logged)return;n.logged=!0;let u=Date.now()-n.startTime,a=e.url||"";e.baseURL&&!a.startsWith("http")&&(a=e.baseURL.replace(/\/+$/,"")+"/"+a.replace(/^\/+/,""));let r,f;try{r=new URL(a,a.startsWith("/")?"http://localhost":void 0),r.port&&(f=parseInt(r.port,10))}catch{}let L=e.headers?g({...e.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},T;L["content-length"]?T=parseInt(L["content-length"],10):e.data&&typeof e.data=="string"&&(T=Buffer.byteLength(e.data,"utf8"));let x=new Date().toISOString(),E=new Date(n.startTime).toISOString(),C=u*1e6,A=r?r.hostname:"unknown",p={"@timestamp":x,ecs:{version:"8.0.0"},agent:{name:"axios-interceptor-logger",type:"axios-logger",version:"1.0.1"},event:{start:E,end:x,duration:C,kind:"event",category:["network"],type:["connection","protocol"],dataset:"http"},http:{request:{method:(e.method||"GET").toUpperCase(),bytes:T,headers:L,body:e.data?{content:g(e.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0},version:"1.1"},url:{full:a,path:r?r.pathname:a,query:r&&r.search?r.search.replace("?",""):void 0,scheme:r?r.protocol.replace(":",""):"unknown",domain:A,port:f},source:{domain:typeof process<"u"&&process.env&&process.env.APP_HOST?process.env.APP_HOST:"localhost"},destination:{domain:A},server:{domain:A},network:{protocol:"http"},status:s?"Error":"OK","trace.id":n.id};if(t){let d=t.headers?g({...t.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},I;d["content-length"]?I=parseInt(d["content-length"],10):t.data&&typeof t.data=="string"&&(I=Buffer.byteLength(t.data,"utf8")),p.http.response={status_code:t.status,headers:d,body:t.data?{content:g(t.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0,bytes:I}}s&&(p.error={message:s.message,code:s.code||s.code}),this.config.verbose&&new h().log(p),this.transport.log(p)}};export{R as AxiosLoggerSingleton};
1
+ import L from"crypto";var x=["authorization","password","token","secret","cookie","apikey","api_key","api-key","privatekey","private_key","private-key"];function m(o,e=x,t=10240){let r=Math.min(Math.max(0,t||0),1e4),a=new WeakSet;function i(s,l=0){if(s==null)return s;if(l>=10)return"[MAX_DEPTH_EXCEEDED]";if(typeof s!="object"&&typeof s!="function")return typeof s=="string"?s.length>r?s.substring(0,r)+`... [TRUNCATED ${s.length-r} CHARACTERS]`:s:typeof s=="bigint"?s.toString():s;if(Buffer.isBuffer(s)||s instanceof ArrayBuffer||typeof Blob<"u"&&s instanceof Blob){let c=Buffer.isBuffer(s)?"Buffer":s instanceof ArrayBuffer?"ArrayBuffer":"Blob",f=s.byteLength||s.size||0;return{"[BINARY_DATA]":`<Type: ${c}, Size: ${f} bytes>`}}if(a.has(s))return"[CIRCULAR_REFERENCE]";a.add(s);let n;if(Array.isArray(s)){let c=s.length>100;n=s.slice(0,100).map(f=>i(f,l+1)),c&&n.push(`[... ${s.length-100} MORE ITEMS TRUNCATED]`)}else{n={};let c=Object.keys(s),f=c.length>50;for(let u of c.slice(0,50))if(e.some(g=>u.toLowerCase().includes(g.toLowerCase())))n[u]="[REDACTED]";else try{n[u]=i(s[u],l+1)}catch{n[u]="[UNREADABLE_PROPERTY]"}f&&(n["[TRUNCATED_KEYS]"]=`${c.length-50} more keys omitted`)}return a.delete(s),n}return i(o)}var d=class{log(e){console.log(JSON.stringify(e,null,2))}};import*as A from"fs";import*as E from"path";var h=class{filePath;stream=null;fallbackTransport=null;initializing=!1;backpressured=!1;queue=[];queueBytes=0;MAX_QUEUE_BYTES=20*1024*1024;lastErrorTime=0;COOLDOWN_MS=6e4;constructor(e){this.filePath=e||E.join(process.cwd(),"logs","http-transactions.log")}async initStream(){if(!(this.initializing||this.stream)&&!(Date.now()-this.lastErrorTime<this.COOLDOWN_MS)){this.initializing=!0;try{let e=E.dirname(this.filePath);await A.promises.mkdir(e,{recursive:!0}),this.stream=A.createWriteStream(this.filePath,{flags:"a"}),this.stream.on("error",t=>{this.handleStreamError(t)}),this.stream.on("drain",()=>{this.backpressured=!1,this.flushQueue()}),this.flushQueue()}catch(e){this.handleStreamError(e)}finally{this.initializing=!1}}}handleStreamError(e){let t=!this.fallbackTransport;this.stream&&(this.stream.destroy(),this.stream=null),this.lastErrorTime=Date.now(),t&&(this.fallbackTransport=new d,console.error(`[Axios Logger] Critical failure creating file transport at ${this.filePath}. Falling back to console. Error:`,e)),this.flushQueue()}flushQueue(){if(this.queue.length!==0){if(!this.stream||this.backpressured){if(this.fallbackTransport)for(;this.queue.length>0;){let e=this.queue.shift();e&&(this.queueBytes-=e.length*2,process.stdout.write(e))}return}for(;this.queue.length>0;){let e=this.queue.shift();if(e&&(this.queueBytes-=e.length*2,!this.stream.write(e))){this.backpressured=!0;break}}}}log(e){if(!this.stream&&this.lastErrorTime>0){if(Date.now()-this.lastErrorTime>this.COOLDOWN_MS)this.initStream();else if(this.fallbackTransport){this.fallbackTransport.log(e);return}}!this.stream&&!this.initializing&&this.lastErrorTime===0&&this.initStream();let t;try{t=JSON.stringify(e)+`
2
+ `}catch(r){t=JSON.stringify({error:"Failed to stringify log payload",message:r.message})+`
3
+ `}if(this.stream&&!this.backpressured)this.stream.write(t)||(this.backpressured=!0);else{let r=t.length*2,a=e.status==="Error"||!!e.error,i=this.MAX_QUEUE_BYTES*.8;if(this.queueBytes+r<(a?this.MAX_QUEUE_BYTES:i))this.queue.push(t),this.queueBytes+=r;else{let s=Date.now();(s-this.lastDropWarningTime>1e4||!this.lastDropWarningTime)&&(this.lastDropWarningTime=s,console.warn(`[Axios Logger] Dropping ${a?"ERROR":"INFO"} logs due to disk backpressure. Queue memory limit reached.`))}}}};var D="1.1.0";var T=Symbol("axios_logging_metadata"),I=new WeakSet;function _(o){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(o)}function M(){if(!(typeof process<"u"&&!!process.versions&&!!process.versions.node))throw new Error("[axios-interceptor-logger] This library uses Node.js core modules (fs, crypto) and is not supported outside a Node.js runtime (e.g. browser bundles).")}var v=class o{static instance;config;transport;constructor(e){M(),this.config=e||{},this.config.customLogger?this.transport=this.config.customLogger:this.config.transportMode==="otlp"?(console.warn("OTLP transport is marked as future scope. Falling back to FileTransport."),this.transport=new h(this.config.filePath)):this.config.transportMode==="file"||!this.config.transportMode?this.transport=new h(this.config.filePath):this.transport=new h(this.config.filePath),!this.config.sourceDomain&&typeof process<"u"&&process.env&&process.env.APP_HOST&&console.warn("[axios-interceptor-logger] The APP_HOST environment variable is deprecated and will be removed in a future release. Pass `sourceDomain` to AxiosLoggerSingleton.getInstance() instead.")}static getInstance(e){return o.instance||(o.instance=new o(e)),o.instance}attach(e){let t=e.interceptors.request.use(a=>this.handleRequest(a),a=>Promise.reject(a)),r=e.interceptors.response.use(a=>this.handleResponse(a),a=>this.handleError(a));return this.config.autoPatchCreate!==!1&&this.patchCreateIfPresent(e),{eject:()=>{e.interceptors.request.eject(t),e.interceptors.response.eject(r)}}}patchCreateIfPresent(e){let t=e;if(typeof t.create!="function"||I.has(t))return;I.add(t);let r=t.create.bind(t);t.create=(a=>{let i=r(a);return this.attach(i),i})}isDomainIgnored(e){if(!e||!this.config.ignoreDomains)return!1;let t=e.url||"";e.baseURL&&!t.startsWith("http")&&(t=e.baseURL.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""));try{let r=new URL(t,t.startsWith("/")?"http://localhost":void 0);return this.config.ignoreDomains.includes(r.hostname)}catch{return!1}}handleRequest(e){if(this.isDomainIgnored(e))return e;let t=e.headers["X-Correlation-ID"];return(!t||!_(t))&&(t=L.randomUUID()),e.headers["X-Correlation-ID"]=t,e[T]={id:t,startTime:Date.now(),logged:!1},e}handleResponse(e){return this.isDomainIgnored(e.config)||setImmediate(()=>this.logTransaction(e.config,e,null)),e}handleError(e){if(e.config&&this.isDomainIgnored(e.config))return Promise.reject(e);let t=e.config,r,a=!1;if(t&&(r=t[T]),!r){let i;t&&t.headers&&t.headers["X-Correlation-ID"]?i=t.headers["X-Correlation-ID"]:e.response&&e.response.config&&e.response.config.headers["X-Correlation-ID"]?i=e.response.config.headers["X-Correlation-ID"]:e.request&&typeof e.request.getHeader=="function"&&(i=e.request.getHeader("X-Correlation-ID")),(!i||!_(i))&&(i=L.randomUUID(),a=!0),r={id:i,startTime:Date.now(),logged:!1},t&&(t[T]=r)}return setImmediate(()=>this.logTransaction(t,e.response,e,a)),Promise.reject(e)}logTransaction(e,t,r,a=!1){if(!e)return;let i=e[T];if(!i||i.logged)return;i.logged=!0;let s=Date.now()-i.startTime,l=e.url||"";e.baseURL&&!l.startsWith("http")&&(l=e.baseURL.replace(/\/+$/,"")+"/"+l.replace(/^\/+/,""));let n,c;try{n=new URL(l,l.startsWith("/")?"http://localhost":void 0),n.port&&(c=parseInt(n.port,10))}catch{}let f=e.headers?m({...e.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},u;f["content-length"]?u=parseInt(f["content-length"],10):e.data&&typeof e.data=="string"&&(u=Buffer.byteLength(e.data,"utf8"));let g=new Date().toISOString(),C=new Date(i.startTime).toISOString(),k=s*1e6,R=n?n.hostname:"unknown",w=this.config.sourceDomain||typeof process<"u"&&process.env&&process.env.APP_HOST||"localhost",y={"@timestamp":g,ecs:{version:"8.0.0"},agent:{name:"axios-interceptor-logger",type:"axios-logger",version:D},event:{start:C,end:g,duration:k,kind:"event",category:["network"],type:["connection","protocol"],dataset:"http"},http:{request:{method:(e.method||"GET").toUpperCase(),bytes:u,headers:f,body:e.data?{content:m(e.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0},version:"1.1"},url:{full:l,path:n?n.pathname:l,query:n&&n.search?n.search.replace("?",""):void 0,scheme:n?n.protocol.replace(":",""):"unknown",domain:R,port:c},source:{domain:w},destination:{domain:R},server:{domain:R},network:{protocol:"http"},status:r?"Error":"OK","trace.id":i.id};if(t){let p=t.headers?m({...t.headers},this.config.redactKeys,this.config.maxPayloadBytes):{},S;p["content-length"]?S=parseInt(p["content-length"],10):t.data&&typeof t.data=="string"&&(S=Buffer.byteLength(t.data,"utf8")),y.http.response={status_code:t.status,headers:p,body:t.data?{content:m(t.data,this.config.redactKeys,this.config.maxPayloadBytes)}:void 0,bytes:S}}r&&(y.error={message:r.message?String(r.message):"Unknown error",code:r.code||r.code?String(r.code||r.code):void 0}),this.config.verbose&&new d().log(y),this.transport.log(y)}};export{v as AxiosLoggerSingleton};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axios-interceptor-logger",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Axios Unified HTTP Transaction Logger",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "build": "tsup src/index.ts --format cjs,esm --dts --clean --minify",
20
- "test": "jest"
20
+ "test": "NODE_OPTIONS='--max-old-space-size=16' node --import tsx --test tests/**/*.test.ts"
21
21
  },
22
22
  "keywords": [
23
23
  "axios",
@@ -30,6 +30,9 @@
30
30
  ],
31
31
  "author": "Kamalyesh Kannadkar <k.kamalyesh@gmail.com>",
32
32
  "license": "MIT",
33
+ "engines": {
34
+ "node": ">=16.0.0"
35
+ },
33
36
  "repository": {
34
37
  "type": "git",
35
38
  "url": "git+https://github.com/kkamalyesh-ops/axios-interceptor-logger.git"
@@ -38,7 +41,6 @@
38
41
  "url": "https://github.com/kkamalyesh-ops/axios-interceptor-logger/issues"
39
42
  },
40
43
  "homepage": "https://github.com/kkamalyesh-ops/axios-interceptor-logger#readme",
41
- "dependencies": {},
42
44
  "peerDependencies": {
43
45
  "axios": "^1.0.0"
44
46
  },
@@ -46,6 +48,7 @@
46
48
  "@types/node": "^20.0.0",
47
49
  "axios": "^1.6.0",
48
50
  "tsup": "^8.0.0",
51
+ "tsx": "^4.23.13",
49
52
  "typescript": "^5.0.0"
50
53
  }
51
54
  }