@reproapp/node-sdk 0.0.38 → 0.0.39
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 +225 -310
- package/dist/index.d.ts +42 -0
- package/dist/index.js +447 -20
- package/package.json +2 -2
- package/src/index.ts +670 -20
- package/test/backend-live-mode.test.js +403 -0
package/README.md
CHANGED
|
@@ -1,377 +1,292 @@
|
|
|
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`, `kafkajs`, `amqplib`, `redis`, and `@sendgrid/mail`
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
function tracing, call `initReproTracing` before importing your `AppModule` so
|
|
27
|
-
Nest classes are instrumented at load time.
|
|
22
|
+
## Quickstart
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
import mongoose from 'mongoose';
|
|
33
|
-
import {
|
|
34
|
-
initReproTracing,
|
|
35
|
-
reproMiddleware,
|
|
36
|
-
reproMongoosePlugin,
|
|
37
|
-
} from 'repro-nest';
|
|
24
|
+
Call `initRepro` before importing your application modules. This lets the SDK
|
|
25
|
+
install tracing hooks, auto-attach HTTP middleware to Express/Nest apps, register
|
|
26
|
+
database/message/email integrations, and fetch the runtime privacy policy.
|
|
38
27
|
|
|
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
|
-
};
|
|
28
|
+
```ts
|
|
29
|
+
import { initRepro } from "@reproapp/node-sdk";
|
|
46
30
|
|
|
47
31
|
async function bootstrap() {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
32
|
+
await initRepro({
|
|
33
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
34
|
+
appId: process.env.REPRO_APP_ID!,
|
|
35
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
36
|
+
appName: process.env.REPRO_APP_NAME ?? "Acme Web",
|
|
37
|
+
serviceName: process.env.REPRO_SERVICE_NAME ?? "api",
|
|
38
|
+
apiBase: process.env.REPRO_API_BASE,
|
|
39
|
+
tracing: {
|
|
40
|
+
disableFunctionTypes: ["constructor"],
|
|
41
|
+
logFunctionCalls: false,
|
|
42
|
+
},
|
|
43
|
+
privacy: {
|
|
44
|
+
environment: process.env.REPRO_PRIVACY_ENV ?? process.env.NODE_ENV ?? "dev",
|
|
45
|
+
apiBase: process.env.REPRO_PRIVACY_API_BASE ?? process.env.REPRO_API_BASE,
|
|
46
|
+
},
|
|
52
47
|
});
|
|
53
48
|
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
const { AppModule } = await import("./app.module");
|
|
50
|
+
const { NestFactory } = await import("@nestjs/core");
|
|
56
51
|
|
|
57
|
-
const { AppModule } = await import('./app.module');
|
|
58
52
|
const app = await NestFactory.create(AppModule);
|
|
59
|
-
|
|
60
|
-
// Capture request/response payloads for tagged Repro sessions.
|
|
61
|
-
app.use(reproMiddleware(reproConfig));
|
|
62
|
-
|
|
63
53
|
await app.listen(3000);
|
|
64
54
|
}
|
|
65
55
|
|
|
66
|
-
bootstrap();
|
|
56
|
+
void bootstrap();
|
|
67
57
|
```
|
|
68
58
|
|
|
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):
|
|
59
|
+
Express apps follow the same ordering:
|
|
110
60
|
|
|
111
61
|
```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
|
-
```
|
|
62
|
+
import { initRepro } from "@reproapp/node-sdk";
|
|
63
|
+
|
|
64
|
+
async function main() {
|
|
65
|
+
await initRepro({
|
|
66
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
67
|
+
appId: process.env.REPRO_APP_ID!,
|
|
68
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
69
|
+
appName: "Acme Web",
|
|
70
|
+
serviceName: "public-api",
|
|
71
|
+
apiBase: process.env.REPRO_API_BASE,
|
|
72
|
+
});
|
|
151
73
|
|
|
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):
|
|
74
|
+
const express = (await import("express")).default;
|
|
75
|
+
const app = express();
|
|
76
|
+
app.use(express.json());
|
|
77
|
+
app.get("/health", (_req, res) => res.json({ ok: true }));
|
|
78
|
+
app.listen(3000);
|
|
79
|
+
}
|
|
196
80
|
|
|
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
|
-
}));
|
|
81
|
+
void main();
|
|
253
82
|
```
|
|
254
83
|
|
|
255
|
-
|
|
84
|
+
## Capture modes: tagged sessions and backend live mode
|
|
256
85
|
|
|
257
|
-
|
|
86
|
+
The Node SDK has two capture modes:
|
|
258
87
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
88
|
+
- Tagged session capture is the default. Browser live and staging sessions both
|
|
89
|
+
add Repro context to app requests. The Node SDK captures backend requests,
|
|
90
|
+
traces, DB changes, queue work, Redis calls, and email events when that context
|
|
91
|
+
is present.
|
|
92
|
+
- Backend-owned live mode is opt-in. Use `liveMode` when the backend must create
|
|
93
|
+
sessions without frontend Repro context, such as backend-only APIs or jobs.
|
|
262
94
|
|
|
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
|
|
95
|
+
The backend SDK does not set `ingest.mode`; `live` and `staging` are selected in
|
|
96
|
+
the React SDK. Staging manual QA captures from the small Repro button still tag
|
|
97
|
+
backend requests, so the Node SDK captures them through the default tagged flow.
|
|
269
98
|
|
|
270
|
-
|
|
99
|
+
Default tagged capture:
|
|
271
100
|
|
|
272
101
|
```ts
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
appSecret: process.env.REPRO_APP_SECRET as string,
|
|
282
|
-
}));
|
|
102
|
+
await initRepro({
|
|
103
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
104
|
+
appId: process.env.REPRO_APP_ID!,
|
|
105
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
106
|
+
appName: "Acme Web",
|
|
107
|
+
serviceName: "api",
|
|
108
|
+
apiBase: process.env.REPRO_API_BASE,
|
|
109
|
+
});
|
|
283
110
|
```
|
|
284
111
|
|
|
285
|
-
|
|
112
|
+
Backend-owned live mode:
|
|
286
113
|
|
|
287
114
|
```ts
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}))
|
|
300
|
-
|
|
115
|
+
await initRepro({
|
|
116
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
117
|
+
appId: process.env.REPRO_APP_ID!,
|
|
118
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
119
|
+
appName: "Acme Web",
|
|
120
|
+
serviceName: "api",
|
|
121
|
+
apiBase: process.env.REPRO_API_BASE,
|
|
122
|
+
liveMode: {
|
|
123
|
+
enabled: true,
|
|
124
|
+
backendOnly: true,
|
|
125
|
+
defineActorId: ({ req, ip }) => req.headers["x-user-id"] ?? (ip ? `ip:${ip}` : null),
|
|
126
|
+
shouldCapture: ({ path }) => path.startsWith("/api/") && !path.includes("/health"),
|
|
127
|
+
actorType: "user",
|
|
128
|
+
actorLabels: ({ path }) => ({ routeGroup: path.split("/").filter(Boolean)[0] ?? "api" }),
|
|
301
129
|
},
|
|
302
130
|
});
|
|
303
131
|
```
|
|
304
132
|
|
|
305
|
-
|
|
133
|
+
## Manual Wiring
|
|
134
|
+
|
|
135
|
+
Use the lower-level exports when auto-attach is not appropriate or when you need
|
|
136
|
+
to wire middleware manually.
|
|
306
137
|
|
|
307
138
|
```ts
|
|
308
|
-
import
|
|
309
|
-
import
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
```
|
|
139
|
+
import express from "express";
|
|
140
|
+
import mongoose from "mongoose";
|
|
141
|
+
import {
|
|
142
|
+
initReproTracing,
|
|
143
|
+
patchKafkaJs,
|
|
144
|
+
patchMongoDb,
|
|
145
|
+
patchRedis,
|
|
146
|
+
patchSendgridMail,
|
|
147
|
+
reproMiddleware,
|
|
148
|
+
reproMongoosePlugin,
|
|
149
|
+
} from "@reproapp/node-sdk";
|
|
320
150
|
|
|
321
|
-
|
|
151
|
+
const reproConfig = {
|
|
152
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
153
|
+
appId: process.env.REPRO_APP_ID!,
|
|
154
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
155
|
+
appName: "Acme Web",
|
|
156
|
+
serviceName: "api",
|
|
157
|
+
};
|
|
322
158
|
|
|
323
|
-
|
|
159
|
+
initReproTracing({ disableFunctionTypes: ["constructor"] });
|
|
160
|
+
mongoose.plugin(reproMongoosePlugin(reproConfig));
|
|
161
|
+
patchKafkaJs(reproConfig);
|
|
162
|
+
patchMongoDb(reproConfig);
|
|
163
|
+
patchRedis(reproConfig);
|
|
164
|
+
patchSendgridMail(reproConfig);
|
|
165
|
+
|
|
166
|
+
const app = express();
|
|
167
|
+
app.use(express.json());
|
|
168
|
+
app.use(reproMiddleware(reproConfig));
|
|
169
|
+
```
|
|
324
170
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
type ReproInitConfig = {
|
|
175
|
+
tenantId: string;
|
|
176
|
+
appId: string;
|
|
177
|
+
appSecret?: string;
|
|
178
|
+
appName?: string;
|
|
179
|
+
serviceName?: string;
|
|
180
|
+
apiBase?: string;
|
|
181
|
+
actorId?: string;
|
|
182
|
+
actorType?: string;
|
|
183
|
+
actorLabels?: Record<string, string>;
|
|
184
|
+
captureHeaders?: boolean | HeaderCaptureOptions;
|
|
185
|
+
masking?: ReproMaskingConfig;
|
|
186
|
+
privacy?: RuntimePrivacyConfig;
|
|
187
|
+
liveMode?: boolean | ReproLiveModeConfig;
|
|
188
|
+
tracing?: ReproTracingInitOptions | false;
|
|
189
|
+
resolveContext?: () => { sid?: string; aid?: string } | undefined;
|
|
190
|
+
};
|
|
191
|
+
```
|
|
328
192
|
|
|
329
|
-
|
|
193
|
+
Important options:
|
|
194
|
+
- `tenantId`, `appId`, `appSecret`: Repro service credentials.
|
|
195
|
+
- `appName`: Product or workspace label sent as `X-App-Name`.
|
|
196
|
+
- `serviceName`: Backend service label stored on request, trace, DB, queue, Redis,
|
|
197
|
+
and email events.
|
|
198
|
+
- `apiBase`: Repro API base URL. Used by backend live sessions and privacy policy fetches.
|
|
199
|
+
- `captureHeaders`: Enables header capture. Sensitive headers are masked unless
|
|
200
|
+
explicitly unmasked.
|
|
201
|
+
- `masking`: Local masking rules for request bodies, response bodies, and trace values.
|
|
202
|
+
- `privacy`: Runtime privacy policy fetch and enforcement configuration.
|
|
203
|
+
- `liveMode`: Backend-owned live capture for requests without frontend Repro context.
|
|
204
|
+
- `tracing`: Function and HTTP instrumentation options. Pass `false` to disable.
|
|
205
|
+
|
|
206
|
+
## Header And Body Masking
|
|
330
207
|
|
|
331
208
|
```ts
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
209
|
+
await initRepro({
|
|
210
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
211
|
+
appId: process.env.REPRO_APP_ID!,
|
|
212
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
213
|
+
captureHeaders: {
|
|
214
|
+
maskHeaders: [/authorization/i, /cookie/i],
|
|
215
|
+
unmaskHeaders: ["x-request-id"],
|
|
216
|
+
},
|
|
217
|
+
masking: {
|
|
218
|
+
replacement: "[REDACTED]",
|
|
219
|
+
rules: [
|
|
220
|
+
{
|
|
221
|
+
when: { method: "POST", path: "/api/auth/login" },
|
|
222
|
+
target: ["request.body", "response.body"],
|
|
223
|
+
paths: ["password"],
|
|
224
|
+
keys: [/token/i],
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
when: { wrapperClass: "UsersService", functionName: "create" },
|
|
228
|
+
target: "trace.args",
|
|
229
|
+
paths: ["0.password"],
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
},
|
|
340
233
|
});
|
|
341
234
|
```
|
|
342
235
|
|
|
343
|
-
|
|
344
|
-
- `
|
|
345
|
-
-
|
|
236
|
+
Mask targets:
|
|
237
|
+
- `request.headers`
|
|
238
|
+
- `request.body`
|
|
239
|
+
- `request.params`
|
|
240
|
+
- `request.query`
|
|
241
|
+
- `response.body`
|
|
242
|
+
- `trace.args`
|
|
243
|
+
- `trace.returnValue`
|
|
244
|
+
- `trace.error`
|
|
346
245
|
|
|
347
|
-
##
|
|
246
|
+
## Runtime Privacy
|
|
348
247
|
|
|
349
|
-
|
|
248
|
+
The SDK can fetch an approved runtime privacy policy at startup and apply it to
|
|
249
|
+
captured request, trace, DB, queue, Redis, and email values before upload.
|
|
350
250
|
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
251
|
+
```ts
|
|
252
|
+
await initRepro({
|
|
253
|
+
tenantId: process.env.REPRO_TENANT_ID!,
|
|
254
|
+
appId: process.env.REPRO_APP_ID!,
|
|
255
|
+
appSecret: process.env.REPRO_APP_SECRET!,
|
|
256
|
+
apiBase: process.env.REPRO_API_BASE,
|
|
257
|
+
privacy: {
|
|
258
|
+
environment: process.env.REPRO_PRIVACY_ENV ?? "dev",
|
|
259
|
+
apiBase: process.env.REPRO_PRIVACY_API_BASE ?? process.env.REPRO_API_BASE,
|
|
260
|
+
fetchTimeoutMs: 2500,
|
|
261
|
+
startupMaxWaitMs: 10000,
|
|
262
|
+
startupRetryDelayMs: 500,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
355
265
|
```
|
|
356
266
|
|
|
357
|
-
|
|
358
|
-
|
|
267
|
+
Use `privacy.captureRaw: true` only in controlled local development.
|
|
268
|
+
|
|
269
|
+
## Verify
|
|
359
270
|
|
|
360
|
-
|
|
271
|
+
With the React SDK, record a browser session in `live` mode or start a manual QA
|
|
272
|
+
capture in `staging` mode from the small Repro button, then call your backend.
|
|
273
|
+
The Node SDK captures requests when Repro context is present as headers or safe
|
|
274
|
+
query params.
|
|
361
275
|
|
|
362
|
-
|
|
276
|
+
For a manual smoke test:
|
|
363
277
|
|
|
364
278
|
```bash
|
|
365
279
|
curl \
|
|
366
280
|
-H "x-bug-session-id: session-123" \
|
|
367
281
|
-H "x-bug-action-id: action-123" \
|
|
282
|
+
-H "x-bug-request-start: 1770000000000" \
|
|
368
283
|
http://localhost:3000/health
|
|
369
284
|
```
|
|
370
285
|
|
|
371
|
-
Verification
|
|
372
|
-
-
|
|
373
|
-
|
|
374
|
-
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
286
|
+
Verification checklist:
|
|
287
|
+
- The session timeline shows the backend request and response.
|
|
288
|
+
- Function traces appear for code loaded after `initRepro`.
|
|
289
|
+
- Mongoose or MongoDB operations appear when those packages are used.
|
|
290
|
+
- Queue, Redis, and SendGrid events appear when their integrations are installed
|
|
291
|
+
and exercised during a captured request.
|
|
292
|
+
- Sensitive headers and configured payload fields are masked before upload.
|
package/dist/index.d.ts
CHANGED
|
@@ -287,6 +287,11 @@ type RequestCaptureScopeState = {
|
|
|
287
287
|
dbChangeCount: number;
|
|
288
288
|
emailEventCount: number;
|
|
289
289
|
};
|
|
290
|
+
type BackendLiveTimingConfig = {
|
|
291
|
+
sessionMaxDurationMs: number;
|
|
292
|
+
segmentMaxDurationMs: number;
|
|
293
|
+
segmentIdleMs: number;
|
|
294
|
+
};
|
|
290
295
|
declare function sanitizeTraceValueForPrivacy(value: any): any;
|
|
291
296
|
declare function sanitizeMaterializedTraceValue(value: any): any;
|
|
292
297
|
export declare function __materializePendingTraceEventsForWorker(payload: TraceMaterializationWorkerPayload): Promise<PendingTraceEventRecord[]>;
|
|
@@ -322,12 +327,48 @@ declare function getEventTraceValueEntries(event: TraceEventRecord | PendingTrac
|
|
|
322
327
|
declare function estimateTraceBatchSerializedBytes(batch: TraceEventRecord[], requestRid: string, actionId: string | null | undefined, batchIndex: number): number;
|
|
323
328
|
declare function chunkTraceEventsForTransport(events: TraceEventRecord[], requestRid: string, actionId: string | null | undefined): TraceEventRecord[][];
|
|
324
329
|
export type ReproMiddlewareConfig = IngestClientConfig & {
|
|
330
|
+
/** API base used for backend-owned live session documents. Defaults to REPRO_API_BASE_URL/REPRO_API_BASE/privacy.apiBase. */
|
|
331
|
+
apiBase?: string;
|
|
325
332
|
/** Configure header capture/masking. Defaults to capturing with sensitive headers masked. */
|
|
326
333
|
captureHeaders?: boolean | HeaderCaptureOptions;
|
|
327
334
|
/** Optional masking rules for request/response payloads and function traces. */
|
|
328
335
|
masking?: ReproMaskingConfig;
|
|
329
336
|
/** Optional privacy policy config for deterministic runtime transformations. */
|
|
330
337
|
privacy?: RuntimePrivacyConfig;
|
|
338
|
+
/** Opt-in production/live capture mode. Staging/manual tagged capture is unchanged when disabled. */
|
|
339
|
+
liveMode?: ReproLiveModeConfig | boolean;
|
|
340
|
+
};
|
|
341
|
+
export type ReproLiveActorArgs = {
|
|
342
|
+
req: Request;
|
|
343
|
+
headers: Request['headers'];
|
|
344
|
+
method: string;
|
|
345
|
+
url: string;
|
|
346
|
+
path: string;
|
|
347
|
+
ip?: string | null;
|
|
348
|
+
appId: string;
|
|
349
|
+
appName?: string;
|
|
350
|
+
serviceName?: string;
|
|
351
|
+
};
|
|
352
|
+
export type ReproLiveActorDecision = ReproLiveActorArgs & {
|
|
353
|
+
actorId: string;
|
|
354
|
+
actorType?: string;
|
|
355
|
+
actorLabels?: Record<string, string>;
|
|
356
|
+
};
|
|
357
|
+
export type ReproLiveModeConfig = {
|
|
358
|
+
enabled?: boolean;
|
|
359
|
+
backendOnly?: boolean;
|
|
360
|
+
/**
|
|
361
|
+
* Stable actor/correlation key for live capture. Return null/undefined/empty
|
|
362
|
+
* to skip capture for that request.
|
|
363
|
+
*/
|
|
364
|
+
defineActorId?: (args: ReproLiveActorArgs) => string | number | null | undefined | Promise<string | number | null | undefined>;
|
|
365
|
+
/**
|
|
366
|
+
* Optional filter for segmenting which actors/requests are captured.
|
|
367
|
+
*/
|
|
368
|
+
shouldCapture?: (args: ReproLiveActorDecision) => boolean | Promise<boolean>;
|
|
369
|
+
actorType?: string;
|
|
370
|
+
actorLabels?: Record<string, string | number | boolean | null | undefined> | ((args: ReproLiveActorDecision) => Record<string, string | number | boolean | null | undefined> | null | undefined | Promise<Record<string, string | number | boolean | null | undefined> | null | undefined>);
|
|
371
|
+
apiBase?: string;
|
|
331
372
|
};
|
|
332
373
|
export type ReproInitConfig = ReproMiddlewareConfig & {
|
|
333
374
|
/**
|
|
@@ -407,6 +448,7 @@ export declare const __reproTestHooks: {
|
|
|
407
448
|
}): void;
|
|
408
449
|
drainSdkBackgroundQueueForTest(): Promise<void>;
|
|
409
450
|
resetSdkBackgroundQueuesForTest(): void;
|
|
451
|
+
setBackendLiveTimingForTest(override: Partial<BackendLiveTimingConfig> | null): void;
|
|
410
452
|
runWithRequestContextForTest<T>(ctx: Partial<Ctx>, fn: () => T): T;
|
|
411
453
|
getNativeMongoSuppressionCountForTest(): number;
|
|
412
454
|
getRuntimePrivacyPolicyForTest(cfg: ReproMiddlewareConfig): NormalizedRuntimePrivacyPolicy | null;
|