@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 CHANGED
@@ -1,377 +1,292 @@
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`, `kafkajs`, `amqplib`, `redis`, and `@sendgrid/mail`
24
21
 
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.
22
+ ## Quickstart
28
23
 
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';
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
- 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
- };
28
+ ```ts
29
+ import { initRepro } from "@reproapp/node-sdk";
46
30
 
47
31
  async function bootstrap() {
48
- // Enable function tracing before loading your modules.
49
- initReproTracing({
50
- disableFunctionTypes: ['constructor'],
51
- logFunctionCalls: false,
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
- // Optional: capture MongoDB queries + document diffs.
55
- mongoose.plugin(reproMongoosePlugin(reproConfig));
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
- 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):
59
+ Express apps follow the same ordering:
110
60
 
111
61
  ```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
- ```
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
- ### 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):
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
- ```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
- }));
81
+ void main();
253
82
  ```
254
83
 
255
- See `docs/tracing.md` for full masking and trace filtering details.
84
+ ## Capture modes: tagged sessions and backend live mode
256
85
 
257
- ### Mongoose plugin
86
+ The Node SDK has two capture modes:
258
87
 
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).
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
- 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
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
- Example: global plugin (all schemas)
99
+ Default tagged capture:
271
100
 
272
101
  ```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
- }));
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
- Example: Nest `MongooseModule` connection factory
112
+ Backend-owned live mode:
286
113
 
287
114
  ```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;
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
- Example: schema-level plugin (single model)
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 { 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
- ```
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
- ### KafkaJS integration
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
- `patchKafkaJs` instruments `kafkajs` producers and consumers.
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
- - 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.
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
- Call it once during bootstrap, before creating producers/consumers:
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
- 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,
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
- Notes:
344
- - `kafkajs` must be installed in the host service.
345
- - The integration is a no-op when `kafkajs` is not present.
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
- ## 3) Run
246
+ ## Runtime Privacy
348
247
 
349
- Run your Nest app as usual after wiring the SDK:
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
- ```bash
352
- npm run start:dev
353
- # or
354
- node dist/main.js
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
- If you use `initReproTracing`, keep it at the top of your bootstrap so modules
358
- load after instrumentation is enabled.
267
+ Use `privacy.captureRaw: true` only in controlled local development.
268
+
269
+ ## Verify
359
270
 
360
- ## 4) Verify It Works
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
- Send a request with Repro headers and confirm the session shows up in Repro.
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 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.
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;