dd-trace 6.9.0 → 6.11.0
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 +0 -17
- package/ci/vitest-no-worker-init-setup.mjs +112 -10
- package/index.d.ts +150 -3
- package/initialize.mjs +28 -17
- package/openfeature.js +2 -2
- package/package.json +14 -10
- package/packages/datadog-esbuild/index.js +0 -34
- package/packages/datadog-instrumentations/src/anthropic.js +210 -15
- package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
- package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
- package/packages/datadog-instrumentations/src/bunyan.js +3 -16
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
- package/packages/datadog-instrumentations/src/cucumber.js +26 -11
- package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
- package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
- package/packages/datadog-instrumentations/src/cypress.js +2 -0
- package/packages/datadog-instrumentations/src/fastify.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +634 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +81 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
- package/packages/datadog-instrumentations/src/jest.js +26 -18
- package/packages/datadog-instrumentations/src/mariadb.js +278 -21
- package/packages/datadog-instrumentations/src/mocha/main.js +540 -56
- package/packages/datadog-instrumentations/src/mocha/utils.js +132 -25
- package/packages/datadog-instrumentations/src/mysql.js +45 -15
- package/packages/datadog-instrumentations/src/mysql2.js +119 -18
- package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
- package/packages/datadog-instrumentations/src/pg.js +116 -92
- package/packages/datadog-instrumentations/src/playwright-reporter.js +99 -0
- package/packages/datadog-instrumentations/src/playwright.js +149 -2
- package/packages/datadog-instrumentations/src/router.js +2 -0
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -4
- package/packages/datadog-instrumentations/src/vitest-main.js +180 -20
- package/packages/datadog-instrumentations/src/vitest-util.js +64 -5
- package/packages/datadog-instrumentations/src/vitest-worker.js +150 -25
- package/packages/datadog-instrumentations/src/webdriverio.js +31 -17
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -13
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
- package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
- package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
- package/packages/datadog-plugin-bunyan/src/index.js +5 -8
- package/packages/datadog-plugin-cucumber/src/index.js +11 -0
- package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
- package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +82 -30
- package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
- package/packages/datadog-plugin-cypress/src/index.js +12 -6
- package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
- package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
- package/packages/datadog-plugin-electron/src/index.js +2 -0
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
- package/packages/datadog-plugin-jest/src/index.js +11 -4
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -2
- package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
- package/packages/datadog-plugin-mocha/src/index.js +606 -40
- package/packages/datadog-plugin-mysql/src/index.js +37 -0
- package/packages/datadog-plugin-nats/src/index.js +1 -1
- package/packages/datadog-plugin-next/src/index.js +1 -19
- package/packages/datadog-plugin-openai/src/tracing.js +41 -30
- package/packages/datadog-plugin-openai-agents/src/integration.js +279 -69
- package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
- package/packages/datadog-plugin-pg/src/index.js +1 -0
- package/packages/datadog-plugin-playwright/src/index.js +42 -13
- package/packages/datadog-plugin-vitest/src/index.js +14 -20
- package/packages/datadog-plugin-ws/src/util.js +2 -1
- package/packages/datadog-webpack/index.js +0 -11
- package/packages/dd-trace/index.js +0 -1
- package/packages/dd-trace/src/agent/info.js +6 -5
- package/packages/dd-trace/src/aiguard/client.js +100 -1
- package/packages/dd-trace/src/aiguard/errors.js +41 -0
- package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
- package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
- package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
- package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
- package/packages/dd-trace/src/aiguard/sdk.js +22 -278
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +13 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
- package/packages/dd-trace/src/carrier.js +356 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +27 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +6 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -0
- package/packages/dd-trace/src/config/index.js +13 -2
- package/packages/dd-trace/src/config/major-overrides.js +7 -0
- package/packages/dd-trace/src/config/supported-configurations.json +24 -0
- package/packages/dd-trace/src/datastreams/context.js +6 -1
- package/packages/dd-trace/src/datastreams/index.js +2 -0
- package/packages/dd-trace/src/datastreams/manager.js +14 -3
- package/packages/dd-trace/src/datastreams/pathway.js +23 -22
- package/packages/dd-trace/src/datastreams/processor.js +16 -13
- package/packages/dd-trace/src/datastreams/size.js +5 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
- package/packages/dd-trace/src/exporters/common/writer.js +11 -6
- package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +163 -5
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +241 -87
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +280 -20
- package/packages/dd-trace/src/llmobs/experiments/index.js +58 -59
- package/packages/dd-trace/src/llmobs/experiments/noop.js +71 -3
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +41 -5
- package/packages/dd-trace/src/llmobs/index.js +34 -2
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
- package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
- package/packages/dd-trace/src/llmobs/tagger.js +96 -0
- package/packages/dd-trace/src/llmobs/util.js +78 -0
- package/packages/dd-trace/src/noop/proxy.js +3 -4
- package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
- package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
- package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
- package/packages/dd-trace/src/opentracing/tracer.js +5 -2
- package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
- package/packages/dd-trace/src/plugin_manager.js +5 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +5 -65
- package/packages/dd-trace/src/plugins/util/test.js +93 -14
- package/packages/dd-trace/src/plugins/util/web.js +56 -2
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
- package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
- package/packages/dd-trace/src/proxy.js +38 -29
- package/packages/dd-trace/src/ritm.js +9 -2
- package/packages/dd-trace/src/serverless.js +37 -0
- package/packages/dd-trace/src/span_format.js +1 -0
- package/packages/dd-trace/src/span_stats.js +30 -3
- package/packages/dd-trace/src/storage-channels.js +86 -0
- package/packages/dd-trace/src/tracer.js +9 -2
- package/packages/dd-trace/src/tracer_metadata.js +14 -1
- package/packages/dd-trace/src/web-tags-cache.js +132 -0
- package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
- package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
- package/packages/dd-trace/src/feature-registry.js +0 -29
- package/packages/dd-trace/src/openfeature/register.js +0 -35
- package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
const {
|
|
12
12
|
getTestSuitePath,
|
|
13
13
|
DYNAMIC_NAME_RE,
|
|
14
|
+
getFailedTestReplayPromise,
|
|
14
15
|
recordTestManagementExecution,
|
|
15
16
|
recordAttemptToFixExecution,
|
|
16
17
|
logAttemptToFixTestExecution,
|
|
@@ -34,9 +35,13 @@ const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
|
|
|
34
35
|
/** @typedef {{ length: number, [index: number]: unknown } & Iterable<unknown>} ArgumentsLike */
|
|
35
36
|
|
|
36
37
|
const testToContext = new WeakMap()
|
|
38
|
+
const originalEfdFns = new WeakMap()
|
|
37
39
|
const originalFns = new WeakMap()
|
|
40
|
+
const originalPendingByTest = new WeakMap()
|
|
41
|
+
const originalRetriesByTest = new WeakMap()
|
|
38
42
|
const testToStartLine = new WeakMap()
|
|
39
43
|
const testFileToSuiteCtx = new Map()
|
|
44
|
+
const datadogRetryOriginals = new WeakMap()
|
|
40
45
|
const wrappedFunctions = new WeakSet()
|
|
41
46
|
const newTests = {}
|
|
42
47
|
const efdTests = {}
|
|
@@ -168,6 +173,7 @@ function wrapOriginalEfdTest (test, retryPolicy) {
|
|
|
168
173
|
}
|
|
169
174
|
test._ddEfdDurationWrapped = true
|
|
170
175
|
const originalFn = test.fn
|
|
176
|
+
originalEfdFns.set(test, originalFn)
|
|
171
177
|
test.fn = shimmer.wrapFunction(originalFn, originalFn => function () {
|
|
172
178
|
const start = performance.now()
|
|
173
179
|
const recordDuration = () => {
|
|
@@ -264,6 +270,7 @@ function isEarlyFlakeDetectionTest (test, config) {
|
|
|
264
270
|
function retryTest (test, numRetries, tags, retryPolicy) {
|
|
265
271
|
const suite = test.parent
|
|
266
272
|
const isEfdRetry = tags.includes('_ddIsEfdRetry')
|
|
273
|
+
if (!originalRetriesByTest.has(test)) originalRetriesByTest.set(test, test.retries())
|
|
267
274
|
disableMochaRetries(test)
|
|
268
275
|
if (isEfdRetry) {
|
|
269
276
|
wrapOriginalEfdTest(test, retryPolicy)
|
|
@@ -271,6 +278,7 @@ function retryTest (test, numRetries, tags, retryPolicy) {
|
|
|
271
278
|
for (let retryIndex = 0; retryIndex < numRetries; retryIndex++) {
|
|
272
279
|
const clonedTest = test.clone()
|
|
273
280
|
disableMochaRetries(clonedTest)
|
|
281
|
+
datadogRetryOriginals.set(clonedTest, test)
|
|
274
282
|
suite.addTest(clonedTest)
|
|
275
283
|
if (isEfdRetry) {
|
|
276
284
|
clonedTest._ddEfdRetryIndex = retryIndex + 1
|
|
@@ -294,6 +302,91 @@ function retryTest (test, numRetries, tags, retryPolicy) {
|
|
|
294
302
|
}
|
|
295
303
|
}
|
|
296
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Restores a runnable function wrapped with its Test Optimization context.
|
|
307
|
+
*
|
|
308
|
+
* @param {import('mocha').Runnable} runnable
|
|
309
|
+
* @returns {void}
|
|
310
|
+
*/
|
|
311
|
+
function restoreRunnableFunction (runnable) {
|
|
312
|
+
const wrappedFunction = runnable.fn
|
|
313
|
+
if (!wrappedFunctions.has(wrappedFunction)) return
|
|
314
|
+
|
|
315
|
+
runnable.fn = originalFns.get(wrappedFunction)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Restores a test function wrapped to measure its EFD duration.
|
|
320
|
+
*
|
|
321
|
+
* @param {import('mocha').Test} test
|
|
322
|
+
* @returns {void}
|
|
323
|
+
*/
|
|
324
|
+
function restoreEfdTestFunction (test) {
|
|
325
|
+
if (!originalEfdFns.has(test)) return
|
|
326
|
+
|
|
327
|
+
test.fn = originalEfdFns.get(test)
|
|
328
|
+
originalEfdFns.delete(test)
|
|
329
|
+
delete test._ddEfdDurationWrapped
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Clears state that belongs to one Mocha runner execution and removes retry clones from prior executions.
|
|
334
|
+
*
|
|
335
|
+
* @param {import('mocha').Suite} rootSuite
|
|
336
|
+
* @returns {void}
|
|
337
|
+
*/
|
|
338
|
+
function resetRunState (rootSuite) {
|
|
339
|
+
for (const key of Object.keys(newTests)) delete newTests[key]
|
|
340
|
+
for (const key of Object.keys(efdTests)) delete efdTests[key]
|
|
341
|
+
newTestsWithDynamicNames.clear()
|
|
342
|
+
testsAttemptToFix.clear()
|
|
343
|
+
testsQuarantined.clear()
|
|
344
|
+
testsStatuses.clear()
|
|
345
|
+
efdRetryCountByTestFullName.clear()
|
|
346
|
+
efdSlowAbortedTests.clear()
|
|
347
|
+
attemptToFixExecutions.clear()
|
|
348
|
+
loggedAttemptToFixTests.clear()
|
|
349
|
+
|
|
350
|
+
const suites = [rootSuite]
|
|
351
|
+
while (suites.length) {
|
|
352
|
+
const suite = suites.pop()
|
|
353
|
+
const originalTests = []
|
|
354
|
+
const retainedTests = new Set()
|
|
355
|
+
for (const runnable of suite.tests) {
|
|
356
|
+
const test = datadogRetryOriginals.get(runnable) || runnable
|
|
357
|
+
if (retainedTests.has(test)) continue
|
|
358
|
+
retainedTests.add(test)
|
|
359
|
+
|
|
360
|
+
restoreRunnableFunction(test)
|
|
361
|
+
restoreEfdTestFunction(test)
|
|
362
|
+
if (originalPendingByTest.has(test)) {
|
|
363
|
+
test.pending = originalPendingByTest.get(test)
|
|
364
|
+
originalPendingByTest.delete(test)
|
|
365
|
+
}
|
|
366
|
+
if (originalRetriesByTest.has(test)) {
|
|
367
|
+
test.retries(originalRetriesByTest.get(test))
|
|
368
|
+
originalRetriesByTest.delete(test)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
delete test._ddIsAttemptToFix
|
|
372
|
+
delete test._ddIsDisabled
|
|
373
|
+
delete test._ddIsQuarantined
|
|
374
|
+
delete test._ddIsModified
|
|
375
|
+
delete test._ddIsNew
|
|
376
|
+
delete test._ddShouldSkipEfdRetry
|
|
377
|
+
delete test._ddTestFinishStarted
|
|
378
|
+
delete test._ddTestFinishPublished
|
|
379
|
+
delete test._ddIsFinalAttempt
|
|
380
|
+
delete test._ddHookFailed
|
|
381
|
+
delete test._ddReporterStartFailed
|
|
382
|
+
delete test._ddReporterTerminalFailed
|
|
383
|
+
originalTests.push(test)
|
|
384
|
+
}
|
|
385
|
+
suite.tests = originalTests
|
|
386
|
+
suites.push(...suite.suites)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
297
390
|
function getSuitesByTestFile (root) {
|
|
298
391
|
const suitesByTestFile = {}
|
|
299
392
|
function getSuites (suite) {
|
|
@@ -370,6 +463,19 @@ function getTestContext (test) {
|
|
|
370
463
|
return testToContext.get(key)
|
|
371
464
|
}
|
|
372
465
|
|
|
466
|
+
/**
|
|
467
|
+
* Claims publication of a test finish event across Mocha's terminal event paths.
|
|
468
|
+
*
|
|
469
|
+
* @param {object} test
|
|
470
|
+
* @returns {boolean}
|
|
471
|
+
*/
|
|
472
|
+
function startTestFinish (test) {
|
|
473
|
+
if (test._ddTestFinishStarted || test._ddTestFinishPublished) return false
|
|
474
|
+
|
|
475
|
+
test._ddTestFinishStarted = true
|
|
476
|
+
return true
|
|
477
|
+
}
|
|
478
|
+
|
|
373
479
|
/**
|
|
374
480
|
* Copies Test Management metadata from Mocha's original runnable to its native retry clone.
|
|
375
481
|
* @param {{
|
|
@@ -412,11 +518,7 @@ function runnableWrapper (RunnablePackage, libraryConfig) {
|
|
|
412
518
|
const test = isTestHook ? this.ctx.currentTest : this
|
|
413
519
|
|
|
414
520
|
// we restore the original user defined function
|
|
415
|
-
|
|
416
|
-
const originalFn = originalFns.get(this.fn)
|
|
417
|
-
this.fn = originalFn
|
|
418
|
-
wrappedFunctions.delete(this.fn)
|
|
419
|
-
}
|
|
521
|
+
restoreRunnableFunction(this)
|
|
420
522
|
|
|
421
523
|
if (isDatadogManagedRetryTest(test, libraryConfig)) {
|
|
422
524
|
disableMochaRetries(this)
|
|
@@ -459,11 +561,7 @@ function getOnTestHandler (isMain) {
|
|
|
459
561
|
|
|
460
562
|
// This may be a retry. If this is the case, `test.fn` is already wrapped,
|
|
461
563
|
// so we need to restore it.
|
|
462
|
-
|
|
463
|
-
const originalFn = originalFns.get(test.fn)
|
|
464
|
-
test.fn = originalFn
|
|
465
|
-
wrappedFunctions.delete(test.fn)
|
|
466
|
-
}
|
|
564
|
+
restoreRunnableFunction(test)
|
|
467
565
|
|
|
468
566
|
inheritDatadogPropertiesFromRetriedTest(test)
|
|
469
567
|
|
|
@@ -526,6 +624,9 @@ function getOnTestHandler (isMain) {
|
|
|
526
624
|
}
|
|
527
625
|
|
|
528
626
|
if (!isAttemptToFix && isDisabled) {
|
|
627
|
+
if (!originalPendingByTest.has(test)) {
|
|
628
|
+
originalPendingByTest.set(test, test.pending)
|
|
629
|
+
}
|
|
529
630
|
test.pending = true
|
|
530
631
|
}
|
|
531
632
|
|
|
@@ -700,9 +801,16 @@ function getOnTestEndHandler (config, finalAttemptHandlers) {
|
|
|
700
801
|
if (test._ddShouldSkipEfdRetry) {
|
|
701
802
|
return
|
|
702
803
|
}
|
|
804
|
+
const shouldWaitForHitProbe = test._retriedTest?._ddShouldWaitForHitProbe
|
|
703
805
|
const ctx = getTestContext(test)
|
|
704
806
|
const status = getTestStatus(test)
|
|
705
|
-
const shouldFinishTest = ctx && (
|
|
807
|
+
const shouldFinishTest = ctx && (
|
|
808
|
+
test._ddReporterStartFailed ||
|
|
809
|
+
test._ddReporterTerminalFailed ||
|
|
810
|
+
!getAfterEachHooks(test).length ||
|
|
811
|
+
(test._ddIsDisabled && !test._ddIsAttemptToFix)
|
|
812
|
+
)
|
|
813
|
+
if (shouldFinishTest && !startTestFinish(test)) return
|
|
706
814
|
let testFinishInfo
|
|
707
815
|
let isFinalAttempt = false
|
|
708
816
|
|
|
@@ -728,11 +836,12 @@ function getOnTestEndHandler (config, finalAttemptHandlers) {
|
|
|
728
836
|
finalAttemptHandlers?.onStart?.(test)
|
|
729
837
|
}
|
|
730
838
|
|
|
731
|
-
if (
|
|
839
|
+
if (shouldWaitForHitProbe) {
|
|
732
840
|
await waitForHitProbe()
|
|
733
841
|
}
|
|
734
842
|
|
|
735
843
|
if (shouldFinishTest) {
|
|
844
|
+
test._ddTestFinishPublished = true
|
|
736
845
|
testFinishCh.publish({
|
|
737
846
|
status,
|
|
738
847
|
hasBeenRetried: isMochaRetry(test),
|
|
@@ -759,10 +868,11 @@ function getOnHookEndHandler (config, finalAttemptHandlers) {
|
|
|
759
868
|
const ctx = getTestContext(test)
|
|
760
869
|
// Disabled tests are already finished in getOnTestEndHandler,
|
|
761
870
|
// skip to avoid double-publishing
|
|
762
|
-
if (ctx && (!test._ddIsDisabled || test._ddIsAttemptToFix)) {
|
|
871
|
+
if (ctx && (!test._ddIsDisabled || test._ddIsAttemptToFix) && startTestFinish(test)) {
|
|
763
872
|
const testFinishInfo = getTestFinishInfo(test, status, config, ctx.err || test.err)
|
|
764
873
|
const isFinalAttempt = testFinishInfo.finalStatus !== undefined
|
|
765
874
|
const publishTestFinish = () => {
|
|
875
|
+
test._ddTestFinishPublished = true
|
|
766
876
|
testFinishCh.publish({
|
|
767
877
|
status,
|
|
768
878
|
hasBeenRetried: isMochaRetry(test),
|
|
@@ -886,11 +996,13 @@ function getOnFailHandler (isMain, config) {
|
|
|
886
996
|
testContext = getTestContext(test)
|
|
887
997
|
}
|
|
888
998
|
if (testContext) {
|
|
889
|
-
if (isHook) {
|
|
890
|
-
|
|
891
|
-
|
|
999
|
+
if (isHook && startTestFinish(test)) {
|
|
1000
|
+
const hookError = new Error(`${testOrHook.fullTitle()}: ${err.message}`, { cause: err })
|
|
1001
|
+
hookError.name = err.name
|
|
1002
|
+
hookError.stack = err.stack
|
|
1003
|
+
testContext.err = hookError
|
|
892
1004
|
errorCh.runStores(testContext, () => {})
|
|
893
|
-
const testFinishInfo = getTestFinishInfo(test, 'fail', config,
|
|
1005
|
+
const testFinishInfo = getTestFinishInfo(test, 'fail', config, hookError)
|
|
894
1006
|
// ATR never retries hook failures: this.retries(N) is set in runnableWrapper
|
|
895
1007
|
// which only runs when the test function executes — hooks bypass that path,
|
|
896
1008
|
// so _retries stays at -1 and getIsLastRetry returns false, leaving finalStatus
|
|
@@ -905,6 +1017,7 @@ function getOnFailHandler (isMain, config) {
|
|
|
905
1017
|
// test.state is never set to 'failed' for hook failures (Mocha marks the hook,
|
|
906
1018
|
// not the test). Flag it so finishRootSuiteForFile can compute the correct status.
|
|
907
1019
|
test._ddHookFailed = true
|
|
1020
|
+
test._ddTestFinishPublished = true
|
|
908
1021
|
testFinishCh.publish({
|
|
909
1022
|
status: 'fail',
|
|
910
1023
|
hasBeenRetried: isMochaRetry(test),
|
|
@@ -953,14 +1066,7 @@ function getOnTestRetryHandler (config) {
|
|
|
953
1066
|
promises,
|
|
954
1067
|
...ctx.currentStore,
|
|
955
1068
|
})
|
|
956
|
-
|
|
957
|
-
test._ddFailedTestReplayPromise = Promise.all([
|
|
958
|
-
promises.setProbePromise,
|
|
959
|
-
promises.finishTestPromise,
|
|
960
|
-
]).then(() => {})
|
|
961
|
-
} else if (promises.setProbePromise || promises.finishTestPromise) {
|
|
962
|
-
test._ddFailedTestReplayPromise = promises.setProbePromise || promises.finishTestPromise
|
|
963
|
-
}
|
|
1069
|
+
test._ddFailedTestReplayPromise = getFailedTestReplayPromise(promises)
|
|
964
1070
|
}
|
|
965
1071
|
const key = getTestToContextKey(test)
|
|
966
1072
|
testToContext.delete(key)
|
|
@@ -1094,6 +1200,7 @@ module.exports = {
|
|
|
1094
1200
|
getOnPendingHandler,
|
|
1095
1201
|
testFileToSuiteCtx,
|
|
1096
1202
|
getRunTestsWrapper,
|
|
1203
|
+
resetRunState,
|
|
1097
1204
|
newTests,
|
|
1098
1205
|
efdTests,
|
|
1099
1206
|
newTestsWithDynamicNames,
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const shimmer = require('../../datadog-shimmer')
|
|
4
4
|
const { channel, addHook } = require('./helpers/instrument')
|
|
5
|
+
const {
|
|
6
|
+
dispatchesAcquireSynchronously,
|
|
7
|
+
takePoolWaitTime,
|
|
8
|
+
wrapPoolClusterQueryMethod,
|
|
9
|
+
wrapPoolGetConnection,
|
|
10
|
+
wrapPoolQueryMethod,
|
|
11
|
+
} = require('./helpers/pool-acquire')
|
|
5
12
|
|
|
6
13
|
addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connection => {
|
|
7
14
|
const startCh = channel('apm:mysql:query:start')
|
|
@@ -17,6 +24,11 @@ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connect
|
|
|
17
24
|
const conf = this.config
|
|
18
25
|
const ctx = { sql, conf }
|
|
19
26
|
|
|
27
|
+
const waitTime = takePoolWaitTime(this)
|
|
28
|
+
if (waitTime !== undefined) {
|
|
29
|
+
ctx.poolWaitTime = waitTime
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
return startCh.runStores(ctx, () => {
|
|
21
33
|
if (args[0].sql) {
|
|
22
34
|
args[0].sql = ctx.sql
|
|
@@ -61,20 +73,24 @@ addHook({ name: 'mysql', file: 'lib/Pool.js', versions: ['>=2'] }, Pool => {
|
|
|
61
73
|
const connectionFinishCh = channel('apm:mysql:connection:finish')
|
|
62
74
|
const startPoolQueryCh = channel('datadog:mysql:pool:query:start')
|
|
63
75
|
const finishPoolQueryCh = channel('datadog:mysql:pool:query:finish')
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
const acquireStartCh = channel('apm:mysql:pool:acquire:start')
|
|
77
|
+
const acquireFinishCh = channel('apm:mysql:pool:acquire:finish')
|
|
78
|
+
const deferredPoolAcquire = !dispatchesAcquireSynchronously(
|
|
79
|
+
Pool.prototype.query,
|
|
80
|
+
Object.assign(Object.create(Pool.prototype), { config: { connectionConfig: {} } }),
|
|
81
|
+
'getConnection',
|
|
82
|
+
['SELECT 1', () => {}]
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
shimmer.wrap(Pool.prototype, 'getConnection', getConnection => wrapPoolGetConnection(getConnection, {
|
|
86
|
+
connectionStartCh,
|
|
87
|
+
connectionFinishCh,
|
|
88
|
+
acquireStartCh,
|
|
89
|
+
acquireFinishCh,
|
|
90
|
+
reentersQueuedCallbacks: true,
|
|
91
|
+
}, deferredPoolAcquire))
|
|
92
|
+
|
|
93
|
+
shimmer.wrap(Pool.prototype, 'query', query => wrapPoolQueryMethod(function (...args) {
|
|
78
94
|
if (!startPoolQueryCh.hasSubscribers) {
|
|
79
95
|
return query.apply(this, args)
|
|
80
96
|
}
|
|
@@ -99,7 +115,21 @@ addHook({ name: 'mysql', file: 'lib/Pool.js', versions: ['>=2'] }, Pool => {
|
|
|
99
115
|
|
|
100
116
|
return retval
|
|
101
117
|
})
|
|
102
|
-
})
|
|
118
|
+
}, connectionStartCh, deferredPoolAcquire))
|
|
103
119
|
|
|
104
120
|
return Pool
|
|
105
121
|
})
|
|
122
|
+
|
|
123
|
+
// A `canRetry` failover re-invokes `query` with the same Query object. Key the acquire state by that
|
|
124
|
+
// object so a retry remains part of the query even if a future mysql version defers it.
|
|
125
|
+
addHook({ name: 'mysql', file: 'lib/PoolNamespace.js', versions: ['>=2'] }, PoolNamespace => {
|
|
126
|
+
const channels = {
|
|
127
|
+
connectionStartCh: channel('apm:mysql:connection:start'),
|
|
128
|
+
connectionFinishCh: channel('apm:mysql:connection:finish'),
|
|
129
|
+
acquireStartCh: channel('apm:mysql:pool:acquire:start'),
|
|
130
|
+
acquireFinishCh: channel('apm:mysql:pool:acquire:finish'),
|
|
131
|
+
}
|
|
132
|
+
shimmer.wrap(PoolNamespace.prototype, 'query', query => wrapPoolClusterQueryMethod(query, channels))
|
|
133
|
+
|
|
134
|
+
return PoolNamespace
|
|
135
|
+
})
|
|
@@ -5,10 +5,55 @@ const { errorMonitor } = require('node:events')
|
|
|
5
5
|
const shimmer = require('../../datadog-shimmer')
|
|
6
6
|
const satisfies = require('../../../vendor/dist/semifies')
|
|
7
7
|
const { channel, addHook } = require('./helpers/instrument')
|
|
8
|
+
const {
|
|
9
|
+
dispatchesAcquireSynchronously,
|
|
10
|
+
takePoolWaitTime,
|
|
11
|
+
wrapPoolClusterGetConnection,
|
|
12
|
+
wrapPoolClusterMethod,
|
|
13
|
+
wrapPoolGetConnection,
|
|
14
|
+
wrapPoolQueryMethod,
|
|
15
|
+
} = require('./helpers/pool-acquire')
|
|
8
16
|
|
|
9
17
|
/** @type {WeakMap<object, Function>} */
|
|
10
18
|
const wrappedOnResult = new WeakMap()
|
|
11
19
|
|
|
20
|
+
/** @type {WeakSet<object>} */
|
|
21
|
+
const flagWrappedNamespaces = new WeakSet()
|
|
22
|
+
|
|
23
|
+
const connectionStartCh = channel('apm:mysql2:connection:start')
|
|
24
|
+
const connectionFinishCh = channel('apm:mysql2:connection:finish')
|
|
25
|
+
const acquireStartCh = channel('apm:mysql2:pool:acquire:start')
|
|
26
|
+
const acquireFinishCh = channel('apm:mysql2:pool:acquire:finish')
|
|
27
|
+
const poolAcquireChannels = {
|
|
28
|
+
connectionStartCh,
|
|
29
|
+
connectionFinishCh,
|
|
30
|
+
acquireStartCh,
|
|
31
|
+
acquireFinishCh,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Bracket a pool-cluster namespace's internal acquires and key retry state by its stable callback.
|
|
36
|
+
* @param {{ query?: Function, execute?: Function, getConnection?: Function }} poolNamespace
|
|
37
|
+
*/
|
|
38
|
+
function flagWrapNamespace (poolNamespace) {
|
|
39
|
+
if (poolNamespace == null || flagWrappedNamespaces.has(poolNamespace)) return
|
|
40
|
+
flagWrappedNamespaces.add(poolNamespace)
|
|
41
|
+
|
|
42
|
+
if (typeof poolNamespace.query === 'function') {
|
|
43
|
+
shimmer.wrap(poolNamespace, 'query', query => wrapPoolClusterMethod(query, connectionStartCh))
|
|
44
|
+
}
|
|
45
|
+
if (typeof poolNamespace.execute === 'function') {
|
|
46
|
+
shimmer.wrap(poolNamespace, 'execute', execute => wrapPoolClusterMethod(execute, connectionStartCh))
|
|
47
|
+
}
|
|
48
|
+
if (typeof poolNamespace.getConnection === 'function') {
|
|
49
|
+
shimmer.wrap(
|
|
50
|
+
poolNamespace,
|
|
51
|
+
'getConnection',
|
|
52
|
+
getConnection => wrapPoolClusterGetConnection(getConnection, poolAcquireChannels)
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
12
57
|
/**
|
|
13
58
|
* @param {unknown} sql
|
|
14
59
|
* @returns {string|undefined}
|
|
@@ -43,6 +88,11 @@ function wrapConnection (Connection, version) {
|
|
|
43
88
|
const ctx = {}
|
|
44
89
|
|
|
45
90
|
if (isQuery) {
|
|
91
|
+
const waitTime = takePoolWaitTime(this)
|
|
92
|
+
if (waitTime !== undefined) {
|
|
93
|
+
ctx.poolWaitTime = waitTime
|
|
94
|
+
}
|
|
95
|
+
|
|
46
96
|
command.execute = wrapExecute(command, command.execute, ctx, this.config)
|
|
47
97
|
|
|
48
98
|
return commandAddCh.runStores(ctx, addCommand, this, ...arguments)
|
|
@@ -212,22 +262,41 @@ function wrapConnection (Connection, version) {
|
|
|
212
262
|
* by the queued query attach to the caller rather than the previous query that freed the connection.
|
|
213
263
|
*
|
|
214
264
|
* @param {Function} Pool
|
|
265
|
+
* @param {boolean} deferredPoolAcquire
|
|
215
266
|
* @returns {Function}
|
|
216
267
|
*/
|
|
217
|
-
function wrapGetConnection (Pool) {
|
|
218
|
-
|
|
219
|
-
|
|
268
|
+
function wrapGetConnection (Pool, deferredPoolAcquire) {
|
|
269
|
+
shimmer.wrap(
|
|
270
|
+
Pool.prototype,
|
|
271
|
+
'getConnection',
|
|
272
|
+
getConnection => wrapPoolGetConnection(getConnection, poolAcquireChannels, deferredPoolAcquire)
|
|
273
|
+
)
|
|
220
274
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
arguments[0] = function (...args) {
|
|
224
|
-
return connectionFinishCh.runStores(ctx, cb, this, ...args)
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
connectionStartCh.publish(ctx)
|
|
275
|
+
return Pool
|
|
276
|
+
}
|
|
228
277
|
|
|
229
|
-
|
|
230
|
-
|
|
278
|
+
/**
|
|
279
|
+
* mysql2 >=3.11.5 keeps the outer-query abort at the connection level, so the pool only needs the
|
|
280
|
+
* acquire wrap plus the pool-query flag around `query` / `execute`.
|
|
281
|
+
*
|
|
282
|
+
* @param {Function} Pool
|
|
283
|
+
* @returns {Function}
|
|
284
|
+
*/
|
|
285
|
+
function wrapBasePool (Pool) {
|
|
286
|
+
const deferredQueryAcquire = defersPoolAcquire(Pool, 'query')
|
|
287
|
+
const deferredExecuteAcquire = defersPoolAcquire(Pool, 'execute')
|
|
288
|
+
wrapGetConnection(Pool, deferredQueryAcquire || deferredExecuteAcquire)
|
|
289
|
+
|
|
290
|
+
shimmer.wrap(
|
|
291
|
+
Pool.prototype,
|
|
292
|
+
'query',
|
|
293
|
+
query => wrapPoolQueryMethod(query, connectionStartCh, deferredQueryAcquire)
|
|
294
|
+
)
|
|
295
|
+
shimmer.wrap(
|
|
296
|
+
Pool.prototype,
|
|
297
|
+
'execute',
|
|
298
|
+
execute => wrapPoolQueryMethod(execute, connectionStartCh, deferredExecuteAcquire)
|
|
299
|
+
)
|
|
231
300
|
|
|
232
301
|
return Pool
|
|
233
302
|
}
|
|
@@ -240,10 +309,12 @@ function wrapGetConnection (Pool) {
|
|
|
240
309
|
function wrapPool (Pool, version) {
|
|
241
310
|
const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
|
|
242
311
|
const shouldEmitEndAfterQueryAbort = satisfies(version, '>=1.3.3')
|
|
312
|
+
const deferredQueryAcquire = defersPoolAcquire(Pool, 'query')
|
|
313
|
+
const deferredExecuteAcquire = defersPoolAcquire(Pool, 'execute')
|
|
243
314
|
|
|
244
|
-
wrapGetConnection(Pool)
|
|
315
|
+
wrapGetConnection(Pool, deferredQueryAcquire || deferredExecuteAcquire)
|
|
245
316
|
|
|
246
|
-
shimmer.wrap(Pool.prototype, 'query', query => function (sql, values, cb) {
|
|
317
|
+
shimmer.wrap(Pool.prototype, 'query', query => wrapPoolQueryMethod(function (sql, values, cb) {
|
|
247
318
|
if (!startOuterQueryCh.hasSubscribers) return query.apply(this, arguments)
|
|
248
319
|
|
|
249
320
|
const resolvedSql = resolveSqlString(sql)
|
|
@@ -279,9 +350,9 @@ function wrapPool (Pool, version) {
|
|
|
279
350
|
}
|
|
280
351
|
|
|
281
352
|
return query.apply(this, arguments)
|
|
282
|
-
})
|
|
353
|
+
}, connectionStartCh, deferredQueryAcquire))
|
|
283
354
|
|
|
284
|
-
shimmer.wrap(Pool.prototype, 'execute', execute => function (sql, values, cb) {
|
|
355
|
+
shimmer.wrap(Pool.prototype, 'execute', execute => wrapPoolQueryMethod(function (sql, values, cb) {
|
|
285
356
|
if (!startOuterQueryCh.hasSubscribers) return execute.apply(this, arguments)
|
|
286
357
|
|
|
287
358
|
const resolvedSql = resolveSqlString(sql)
|
|
@@ -304,11 +375,25 @@ function wrapPool (Pool, version) {
|
|
|
304
375
|
}
|
|
305
376
|
|
|
306
377
|
return execute.apply(this, arguments)
|
|
307
|
-
})
|
|
378
|
+
}, connectionStartCh, deferredExecuteAcquire))
|
|
308
379
|
|
|
309
380
|
return Pool
|
|
310
381
|
}
|
|
311
382
|
|
|
383
|
+
/**
|
|
384
|
+
* @param {Function} Pool
|
|
385
|
+
* @param {'query'|'execute'} method
|
|
386
|
+
* @returns {boolean}
|
|
387
|
+
*/
|
|
388
|
+
function defersPoolAcquire (Pool, method) {
|
|
389
|
+
return !dispatchesAcquireSynchronously(
|
|
390
|
+
Pool.prototype[method],
|
|
391
|
+
Object.assign(Object.create(Pool.prototype), { config: { connectionConfig: {} } }),
|
|
392
|
+
'getConnection',
|
|
393
|
+
['SELECT 1', () => {}]
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
|
|
312
397
|
/**
|
|
313
398
|
* @param {Function} PoolCluster
|
|
314
399
|
* @returns {Function}
|
|
@@ -319,6 +404,7 @@ function wrapPoolCluster (PoolCluster) {
|
|
|
319
404
|
|
|
320
405
|
shimmer.wrap(PoolCluster.prototype, 'of', of => function (...args) {
|
|
321
406
|
const poolNamespace = of.apply(this, args)
|
|
407
|
+
flagWrapNamespace(poolNamespace)
|
|
322
408
|
|
|
323
409
|
if (startOuterQueryCh.hasSubscribers && !wrappedPoolNamespaces.has(poolNamespace)) {
|
|
324
410
|
shimmer.wrap(poolNamespace, 'query', query => function (sql, values, cb) {
|
|
@@ -404,7 +490,7 @@ addHook(
|
|
|
404
490
|
// mysql2 >=3.11.5 moved the pool onto BasePool in lib/base/pool.js.
|
|
405
491
|
addHook(
|
|
406
492
|
{ name: 'mysql2', file: 'lib/base/pool.js', versions: ['>=3.11.5'] },
|
|
407
|
-
/** @type {(moduleExports: unknown, version: string) => unknown} */ (
|
|
493
|
+
/** @type {(moduleExports: unknown, version: string) => unknown} */ (wrapBasePool)
|
|
408
494
|
)
|
|
409
495
|
|
|
410
496
|
// PoolNamespace.prototype.query does not exist in mysql2<2.3.0
|
|
@@ -412,3 +498,18 @@ addHook(
|
|
|
412
498
|
{ name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['2.3.0 - 3.11.4'] },
|
|
413
499
|
/** @type {(moduleExports: unknown, version: string) => unknown} */ (wrapPoolCluster)
|
|
414
500
|
)
|
|
501
|
+
|
|
502
|
+
// mysql2 >=3.11.5 keeps the outer-query abort at the connection level, so a cluster namespace only
|
|
503
|
+
// needs the pool-query flag so its internal query/execute acquire does not open an acquire span.
|
|
504
|
+
addHook(
|
|
505
|
+
{ name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['>=3.11.5'] },
|
|
506
|
+
/** @type {(moduleExports: unknown, version: string) => unknown} */ (PoolCluster => {
|
|
507
|
+
shimmer.wrap(PoolCluster.prototype, 'of', of => function () {
|
|
508
|
+
const poolNamespace = of.apply(this, arguments)
|
|
509
|
+
flagWrapNamespace(poolNamespace)
|
|
510
|
+
return poolNamespace
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
return PoolCluster
|
|
514
|
+
})
|
|
515
|
+
)
|
|
@@ -5,6 +5,12 @@ const { addHook } = require('./helpers/instrument')
|
|
|
5
5
|
/** @type {((pattern: string | RegExp) => RegExp | undefined) | undefined} */
|
|
6
6
|
let compileToRegexp
|
|
7
7
|
|
|
8
|
+
/** @type {((pattern: string) => { tokens: object[] } | undefined) | undefined} */
|
|
9
|
+
let parseTokens
|
|
10
|
+
|
|
11
|
+
/** @type {((route: string) => ((url: string) => object | undefined) | undefined) | undefined} */
|
|
12
|
+
let makeMatcher
|
|
13
|
+
|
|
8
14
|
addHook({ name: 'path-to-regexp', versions: ['*'] }, moduleExports => {
|
|
9
15
|
// 0.1.x and 6.x: `module.exports = (path, ...) => RegExp`.
|
|
10
16
|
// 7.x: `module.exports = { pathToRegexp(path, ...) => RegExp }`.
|
|
@@ -29,6 +35,47 @@ addHook({ name: 'path-to-regexp', versions: ['*'] }, moduleExports => {
|
|
|
29
35
|
return moduleExports
|
|
30
36
|
})
|
|
31
37
|
|
|
38
|
+
// 8.x only: earlier majors also expose `parse`/`match`, but with incompatible shapes — 7.x returns
|
|
39
|
+
// `{ tokens }` too, yet its tokens are bare strings rather than typed nodes. Gating on the version
|
|
40
|
+
// keeps those from installing adapters the consumers would misread.
|
|
41
|
+
addHook({ name: 'path-to-regexp', versions: ['>=8'] }, moduleExports => {
|
|
42
|
+
const { parse, match } = moduleExports
|
|
43
|
+
|
|
44
|
+
if (typeof parse === 'function' && typeof match === 'function') {
|
|
45
|
+
parseTokens = pattern => {
|
|
46
|
+
let result
|
|
47
|
+
try {
|
|
48
|
+
result = parse(pattern)
|
|
49
|
+
} catch {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(result?.tokens)) return result
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
makeMatcher = route => {
|
|
56
|
+
let matcher
|
|
57
|
+
try {
|
|
58
|
+
// Callers read which params matched, never their values: skipping decode is faster and
|
|
59
|
+
// stops a malformed escape ('%ZZ') from throwing URIError, which would read as "no match".
|
|
60
|
+
matcher = match(route, { decode: false })
|
|
61
|
+
} catch {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
return url => {
|
|
65
|
+
let result
|
|
66
|
+
try {
|
|
67
|
+
result = matcher(url)
|
|
68
|
+
} catch {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
return result ? result.params : undefined
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return moduleExports
|
|
77
|
+
})
|
|
78
|
+
|
|
32
79
|
/**
|
|
33
80
|
* Returns whatever path-to-regexp compile adapter the host most recently
|
|
34
81
|
* loaded. Capture this once at addHook fire time so each express/router
|
|
@@ -41,4 +88,23 @@ function getCompileToRegexp () {
|
|
|
41
88
|
return compileToRegexp
|
|
42
89
|
}
|
|
43
90
|
|
|
44
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Returns the host's path-to-regexp `parse` adapter (8.x token tree), or `undefined` when the
|
|
93
|
+
* host has not loaded an 8.x path-to-regexp (e.g. Express 4 ships 0.1.x, which has no `parse`).
|
|
94
|
+
* @returns {((pattern: string) => { tokens: object[] } | undefined) | undefined}
|
|
95
|
+
*/
|
|
96
|
+
function getParse () {
|
|
97
|
+
return parseTokens
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns the host's path-to-regexp 8.x `match` adapter: a factory `route => (url => params)` that
|
|
102
|
+
* compiles a route once and returns a matcher yielding the captured params (or undefined on no
|
|
103
|
+
* match / unusable route). `undefined` when the host has not loaded an 8.x path-to-regexp.
|
|
104
|
+
* @returns {((route: string) => ((url: string) => object | undefined) | undefined) | undefined}
|
|
105
|
+
*/
|
|
106
|
+
function getMatch () {
|
|
107
|
+
return makeMatcher
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = { getCompileToRegexp, getParse, getMatch }
|