@reproapp/node-sdk 0.0.38 → 0.0.41
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 +228 -310
- package/dist/index.d.ts +55 -0
- package/dist/index.js +1038 -21
- package/dist/ingest/client.js +2 -1
- package/dist/ingest/mapper.js +2 -0
- package/dist/ingest/types.d.ts +1 -0
- package/package.json +10 -4
- package/src/index.ts +1320 -20
- package/src/ingest/client.ts +1 -0
- package/src/ingest/mapper.ts +1 -0
- package/src/ingest/types.ts +1 -0
- package/test/backend-live-mode.test.js +421 -0
- package/test/postgresql-integration.test.js +302 -0
- package/test/postgresql-integrations.testcontainers.test.js +261 -0
- package/test/request-manifest-priority.test.js +6 -0
package/README.md
CHANGED
|
@@ -1,377 +1,295 @@
|
|
|
1
|
-
# Repro
|
|
1
|
+
# Repro Node SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Backend recorder for Repro sessions. It captures tagged request/response data,
|
|
4
|
+
function traces, database activity, queue messages, Redis operations, and SendGrid
|
|
5
|
+
email events, then uploads that context to Repro.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The current package is `@reproapp/node-sdk`. New apps should start with
|
|
8
|
+
`initRepro`.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
- Node.js 18+
|
|
11
|
-
- NestJS app using Express (default) and optionally Mongoose 6+
|
|
12
|
-
|
|
13
|
-
Install the package:
|
|
10
|
+
## Install
|
|
14
11
|
|
|
15
12
|
```bash
|
|
16
|
-
npm install
|
|
17
|
-
# or
|
|
18
|
-
yarn add repro-nest
|
|
19
|
-
# or
|
|
20
|
-
pnpm add repro-nest
|
|
13
|
+
npm install @reproapp/node-sdk
|
|
21
14
|
```
|
|
22
15
|
|
|
23
|
-
|
|
16
|
+
Requirements:
|
|
17
|
+
- Node.js 18+
|
|
18
|
+
- Express or NestJS on the Express adapter for HTTP capture
|
|
19
|
+
- Optional integrations are patched when their packages are installed: `mongoose`,
|
|
20
|
+
`mongodb`, `pg`, `postgres`, TypeORM (standalone or NestJS), `kafkajs`,
|
|
21
|
+
`amqplib`, `redis`, and `@sendgrid/mail`
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
function tracing, call `initReproTracing` before importing your `AppModule` so
|
|
27
|
-
Nest classes are instrumented at load time.
|
|
23
|
+
## Quickstart
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
import mongoose from 'mongoose';
|
|
33
|
-
import {
|
|
34
|
-
initReproTracing,
|
|
35
|
-
reproMiddleware,
|
|
36
|
-
reproMongoosePlugin,
|
|
37
|
-
} from 'repro-nest';
|
|
25
|
+
Call `initRepro` before importing your application modules. This lets the SDK
|
|
26
|
+
install tracing hooks, auto-attach HTTP middleware to Express/Nest apps, register
|
|
27
|
+
database/message/email integrations, and fetch the runtime privacy policy.
|
|
38
28
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
appName: process.env.REPRO_APP_NAME as string,
|
|
42
|
-
serviceName: process.env.REPRO_SERVICE_NAME as string,
|
|
43
|
-
tenantId: process.env.REPRO_TENANT_ID as string,
|
|
44
|
-
appSecret: process.env.REPRO_APP_SECRET as string,
|
|
45
|
-
};
|
|
29
|
+
```ts
|
|
30
|
+
import { initRepro } from "@reproapp/node-sdk";
|
|
46
31
|
|
|
47
32
|
async function bootstrap() {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
33
|
+
await initRepro({
|
|
34
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
35
|
+
appId: process.env.REPRO_APP_ID!,
|
|
36
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
37
|
+
appName: process.env.REPRO_APP_NAME ?? "Acme Web",
|
|
38
|
+
serviceName: process.env.REPRO_SERVICE_NAME ?? "api",
|
|
39
|
+
apiBase: process.env.REPRO_API_BASE,
|
|
40
|
+
tracing: {
|
|
41
|
+
disableFunctionTypes: ["constructor"],
|
|
42
|
+
logFunctionCalls: false,
|
|
43
|
+
},
|
|
44
|
+
privacy: {
|
|
45
|
+
environment: process.env.REPRO_PRIVACY_ENV ?? process.env.NODE_ENV ?? "dev",
|
|
46
|
+
apiBase: process.env.REPRO_PRIVACY_API_BASE ?? process.env.REPRO_API_BASE,
|
|
47
|
+
},
|
|
52
48
|
});
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
const { AppModule } = await import("./app.module");
|
|
51
|
+
const { NestFactory } = await import("@nestjs/core");
|
|
56
52
|
|
|
57
|
-
const { AppModule } = await import('./app.module');
|
|
58
53
|
const app = await NestFactory.create(AppModule);
|
|
59
|
-
|
|
60
|
-
// Capture request/response payloads for tagged Repro sessions.
|
|
61
|
-
app.use(reproMiddleware(reproConfig));
|
|
62
|
-
|
|
63
54
|
await app.listen(3000);
|
|
64
55
|
}
|
|
65
56
|
|
|
66
|
-
bootstrap();
|
|
57
|
+
void bootstrap();
|
|
67
58
|
```
|
|
68
59
|
|
|
69
|
-
|
|
70
|
-
- `REPRO_INGEST_BASE` (optional) overrides the log-collector base URL used to send ingest events.
|
|
71
|
-
- `REPRO_APP_NAME` (optional) sets `appName`, which is sent as `X-App-Name`.
|
|
72
|
-
- `REPRO_SERVICE_NAME` (optional) sets the backend service label stored on request, trace, DB, and email events. Defaults to `appName` when omitted.
|
|
73
|
-
|
|
74
|
-
### initReproTracing
|
|
75
|
-
|
|
76
|
-
Enables function tracing. Call this before importing `AppModule` so Nest classes
|
|
77
|
-
are loaded through the tracer.
|
|
78
|
-
|
|
79
|
-
Options (type -> purpose):
|
|
80
|
-
- `instrument` (boolean): enable or disable instrumentation of loaded modules.
|
|
81
|
-
- `include` (RegExp[]): file path include patterns for instrumentation.
|
|
82
|
-
- `exclude` (RegExp[]): file path exclude patterns for instrumentation.
|
|
83
|
-
- `mode` (string): tracer mode (defaults to `TRACE_MODE` or `trace`).
|
|
84
|
-
- `samplingMs` (number): sampling interval in milliseconds.
|
|
85
|
-
- `disableFunctionTraces` (DisableFunctionTraceConfig[] | null): drop trace events
|
|
86
|
-
that match rule objects or predicates.
|
|
87
|
-
- `disableFunctionTypes` (TraceRulePattern | null): drop events for matching
|
|
88
|
-
function kinds (for example, constructors).
|
|
89
|
-
- `disableTraceFiles` (DisableTraceFileConfig | DisableTraceFileConfig[] | null):
|
|
90
|
-
drop events emitted from matching files.
|
|
91
|
-
- `traceInterceptors` (boolean): include Nest interceptors in traces (default false).
|
|
92
|
-
- `logFunctionCalls` (boolean): log enter/exit events to console.
|
|
93
|
-
|
|
94
|
-
Type reference:
|
|
95
|
-
- `TraceRulePattern`: `string | number | RegExp | Array<string | number | RegExp>`
|
|
96
|
-
- `DisableFunctionTraceConfig`: `DisableFunctionTraceRule | DisableFunctionTracePredicate`
|
|
97
|
-
- `DisableFunctionTracePredicate`: `(event: TraceEventForFilter) => boolean`
|
|
98
|
-
- `DisableFunctionTraceRule` fields (all accept `TraceRulePattern`):
|
|
99
|
-
- `fn` / `functionName`: function name substring/regex
|
|
100
|
-
- `wrapper` / `wrapperClass` / `className` / `owner`: wrapper/owner name
|
|
101
|
-
- `file`: source filename
|
|
102
|
-
- `line`: source line number
|
|
103
|
-
- `lib` / `library`: npm package name inferred from path
|
|
104
|
-
- `type` / `functionType`: function kind (`method`, `constructor`, etc.)
|
|
105
|
-
- `event` / `eventType`: trace phase (`enter` or `exit`)
|
|
106
|
-
- `DisableTraceByFilename`: `{ file: TraceRulePattern }`
|
|
107
|
-
- `DisableTraceFileConfig`: `TraceRulePattern | DisableTraceByFilename | null | undefined`
|
|
108
|
-
|
|
109
|
-
Full config example (shows every option; aliases are interchangeable, use one per group in real configs):
|
|
60
|
+
Express apps follow the same ordering:
|
|
110
61
|
|
|
111
62
|
```ts
|
|
112
|
-
import {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
wrapper: 'TasksService',
|
|
124
|
-
file: 'src/tasks/tasks.service.ts',
|
|
125
|
-
line: 27,
|
|
126
|
-
lib: 'mongoose',
|
|
127
|
-
type: 'method',
|
|
128
|
-
event: 'exit',
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
functionName: 'formatSensitiveData',
|
|
132
|
-
wrapperClass: 'AuthService',
|
|
133
|
-
className: 'AuthService',
|
|
134
|
-
owner: 'AuthService',
|
|
135
|
-
library: 'bcrypt',
|
|
136
|
-
functionType: 'method',
|
|
137
|
-
eventType: 'enter',
|
|
138
|
-
},
|
|
139
|
-
(event) => event.fn?.startsWith('debug') ?? false,
|
|
140
|
-
],
|
|
141
|
-
disableFunctionTypes: ['constructor', /getter/i],
|
|
142
|
-
disableTraceFiles: [
|
|
143
|
-
/node_modules\/some-logger\//,
|
|
144
|
-
{ file: 'dist/health.check.js' },
|
|
145
|
-
'dist/generated.js',
|
|
146
|
-
],
|
|
147
|
-
traceInterceptors: true,
|
|
148
|
-
logFunctionCalls: false,
|
|
149
|
-
});
|
|
150
|
-
```
|
|
63
|
+
import { initRepro } from "@reproapp/node-sdk";
|
|
64
|
+
|
|
65
|
+
async function main() {
|
|
66
|
+
await initRepro({
|
|
67
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
68
|
+
appId: process.env.REPRO_APP_ID!,
|
|
69
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
70
|
+
appName: "Acme Web",
|
|
71
|
+
serviceName: "public-api",
|
|
72
|
+
apiBase: process.env.REPRO_API_BASE,
|
|
73
|
+
});
|
|
151
74
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
- `appId` (string): Repro app id.
|
|
159
|
-
- `appName` (string, optional): Repro app name (sent as `X-App-Name`).
|
|
160
|
-
- `serviceName` (string, optional): service label persisted with backend events.
|
|
161
|
-
- `tenantId` (string): Repro tenant id.
|
|
162
|
-
- `appSecret` (string): Repro app secret.
|
|
163
|
-
- `captureHeaders` (boolean | HeaderCaptureOptions): enable/disable header capture
|
|
164
|
-
and masking.
|
|
165
|
-
- `masking` (ReproMaskingConfig): mask request/response bodies and trace args/returns.
|
|
166
|
-
|
|
167
|
-
Header capture options (`HeaderCaptureOptions`):
|
|
168
|
-
- `allowSensitiveHeaders` (boolean): keep default sensitive headers unmasked.
|
|
169
|
-
- `maskHeaders` (HeaderRule | HeaderRule[]): header names to mask.
|
|
170
|
-
- `dropHeaders` (HeaderRule | HeaderRule[]): alias for `maskHeaders`.
|
|
171
|
-
- `unmaskHeaders` (HeaderRule | HeaderRule[]): header names to keep unmasked.
|
|
172
|
-
- `keepHeaders` (HeaderRule | HeaderRule[]): alias for `unmaskHeaders`.
|
|
173
|
-
- `HeaderRule`: `string | RegExp`
|
|
174
|
-
|
|
175
|
-
Masking options (`ReproMaskingConfig`):
|
|
176
|
-
- `replacement` (any): default replacement value (defaults to `"[REDACTED]"`).
|
|
177
|
-
- `rules` (ReproMaskRule[] | null): list of masking rules.
|
|
178
|
-
|
|
179
|
-
Masking rule options (`ReproMaskRule`):
|
|
180
|
-
- `when` (ReproMaskWhen): scope rules by request or function trace fields.
|
|
181
|
-
- `target` (ReproMaskTarget | ReproMaskTarget[]): where to apply the mask.
|
|
182
|
-
- `paths` (string | string[]): dot/bracket paths to mask.
|
|
183
|
-
- `keys` (TraceRulePattern): mask keys anywhere in the payload.
|
|
184
|
-
- `replacement` (any): override replacement value for this rule.
|
|
185
|
-
|
|
186
|
-
`ReproMaskTarget` values:
|
|
187
|
-
- `request.headers`, `request.body`, `request.params`, `request.query`
|
|
188
|
-
- `response.body`, `trace.args`, `trace.returnValue`, `trace.error`
|
|
189
|
-
|
|
190
|
-
`ReproMaskWhen` fields (all accept `TraceRulePattern` unless noted):
|
|
191
|
-
- Request scope: `method`, `path`, `key`
|
|
192
|
-
- Function scope: `fn` / `functionName`, `wrapper` / `wrapperClass` / `className` / `owner`,
|
|
193
|
-
`file`, `line`, `lib` / `library`, `type` / `functionType`, `event` / `eventType`
|
|
194
|
-
|
|
195
|
-
Full config example (shows every option; aliases are interchangeable, use one per group in real configs):
|
|
75
|
+
const express = (await import("express")).default;
|
|
76
|
+
const app = express();
|
|
77
|
+
app.use(express.json());
|
|
78
|
+
app.get("/health", (_req, res) => res.json({ ok: true }));
|
|
79
|
+
app.listen(3000);
|
|
80
|
+
}
|
|
196
81
|
|
|
197
|
-
|
|
198
|
-
import { reproMiddleware } from 'repro-nest';
|
|
199
|
-
|
|
200
|
-
app.use(reproMiddleware({
|
|
201
|
-
appId: process.env.REPRO_APP_ID as string,
|
|
202
|
-
appName: process.env.REPRO_APP_NAME as string,
|
|
203
|
-
serviceName: process.env.REPRO_SERVICE_NAME as string,
|
|
204
|
-
tenantId: process.env.REPRO_TENANT_ID as string,
|
|
205
|
-
appSecret: process.env.REPRO_APP_SECRET as string,
|
|
206
|
-
captureHeaders: {
|
|
207
|
-
allowSensitiveHeaders: false,
|
|
208
|
-
maskHeaders: [/authorization/i, /cookie/i],
|
|
209
|
-
dropHeaders: ['x-api-key'],
|
|
210
|
-
unmaskHeaders: ['x-request-id'],
|
|
211
|
-
keepHeaders: ['x-trace-id'],
|
|
212
|
-
},
|
|
213
|
-
masking: {
|
|
214
|
-
replacement: '[REDACTED]',
|
|
215
|
-
rules: [
|
|
216
|
-
{
|
|
217
|
-
when: { method: 'POST', path: '/api/auth/login', key: 'POST /api/auth/login' },
|
|
218
|
-
target: ['request.body', 'response.body'],
|
|
219
|
-
paths: ['password'],
|
|
220
|
-
keys: [/token/i],
|
|
221
|
-
replacement: '[FILTERED]',
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
when: {
|
|
225
|
-
functionName: 'findAll',
|
|
226
|
-
wrapperClass: 'TasksService',
|
|
227
|
-
file: 'src/tasks/tasks.service.ts',
|
|
228
|
-
line: 27,
|
|
229
|
-
library: 'mongoose',
|
|
230
|
-
functionType: 'method',
|
|
231
|
-
eventType: 'exit',
|
|
232
|
-
},
|
|
233
|
-
target: ['trace.args', 'trace.returnValue', 'trace.error'],
|
|
234
|
-
paths: ['0.user.token'],
|
|
235
|
-
keys: ['password'],
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
when: {
|
|
239
|
-
fn: 'create',
|
|
240
|
-
wrapper: 'UsersService',
|
|
241
|
-
className: 'UsersService',
|
|
242
|
-
owner: 'UsersService',
|
|
243
|
-
lib: 'mongoose',
|
|
244
|
-
type: 'method',
|
|
245
|
-
event: 'enter',
|
|
246
|
-
},
|
|
247
|
-
target: 'trace.args',
|
|
248
|
-
paths: ['0.password'],
|
|
249
|
-
},
|
|
250
|
-
],
|
|
251
|
-
},
|
|
252
|
-
}));
|
|
82
|
+
void main();
|
|
253
83
|
```
|
|
254
84
|
|
|
255
|
-
|
|
85
|
+
## Capture modes: tagged sessions and backend live mode
|
|
256
86
|
|
|
257
|
-
|
|
87
|
+
The Node SDK has two capture modes:
|
|
258
88
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
89
|
+
- Tagged session capture is the default. Browser live and staging sessions both
|
|
90
|
+
add Repro context to app requests. The Node SDK captures backend requests,
|
|
91
|
+
traces, DB changes, queue work, Redis calls, and email events when that context
|
|
92
|
+
is present.
|
|
93
|
+
- Backend-owned live mode is opt-in. Use `liveMode` when the backend must create
|
|
94
|
+
sessions without frontend Repro context, such as backend-only APIs or jobs.
|
|
262
95
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
- `serviceName` (string, optional): service label persisted with backend events
|
|
267
|
-
- `tenantId` (string): Repro tenant id
|
|
268
|
-
- `appSecret` (string): Repro app secret
|
|
96
|
+
The backend SDK does not set `ingest.mode`; `live` and `staging` are selected in
|
|
97
|
+
the React SDK. Staging manual QA captures from the small Repro button still tag
|
|
98
|
+
backend requests, so the Node SDK captures them through the default tagged flow.
|
|
269
99
|
|
|
270
|
-
|
|
100
|
+
Default tagged capture:
|
|
271
101
|
|
|
272
102
|
```ts
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
appSecret: process.env.REPRO_APP_SECRET as string,
|
|
282
|
-
}));
|
|
103
|
+
await initRepro({
|
|
104
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
105
|
+
appId: process.env.REPRO_APP_ID!,
|
|
106
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
107
|
+
appName: "Acme Web",
|
|
108
|
+
serviceName: "api",
|
|
109
|
+
apiBase: process.env.REPRO_API_BASE,
|
|
110
|
+
});
|
|
283
111
|
```
|
|
284
112
|
|
|
285
|
-
|
|
113
|
+
Backend-owned live mode:
|
|
286
114
|
|
|
287
115
|
```ts
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}))
|
|
300
|
-
|
|
116
|
+
await initRepro({
|
|
117
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
118
|
+
appId: process.env.REPRO_APP_ID!,
|
|
119
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
120
|
+
appName: "Acme Web",
|
|
121
|
+
serviceName: "api",
|
|
122
|
+
apiBase: process.env.REPRO_API_BASE,
|
|
123
|
+
liveMode: {
|
|
124
|
+
enabled: true,
|
|
125
|
+
backendOnly: true,
|
|
126
|
+
defineActorId: ({ req, ip }) => req.headers["x-user-id"] ?? (ip ? `ip:${ip}` : null),
|
|
127
|
+
shouldCapture: ({ path }) => path.startsWith("/api/") && !path.includes("/health"),
|
|
128
|
+
actorType: "user",
|
|
129
|
+
actorLabels: ({ path }) => ({ routeGroup: path.split("/").filter(Boolean)[0] ?? "api" }),
|
|
301
130
|
},
|
|
302
131
|
});
|
|
303
132
|
```
|
|
304
133
|
|
|
305
|
-
|
|
134
|
+
## Manual Wiring
|
|
135
|
+
|
|
136
|
+
Use the lower-level exports when auto-attach is not appropriate or when you need
|
|
137
|
+
to wire middleware manually.
|
|
306
138
|
|
|
307
139
|
```ts
|
|
308
|
-
import
|
|
309
|
-
import
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
140
|
+
import express from "express";
|
|
141
|
+
import mongoose from "mongoose";
|
|
142
|
+
import {
|
|
143
|
+
initReproTracing,
|
|
144
|
+
patchKafkaJs,
|
|
145
|
+
patchMongoDb,
|
|
146
|
+
patchPostgreSql,
|
|
147
|
+
patchRedis,
|
|
148
|
+
patchSendgridMail,
|
|
149
|
+
reproMiddleware,
|
|
150
|
+
reproMongoosePlugin,
|
|
151
|
+
} from "@reproapp/node-sdk";
|
|
152
|
+
|
|
153
|
+
const reproConfig = {
|
|
154
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
155
|
+
appId: process.env.REPRO_APP_ID!,
|
|
156
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
157
|
+
appName: "Acme Web",
|
|
158
|
+
serviceName: "api",
|
|
159
|
+
};
|
|
320
160
|
|
|
321
|
-
|
|
161
|
+
initReproTracing({ disableFunctionTypes: ["constructor"] });
|
|
162
|
+
mongoose.plugin(reproMongoosePlugin(reproConfig));
|
|
163
|
+
patchKafkaJs(reproConfig);
|
|
164
|
+
patchMongoDb(reproConfig);
|
|
165
|
+
patchPostgreSql(reproConfig);
|
|
166
|
+
patchRedis(reproConfig);
|
|
167
|
+
patchSendgridMail(reproConfig);
|
|
168
|
+
|
|
169
|
+
const app = express();
|
|
170
|
+
app.use(express.json());
|
|
171
|
+
app.use(reproMiddleware(reproConfig));
|
|
172
|
+
```
|
|
322
173
|
|
|
323
|
-
|
|
174
|
+
## Configuration
|
|
324
175
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
176
|
+
```ts
|
|
177
|
+
type ReproInitConfig = {
|
|
178
|
+
tenantId: string;
|
|
179
|
+
appId: string;
|
|
180
|
+
appSecret?: string;
|
|
181
|
+
appName?: string;
|
|
182
|
+
serviceName?: string;
|
|
183
|
+
apiBase?: string;
|
|
184
|
+
actorId?: string;
|
|
185
|
+
actorType?: string;
|
|
186
|
+
actorLabels?: Record<string, string>;
|
|
187
|
+
captureHeaders?: boolean | HeaderCaptureOptions;
|
|
188
|
+
masking?: ReproMaskingConfig;
|
|
189
|
+
privacy?: RuntimePrivacyConfig;
|
|
190
|
+
liveMode?: boolean | ReproLiveModeConfig;
|
|
191
|
+
tracing?: ReproTracingInitOptions | false;
|
|
192
|
+
resolveContext?: () => { sid?: string; aid?: string } | undefined;
|
|
193
|
+
};
|
|
194
|
+
```
|
|
328
195
|
|
|
329
|
-
|
|
196
|
+
Important options:
|
|
197
|
+
- `tenantId`, `appId`, `appSecret`: Repro service credentials.
|
|
198
|
+
- `appName`: Product or workspace label sent as `X-App-Name`.
|
|
199
|
+
- `serviceName`: Backend service label stored on request, trace, DB, queue, Redis,
|
|
200
|
+
and email events.
|
|
201
|
+
- `apiBase`: Repro API base URL. Used by backend live sessions and privacy policy fetches.
|
|
202
|
+
- `captureHeaders`: Enables header capture. Sensitive headers are masked unless
|
|
203
|
+
explicitly unmasked.
|
|
204
|
+
- `masking`: Local masking rules for request bodies, response bodies, and trace values.
|
|
205
|
+
- `privacy`: Runtime privacy policy fetch and enforcement configuration.
|
|
206
|
+
- `liveMode`: Backend-owned live capture for requests without frontend Repro context.
|
|
207
|
+
- `tracing`: Function and HTTP instrumentation options. Pass `false` to disable.
|
|
208
|
+
|
|
209
|
+
## Header And Body Masking
|
|
330
210
|
|
|
331
211
|
```ts
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
212
|
+
await initRepro({
|
|
213
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
214
|
+
appId: process.env.REPRO_APP_ID!,
|
|
215
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
216
|
+
captureHeaders: {
|
|
217
|
+
maskHeaders: [/authorization/i, /cookie/i],
|
|
218
|
+
unmaskHeaders: ["x-request-id"],
|
|
219
|
+
},
|
|
220
|
+
masking: {
|
|
221
|
+
replacement: "[REDACTED]",
|
|
222
|
+
rules: [
|
|
223
|
+
{
|
|
224
|
+
when: { method: "POST", path: "/api/auth/login" },
|
|
225
|
+
target: ["request.body", "response.body"],
|
|
226
|
+
paths: ["password"],
|
|
227
|
+
keys: [/token/i],
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
when: { wrapperClass: "UsersService", functionName: "create" },
|
|
231
|
+
target: "trace.args",
|
|
232
|
+
paths: ["0.password"],
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
},
|
|
340
236
|
});
|
|
341
237
|
```
|
|
342
238
|
|
|
343
|
-
|
|
344
|
-
- `
|
|
345
|
-
-
|
|
239
|
+
Mask targets:
|
|
240
|
+
- `request.headers`
|
|
241
|
+
- `request.body`
|
|
242
|
+
- `request.params`
|
|
243
|
+
- `request.query`
|
|
244
|
+
- `response.body`
|
|
245
|
+
- `trace.args`
|
|
246
|
+
- `trace.returnValue`
|
|
247
|
+
- `trace.error`
|
|
346
248
|
|
|
347
|
-
##
|
|
249
|
+
## Runtime Privacy
|
|
348
250
|
|
|
349
|
-
|
|
251
|
+
The SDK can fetch an approved runtime privacy policy at startup and apply it to
|
|
252
|
+
captured request, trace, DB, queue, Redis, and email values before upload.
|
|
350
253
|
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
254
|
+
```ts
|
|
255
|
+
await initRepro({
|
|
256
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
257
|
+
appId: process.env.REPRO_APP_ID!,
|
|
258
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
259
|
+
apiBase: process.env.REPRO_API_BASE,
|
|
260
|
+
privacy: {
|
|
261
|
+
environment: process.env.REPRO_PRIVACY_ENV ?? "dev",
|
|
262
|
+
apiBase: process.env.REPRO_PRIVACY_API_BASE ?? process.env.REPRO_API_BASE,
|
|
263
|
+
fetchTimeoutMs: 2500,
|
|
264
|
+
startupMaxWaitMs: 10000,
|
|
265
|
+
startupRetryDelayMs: 500,
|
|
266
|
+
},
|
|
267
|
+
});
|
|
355
268
|
```
|
|
356
269
|
|
|
357
|
-
|
|
358
|
-
|
|
270
|
+
Use `privacy.captureRaw: true` only in controlled local development.
|
|
271
|
+
|
|
272
|
+
## Verify
|
|
359
273
|
|
|
360
|
-
|
|
274
|
+
With the React SDK, record a browser session in `live` mode or start a manual QA
|
|
275
|
+
capture in `staging` mode from the small Repro button, then call your backend.
|
|
276
|
+
The Node SDK captures requests when Repro context is present as headers or safe
|
|
277
|
+
query params.
|
|
361
278
|
|
|
362
|
-
|
|
279
|
+
For a manual smoke test:
|
|
363
280
|
|
|
364
281
|
```bash
|
|
365
282
|
curl \
|
|
366
283
|
-H "x-bug-session-id: session-123" \
|
|
367
284
|
-H "x-bug-action-id: action-123" \
|
|
285
|
+
-H "x-bug-request-start: 1770000000000" \
|
|
368
286
|
http://localhost:3000/health
|
|
369
287
|
```
|
|
370
288
|
|
|
371
|
-
Verification
|
|
372
|
-
-
|
|
373
|
-
|
|
374
|
-
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
289
|
+
Verification checklist:
|
|
290
|
+
- The session timeline shows the backend request and response.
|
|
291
|
+
- Function traces appear for code loaded after `initRepro`.
|
|
292
|
+
- Mongoose or MongoDB operations appear when those packages are used.
|
|
293
|
+
- Queue, Redis, and SendGrid events appear when their integrations are installed
|
|
294
|
+
and exercised during a captured request.
|
|
295
|
+
- Sensitive headers and configured payload fields are masked before upload.
|