@reproapp/node-sdk 0.0.3 → 0.0.4

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.
Files changed (45) hide show
  1. package/README.md +36 -2
  2. package/dist/index.d.ts +140 -15
  3. package/dist/index.js +4927 -927
  4. package/dist/ingest/client.d.ts +10 -0
  5. package/dist/ingest/client.js +158 -0
  6. package/dist/ingest/mapper.d.ts +2 -0
  7. package/dist/ingest/mapper.js +92 -0
  8. package/dist/ingest/types.d.ts +40 -0
  9. package/dist/ingest/types.js +2 -0
  10. package/dist/ingest/worker.js +19 -0
  11. package/dist/integrations/sendgrid.d.ts +2 -4
  12. package/dist/integrations/sendgrid.js +4 -14
  13. package/dist/privacy-fallback.d.ts +1 -0
  14. package/dist/privacy-fallback.js +27 -0
  15. package/dist/privacy-redaction.d.ts +3 -0
  16. package/dist/privacy-redaction.js +38 -0
  17. package/dist/privacy.d.ts +108 -0
  18. package/dist/privacy.js +2868 -0
  19. package/dist/trace-materializer-worker.d.ts +1 -0
  20. package/dist/trace-materializer-worker.js +33 -0
  21. package/docs/tracing.md +1 -0
  22. package/package.json +8 -2
  23. package/src/index.ts +5583 -954
  24. package/src/ingest/client.ts +194 -0
  25. package/src/ingest/mapper.ts +104 -0
  26. package/src/ingest/types.ts +42 -0
  27. package/src/integrations/sendgrid.ts +6 -19
  28. package/src/privacy-fallback.ts +25 -0
  29. package/src/privacy-redaction.ts +37 -0
  30. package/src/privacy.ts +3593 -0
  31. package/src/trace-materializer-worker.ts +39 -0
  32. package/test/circular-capture.test.js +111 -0
  33. package/test/disable-subtree.test.js +154 -0
  34. package/test/integration-unawaited.js +183 -0
  35. package/test/kafka-runtime-privacy-policy.test.js +285 -0
  36. package/test/privacy-runtime-policy.test.js +2043 -0
  37. package/test/promise-map.test.js +72 -0
  38. package/test/unawaited.test.js +163 -0
  39. package/test/wrap-plugin-arrow-args.test.js +80 -0
  40. package/tracer/cjs-hook.js +0 -1
  41. package/tracer/wrap-plugin.js +96 -10
  42. package/dist/redaction.d.ts +0 -44
  43. package/dist/redaction.js +0 -167
  44. package/dist/server.js +0 -26
  45. /package/dist/{server.d.ts → ingest/worker.d.ts} +0 -0
package/README.md CHANGED
@@ -39,6 +39,7 @@ import {
39
39
  const reproConfig = {
40
40
  appId: process.env.REPRO_APP_ID as string,
41
41
  appName: process.env.REPRO_APP_NAME as string,
42
+ serviceName: process.env.REPRO_SERVICE_NAME as string,
42
43
  tenantId: process.env.REPRO_TENANT_ID as string,
43
44
  appSecret: process.env.REPRO_APP_SECRET as string,
44
45
  };
@@ -66,8 +67,9 @@ bootstrap();
66
67
  ```
67
68
 
68
69
  Configuration notes:
69
- - `REPRO_API_BASE` (optional) overrides the backend base URL used to send data.
70
+ - `REPRO_INGEST_BASE` (optional) overrides the log-collector base URL used to send ingest events.
70
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.
71
73
 
72
74
  ### initReproTracing
73
75
 
@@ -155,6 +157,7 @@ include `x-bug-session-id` and `x-bug-action-id` headers to be captured.
155
157
  Options (type -> purpose):
156
158
  - `appId` (string): Repro app id.
157
159
  - `appName` (string, optional): Repro app name (sent as `X-App-Name`).
160
+ - `serviceName` (string, optional): service label persisted with backend events.
158
161
  - `tenantId` (string): Repro tenant id.
159
162
  - `appSecret` (string): Repro app secret.
160
163
  - `captureHeaders` (boolean | HeaderCaptureOptions): enable/disable header capture
@@ -197,6 +200,7 @@ import { reproMiddleware } from 'repro-nest';
197
200
  app.use(reproMiddleware({
198
201
  appId: process.env.REPRO_APP_ID as string,
199
202
  appName: process.env.REPRO_APP_NAME as string,
203
+ serviceName: process.env.REPRO_SERVICE_NAME as string,
200
204
  tenantId: process.env.REPRO_TENANT_ID as string,
201
205
  appSecret: process.env.REPRO_APP_SECRET as string,
202
206
  captureHeaders: {
@@ -259,6 +263,7 @@ document diffs. It emits data only when a Repro session is active (i.e., when
259
263
  Arguments:
260
264
  - `appId` (string): Repro app id
261
265
  - `appName` (string, optional): Repro app name (sent as `X-App-Name`)
266
+ - `serviceName` (string, optional): service label persisted with backend events
262
267
  - `tenantId` (string): Repro tenant id
263
268
  - `appSecret` (string): Repro app secret
264
269
 
@@ -271,6 +276,7 @@ import { reproMongoosePlugin } from 'repro-nest';
271
276
  mongoose.plugin(reproMongoosePlugin({
272
277
  appId: process.env.REPRO_APP_ID as string,
273
278
  appName: process.env.REPRO_APP_NAME as string,
279
+ serviceName: process.env.REPRO_SERVICE_NAME as string,
274
280
  tenantId: process.env.REPRO_TENANT_ID as string,
275
281
  appSecret: process.env.REPRO_APP_SECRET as string,
276
282
  }));
@@ -287,6 +293,7 @@ MongooseModule.forRoot(process.env.MONGO_URL as string, {
287
293
  connection.plugin(reproMongoosePlugin({
288
294
  appId: process.env.REPRO_APP_ID as string,
289
295
  appName: process.env.REPRO_APP_NAME as string,
296
+ serviceName: process.env.REPRO_SERVICE_NAME as string,
290
297
  tenantId: process.env.REPRO_TENANT_ID as string,
291
298
  appSecret: process.env.REPRO_APP_SECRET as string,
292
299
  }));
@@ -305,11 +312,38 @@ const userSchema = new Schema({ email: String });
305
312
  userSchema.plugin(reproMongoosePlugin({
306
313
  appId: process.env.REPRO_APP_ID as string,
307
314
  appName: process.env.REPRO_APP_NAME as string,
315
+ serviceName: process.env.REPRO_SERVICE_NAME as string,
308
316
  tenantId: process.env.REPRO_TENANT_ID as string,
309
317
  appSecret: process.env.REPRO_APP_SECRET as string,
310
318
  }));
311
319
  ```
312
320
 
321
+ ### KafkaJS integration
322
+
323
+ `patchKafkaJs` instruments `kafkajs` producers and consumers.
324
+
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.
328
+
329
+ Call it once during bootstrap, before creating producers/consumers:
330
+
331
+ ```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,
340
+ });
341
+ ```
342
+
343
+ Notes:
344
+ - `kafkajs` must be installed in the host service.
345
+ - The integration is a no-op when `kafkajs` is not present.
346
+
313
347
  ## 3) Run
314
348
 
315
349
  Run your Nest app as usual after wiring the SDK:
@@ -335,7 +369,7 @@ curl \
335
369
  ```
336
370
 
337
371
  Verification tips:
338
- - If the Repro UI does not show the session, confirm `REPRO_API_BASE` and
372
+ - If the Repro UI does not show the session, confirm `REPRO_INGEST_BASE` and
339
373
  credentials are correct.
340
374
  - Temporarily set `logFunctionCalls: true` in `initReproTracing` to see trace
341
375
  enter/exit logs in your console.
package/dist/index.d.ts CHANGED
@@ -25,6 +25,8 @@
25
25
  /// <reference types="mongoose/types/inferrawdoctype" />
26
26
  import type { Request, Response, NextFunction } from 'express';
27
27
  import type { Schema } from 'mongoose';
28
+ import { type IngestClientConfig } from './ingest/client';
29
+ import { type NormalizedRuntimePrivacyPolicy, type RuntimePrivacyConfig } from './privacy';
28
30
  type SpanContext = {
29
31
  traceId: string | null;
30
32
  spanId: string | number | null;
@@ -54,6 +56,68 @@ export type TraceEventForFilter = {
54
56
  depth?: number;
55
57
  library?: string | null;
56
58
  };
59
+ type TraceEventRecord = {
60
+ t: number;
61
+ type: 'enter' | 'exit';
62
+ functionType?: string | null;
63
+ fn?: string;
64
+ file?: string;
65
+ line?: number | null;
66
+ depth?: number;
67
+ spanId?: string | number | null;
68
+ parentSpanId?: string | number | null;
69
+ args?: any;
70
+ returnValue?: any;
71
+ threw?: boolean;
72
+ error?: any;
73
+ unawaited?: boolean;
74
+ argsValueCapture?: TraceValueCaptureRef;
75
+ returnValueCapture?: TraceValueCaptureRef;
76
+ errorValueCapture?: TraceValueCaptureRef;
77
+ };
78
+ type PendingTraceEventRecord = TraceEventRecord & {
79
+ __reproPending?: {
80
+ candidate?: TraceEventForFilter;
81
+ argsRaw?: any;
82
+ returnValueRaw?: any;
83
+ errorRaw?: any;
84
+ };
85
+ __reproSourceFile?: string | null;
86
+ __reproTraceValueEntries?: TraceValueBatchEntry[];
87
+ };
88
+ type TraceValueCaptureRef = {
89
+ status: 'stored' | 'truncated-for-storage' | 'skipped-non-meaningful';
90
+ valueId?: string;
91
+ projectedKind?: string;
92
+ };
93
+ type TraceValueBatchEntry = {
94
+ id: string;
95
+ target: 'db.pk' | 'db.before' | 'db.after' | 'db.query' | 'db.resultMeta' | 'db.error' | 'request.headers' | 'request.body' | 'request.params' | 'request.query' | 'response.body' | 'trace.args' | 'trace.returnValue' | 'trace.error';
96
+ value: any;
97
+ truncatedForStorage?: boolean;
98
+ projectedKind?: string;
99
+ fn?: string;
100
+ file?: string;
101
+ line?: number | null;
102
+ spanId?: string | number | null;
103
+ parentSpanId?: string | number | null;
104
+ batchIndex?: number;
105
+ eventIndex?: number;
106
+ };
107
+ type TraceMaterializationConfig = {
108
+ appId: string;
109
+ appSecret?: string;
110
+ appName?: string;
111
+ privacy?: RuntimePrivacyConfig;
112
+ };
113
+ type TraceMaterializationWorkerPayload = {
114
+ events: PendingTraceEventRecord[];
115
+ cfg: TraceMaterializationConfig;
116
+ maskReq: MaskRequestContext;
117
+ masking: NormalizedMaskingConfig | null;
118
+ privacy: NormalizedRuntimePrivacyPolicy | null;
119
+ fullValueCaptureEnabled: boolean;
120
+ };
57
121
  export type HeaderRule = string | RegExp;
58
122
  export type HeaderCaptureOptions = {
59
123
  /** When true, sensitive headers such as Authorization are kept unmasked; default masks them. */
@@ -120,7 +184,7 @@ export type DisableFunctionTraceRule = {
120
184
  };
121
185
  export type DisableFunctionTracePredicate = (event: TraceEventForFilter) => boolean;
122
186
  export type DisableFunctionTraceConfig = DisableFunctionTraceRule | DisableFunctionTracePredicate;
123
- export type ReproMaskTarget = 'request.headers' | 'request.body' | 'request.params' | 'request.query' | 'response.body' | 'trace.args' | 'trace.returnValue' | 'trace.error';
187
+ export type ReproMaskTarget = 'db.pk' | 'db.before' | 'db.after' | 'db.query' | 'db.resultMeta' | 'db.error' | 'request.headers' | 'request.body' | 'request.params' | 'request.query' | 'response.body' | 'trace.args' | 'trace.returnValue' | 'trace.error';
124
188
  export type ReproMaskWhen = DisableFunctionTraceRule & {
125
189
  /** Match HTTP method (e.g. `"GET"`, `/^post$/i`). */
126
190
  method?: TraceRulePattern;
@@ -161,6 +225,11 @@ type TracerInitOpts = {
161
225
  samplingMs?: number;
162
226
  };
163
227
  export type ReproTracingInitOptions = TracerInitOpts & {
228
+ /**
229
+ * When enabled, trace args/return values/errors that lose meaningful data in
230
+ * preview form are also persisted via sidecar refs for on-demand loading.
231
+ */
232
+ fullValueCapture?: boolean;
164
233
  /**
165
234
  * Optional list of rules or predicates that suppress unwanted function
166
235
  * trace events. When omitted, every instrumented function will be
@@ -193,29 +262,85 @@ export type ReproTracingInitOptions = TracerInitOpts & {
193
262
  export declare function initReproTracing(opts?: ReproTracingInitOptions): TracerApi | null;
194
263
  /** Optional helper if users want to check it. */
195
264
  export declare function isReproTracingEnabled(): boolean;
196
- export type ReproMiddlewareConfig = {
197
- appId: string;
198
- appSecret: string;
199
- appName?: string;
265
+ export declare function __materializePendingTraceEventsForWorker(payload: TraceMaterializationWorkerPayload): Promise<PendingTraceEventRecord[]>;
266
+ type NormalizedMaskRule = {
267
+ when?: ReproMaskWhen;
268
+ targets: ReproMaskTarget[];
269
+ paths: string[][];
270
+ keys?: TraceRulePattern;
271
+ replacement: any;
272
+ };
273
+ type NormalizedMaskingConfig = {
274
+ replacement: any;
275
+ rules: NormalizedMaskRule[];
276
+ };
277
+ type MaskRequestContext = {
278
+ method: string;
279
+ path: string;
280
+ key: string;
281
+ };
282
+ declare function shareRuntimePrivacyState(source: ReproMiddlewareConfig, target: ReproMiddlewareConfig): void;
283
+ export type ReproMiddlewareConfig = IngestClientConfig & {
200
284
  /** Configure header capture/masking. Defaults to capturing with sensitive headers masked. */
201
285
  captureHeaders?: boolean | HeaderCaptureOptions;
202
286
  /** Optional masking rules for request/response payloads and function traces. */
203
287
  masking?: ReproMaskingConfig;
288
+ /** Optional privacy policy config for deterministic runtime transformations. */
289
+ privacy?: RuntimePrivacyConfig;
204
290
  };
205
- export declare function reproMiddleware(cfg: ReproMiddlewareConfig): (req: Request, res: Response, next: NextFunction) => void;
206
- export declare function reproMongoosePlugin(cfg: {
207
- appId: string;
208
- appSecret: string;
209
- appName?: string;
210
- }): (schema: Schema) => void;
211
- export type SendgridPatchConfig = {
212
- appId: string;
213
- appSecret: string;
214
- appName?: string;
291
+ export type ReproInitConfig = ReproMiddlewareConfig & {
292
+ /**
293
+ * Tracing configuration for function/http instrumentation.
294
+ * Pass `false` to skip tracer initialization.
295
+ */
296
+ tracing?: ReproTracingInitOptions | false;
297
+ /**
298
+ * Optional context resolver used by transport integrations when ALS context
299
+ * is not present.
300
+ */
301
+ resolveContext?: () => {
302
+ sid?: string;
303
+ aid?: string;
304
+ } | undefined;
305
+ };
306
+ export declare function reproMiddleware(cfg: ReproMiddlewareConfig): (req: Request, res: Response, next: NextFunction) => Promise<void>;
307
+ export declare function reproMongoosePlugin(cfg: ReproMiddlewareConfig): (schema: Schema) => void;
308
+ type KafkaContextResolver = () => {
309
+ sid?: string;
310
+ aid?: string;
311
+ } | undefined;
312
+ export type KafkaJsPatchConfig = ReproMiddlewareConfig & {
313
+ resolveContext?: KafkaContextResolver;
314
+ };
315
+ declare function recordKafkaTraceEventAsync(raw: any, sink: TraceEventRecord[], cfg: KafkaJsPatchConfig, maskReq: MaskRequestContext): Promise<void>;
316
+ declare function wrapKafkaEachMessageHandler(eachMessage: any, cfg: KafkaJsPatchConfig, consumer: any): (this: any, payload: any) => Promise<any>;
317
+ export declare function patchKafkaJs(cfg: KafkaJsPatchConfig): void;
318
+ export type RedisPatchConfig = ReproMiddlewareConfig & {
319
+ resolveContext?: () => {
320
+ sid?: string;
321
+ aid?: string;
322
+ } | undefined;
323
+ };
324
+ export declare function patchRedis(cfg: RedisPatchConfig): void;
325
+ export type SendgridPatchConfig = ReproMiddlewareConfig & {
215
326
  resolveContext?: () => {
216
327
  sid?: string;
217
328
  aid?: string;
218
329
  } | undefined;
219
330
  };
220
331
  export declare function patchSendgridMail(cfg: SendgridPatchConfig): void;
332
+ /**
333
+ * Datadog-style one-time init.
334
+ * Enables tracing, auto-patches integrations, auto-registers mongoose plugin,
335
+ * and auto-attaches middleware to Nest/Express applications.
336
+ */
337
+ export declare function initRepro(cfg: ReproInitConfig): Promise<void>;
338
+ /** @internal Test hooks for runtime privacy policy wiring. */
339
+ export declare const __reproTestHooks: {
340
+ shareRuntimePrivacyStateForTest: typeof shareRuntimePrivacyState;
341
+ getRuntimePrivacyPolicyForTest(cfg: ReproMiddlewareConfig): NormalizedRuntimePrivacyPolicy | null;
342
+ setRuntimePrivacyPolicyForTest(cfg: ReproMiddlewareConfig, policy: NormalizedRuntimePrivacyPolicy | null): void;
343
+ recordKafkaTraceEventAsyncForTest: typeof recordKafkaTraceEventAsync;
344
+ wrapKafkaEachMessageHandlerForTest: typeof wrapKafkaEachMessageHandler;
345
+ };
221
346
  export {};