@reproapp/node-sdk 0.0.15 → 0.0.17
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.js +44 -28
- package/dist/ingest/client.d.ts +4 -1
- package/dist/ingest/client.js +28 -5
- package/package.json +2 -2
- package/src/index.ts +49 -32
- package/src/ingest/client.ts +31 -4
- package/test/request-manifest-priority.test.js +147 -0
package/dist/index.js
CHANGED
|
@@ -1513,10 +1513,10 @@ function resolveCollectionOrWarn(source, type) {
|
|
|
1513
1513
|
}
|
|
1514
1514
|
return name;
|
|
1515
1515
|
}
|
|
1516
|
-
function post(cfg, sessionId, body) {
|
|
1516
|
+
function post(cfg, sessionId, body, options) {
|
|
1517
1517
|
try {
|
|
1518
1518
|
const entries = Array.isArray(body?.entries) ? body.entries : [];
|
|
1519
|
-
(0, client_1.enqueueIngestEntries)(cfg, sessionId, entries);
|
|
1519
|
+
(0, client_1.enqueueIngestEntries)(cfg, sessionId, entries, options);
|
|
1520
1520
|
}
|
|
1521
1521
|
catch { /* swallow in SDK */ }
|
|
1522
1522
|
}
|
|
@@ -3969,6 +3969,7 @@ function reproMiddleware(cfg) {
|
|
|
3969
3969
|
let hardStopTimer = null;
|
|
3970
3970
|
let flushPayload = null;
|
|
3971
3971
|
let requestCaptureScheduled = false;
|
|
3972
|
+
let requestCaptureManifestScheduled = false;
|
|
3972
3973
|
let sessionDrainWait = null;
|
|
3973
3974
|
const activeSpans = new Set();
|
|
3974
3975
|
let anonymousSpanDepth = 0;
|
|
@@ -4232,6 +4233,19 @@ function reproMiddleware(cfg) {
|
|
|
4232
4233
|
// never break user code
|
|
4233
4234
|
}
|
|
4234
4235
|
};
|
|
4236
|
+
const emitRequestCaptureManifest = (traceBatchTotal) => {
|
|
4237
|
+
if (requestCaptureManifestScheduled)
|
|
4238
|
+
return;
|
|
4239
|
+
requestCaptureManifestScheduled = true;
|
|
4240
|
+
post(cfg, sid, {
|
|
4241
|
+
entries: [{
|
|
4242
|
+
actionId: aid,
|
|
4243
|
+
requestRid: rid,
|
|
4244
|
+
requestCaptureManifest: buildRequestCaptureManifestPayload(captureScope, traceBatchTotal, finishedAtAligned),
|
|
4245
|
+
t: finishedAtAligned ?? alignedNow(),
|
|
4246
|
+
}],
|
|
4247
|
+
}, { priority: 'high' });
|
|
4248
|
+
};
|
|
4235
4249
|
try {
|
|
4236
4250
|
if (__TRACER__?.tracer?.on) {
|
|
4237
4251
|
const getTid = __TRACER__?.getCurrentTraceId;
|
|
@@ -4328,6 +4342,7 @@ function reproMiddleware(cfg) {
|
|
|
4328
4342
|
void emitRequestCaptureAsync();
|
|
4329
4343
|
if (!flushPayload) {
|
|
4330
4344
|
flushPayload = async () => {
|
|
4345
|
+
let traceBatches = [];
|
|
4331
4346
|
try {
|
|
4332
4347
|
const pendingEvents = preparePendingTraceEventsForFlush(events.slice());
|
|
4333
4348
|
const materializedEvents = await materializePendingTraceEventsAsync(pendingEvents, {
|
|
@@ -4340,38 +4355,39 @@ function reproMiddleware(cfg) {
|
|
|
4340
4355
|
const orderedEvents = TRACE_ORDER_MODE === 'tree'
|
|
4341
4356
|
? reorderTraceEvents(baseEvents)
|
|
4342
4357
|
: sortTraceEventsChronologically(baseEvents);
|
|
4343
|
-
|
|
4358
|
+
traceBatches = chunkTraceEventsForTransport(orderedEvents, rid, aid);
|
|
4344
4359
|
if (traceBatches.length) {
|
|
4345
4360
|
captureScope.traceBatchCount = traceBatches.length;
|
|
4346
|
-
for (let i = 0; i < traceBatches.length; i++) {
|
|
4347
|
-
const batch = traceBatches[i];
|
|
4348
|
-
const traceValues = collectBatchTraceValueEntries(batch, i);
|
|
4349
|
-
post(cfg, sid, {
|
|
4350
|
-
entries: [{
|
|
4351
|
-
actionId: aid,
|
|
4352
|
-
trace: batch,
|
|
4353
|
-
traceValues: traceValues.length ? traceValues : undefined,
|
|
4354
|
-
traceBatch: {
|
|
4355
|
-
rid,
|
|
4356
|
-
index: i,
|
|
4357
|
-
total: traceBatches.length,
|
|
4358
|
-
},
|
|
4359
|
-
t: alignedNow(),
|
|
4360
|
-
}],
|
|
4361
|
-
});
|
|
4362
|
-
}
|
|
4363
4361
|
}
|
|
4364
4362
|
else {
|
|
4365
4363
|
captureScope.traceBatchCount = 0;
|
|
4366
4364
|
}
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4365
|
+
}
|
|
4366
|
+
catch {
|
|
4367
|
+
captureScope.traceBatchCount = 0;
|
|
4368
|
+
traceBatches = [];
|
|
4369
|
+
}
|
|
4370
|
+
finally {
|
|
4371
|
+
emitRequestCaptureManifest(traceBatches.length);
|
|
4372
|
+
}
|
|
4373
|
+
try {
|
|
4374
|
+
for (let i = 0; i < traceBatches.length; i++) {
|
|
4375
|
+
const batch = traceBatches[i];
|
|
4376
|
+
const traceValues = collectBatchTraceValueEntries(batch, i);
|
|
4377
|
+
post(cfg, sid, {
|
|
4378
|
+
entries: [{
|
|
4379
|
+
actionId: aid,
|
|
4380
|
+
trace: batch,
|
|
4381
|
+
traceValues: traceValues.length ? traceValues : undefined,
|
|
4382
|
+
traceBatch: {
|
|
4383
|
+
rid,
|
|
4384
|
+
index: i,
|
|
4385
|
+
total: traceBatches.length,
|
|
4386
|
+
},
|
|
4387
|
+
t: alignedNow(),
|
|
4388
|
+
}],
|
|
4389
|
+
});
|
|
4390
|
+
}
|
|
4375
4391
|
}
|
|
4376
4392
|
catch {
|
|
4377
4393
|
// never break user code
|
package/dist/ingest/client.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { IngestClientConfig, LegacyEntry } from './types';
|
|
2
|
+
export type EnqueueIngestOptions = {
|
|
3
|
+
priority?: 'normal' | 'high';
|
|
4
|
+
};
|
|
2
5
|
export declare const configureIngestQueueDrain: (options: {
|
|
3
6
|
shouldDefer?: (() => boolean) | null;
|
|
4
7
|
deferMs?: number;
|
|
5
8
|
}) => void;
|
|
6
9
|
export declare const kickIngestQueueDrain: () => void;
|
|
7
10
|
export declare const postIngestEntries: (config: IngestClientConfig, sessionId: string, entries: LegacyEntry[]) => Promise<void>;
|
|
8
|
-
export declare const enqueueIngestEntries: (config: IngestClientConfig, sessionId: string, entries: LegacyEntry[]) => void;
|
|
11
|
+
export declare const enqueueIngestEntries: (config: IngestClientConfig, sessionId: string, entries: LegacyEntry[], options?: EnqueueIngestOptions) => void;
|
|
9
12
|
export declare const flushIngestQueue: () => Promise<void>;
|
|
10
13
|
export type { IngestClientConfig, LegacyEntry } from './types';
|
package/dist/ingest/client.js
CHANGED
|
@@ -19,7 +19,7 @@ const DEFAULT_INGEST_REQUEST_TIMEOUT_MS = (() => {
|
|
|
19
19
|
if (Number.isFinite(env) && env >= 0) {
|
|
20
20
|
return Math.trunc(env);
|
|
21
21
|
}
|
|
22
|
-
return
|
|
22
|
+
return 120000;
|
|
23
23
|
})();
|
|
24
24
|
const ingestQueue = [];
|
|
25
25
|
let drainTimer = null;
|
|
@@ -171,18 +171,41 @@ const drainIngestQueue = async () => {
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
};
|
|
174
|
-
const enqueueIngestEntries = (config, sessionId, entries) => {
|
|
174
|
+
const enqueueIngestEntries = (config, sessionId, entries, options = {}) => {
|
|
175
175
|
if (!sessionId || !Array.isArray(entries) || entries.length === 0) {
|
|
176
176
|
return;
|
|
177
177
|
}
|
|
178
|
-
|
|
178
|
+
const priority = options.priority === 'high' ? 'high' : 'normal';
|
|
179
|
+
const batch = {
|
|
179
180
|
config: { ...config },
|
|
180
181
|
sessionId,
|
|
181
182
|
entries: entries.slice(),
|
|
182
183
|
attempts: 0,
|
|
183
|
-
|
|
184
|
+
priority,
|
|
185
|
+
};
|
|
186
|
+
if (priority === 'high') {
|
|
187
|
+
const firstNormalIndex = ingestQueue.findIndex((queued) => queued.priority !== 'high');
|
|
188
|
+
if (firstNormalIndex === -1) {
|
|
189
|
+
ingestQueue.push(batch);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
ingestQueue.splice(firstNormalIndex, 0, batch);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
ingestQueue.push(batch);
|
|
197
|
+
}
|
|
184
198
|
if (DEFAULT_QUEUE_MAX_ITEMS > 0 && ingestQueue.length > DEFAULT_QUEUE_MAX_ITEMS) {
|
|
185
|
-
|
|
199
|
+
while (ingestQueue.length > DEFAULT_QUEUE_MAX_ITEMS) {
|
|
200
|
+
let removeIndex = ingestQueue.length - 1;
|
|
201
|
+
for (let index = ingestQueue.length - 1; index >= 0; index -= 1) {
|
|
202
|
+
if (ingestQueue[index]?.priority !== 'high') {
|
|
203
|
+
removeIndex = index;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
ingestQueue.splice(removeIndex, 1);
|
|
208
|
+
}
|
|
186
209
|
}
|
|
187
210
|
scheduleDrain(shouldDeferDrain?.() ? deferDrainMs : 0);
|
|
188
211
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reproapp/node-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
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 test/express-trace-http-args.test.js && node test/trace-batch-size.test.js && node test/sdk-background-priority.test.js && node test/ingest-queue-retention.test.js && node test/ingest-timeout.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/request-manifest-priority.test.js && node test/express-trace-http-args.test.js && node test/trace-batch-size.test.js && node test/sdk-background-priority.test.js && node test/ingest-queue-retention.test.js && node test/ingest-timeout.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
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
configureIngestQueueDrain,
|
|
10
10
|
enqueueIngestEntries,
|
|
11
11
|
kickIngestQueueDrain,
|
|
12
|
+
type EnqueueIngestOptions,
|
|
12
13
|
type IngestClientConfig,
|
|
13
14
|
type LegacyEntry,
|
|
14
15
|
} from './ingest/client';
|
|
@@ -1908,10 +1909,11 @@ function post(
|
|
|
1908
1909
|
cfg: IngestClientConfig,
|
|
1909
1910
|
sessionId: string,
|
|
1910
1911
|
body: any,
|
|
1912
|
+
options?: EnqueueIngestOptions,
|
|
1911
1913
|
) {
|
|
1912
1914
|
try {
|
|
1913
1915
|
const entries = Array.isArray(body?.entries) ? (body.entries as LegacyEntry[]) : [];
|
|
1914
|
-
enqueueIngestEntries(cfg, sessionId, entries);
|
|
1916
|
+
enqueueIngestEntries(cfg, sessionId, entries, options);
|
|
1915
1917
|
} catch { /* swallow in SDK */ }
|
|
1916
1918
|
}
|
|
1917
1919
|
|
|
@@ -4856,6 +4858,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
4856
4858
|
let hardStopTimer: NodeJS.Timeout | null = null;
|
|
4857
4859
|
let flushPayload: null | (() => Promise<void>) = null;
|
|
4858
4860
|
let requestCaptureScheduled = false;
|
|
4861
|
+
let requestCaptureManifestScheduled = false;
|
|
4859
4862
|
let sessionDrainWait: Promise<void> | null = null;
|
|
4860
4863
|
const activeSpans = new Set<string>();
|
|
4861
4864
|
let anonymousSpanDepth = 0;
|
|
@@ -5150,6 +5153,23 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
5150
5153
|
}
|
|
5151
5154
|
};
|
|
5152
5155
|
|
|
5156
|
+
const emitRequestCaptureManifest = (traceBatchTotal: number): void => {
|
|
5157
|
+
if (requestCaptureManifestScheduled) return;
|
|
5158
|
+
requestCaptureManifestScheduled = true;
|
|
5159
|
+
post(cfg, sid, {
|
|
5160
|
+
entries: [{
|
|
5161
|
+
actionId: aid,
|
|
5162
|
+
requestRid: rid,
|
|
5163
|
+
requestCaptureManifest: buildRequestCaptureManifestPayload(
|
|
5164
|
+
captureScope,
|
|
5165
|
+
traceBatchTotal,
|
|
5166
|
+
finishedAtAligned,
|
|
5167
|
+
),
|
|
5168
|
+
t: finishedAtAligned ?? alignedNow(),
|
|
5169
|
+
}],
|
|
5170
|
+
}, { priority: 'high' });
|
|
5171
|
+
};
|
|
5172
|
+
|
|
5153
5173
|
try {
|
|
5154
5174
|
if (__TRACER__?.tracer?.on) {
|
|
5155
5175
|
const getTid = __TRACER__?.getCurrentTraceId;
|
|
@@ -5250,6 +5270,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
5250
5270
|
|
|
5251
5271
|
if (!flushPayload) {
|
|
5252
5272
|
flushPayload = async () => {
|
|
5273
|
+
let traceBatches: TraceEventRecord[][] = [];
|
|
5253
5274
|
try {
|
|
5254
5275
|
const pendingEvents = preparePendingTraceEventsForFlush(events.slice());
|
|
5255
5276
|
const materializedEvents = await materializePendingTraceEventsAsync(pendingEvents, {
|
|
@@ -5262,7 +5283,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
5262
5283
|
const orderedEvents = TRACE_ORDER_MODE === 'tree'
|
|
5263
5284
|
? reorderTraceEvents(baseEvents)
|
|
5264
5285
|
: sortTraceEventsChronologically(baseEvents);
|
|
5265
|
-
|
|
5286
|
+
traceBatches = chunkTraceEventsForTransport(
|
|
5266
5287
|
orderedEvents,
|
|
5267
5288
|
rid,
|
|
5268
5289
|
aid,
|
|
@@ -5270,40 +5291,36 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
5270
5291
|
|
|
5271
5292
|
if (traceBatches.length) {
|
|
5272
5293
|
captureScope.traceBatchCount = traceBatches.length;
|
|
5273
|
-
for (let i = 0; i < traceBatches.length; i++) {
|
|
5274
|
-
const batch = traceBatches[i];
|
|
5275
|
-
const traceValues = collectBatchTraceValueEntries(batch, i);
|
|
5276
|
-
|
|
5277
|
-
post(cfg, sid, {
|
|
5278
|
-
entries: [{
|
|
5279
|
-
actionId: aid,
|
|
5280
|
-
trace: batch,
|
|
5281
|
-
traceValues: traceValues.length ? traceValues : undefined,
|
|
5282
|
-
traceBatch: {
|
|
5283
|
-
rid,
|
|
5284
|
-
index: i,
|
|
5285
|
-
total: traceBatches.length,
|
|
5286
|
-
},
|
|
5287
|
-
t: alignedNow(),
|
|
5288
|
-
}],
|
|
5289
|
-
});
|
|
5290
|
-
}
|
|
5291
5294
|
} else {
|
|
5292
5295
|
captureScope.traceBatchCount = 0;
|
|
5293
5296
|
}
|
|
5294
5297
|
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5298
|
+
} catch {
|
|
5299
|
+
captureScope.traceBatchCount = 0;
|
|
5300
|
+
traceBatches = [];
|
|
5301
|
+
} finally {
|
|
5302
|
+
emitRequestCaptureManifest(traceBatches.length);
|
|
5303
|
+
}
|
|
5304
|
+
|
|
5305
|
+
try {
|
|
5306
|
+
for (let i = 0; i < traceBatches.length; i++) {
|
|
5307
|
+
const batch = traceBatches[i];
|
|
5308
|
+
const traceValues = collectBatchTraceValueEntries(batch, i);
|
|
5309
|
+
|
|
5310
|
+
post(cfg, sid, {
|
|
5311
|
+
entries: [{
|
|
5312
|
+
actionId: aid,
|
|
5313
|
+
trace: batch,
|
|
5314
|
+
traceValues: traceValues.length ? traceValues : undefined,
|
|
5315
|
+
traceBatch: {
|
|
5316
|
+
rid,
|
|
5317
|
+
index: i,
|
|
5318
|
+
total: traceBatches.length,
|
|
5319
|
+
},
|
|
5320
|
+
t: alignedNow(),
|
|
5321
|
+
}],
|
|
5322
|
+
});
|
|
5323
|
+
}
|
|
5307
5324
|
} catch {
|
|
5308
5325
|
// never break user code
|
|
5309
5326
|
}
|
package/src/ingest/client.ts
CHANGED
|
@@ -18,7 +18,7 @@ const DEFAULT_INGEST_REQUEST_TIMEOUT_MS = (() => {
|
|
|
18
18
|
if (Number.isFinite(env) && env >= 0) {
|
|
19
19
|
return Math.trunc(env);
|
|
20
20
|
}
|
|
21
|
-
return
|
|
21
|
+
return 120000;
|
|
22
22
|
})();
|
|
23
23
|
|
|
24
24
|
type QueuedIngestBatch = {
|
|
@@ -26,6 +26,11 @@ type QueuedIngestBatch = {
|
|
|
26
26
|
sessionId: string;
|
|
27
27
|
entries: LegacyEntry[];
|
|
28
28
|
attempts: number;
|
|
29
|
+
priority: 'normal' | 'high';
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type EnqueueIngestOptions = {
|
|
33
|
+
priority?: 'normal' | 'high';
|
|
29
34
|
};
|
|
30
35
|
|
|
31
36
|
const ingestQueue: QueuedIngestBatch[] = [];
|
|
@@ -205,18 +210,40 @@ export const enqueueIngestEntries = (
|
|
|
205
210
|
config: IngestClientConfig,
|
|
206
211
|
sessionId: string,
|
|
207
212
|
entries: LegacyEntry[],
|
|
213
|
+
options: EnqueueIngestOptions = {},
|
|
208
214
|
): void => {
|
|
209
215
|
if (!sessionId || !Array.isArray(entries) || entries.length === 0) {
|
|
210
216
|
return;
|
|
211
217
|
}
|
|
212
|
-
|
|
218
|
+
const priority = options.priority === 'high' ? 'high' : 'normal';
|
|
219
|
+
const batch: QueuedIngestBatch = {
|
|
213
220
|
config: { ...config },
|
|
214
221
|
sessionId,
|
|
215
222
|
entries: entries.slice(),
|
|
216
223
|
attempts: 0,
|
|
217
|
-
|
|
224
|
+
priority,
|
|
225
|
+
};
|
|
226
|
+
if (priority === 'high') {
|
|
227
|
+
const firstNormalIndex = ingestQueue.findIndex((queued) => queued.priority !== 'high');
|
|
228
|
+
if (firstNormalIndex === -1) {
|
|
229
|
+
ingestQueue.push(batch);
|
|
230
|
+
} else {
|
|
231
|
+
ingestQueue.splice(firstNormalIndex, 0, batch);
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
ingestQueue.push(batch);
|
|
235
|
+
}
|
|
218
236
|
if (DEFAULT_QUEUE_MAX_ITEMS > 0 && ingestQueue.length > DEFAULT_QUEUE_MAX_ITEMS) {
|
|
219
|
-
|
|
237
|
+
while (ingestQueue.length > DEFAULT_QUEUE_MAX_ITEMS) {
|
|
238
|
+
let removeIndex = ingestQueue.length - 1;
|
|
239
|
+
for (let index = ingestQueue.length - 1; index >= 0; index -= 1) {
|
|
240
|
+
if (ingestQueue[index]?.priority !== 'high') {
|
|
241
|
+
removeIndex = index;
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
ingestQueue.splice(removeIndex, 1);
|
|
246
|
+
}
|
|
220
247
|
}
|
|
221
248
|
scheduleDrain(shouldDeferDrain?.() ? deferDrainMs : 0);
|
|
222
249
|
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
process.env.REPRO_INGEST_REQUEST_TIMEOUT_MS = '50';
|
|
2
|
+
process.env.REPRO_SDK_BACKGROUND_MAX_DEFER_MS = '25';
|
|
3
|
+
process.env.REPRO_SDK_BACKGROUND_IDLE_DELAY_MS = '0';
|
|
4
|
+
process.env.REPRO_SDK_BACKGROUND_BUSY_IDLE_DELAY_MS = '0';
|
|
5
|
+
process.env.TRACE_IDLE_FLUSH_MS = '10';
|
|
6
|
+
process.env.TRACE_LINGER_AFTER_FINISH_MS = '0';
|
|
7
|
+
|
|
8
|
+
const assert = require('assert');
|
|
9
|
+
const { EventEmitter } = require('events');
|
|
10
|
+
const { initReproTracing, reproMiddleware } = require('../dist');
|
|
11
|
+
const { flushIngestQueue } = require('../dist/ingest/client');
|
|
12
|
+
|
|
13
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
14
|
+
|
|
15
|
+
async function waitFor(predicate, timeoutMs = 1500) {
|
|
16
|
+
const deadline = Date.now() + timeoutMs;
|
|
17
|
+
while (Date.now() < deadline) {
|
|
18
|
+
if (predicate()) return;
|
|
19
|
+
await sleep(20);
|
|
20
|
+
}
|
|
21
|
+
assert(predicate(), 'timed out waiting for expected condition');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function makeTaggedReqRes(sessionId, actionId) {
|
|
25
|
+
const req = new EventEmitter();
|
|
26
|
+
req.method = 'GET';
|
|
27
|
+
req.url = '/manifest-priority';
|
|
28
|
+
req.headers = {
|
|
29
|
+
'x-bug-session-id': sessionId,
|
|
30
|
+
'x-bug-action-id': actionId,
|
|
31
|
+
'x-bug-request-start': String(Date.now()),
|
|
32
|
+
};
|
|
33
|
+
req.body = undefined;
|
|
34
|
+
req.params = {};
|
|
35
|
+
req.query = {};
|
|
36
|
+
|
|
37
|
+
const res = new EventEmitter();
|
|
38
|
+
res.statusCode = 200;
|
|
39
|
+
res.getHeader = () => undefined;
|
|
40
|
+
res.setHeader = () => {};
|
|
41
|
+
res.json = function (body) { this.body = body; this.emit('finish'); return body; };
|
|
42
|
+
res.send = function (body) { this.body = body; this.emit('finish'); return body; };
|
|
43
|
+
res.write = () => true;
|
|
44
|
+
res.end = () => { res.emit('finish'); return true; };
|
|
45
|
+
|
|
46
|
+
return { req, res };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function flattenEvents(posts) {
|
|
50
|
+
return posts.flatMap((body) => Array.isArray(body?.events) ? body.events : []);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function tracedHandler() {
|
|
54
|
+
return {
|
|
55
|
+
ok: true,
|
|
56
|
+
payload: 'x'.repeat(16_000),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function main() {
|
|
61
|
+
const capturedBodies = [];
|
|
62
|
+
const originalFetch = global.fetch;
|
|
63
|
+
global.fetch = async (_url, init = {}) => {
|
|
64
|
+
const body = JSON.parse(String(init.body || '{}'));
|
|
65
|
+
capturedBodies.push(body);
|
|
66
|
+
const hasTraceBatch = Array.isArray(body.events) &&
|
|
67
|
+
body.events.some((event) => event?.event_type === 'trace_batch');
|
|
68
|
+
if (hasTraceBatch) {
|
|
69
|
+
return new Promise((_resolve, reject) => {
|
|
70
|
+
const signal = init.signal;
|
|
71
|
+
if (signal?.aborted) {
|
|
72
|
+
reject(new Error('aborted'));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
signal?.addEventListener?.('abort', () => reject(new Error('aborted')), { once: true });
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
ok: true,
|
|
80
|
+
status: 202,
|
|
81
|
+
json: async () => ({ ok: true }),
|
|
82
|
+
text: async () => '{"ok":true}',
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
initReproTracing({ instrument: false, logFunctionCalls: false });
|
|
88
|
+
const cfg = {
|
|
89
|
+
tenantId: 'TENANT_test',
|
|
90
|
+
appId: 'APP_test',
|
|
91
|
+
appSecret: 'secret',
|
|
92
|
+
captureHeaders: false,
|
|
93
|
+
privacy: { environment: 'dev' },
|
|
94
|
+
ingestBase: 'http://127.0.0.1:65535',
|
|
95
|
+
};
|
|
96
|
+
const { req, res } = makeTaggedReqRes('S_manifest_priority', 'A_manifest_priority');
|
|
97
|
+
|
|
98
|
+
await new Promise((resolve, reject) => {
|
|
99
|
+
void reproMiddleware(cfg)(req, res, async (err) => {
|
|
100
|
+
if (err) {
|
|
101
|
+
reject(err);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
await global.__repro_call(
|
|
107
|
+
tracedHandler,
|
|
108
|
+
null,
|
|
109
|
+
[],
|
|
110
|
+
'app',
|
|
111
|
+
1,
|
|
112
|
+
'tracedHandler',
|
|
113
|
+
true,
|
|
114
|
+
);
|
|
115
|
+
res.json({ ok: true });
|
|
116
|
+
resolve();
|
|
117
|
+
} catch (error) {
|
|
118
|
+
reject(error);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await waitFor(() => flattenEvents(capturedBodies)
|
|
124
|
+
.some((event) => event?.event_type === 'request_capture_manifest'));
|
|
125
|
+
|
|
126
|
+
const eventTypes = flattenEvents(capturedBodies).map((event) => event?.event_type);
|
|
127
|
+
const manifestIndex = eventTypes.indexOf('request_capture_manifest');
|
|
128
|
+
const traceIndex = eventTypes.indexOf('trace_batch');
|
|
129
|
+
|
|
130
|
+
assert(manifestIndex >= 0, JSON.stringify(eventTypes));
|
|
131
|
+
assert(traceIndex >= 0, JSON.stringify(eventTypes));
|
|
132
|
+
assert(
|
|
133
|
+
manifestIndex < traceIndex,
|
|
134
|
+
`request_capture_manifest must be sent before trace_batch; got ${JSON.stringify(eventTypes)}`,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
console.log('request manifest priority OK');
|
|
138
|
+
} finally {
|
|
139
|
+
global.fetch = originalFetch;
|
|
140
|
+
await flushIngestQueue();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
main().catch((error) => {
|
|
145
|
+
console.error(error);
|
|
146
|
+
process.exitCode = 1;
|
|
147
|
+
});
|