@reproapp/node-sdk 0.0.7 → 0.0.9

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/dist/index.d.ts CHANGED
@@ -283,6 +283,8 @@ type InlinePrivacyMaterializationResult = {
283
283
  };
284
284
  };
285
285
  declare function materializeInlinePrivacyValueAsync(target: RuntimePrivacySurface, value: any, cfg: ReproMiddlewareConfig, req: MaskRequestContext, trace: TraceEventForFilter | null, masking: NormalizedMaskingConfig | null, privacy: NormalizedRuntimePrivacyPolicy | null, db?: RuntimePrivacyDbContext | null): Promise<InlinePrivacyMaterializationResult>;
286
+ declare function estimateTraceBatchSerializedBytes(batch: TraceEventRecord[], requestRid: string, actionId: string | null | undefined, batchIndex: number): number;
287
+ declare function chunkTraceEventsForTransport(events: TraceEventRecord[], requestRid: string, actionId: string | null | undefined): TraceEventRecord[][];
286
288
  export type ReproMiddlewareConfig = IngestClientConfig & {
287
289
  /** Configure header capture/masking. Defaults to capturing with sensitive headers masked. */
288
290
  captureHeaders?: boolean | HeaderCaptureOptions;
@@ -348,5 +350,7 @@ export declare const __reproTestHooks: {
348
350
  materializeInlinePrivacyValueAsyncForTest: typeof materializeInlinePrivacyValueAsync;
349
351
  patchKafkaProducerInstanceForTest: typeof patchKafkaProducerInstance;
350
352
  wrapKafkaEachMessageHandlerForTest: typeof wrapKafkaEachMessageHandler;
353
+ chunkTraceEventsForTransportForTest: typeof chunkTraceEventsForTransport;
354
+ estimateTraceBatchSerializedBytesForTest: typeof estimateTraceBatchSerializedBytes;
351
355
  };
352
356
  export {};
package/dist/index.js CHANGED
@@ -1657,6 +1657,12 @@ const TRACE_INLINE_VALUE_MAX_SERIALIZED_CHARS = (() => {
1657
1657
  })();
1658
1658
  const TRACE_VALUE_SIZE_EXCEEDED = Symbol('trace-value-size-exceeded');
1659
1659
  const TRACE_BATCH_SIZE = 100;
1660
+ const TRACE_BATCH_MAX_SERIALIZED_BYTES = (() => {
1661
+ const env = Number(process.env.REPRO_TRACE_BATCH_MAX_SERIALIZED_BYTES);
1662
+ if (Number.isFinite(env) && env > 0)
1663
+ return Math.trunc(env);
1664
+ return 512 * 1024;
1665
+ })();
1660
1666
  const TRACE_FLUSH_DELAY_MS = 20;
1661
1667
  // Choose how to order trace events in payloads.
1662
1668
  // - "chronological" (default): preserve event arrival order (no reshuffle).
@@ -1929,6 +1935,50 @@ function sanitizeTraceValue(value, depth = 0, seen = new WeakMap(), options = {}
1929
1935
  const mongoId = coerceMongoId(value);
1930
1936
  if (mongoId !== null)
1931
1937
  return mongoId;
1938
+ if (isHttpRequestLike(value)) {
1939
+ const projected = {
1940
+ __kind: 'http-request',
1941
+ };
1942
+ if (typeof value.method === 'string') {
1943
+ projected.method = value.method;
1944
+ }
1945
+ const url = typeof value.originalUrl === 'string'
1946
+ ? value.originalUrl
1947
+ : typeof value.url === 'string'
1948
+ ? value.url
1949
+ : undefined;
1950
+ if (url) {
1951
+ projected.url = url;
1952
+ }
1953
+ const headers = sanitizeHeaders(value.headers, true);
1954
+ if (headers !== undefined) {
1955
+ projected.headers = sanitizeTraceValue(headers, depth + 1, seen, options, childCapturePath(valuePath, 'headers'));
1956
+ }
1957
+ if (value.params !== undefined) {
1958
+ projected.params = sanitizeTraceValue(value.params, depth + 1, seen, options, childCapturePath(valuePath, 'params'));
1959
+ }
1960
+ if (value.query !== undefined) {
1961
+ projected.query = sanitizeTraceValue(value.query, depth + 1, seen, options, childCapturePath(valuePath, 'query'));
1962
+ }
1963
+ if (value.body !== undefined) {
1964
+ projected.body = sanitizeTraceValue(value.body, depth + 1, seen, options, childCapturePath(valuePath, 'body'));
1965
+ }
1966
+ return projected;
1967
+ }
1968
+ if (isHttpResponseLike(value)) {
1969
+ const projected = {
1970
+ __kind: 'http-response',
1971
+ statusCode: Number(value.statusCode) || 0,
1972
+ };
1973
+ const rawHeaders = typeof value.getHeaders === 'function'
1974
+ ? value.getHeaders()
1975
+ : value._headers;
1976
+ const headers = sanitizeHeaders(rawHeaders, true);
1977
+ if (headers !== undefined) {
1978
+ projected.headers = sanitizeTraceValue(headers, depth + 1, seen, options, childCapturePath(valuePath, 'headers'));
1979
+ }
1980
+ return projected;
1981
+ }
1932
1982
  if (isMongooseQueryLike(value)) {
1933
1983
  const captured = value.__repro_result;
1934
1984
  if (captured !== undefined) {
@@ -3482,6 +3532,51 @@ function collectBatchTraceValueEntries(batch, batchIndex) {
3482
3532
  });
3483
3533
  return collected;
3484
3534
  }
3535
+ function serializedByteLength(value) {
3536
+ try {
3537
+ return Buffer.byteLength(JSON.stringify(value), 'utf8');
3538
+ }
3539
+ catch {
3540
+ return Number.MAX_SAFE_INTEGER;
3541
+ }
3542
+ }
3543
+ function estimateTraceBatchSerializedBytes(batch, requestRid, actionId, batchIndex) {
3544
+ const traceValues = collectBatchTraceValueEntries(batch, batchIndex);
3545
+ return serializedByteLength({
3546
+ actionId: actionId ?? null,
3547
+ trace: batch,
3548
+ traceValues: traceValues.length ? traceValues : undefined,
3549
+ traceBatch: {
3550
+ rid: requestRid,
3551
+ index: batchIndex,
3552
+ total: 0,
3553
+ },
3554
+ t: 0,
3555
+ });
3556
+ }
3557
+ function chunkTraceEventsForTransport(events, requestRid, actionId) {
3558
+ if (!Array.isArray(events) || events.length === 0)
3559
+ return [];
3560
+ const countBatches = chunkArray(events, TRACE_BATCH_SIZE);
3561
+ const sizedBatches = [];
3562
+ let current = [];
3563
+ for (const event of countBatches.flat()) {
3564
+ const candidate = current.concat(event);
3565
+ const batchIndex = sizedBatches.length;
3566
+ const estimatedBytes = estimateTraceBatchSerializedBytes(candidate, requestRid, actionId, batchIndex);
3567
+ if (current.length > 0 &&
3568
+ estimatedBytes > TRACE_BATCH_MAX_SERIALIZED_BYTES) {
3569
+ sizedBatches.push(current);
3570
+ current = [event];
3571
+ continue;
3572
+ }
3573
+ current = candidate;
3574
+ }
3575
+ if (current.length > 0) {
3576
+ sizedBatches.push(current);
3577
+ }
3578
+ return sizedBatches;
3579
+ }
3485
3580
  function createCapturedValueEntry(params) {
3486
3581
  if (!__FULL_VALUE_CAPTURE_ENABLED)
3487
3582
  return undefined;
@@ -4065,7 +4160,7 @@ function reproMiddleware(cfg) {
4065
4160
  const orderedEvents = TRACE_ORDER_MODE === 'tree'
4066
4161
  ? reorderTraceEvents(baseEvents)
4067
4162
  : sortTraceEventsChronologically(baseEvents);
4068
- const traceBatches = chunkArray(orderedEvents, TRACE_BATCH_SIZE);
4163
+ const traceBatches = chunkTraceEventsForTransport(orderedEvents, rid, aid);
4069
4164
  if (traceBatches.length) {
4070
4165
  for (let i = 0; i < traceBatches.length; i++) {
4071
4166
  const batch = traceBatches[i];
@@ -5268,7 +5363,7 @@ function buildKafkaTraceEntries(actionId, requestRid, events) {
5268
5363
  const orderedEvents = TRACE_ORDER_MODE === 'tree'
5269
5364
  ? reorderTraceEvents(baseEvents)
5270
5365
  : sortTraceEventsChronologically(baseEvents);
5271
- const batches = chunkArray(orderedEvents, TRACE_BATCH_SIZE);
5366
+ const batches = chunkTraceEventsForTransport(orderedEvents, requestRid, actionId);
5272
5367
  return batches.map((batch, index) => ({
5273
5368
  actionId: actionId ?? null,
5274
5369
  trace: batch,
@@ -6582,4 +6677,6 @@ exports.__reproTestHooks = {
6582
6677
  materializeInlinePrivacyValueAsyncForTest: materializeInlinePrivacyValueAsync,
6583
6678
  patchKafkaProducerInstanceForTest: patchKafkaProducerInstance,
6584
6679
  wrapKafkaEachMessageHandlerForTest: wrapKafkaEachMessageHandler,
6680
+ chunkTraceEventsForTransportForTest: chunkTraceEventsForTransport,
6681
+ estimateTraceBatchSerializedBytesForTest: estimateTraceBatchSerializedBytes,
6585
6682
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reproapp/node-sdk",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Repro Nest SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,7 +12,7 @@
12
12
  "build": "tsc -p tsconfig.json",
13
13
  "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
14
14
  "prepublishOnly": "npm run build",
15
- "test": "npm run build && node test/unawaited.test.js && node test/integration-unawaited.js && node test/request-flush-timing.test.js && node -r ./tracer/register test/promise-map.test.js && node test/disable-subtree.test.js && node test/circular-capture.test.js && node test/wrap-plugin-arrow-args.test.js && node test/privacy-runtime-policy.test.js && node test/runtime-privacy-materialization.test.js && node test/kafka-runtime-privacy-policy.test.js"
15
+ "test": "npm run build && node test/unawaited.test.js && node test/integration-unawaited.js && node test/request-flush-timing.test.js && node test/express-trace-http-args.test.js && node test/trace-batch-size.test.js && node -r ./tracer/register test/promise-map.test.js && node test/disable-subtree.test.js && node test/circular-capture.test.js && node test/wrap-plugin-arrow-args.test.js && node test/privacy-runtime-policy.test.js && node test/runtime-privacy-materialization.test.js && node test/kafka-runtime-privacy-policy.test.js"
16
16
  },
17
17
  "peerDependencies": {
18
18
  "express": "^5.1.0",
package/src/index.ts CHANGED
@@ -2028,6 +2028,11 @@ const TRACE_INLINE_VALUE_MAX_SERIALIZED_CHARS = (() => {
2028
2028
  })();
2029
2029
  const TRACE_VALUE_SIZE_EXCEEDED = Symbol('trace-value-size-exceeded');
2030
2030
  const TRACE_BATCH_SIZE = 100;
2031
+ const TRACE_BATCH_MAX_SERIALIZED_BYTES = (() => {
2032
+ const env = Number(process.env.REPRO_TRACE_BATCH_MAX_SERIALIZED_BYTES);
2033
+ if (Number.isFinite(env) && env > 0) return Math.trunc(env);
2034
+ return 512 * 1024;
2035
+ })();
2031
2036
  const TRACE_FLUSH_DELAY_MS = 20;
2032
2037
  // Choose how to order trace events in payloads.
2033
2038
  // - "chronological" (default): preserve event arrival order (no reshuffle).
@@ -2318,6 +2323,55 @@ function sanitizeTraceValue(
2318
2323
  const mongoId = coerceMongoId(value);
2319
2324
  if (mongoId !== null) return mongoId;
2320
2325
 
2326
+ if (isHttpRequestLike(value)) {
2327
+ const projected: Record<string, any> = {
2328
+ __kind: 'http-request',
2329
+ };
2330
+ if (typeof (value as any).method === 'string') {
2331
+ projected.method = (value as any).method;
2332
+ }
2333
+ const url =
2334
+ typeof (value as any).originalUrl === 'string'
2335
+ ? (value as any).originalUrl
2336
+ : typeof (value as any).url === 'string'
2337
+ ? (value as any).url
2338
+ : undefined;
2339
+ if (url) {
2340
+ projected.url = url;
2341
+ }
2342
+
2343
+ const headers = sanitizeHeaders((value as any).headers, true);
2344
+ if (headers !== undefined) {
2345
+ projected.headers = sanitizeTraceValue(headers, depth + 1, seen, options, childCapturePath(valuePath, 'headers'));
2346
+ }
2347
+ if ((value as any).params !== undefined) {
2348
+ projected.params = sanitizeTraceValue((value as any).params, depth + 1, seen, options, childCapturePath(valuePath, 'params'));
2349
+ }
2350
+ if ((value as any).query !== undefined) {
2351
+ projected.query = sanitizeTraceValue((value as any).query, depth + 1, seen, options, childCapturePath(valuePath, 'query'));
2352
+ }
2353
+ if ((value as any).body !== undefined) {
2354
+ projected.body = sanitizeTraceValue((value as any).body, depth + 1, seen, options, childCapturePath(valuePath, 'body'));
2355
+ }
2356
+ return projected;
2357
+ }
2358
+
2359
+ if (isHttpResponseLike(value)) {
2360
+ const projected: Record<string, any> = {
2361
+ __kind: 'http-response',
2362
+ statusCode: Number((value as any).statusCode) || 0,
2363
+ };
2364
+ const rawHeaders =
2365
+ typeof (value as any).getHeaders === 'function'
2366
+ ? (value as any).getHeaders()
2367
+ : (value as any)._headers;
2368
+ const headers = sanitizeHeaders(rawHeaders, true);
2369
+ if (headers !== undefined) {
2370
+ projected.headers = sanitizeTraceValue(headers, depth + 1, seen, options, childCapturePath(valuePath, 'headers'));
2371
+ }
2372
+ return projected;
2373
+ }
2374
+
2321
2375
  if (isMongooseQueryLike(value)) {
2322
2376
  const captured = (value as any).__repro_result;
2323
2377
  if (captured !== undefined) {
@@ -4166,6 +4220,74 @@ function collectBatchTraceValueEntries(batch: TraceEventRecord[], batchIndex: nu
4166
4220
  return collected;
4167
4221
  }
4168
4222
 
4223
+ function serializedByteLength(value: any): number {
4224
+ try {
4225
+ return Buffer.byteLength(JSON.stringify(value), 'utf8');
4226
+ } catch {
4227
+ return Number.MAX_SAFE_INTEGER;
4228
+ }
4229
+ }
4230
+
4231
+ function estimateTraceBatchSerializedBytes(
4232
+ batch: TraceEventRecord[],
4233
+ requestRid: string,
4234
+ actionId: string | null | undefined,
4235
+ batchIndex: number,
4236
+ ): number {
4237
+ const traceValues = collectBatchTraceValueEntries(batch, batchIndex);
4238
+ return serializedByteLength({
4239
+ actionId: actionId ?? null,
4240
+ trace: batch,
4241
+ traceValues: traceValues.length ? traceValues : undefined,
4242
+ traceBatch: {
4243
+ rid: requestRid,
4244
+ index: batchIndex,
4245
+ total: 0,
4246
+ },
4247
+ t: 0,
4248
+ });
4249
+ }
4250
+
4251
+ function chunkTraceEventsForTransport(
4252
+ events: TraceEventRecord[],
4253
+ requestRid: string,
4254
+ actionId: string | null | undefined,
4255
+ ): TraceEventRecord[][] {
4256
+ if (!Array.isArray(events) || events.length === 0) return [];
4257
+
4258
+ const countBatches = chunkArray(events, TRACE_BATCH_SIZE);
4259
+ const sizedBatches: TraceEventRecord[][] = [];
4260
+ let current: TraceEventRecord[] = [];
4261
+
4262
+ for (const event of countBatches.flat()) {
4263
+ const candidate = current.concat(event);
4264
+ const batchIndex = sizedBatches.length;
4265
+ const estimatedBytes = estimateTraceBatchSerializedBytes(
4266
+ candidate,
4267
+ requestRid,
4268
+ actionId,
4269
+ batchIndex,
4270
+ );
4271
+
4272
+ if (
4273
+ current.length > 0 &&
4274
+ estimatedBytes > TRACE_BATCH_MAX_SERIALIZED_BYTES
4275
+ ) {
4276
+ sizedBatches.push(current);
4277
+ current = [event];
4278
+ continue;
4279
+ }
4280
+
4281
+ current = candidate;
4282
+ }
4283
+
4284
+ if (current.length > 0) {
4285
+ sizedBatches.push(current);
4286
+ }
4287
+
4288
+ return sizedBatches;
4289
+ }
4290
+
4169
4291
  function createCapturedValueEntry(
4170
4292
  params: {
4171
4293
  target:
@@ -4904,7 +5026,11 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
4904
5026
  const orderedEvents = TRACE_ORDER_MODE === 'tree'
4905
5027
  ? reorderTraceEvents(baseEvents)
4906
5028
  : sortTraceEventsChronologically(baseEvents);
4907
- const traceBatches = chunkArray(orderedEvents, TRACE_BATCH_SIZE);
5029
+ const traceBatches = chunkTraceEventsForTransport(
5030
+ orderedEvents,
5031
+ rid,
5032
+ aid,
5033
+ );
4908
5034
 
4909
5035
  if (traceBatches.length) {
4910
5036
  for (let i = 0; i < traceBatches.length; i++) {
@@ -6204,7 +6330,11 @@ function buildKafkaTraceEntries(
6204
6330
  TRACE_ORDER_MODE === 'tree'
6205
6331
  ? reorderTraceEvents(baseEvents)
6206
6332
  : sortTraceEventsChronologically(baseEvents);
6207
- const batches = chunkArray(orderedEvents, TRACE_BATCH_SIZE);
6333
+ const batches = chunkTraceEventsForTransport(
6334
+ orderedEvents,
6335
+ requestRid,
6336
+ actionId,
6337
+ );
6208
6338
  return batches.map((batch, index) => ({
6209
6339
  actionId: actionId ?? null,
6210
6340
  trace: batch,
@@ -7569,4 +7699,6 @@ export const __reproTestHooks = {
7569
7699
  materializeInlinePrivacyValueAsyncForTest: materializeInlinePrivacyValueAsync,
7570
7700
  patchKafkaProducerInstanceForTest: patchKafkaProducerInstance,
7571
7701
  wrapKafkaEachMessageHandlerForTest: wrapKafkaEachMessageHandler,
7702
+ chunkTraceEventsForTransportForTest: chunkTraceEventsForTransport,
7703
+ estimateTraceBatchSerializedBytesForTest: estimateTraceBatchSerializedBytes,
7572
7704
  };
@@ -0,0 +1,126 @@
1
+ const assert = require('assert');
2
+ const http = require('http');
3
+
4
+ const originalFetch = global.fetch;
5
+ const capturedBodies = [];
6
+
7
+ global.fetch = async (url, init = {}) => {
8
+ const target = String(url || '');
9
+ if (!target.includes('/v1/ingest/events')) {
10
+ throw new Error(`unexpected fetch target: ${target}`);
11
+ }
12
+ capturedBodies.push({
13
+ at: Date.now(),
14
+ url: target,
15
+ body: JSON.parse(String(init.body || '{}')),
16
+ });
17
+ return {
18
+ ok: true,
19
+ status: 200,
20
+ json: async () => ({ ok: true }),
21
+ text: async () => '{"ok":true}',
22
+ };
23
+ };
24
+
25
+ const { initRepro } = require('../dist');
26
+ const { flushIngestQueue } = require('../dist/ingest/client');
27
+
28
+ function findEvents(eventType) {
29
+ return capturedBodies.flatMap((entry) => Array.isArray(entry.body?.events) ? entry.body.events : [])
30
+ .filter((event) => event?.event_type === eventType);
31
+ }
32
+
33
+ function sendGet(url) {
34
+ return new Promise((resolve, reject) => {
35
+ const request = http.get(url, (response) => {
36
+ let text = '';
37
+ response.setEncoding('utf8');
38
+ response.on('data', (chunk) => {
39
+ text += chunk;
40
+ });
41
+ response.on('end', () => resolve({
42
+ statusCode: response.statusCode,
43
+ body: text,
44
+ }));
45
+ });
46
+ request.on('error', reject);
47
+ });
48
+ }
49
+
50
+ async function main() {
51
+ await initRepro({
52
+ tenantId: 'TENANT_express_trace_http_args',
53
+ appId: 'APP_express_trace_http_args',
54
+ appSecret: 'secret',
55
+ appName: 'express-trace-http-args',
56
+ serviceName: 'express-trace-http-args',
57
+ ingestBase: 'http://127.0.0.1:65535',
58
+ tracing: {
59
+ disableFunctionTypes: ['constructor'],
60
+ logFunctionCalls: false,
61
+ },
62
+ });
63
+
64
+ const { startServer } = require('./fixtures/express-trace-http-args-server');
65
+ const server = await startServer({
66
+ tenantId: 'TENANT_express_trace_http_args',
67
+ appId: 'APP_express_trace_http_args',
68
+ appSecret: 'secret',
69
+ appName: 'express-trace-http-args',
70
+ serviceName: 'express-trace-http-args',
71
+ ingestBase: 'http://127.0.0.1:65535',
72
+ });
73
+
74
+ try {
75
+ const port = server.address().port;
76
+ const requestUrl = `http://127.0.0.1:${port}/ping?name=Avery&__repro_sid=S_express_trace_http_args&__repro_aid=A_express_trace_http_args&__repro_start=${Date.now()}`;
77
+ const response = await sendGet(requestUrl);
78
+ assert.equal(response.statusCode, 200);
79
+
80
+ await new Promise((resolve) => setTimeout(resolve, 2500));
81
+ await flushIngestQueue();
82
+
83
+ const requestEvents = findEvents('backend_request');
84
+ const traceEvents = findEvents('trace_batch');
85
+
86
+ assert.equal(requestEvents.length, 1, JSON.stringify(capturedBodies));
87
+ assert.equal(traceEvents.length, 1, JSON.stringify(capturedBodies));
88
+
89
+ const batch = traceEvents[0]?.payload?.trace;
90
+ assert(Array.isArray(batch) && batch.length > 0, JSON.stringify(traceEvents[0]));
91
+
92
+ const handlePingEnter = batch.find((event) => event?.type === 'enter' && event?.fn === 'handlePing');
93
+ assert(handlePingEnter, JSON.stringify(batch));
94
+ assert(Array.isArray(handlePingEnter.args), JSON.stringify(handlePingEnter));
95
+ assert.equal(handlePingEnter.args[0]?.__kind, 'http-request', JSON.stringify(handlePingEnter.args[0]));
96
+ assert.equal(handlePingEnter.args[1]?.__kind, 'http-response', JSON.stringify(handlePingEnter.args[1]));
97
+ assert.equal(handlePingEnter.args[0]?.url, '/ping?name=Avery');
98
+ assert.equal(handlePingEnter.args[0]?.query?.name, 'Avery');
99
+
100
+ const serializedBatch = JSON.stringify(traceEvents[0]);
101
+ assert(serializedBatch.length < 250000, `trace batch still too large: ${serializedBatch.length}`);
102
+
103
+ // eslint-disable-next-line no-console
104
+ console.log('express trace http arg projection OK');
105
+ } finally {
106
+ await new Promise((resolve, reject) => {
107
+ server.close((error) => {
108
+ if (!error || error.code === 'ERR_SERVER_NOT_RUNNING') {
109
+ resolve();
110
+ return;
111
+ }
112
+ reject(error);
113
+ });
114
+ });
115
+ }
116
+ }
117
+
118
+ main()
119
+ .catch((error) => {
120
+ // eslint-disable-next-line no-console
121
+ console.error(error);
122
+ process.exitCode = 1;
123
+ })
124
+ .finally(() => {
125
+ global.fetch = originalFetch;
126
+ });
@@ -0,0 +1,29 @@
1
+ async function buildPayload(name) {
2
+ const normalized = normalizeName(name);
3
+ const emphasized = emphasize(normalized);
4
+ return {
5
+ original: name,
6
+ normalized,
7
+ emphasized,
8
+ };
9
+ }
10
+
11
+ function normalizeName(name) {
12
+ return String(name || 'anonymous').trim().toLowerCase();
13
+ }
14
+
15
+ function emphasize(name) {
16
+ return `${name.toUpperCase()}!`;
17
+ }
18
+
19
+ async function handlePing(req, res) {
20
+ const payload = await buildPayload(req.query?.name || 'Avery Debugson');
21
+ res.json({
22
+ ok: true,
23
+ payload,
24
+ });
25
+ }
26
+
27
+ module.exports = {
28
+ handlePing,
29
+ };
@@ -0,0 +1,21 @@
1
+ const express = require('express');
2
+ const { reproMiddleware } = require('../../dist');
3
+ const { handlePing } = require('./express-trace-http-args-controller');
4
+
5
+ async function startServer(cfg) {
6
+ const app = express();
7
+ app.use(reproMiddleware(cfg));
8
+ app.get('/ping', handlePing);
9
+
10
+ const server = await new Promise((resolve, reject) => {
11
+ const instance = app.listen(0);
12
+ instance.once('listening', () => resolve(instance));
13
+ instance.once('error', reject);
14
+ });
15
+
16
+ return server;
17
+ }
18
+
19
+ module.exports = {
20
+ startServer,
21
+ };
@@ -0,0 +1,58 @@
1
+ const assert = require('assert');
2
+ const { __reproTestHooks } = require('../dist');
3
+
4
+ function makeEvent(index, payload) {
5
+ return {
6
+ t: index,
7
+ type: 'enter',
8
+ fn: `fn${index}`,
9
+ file: '/app/src/controllers/subjects/index.ts',
10
+ line: index + 1,
11
+ depth: 1,
12
+ spanId: index + 1,
13
+ parentSpanId: null,
14
+ args: [payload],
15
+ };
16
+ }
17
+
18
+ function main() {
19
+ const payload = {
20
+ __kind: 'http-request',
21
+ method: 'POST',
22
+ url: '/api/v1/subject_visits/697001aaac70cc3d60f21273/subjects/createNewSubject?tenantId=tgtherapeutics',
23
+ headers: {
24
+ x_http_user: 'x'.repeat(12000),
25
+ authorization: '[dropped]',
26
+ },
27
+ body: {
28
+ value: 'y'.repeat(12000),
29
+ },
30
+ };
31
+
32
+ const events = Array.from({ length: 120 }, (_, index) => makeEvent(index, payload));
33
+ const batches = __reproTestHooks.chunkTraceEventsForTransportForTest(
34
+ events,
35
+ 'RID_test_trace_batch_size',
36
+ 'A_test_trace_batch_size',
37
+ );
38
+
39
+ assert(batches.length > 1, `expected more than one batch, got ${batches.length}`);
40
+
41
+ batches.forEach((batch, index) => {
42
+ const size = __reproTestHooks.estimateTraceBatchSerializedBytesForTest(
43
+ batch,
44
+ 'RID_test_trace_batch_size',
45
+ 'A_test_trace_batch_size',
46
+ index,
47
+ );
48
+ assert(
49
+ size <= 512 * 1024,
50
+ `batch ${index} too large: ${size}`,
51
+ );
52
+ });
53
+
54
+ // eslint-disable-next-line no-console
55
+ console.log('trace batch size chunking OK');
56
+ }
57
+
58
+ main();