api-tracer-kit 1.0.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/CHANGELOG.md +32 -0
- package/LICENSE +21 -0
- package/README.md +466 -0
- package/cli/bin/api-tracer.mjs +266 -0
- package/cli/config.mjs +224 -0
- package/cli/import.mjs +231 -0
- package/cli/index.mjs +10 -0
- package/cli/presets.mjs +212 -0
- package/cli/report.mjs +346 -0
- package/cli/scan.mjs +142 -0
- package/cli/server.mjs +1576 -0
- package/cli/shape.mjs +90 -0
- package/cli/test.mjs +342 -0
- package/cli/web/app.css +1424 -0
- package/cli/web/app.js +2260 -0
- package/cli/web/favicon.svg +5 -0
- package/cli/web/index.html +159 -0
- package/cli/web/logo.svg +7 -0
- package/dist/axios.cjs +856 -0
- package/dist/axios.cjs.map +1 -0
- package/dist/axios.d.cts +27 -0
- package/dist/axios.d.ts +27 -0
- package/dist/axios.js +853 -0
- package/dist/axios.js.map +1 -0
- package/dist/index.cjs +872 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +74 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +857 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +896 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +22 -0
- package/dist/react.d.ts +22 -0
- package/dist/react.js +893 -0
- package/dist/react.js.map +1 -0
- package/dist/tracer-BUWdU2lG.d.ts +76 -0
- package/dist/tracer-DG2YUqK0.d.cts +76 -0
- package/dist/types-Bl2-K6_g.d.cts +111 -0
- package/dist/types-Bl2-K6_g.d.ts +111 -0
- package/dist/ui.cjs +1162 -0
- package/dist/ui.cjs.map +1 -0
- package/dist/ui.d.cts +16 -0
- package/dist/ui.d.ts +16 -0
- package/dist/ui.js +1157 -0
- package/dist/ui.js.map +1 -0
- package/package.json +92 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
First release. Extracted from an in-repo API console and made standalone.
|
|
6
|
+
|
|
7
|
+
### The tracer
|
|
8
|
+
|
|
9
|
+
- Automatic `fetch`, `XMLHttpRequest` and axios interception
|
|
10
|
+
- `ApiTrace` model: request, response, timing, status, error, envelope verdict
|
|
11
|
+
- `FormData`, `URLSearchParams`, JSON, text and binary bodies, each recorded as sent
|
|
12
|
+
- HTTP errors, network failures, timeouts and aborts told apart
|
|
13
|
+
- Credential redaction in headers and bodies, on by default
|
|
14
|
+
- Subscribers, a `TraceStorage` interface and a capped in-memory default
|
|
15
|
+
- `reportTo`, streaming finished traces to the console
|
|
16
|
+
- `destroy()`, restoring every patched global and axios adapter
|
|
17
|
+
- ESM and CJS builds, TypeScript declarations, four tree-shakeable entry points
|
|
18
|
+
- `api-tracer-kit/react` hooks and an `api-tracer-kit/ui` panel
|
|
19
|
+
|
|
20
|
+
### The console
|
|
21
|
+
|
|
22
|
+
- Preset-driven scanner (`service-object`, `axios-direct`, `fetch-direct`) that
|
|
23
|
+
picks itself by running each preset and keeping whichever reads the codebase
|
|
24
|
+
- Base URLs and the auth header read from the source rather than configured
|
|
25
|
+
- Live recording, endpoint matching, and adoption of calls the scan cannot explain
|
|
26
|
+
- Send, bulk run, and full replay with a plan shown first
|
|
27
|
+
- Response-shape contracts and drift reporting
|
|
28
|
+
- Coverage, and an insight report with Markdown and JSON export
|
|
29
|
+
- HAR / cURL / Postman export, HAR / cURL import
|
|
30
|
+
- `{{variables}}` captured from responses and chained between calls
|
|
31
|
+
- A config-driven multi-step sign-in flow, replacing a hardcoded one
|
|
32
|
+
- Data kept in `<project>/.api-tracer` rather than inside the package
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 api-tracer-kit contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# api-tracer-kit
|
|
2
|
+
|
|
3
|
+
Trace every API call an application makes — and map, exercise and watch the
|
|
4
|
+
endpoints it has.
|
|
5
|
+
|
|
6
|
+
Two halves that work on their own or together:
|
|
7
|
+
|
|
8
|
+
- **the tracer** — a framework-free library that records every `fetch`, `XMLHttpRequest`
|
|
9
|
+
and axios call, with no change to your API layer and no bundler configuration.
|
|
10
|
+
- **the console** — a CLI and web UI that reads your source into an endpoint
|
|
11
|
+
catalog, receives the tracer's traffic, and lets you re-send any call, watch
|
|
12
|
+
for response-shape drift, and export what it captured.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install api-tracer-kit
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { apiTracer } from 'api-tracer-kit';
|
|
22
|
+
|
|
23
|
+
apiTracer.init();
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
That is the whole setup. Every call made afterwards is traced:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
await fetch('/api/users');
|
|
30
|
+
|
|
31
|
+
apiTracer.getTraces();
|
|
32
|
+
// [{ id, request: { url, method, headers, params, body }, response: { status, headers, body },
|
|
33
|
+
// timing: { startedAt, completedAt, duration }, status: 'success' }]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Nothing happens at import time, so the package is safe to import during SSR, in
|
|
37
|
+
a build step, or in a test. `init()` is the only thing that patches anything.
|
|
38
|
+
|
|
39
|
+
Then, optionally, the console:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx api-tracer start
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
It reads your source, finds your endpoints, and opens at
|
|
46
|
+
`http://localhost:4400`. Point the tracer at it with `reportTo: true` and your
|
|
47
|
+
app's own traffic fills it in as you click around.
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
**Tracing**
|
|
52
|
+
|
|
53
|
+
- `fetch`, `XMLHttpRequest` and axios, automatically
|
|
54
|
+
- method, absolute URL, path, parsed query params, headers, request body
|
|
55
|
+
- response status, status text, headers and body
|
|
56
|
+
- start time, completion time and duration
|
|
57
|
+
- HTTP errors, network failures, timeouts and aborts, told apart
|
|
58
|
+
- `FormData`, `URLSearchParams`, JSON, text and binary bodies, each recorded as
|
|
59
|
+
what it was, so a captured call can be replayed the way it was sent
|
|
60
|
+
- files in a `FormData` recorded as `<file: scan.pdf, 20418 bytes>`
|
|
61
|
+
- a unique id per call, and no mixing between concurrent requests
|
|
62
|
+
- credential redaction in headers and bodies, on by default
|
|
63
|
+
- envelope reading, for APIs that answer `200` and put the real verdict in the body
|
|
64
|
+
- subscribers, a replaceable storage layer, and a capped in-memory store by default
|
|
65
|
+
|
|
66
|
+
**Console**
|
|
67
|
+
|
|
68
|
+
- an endpoint catalog read straight out of your source — no hand-maintained list
|
|
69
|
+
- live recording: your app's traffic lands on the endpoint it belongs to, with
|
|
70
|
+
the real params, payload and path ids already filled in
|
|
71
|
+
- send any endpoint, with a Postman-style tabbed editor (params, headers, body,
|
|
72
|
+
capture rules) and a syntax-highlighted JSON editor
|
|
73
|
+
- bulk runs, and a full replay of every captured call, with a plan shown before
|
|
74
|
+
anything is sent
|
|
75
|
+
- response-shape contracts, so a backend that quietly drops a field is caught
|
|
76
|
+
even though it answered `200`
|
|
77
|
+
- coverage: which endpoints your app has never exercised
|
|
78
|
+
- an insight report — coverage, failures, drift, risks, hygiene, auth surface,
|
|
79
|
+
latency, inventory and a trend line — exportable as Markdown or JSON
|
|
80
|
+
- HAR / cURL / Postman export, and HAR / cURL import
|
|
81
|
+
- `{{variables}}`, captured from responses and chained between calls
|
|
82
|
+
|
|
83
|
+
## Supported environments
|
|
84
|
+
|
|
85
|
+
| | |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| Browsers | ✅ `fetch`, `XMLHttpRequest`, axios |
|
|
88
|
+
| Node 18+ | ✅ `fetch` and axios; `XMLHttpRequest` is skipped when absent |
|
|
89
|
+
| React / Next.js / Vite / CRA / webpack | ✅ no plugin, loader, alias or config change |
|
|
90
|
+
| SSR and build-time execution | ✅ importing does nothing; `init()` no-ops without a runtime |
|
|
91
|
+
| TypeScript | ✅ types shipped, `strict`-clean |
|
|
92
|
+
| Deno / Bun | ⚠️ untested, but the tracer only uses standard globals |
|
|
93
|
+
|
|
94
|
+
Nothing in the core imports React or axios. Both are optional peer dependencies
|
|
95
|
+
used only by the entry points that need them.
|
|
96
|
+
|
|
97
|
+
## Entry points
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { apiTracer } from 'api-tracer-kit'; // the tracer
|
|
101
|
+
import { useAxios } from 'api-tracer-kit/axios'; // axios integration
|
|
102
|
+
import { useApiTraces } from 'api-tracer-kit/react'; // hooks
|
|
103
|
+
import { ApiTracerPanel } from 'api-tracer-kit/ui'; // in-app panel
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Each is bundled separately, so importing the tracer never pulls in React.
|
|
107
|
+
|
|
108
|
+
## Configuration
|
|
109
|
+
|
|
110
|
+
All of it is optional.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
apiTracer.init({
|
|
114
|
+
// which transports to patch. Default: all of them that exist.
|
|
115
|
+
transports: ['fetch', 'xhr'],
|
|
116
|
+
|
|
117
|
+
// only trace URLs matching one of these. Strings match by substring.
|
|
118
|
+
include: ['/api/'],
|
|
119
|
+
exclude: [/\/health$/],
|
|
120
|
+
|
|
121
|
+
// header names replaced with "<redacted>"
|
|
122
|
+
redactHeaders: ['authorization', 'cookie', 'set-cookie', 'x-api-key'],
|
|
123
|
+
|
|
124
|
+
// body and query field names replaced with "<redacted>"
|
|
125
|
+
redactFields: /(token|password|secret|api[-_]?key)/i,
|
|
126
|
+
|
|
127
|
+
// bodies bigger than this are recorded as omitted rather than kept
|
|
128
|
+
maxBodyBytes: 200_000,
|
|
129
|
+
|
|
130
|
+
// how many traces to keep; the oldest fall off the end
|
|
131
|
+
maxTraces: 500,
|
|
132
|
+
|
|
133
|
+
// for APIs that answer 200 and put the verdict in the body
|
|
134
|
+
envelope: { codeFields: ['status', 'code'], okField: 'success', failFrom: 400 },
|
|
135
|
+
|
|
136
|
+
// stream finished traces to the console; `true` means http://localhost:4400
|
|
137
|
+
reportTo: true,
|
|
138
|
+
|
|
139
|
+
// replace the in-memory store
|
|
140
|
+
storage: myStorage,
|
|
141
|
+
|
|
142
|
+
onTrace: (trace) => console.log(trace),
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
There is no config file, and the consuming project needs no changes to its
|
|
147
|
+
webpack, Vite, Babel, TypeScript, Next.js, CRA or ESLint setup.
|
|
148
|
+
|
|
149
|
+
## API reference
|
|
150
|
+
|
|
151
|
+
### `apiTracer`
|
|
152
|
+
|
|
153
|
+
| | |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `init(options?)` | installs the interceptors. Idempotent — calling it twice does not double-install. |
|
|
156
|
+
| `useAxios(axios)` | traces an axios object and everything `axios.create()` makes from it |
|
|
157
|
+
| `getTraces()` | every trace held, oldest first |
|
|
158
|
+
| `getTrace(id)` | one trace |
|
|
159
|
+
| `subscribe(fn)` | called with each finished trace; returns the unsubscribe function |
|
|
160
|
+
| `clear()` | drops every trace |
|
|
161
|
+
| `remove(id)` | drops one |
|
|
162
|
+
| `destroy()` | restores every patched global; the app behaves exactly as it did before `init()` |
|
|
163
|
+
| `isActive` | whether tracing is installed |
|
|
164
|
+
|
|
165
|
+
Standalone equivalents are exported too, for callers who prefer functions:
|
|
166
|
+
`initApiTracer`, `getApiTraces`, `clearApiTraces`, `subscribeToApiTraces`,
|
|
167
|
+
`destroyApiTracer`.
|
|
168
|
+
|
|
169
|
+
### `ApiTrace`
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
interface ApiTrace {
|
|
173
|
+
id: string;
|
|
174
|
+
transport: 'fetch' | 'xhr' | 'axios';
|
|
175
|
+
status: 'pending' | 'success' | 'error' | 'network-error' | 'cancelled';
|
|
176
|
+
|
|
177
|
+
request: {
|
|
178
|
+
url: string;
|
|
179
|
+
path: string; // origin + pathname, for grouping
|
|
180
|
+
method: string;
|
|
181
|
+
headers: Record<string, string>;
|
|
182
|
+
params: Record<string, unknown>; // parsed query string
|
|
183
|
+
body?: unknown; // parsed and redacted
|
|
184
|
+
bodyType: 'json' | 'formdata' | 'urlencoded' | 'text' | 'binary' | 'none';
|
|
185
|
+
bodyOmitted?: 'too-large' | 'stream' | 'binary' | 'unreadable';
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
response?: {
|
|
189
|
+
status: number;
|
|
190
|
+
statusText?: string;
|
|
191
|
+
headers: Record<string, string>;
|
|
192
|
+
body?: string;
|
|
193
|
+
bodyOmitted?: 'too-large' | 'stream' | 'binary' | 'unreadable';
|
|
194
|
+
size?: number;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
timing: { startedAt: number; completedAt?: number; duration?: number };
|
|
198
|
+
error?: { message: string; name?: string; stack?: string };
|
|
199
|
+
|
|
200
|
+
envelopeOk?: boolean; // false when a 2xx carried an error code
|
|
201
|
+
envelopeCode?: number;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Storage
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
interface TraceStorage {
|
|
209
|
+
add(trace: ApiTrace): void;
|
|
210
|
+
update(id: string, patch: Partial<ApiTrace>): ApiTrace | undefined;
|
|
211
|
+
get(id: string): ApiTrace | undefined;
|
|
212
|
+
getAll(): ApiTrace[];
|
|
213
|
+
remove(id: string): void;
|
|
214
|
+
clear(): void;
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The default is `MemoryTraceStorage`, a capped ring — an unbounded store in a
|
|
219
|
+
long-lived tab is a memory leak. Nothing is persisted; supply your own storage
|
|
220
|
+
if you want it to be.
|
|
221
|
+
|
|
222
|
+
## React
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
import { useApiTracer, useApiTraces } from 'api-tracer-kit/react';
|
|
226
|
+
|
|
227
|
+
function App() {
|
|
228
|
+
useApiTracer(); // init once, StrictMode-safe
|
|
229
|
+
const { traces, clear } = useApiTraces();
|
|
230
|
+
return <span>{traces.length} calls</span>;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Updates are batched on an animation frame, so a burst of calls on page load
|
|
235
|
+
re-renders once rather than once per request.
|
|
236
|
+
|
|
237
|
+
## The in-app panel
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { ApiTracerPanel } from 'api-tracer-kit/ui';
|
|
241
|
+
|
|
242
|
+
<ApiTracerPanel enabled={process.env.NODE_ENV !== 'production'} />;
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
A floating panel with the call list, per-call detail (request and response
|
|
246
|
+
headers, params, bodies, status, duration, errors), search, method and status
|
|
247
|
+
filters, sorting, expand/collapse and clear. Styles are inline, so there is no
|
|
248
|
+
stylesheet to import and nothing to collide with your app's CSS.
|
|
249
|
+
|
|
250
|
+
## Axios
|
|
251
|
+
|
|
252
|
+
axios calls are traced two ways.
|
|
253
|
+
|
|
254
|
+
In a browser axios rides on `XMLHttpRequest`, so `apiTracer.init()` alone
|
|
255
|
+
already sees them. For richer data — the params object as axios received it, the
|
|
256
|
+
config, and Node's `http` adapter, which no global patch can reach — attach it
|
|
257
|
+
explicitly:
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import axios from 'axios';
|
|
261
|
+
import { apiTracer } from 'api-tracer-kit';
|
|
262
|
+
|
|
263
|
+
apiTracer.init();
|
|
264
|
+
apiTracer.useAxios(axios);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
One call covers the default export **and every instance `axios.create()` makes
|
|
268
|
+
afterwards**. That matters in codebases that send some requests through per-page
|
|
269
|
+
instances and the rest through the default object — patching only the default
|
|
270
|
+
misses half the traffic.
|
|
271
|
+
|
|
272
|
+
A call seen by both adapters is recorded once, not twice.
|
|
273
|
+
|
|
274
|
+
Why an explicit call at all: an axios instance is a plain object with no global
|
|
275
|
+
to patch, so there is no way for the tracer to reach one it was never handed.
|
|
276
|
+
This is the smallest integration that works, and it is the only one in the
|
|
277
|
+
package.
|
|
278
|
+
|
|
279
|
+
## SSR
|
|
280
|
+
|
|
281
|
+
Importing the package evaluates no browser global and installs nothing. `init()`
|
|
282
|
+
checks for each runtime capability before patching it, so on a server it simply
|
|
283
|
+
finds nothing to patch and returns.
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
// safe anywhere
|
|
287
|
+
import { apiTracer } from 'api-tracer-kit';
|
|
288
|
+
|
|
289
|
+
if (typeof window !== 'undefined') apiTracer.init();
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The guard is not required — `init()` is already a no-op without a runtime — but
|
|
293
|
+
it makes the intent obvious in a Next.js file that runs on both sides.
|
|
294
|
+
|
|
295
|
+
## Not breaking your API calls
|
|
296
|
+
|
|
297
|
+
The tracer is built so that removing it changes nothing. Specifically:
|
|
298
|
+
|
|
299
|
+
- responses are read through `Response.clone()`, after the original has been
|
|
300
|
+
handed back, so your `.json()` still works
|
|
301
|
+
- streaming responses (`text/event-stream`) are never buffered
|
|
302
|
+
- binary and non-textual responses are described, not read
|
|
303
|
+
- request bodies that are `ReadableStream`s are never consumed
|
|
304
|
+
- bodies over `maxBodyBytes` are recorded as omitted rather than copied
|
|
305
|
+
- errors are rethrown exactly as they arrived; aborts stay `AbortError`
|
|
306
|
+
- `AbortSignal`, redirects, credentials and headers are passed through untouched
|
|
307
|
+
- nothing is retried, and no request is ever made twice
|
|
308
|
+
- XHR is observed with `addEventListener`, never by taking `onload` or
|
|
309
|
+
`onreadystatechange`
|
|
310
|
+
- a subscriber that throws is caught, so a bad listener cannot break a request
|
|
311
|
+
- `destroy()` restores every patched global and every axios adapter
|
|
312
|
+
|
|
313
|
+
## The console
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
npx api-tracer scan # read the source, write the endpoint catalog
|
|
317
|
+
npx api-tracer serve # the UI, at http://127.0.0.1:4400
|
|
318
|
+
npx api-tracer start # both
|
|
319
|
+
npx api-tracer report # the insight report, Markdown or JSON
|
|
320
|
+
npx api-tracer init # write a config file, if the guesses need help
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### How it finds your endpoints
|
|
324
|
+
|
|
325
|
+
It runs every scanner preset over your codebase and keeps whichever actually
|
|
326
|
+
reads it:
|
|
327
|
+
|
|
328
|
+
| preset | what it reads |
|
|
329
|
+
| --- | --- |
|
|
330
|
+
| `service-object` | `const request = { subUrl: '/users' }; return get(request)` |
|
|
331
|
+
| `axios-direct` | `axios.get('/users')`, `api.post(\`/users/${id}\`, body)` |
|
|
332
|
+
| `fetch-direct` | `fetch('/api/users', { method: 'POST' })` |
|
|
333
|
+
|
|
334
|
+
Base URLs and the auth header name are read from your source too, so your
|
|
335
|
+
environments stay in sync with the app rather than being copied into a config.
|
|
336
|
+
|
|
337
|
+
If none of the presets fit, supply your own in `api-tracer.config.mjs`:
|
|
338
|
+
|
|
339
|
+
```js
|
|
340
|
+
export default {
|
|
341
|
+
parse: (src, config) => [
|
|
342
|
+
{ name, method, subUrl, usesParams, usesData, chunk, index },
|
|
343
|
+
],
|
|
344
|
+
};
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Config file
|
|
348
|
+
|
|
349
|
+
Optional. `api-tracer init` writes one filled in with what the scan worked out.
|
|
350
|
+
Every field has a default:
|
|
351
|
+
|
|
352
|
+
```js
|
|
353
|
+
export default {
|
|
354
|
+
name: 'My API',
|
|
355
|
+
preset: 'service-object',
|
|
356
|
+
sources: ['src'],
|
|
357
|
+
baseUrls: { dev: 'https://dev.example.com/api' },
|
|
358
|
+
auth: { header: 'Authorization' },
|
|
359
|
+
envelope: { codeFields: ['status', 'code'], okField: 'success', failFrom: 400 },
|
|
360
|
+
login: { steps: [/* an optional multi-step sign-in chain */] },
|
|
361
|
+
};
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Environment variables
|
|
365
|
+
|
|
366
|
+
| | |
|
|
367
|
+
| --- | --- |
|
|
368
|
+
| `API_TRACER_DATA` | where captures are kept (default `<project>/.api-tracer`) |
|
|
369
|
+
| `PORT` / `HOST` | default `4400` / `127.0.0.1` |
|
|
370
|
+
| `BASE_PATH` | serve under a path, e.g. `/api-console`, behind a proxy |
|
|
371
|
+
| `ALLOWED_ORIGINS` | comma-separated origins allowed to post recordings |
|
|
372
|
+
| `CONSOLE_USER` / `CONSOLE_PASS` | basic auth on everything except `/api/record` |
|
|
373
|
+
| `LOCK_RECORDING` / `LOCK_ENV` | stop one person's click changing a shared console |
|
|
374
|
+
|
|
375
|
+
## Security
|
|
376
|
+
|
|
377
|
+
The tracer sees whatever your API sees, which on some products means
|
|
378
|
+
credentials and personal data. So:
|
|
379
|
+
|
|
380
|
+
- `Authorization`, `Cookie`, `Set-Cookie`, `X-API-Key` and the usual token
|
|
381
|
+
header names are redacted by default, and the list is configurable
|
|
382
|
+
- any field whose **name** looks like a credential (`token`, `password`,
|
|
383
|
+
`secret`, `api_key`, `otp`, …) is replaced with `<redacted>`, keeping the
|
|
384
|
+
surrounding shape so the sample stays useful
|
|
385
|
+
- redaction happens before a trace is stored, so a redacted value is never held
|
|
386
|
+
in memory either
|
|
387
|
+
- the console redacts again before writing anything to disk
|
|
388
|
+
- HAR, cURL and Postman exports carry `<paste your token>` in place of a real
|
|
389
|
+
token, so an export is safe to attach to a ticket
|
|
390
|
+
|
|
391
|
+
The console's data directory holds captured request and response bodies from a
|
|
392
|
+
real environment. Treat it as sensitive: keep it out of version control (it is
|
|
393
|
+
`.api-tracer/`, add it to `.gitignore`) and keep a deployed console behind VPN
|
|
394
|
+
or SSO.
|
|
395
|
+
|
|
396
|
+
## Limitations
|
|
397
|
+
|
|
398
|
+
Worth knowing before you rely on it.
|
|
399
|
+
|
|
400
|
+
- **An axios instance must be handed over.** There is no global to patch. In a
|
|
401
|
+
browser the XHR adapter catches axios anyway; in Node it cannot.
|
|
402
|
+
- **A request body that is a stream is never read**, because reading it would
|
|
403
|
+
empty it. It is recorded as `omitted: 'stream'`.
|
|
404
|
+
- **Files in a `FormData` are described, not captured**, so a captured upload
|
|
405
|
+
cannot be replayed with its file.
|
|
406
|
+
- **Response bodies over `maxBodyBytes` are omitted**, not truncated — a partial
|
|
407
|
+
JSON body is more misleading than none.
|
|
408
|
+
- **`Response.clone()` buffers.** For a large non-streaming response the clone
|
|
409
|
+
holds a second copy until it is read. The size cap keeps this bounded.
|
|
410
|
+
- **The scanner is regex-based.** It reads the three shapes above well and says
|
|
411
|
+
what it could not read; it does not understand a URL assembled from three
|
|
412
|
+
variables. Live recording covers what the scan misses — an unexplained call
|
|
413
|
+
becomes an endpoint of its own.
|
|
414
|
+
- **The "unused endpoint" count is a substring count**, so `import * as services`
|
|
415
|
+
or a re-export would hide a real usage. Confirm with a grep before deleting.
|
|
416
|
+
- **The console is single-user by design**: one token, one selected environment,
|
|
417
|
+
one set of results.
|
|
418
|
+
|
|
419
|
+
## Architecture
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
api-tracer-kit
|
|
423
|
+
├── src/
|
|
424
|
+
│ ├── core/ tracer, data model, redaction, bodies, URLs, envelopes, runtime detection
|
|
425
|
+
│ ├── adapters/ fetch, xhr, axios, and the suppression that stops double-counting
|
|
426
|
+
│ ├── storage/ the TraceStorage interface and the capped in-memory default
|
|
427
|
+
│ ├── transport/ posting finished traces to a console
|
|
428
|
+
│ ├── ui/ the React panel (optional entry point)
|
|
429
|
+
│ ├── index.ts the public API
|
|
430
|
+
│ ├── axios.ts api-tracer-kit/axios
|
|
431
|
+
│ └── react.ts api-tracer-kit/react
|
|
432
|
+
└── cli/
|
|
433
|
+
├── bin/ the api-tracer command
|
|
434
|
+
├── presets.mjs how to find endpoints in a codebase
|
|
435
|
+
├── config.mjs defaults, and guessing what was not configured
|
|
436
|
+
├── scan.mjs source -> endpoint catalog
|
|
437
|
+
├── server.mjs the console: forwards calls, replays, exports, records
|
|
438
|
+
├── import.mjs HAR and cURL, matched back to the catalog
|
|
439
|
+
├── shape.mjs response shapes and drift diffing
|
|
440
|
+
├── report.mjs the insight report model and its Markdown export
|
|
441
|
+
└── web/ the dashboard (no framework, no build step)
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
The core is framework-free and knows nothing about any particular application.
|
|
445
|
+
The console knows nothing about it either: the catalog supplies the endpoints,
|
|
446
|
+
base URLs, auth header and envelope convention, and the config file supplies the
|
|
447
|
+
sign-in flow if there is one worth automating.
|
|
448
|
+
|
|
449
|
+
## Development
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
npm install
|
|
453
|
+
npm run build # tsup -> dist/, ESM + CJS + .d.ts
|
|
454
|
+
npm test # the tracer suite, then the console suite
|
|
455
|
+
npm run typecheck
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
The example project doubles as an end-to-end check:
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
cd examples/basic && npm install && npm start
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
## Licence
|
|
465
|
+
|
|
466
|
+
MIT
|