@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 CHANGED
@@ -1,377 +1,295 @@
1
- # Repro Nest SDK
1
+ # Repro Node SDK
2
2
 
3
- Capture NestJS request/response data, function traces, and Mongoose activity for
4
- Repro sessions. This SDK is designed for Nest apps running on Express, with
5
- optional tracing and masking controls.
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
- ## 1) Install
7
+ The current package is `@reproapp/node-sdk`. New apps should start with
8
+ `initRepro`.
8
9
 
9
- Requirements:
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 repro-nest
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
- ## 2) Configure
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
- At minimum, provide your Repro credentials and wire the middleware. If you want
26
- function tracing, call `initReproTracing` before importing your `AppModule` so
27
- Nest classes are instrumented at load time.
23
+ ## Quickstart
28
24
 
29
- ```ts
30
- import 'reflect-metadata';
31
- import { NestFactory } from '@nestjs/core';
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
- const reproConfig = {
40
- appId: process.env.REPRO_APP_ID as string,
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
- // Enable function tracing before loading your modules.
49
- initReproTracing({
50
- disableFunctionTypes: ['constructor'],
51
- logFunctionCalls: false,
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
- // Optional: capture MongoDB queries + document diffs.
55
- mongoose.plugin(reproMongoosePlugin(reproConfig));
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
- Configuration notes:
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 { initReproTracing } from 'repro-nest';
113
-
114
- initReproTracing({
115
- instrument: true,
116
- include: [/^\/app\/src\//],
117
- exclude: [/node_modules\//, /dist\//],
118
- mode: process.env.TRACE_MODE || 'trace',
119
- samplingMs: 10,
120
- disableFunctionTraces: [
121
- {
122
- fn: 'findAll',
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
- ### reproMiddleware
153
-
154
- Captures request/response payloads for active Repro sessions. Requests must
155
- include `x-bug-session-id` and `x-bug-action-id` headers to be captured.
156
-
157
- Options (type -> purpose):
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
- ```ts
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
- See `docs/tracing.md` for full masking and trace filtering details.
85
+ ## Capture modes: tagged sessions and backend live mode
256
86
 
257
- ### Mongoose plugin
87
+ The Node SDK has two capture modes:
258
88
 
259
- `reproMongoosePlugin` attaches schema middleware to capture query activity and
260
- document diffs. It emits data only when a Repro session is active (i.e., when
261
- `reproMiddleware` has set the session context for the current request).
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
- Arguments:
264
- - `appId` (string): Repro app id
265
- - `appName` (string, optional): Repro app name (sent as `X-App-Name`)
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
- Example: global plugin (all schemas)
100
+ Default tagged capture:
271
101
 
272
102
  ```ts
273
- import mongoose from 'mongoose';
274
- import { reproMongoosePlugin } from 'repro-nest';
275
-
276
- mongoose.plugin(reproMongoosePlugin({
277
- appId: process.env.REPRO_APP_ID as string,
278
- appName: process.env.REPRO_APP_NAME as string,
279
- serviceName: process.env.REPRO_SERVICE_NAME as string,
280
- tenantId: process.env.REPRO_TENANT_ID as string,
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
- Example: Nest `MongooseModule` connection factory
113
+ Backend-owned live mode:
286
114
 
287
115
  ```ts
288
- import { MongooseModule } from '@nestjs/mongoose';
289
- import { reproMongoosePlugin } from 'repro-nest';
290
-
291
- MongooseModule.forRoot(process.env.MONGO_URL as string, {
292
- connectionFactory: (connection) => {
293
- connection.plugin(reproMongoosePlugin({
294
- appId: process.env.REPRO_APP_ID as string,
295
- appName: process.env.REPRO_APP_NAME as string,
296
- serviceName: process.env.REPRO_SERVICE_NAME as string,
297
- tenantId: process.env.REPRO_TENANT_ID as string,
298
- appSecret: process.env.REPRO_APP_SECRET as string,
299
- }));
300
- return connection;
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
- Example: schema-level plugin (single model)
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 { Schema } from 'mongoose';
309
- import { reproMongoosePlugin } from 'repro-nest';
310
-
311
- const userSchema = new Schema({ email: String });
312
- userSchema.plugin(reproMongoosePlugin({
313
- appId: process.env.REPRO_APP_ID as string,
314
- appName: process.env.REPRO_APP_NAME as string,
315
- serviceName: process.env.REPRO_SERVICE_NAME as string,
316
- tenantId: process.env.REPRO_TENANT_ID as string,
317
- appSecret: process.env.REPRO_APP_SECRET as string,
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
- ### KafkaJS integration
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
- `patchKafkaJs` instruments `kafkajs` producers and consumers.
174
+ ## Configuration
324
175
 
325
- - Producer calls (`send`, `sendBatch`) propagate Repro trace/session headers.
326
- - Consumer handlers (`eachMessage`, `eachBatch`) restore context and emit backend
327
- request + trace batches for Kafka processing.
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
- Call it once during bootstrap, before creating producers/consumers:
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
- import { patchKafkaJs } from 'repro-nest';
333
-
334
- patchKafkaJs({
335
- appId: process.env.REPRO_APP_ID as string,
336
- appName: process.env.REPRO_APP_NAME as string,
337
- serviceName: process.env.REPRO_SERVICE_NAME as string,
338
- tenantId: process.env.REPRO_TENANT_ID as string,
339
- appSecret: process.env.REPRO_APP_SECRET as string,
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
- Notes:
344
- - `kafkajs` must be installed in the host service.
345
- - The integration is a no-op when `kafkajs` is not present.
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
- ## 3) Run
249
+ ## Runtime Privacy
348
250
 
349
- Run your Nest app as usual after wiring the SDK:
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
- ```bash
352
- npm run start:dev
353
- # or
354
- node dist/main.js
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
- If you use `initReproTracing`, keep it at the top of your bootstrap so modules
358
- load after instrumentation is enabled.
270
+ Use `privacy.captureRaw: true` only in controlled local development.
271
+
272
+ ## Verify
359
273
 
360
- ## 4) Verify It Works
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
- Send a request with Repro headers and confirm the session shows up in Repro.
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 tips:
372
- - If the Repro UI does not show the session, confirm `REPRO_INGEST_BASE` and
373
- credentials are correct.
374
- - Temporarily set `logFunctionCalls: true` in `initReproTracing` to see trace
375
- enter/exit logs in your console.
376
- - If you use Mongoose, exercise a query/mutation and verify DB entries appear
377
- in the session trace.
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.