dd-trace 5.93.0 → 5.95.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/LICENSE-3rdparty.csv +46 -44
- package/index.d.ts +182 -13
- package/package.json +14 -10
- package/packages/datadog-instrumentations/src/anthropic.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +23 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/{orchestrion/compiler.js → compiler.js} +4 -13
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +16 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/{orchestrion/transforms.js → transforms.js} +3 -89
- package/packages/datadog-instrumentations/src/jest.js +118 -32
- package/packages/datadog-instrumentations/src/mocha/main.js +6 -0
- package/packages/datadog-instrumentations/src/mocha/utils.js +89 -5
- package/packages/datadog-instrumentations/src/playwright.js +10 -0
- package/packages/datadog-instrumentations/src/vitest.js +119 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +12 -0
- package/packages/datadog-plugin-dd-trace-api/src/index.js +1 -4
- package/packages/datadog-plugin-jest/src/index.js +6 -0
- package/packages/datadog-plugin-mocha/src/index.js +11 -0
- package/packages/datadog-plugin-playwright/src/index.js +9 -0
- package/packages/datadog-plugin-vitest/src/index.js +9 -0
- package/packages/datadog-webpack/index.js +187 -0
- package/packages/datadog-webpack/src/loader.js +27 -0
- package/packages/datadog-webpack/src/log.js +32 -0
- package/packages/dd-trace/src/azure_metadata.js +15 -15
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +176 -33
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +10 -21
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +76 -1
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +259 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +56 -0
- package/packages/dd-trace/src/config/config-base.d.ts +7 -0
- package/packages/dd-trace/src/config/config-base.js +5 -0
- package/packages/dd-trace/src/config/config-types.d.ts +78 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +582 -0
- package/packages/dd-trace/src/config/supported-configurations.json +7 -0
- package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
- package/packages/dd-trace/src/llmobs/constants/text.js +1 -1
- package/packages/dd-trace/src/llmobs/constants/writers.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic.js +11 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +4 -1
- package/packages/dd-trace/src/llmobs/writers/spans.js +1 -1
- package/packages/dd-trace/src/opentracing/span.js +5 -0
- package/packages/dd-trace/src/plugin_manager.js +10 -7
- package/packages/dd-trace/src/plugins/util/test.js +76 -0
- package/packages/dd-trace/src/priority_sampler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +35 -28
- package/packages/dd-trace/src/rate_limiter.js +2 -1
- package/packages/dd-trace/src/tagger.js +31 -35
- package/vendor/dist/@apm-js-collab/code-transformer/LICENSE +28 -0
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +133 -0
- package/vendor/dist/@opentelemetry/core/index.js +1 -1
- package/vendor/dist/@opentelemetry/resources/index.js +1 -1
- package/vendor/dist/esquery/index.js +1 -1
- package/vendor/dist/meriyah/index.js +1 -1
- package/webpack.js +3 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/index.js +0 -43
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/matcher.js +0 -49
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/transformer.js +0 -121
- package/vendor/dist/astring/LICENSE +0 -19
- package/vendor/dist/astring/index.js +0 -1
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const path = require('node:path')
|
|
5
|
+
const { createHash } = require('node:crypto')
|
|
6
|
+
const { tmpdir } = require('node:os')
|
|
7
|
+
|
|
8
|
+
const log = require('../../log')
|
|
9
|
+
const { getValueFromEnvSources } = require('../../config/helper')
|
|
10
|
+
|
|
11
|
+
const CACHE_TTL_MS = 30 * 60 * 1000 // 30 minutes
|
|
12
|
+
const CACHE_LOCK_POLL_MS = 500
|
|
13
|
+
const CACHE_LOCK_TIMEOUT_MS = 120_000 // 2 minutes
|
|
14
|
+
const CACHE_LOCK_HEARTBEAT_MS = 30_000 // 30 seconds
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns whether the filesystem cache is enabled via the env var.
|
|
18
|
+
*
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
function isCacheEnabled () {
|
|
22
|
+
const { isTrue } = require('../../util')
|
|
23
|
+
return isTrue(getValueFromEnvSources('DD_EXPERIMENTAL_TEST_REQUESTS_FS_CACHE'))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Builds a deterministic cache key by hashing arbitrary key-value parts.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} prefix - Cache file prefix (e.g. 'known-tests', 'skippable', 'test-mgmt')
|
|
30
|
+
* @param {Array<unknown>} parts - Values that uniquely identify the cached response
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
function buildCacheKey (prefix, parts) {
|
|
34
|
+
const hash = createHash('sha256').update(JSON.stringify(parts)).digest('hex').slice(0, 16)
|
|
35
|
+
return `${prefix}-${hash}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {string} cacheKey
|
|
40
|
+
* @returns {string}
|
|
41
|
+
*/
|
|
42
|
+
function getCachePath (cacheKey) {
|
|
43
|
+
return path.join(tmpdir(), `dd-${cacheKey}.json`)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {string} cacheKey
|
|
48
|
+
* @returns {string}
|
|
49
|
+
*/
|
|
50
|
+
function getLockPath (cacheKey) {
|
|
51
|
+
return path.join(tmpdir(), `dd-${cacheKey}.lock`)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Attempts to read cached data from the filesystem.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} cacheKey
|
|
58
|
+
* @returns {{ data: unknown } | undefined}
|
|
59
|
+
*/
|
|
60
|
+
function readFromCache (cacheKey) {
|
|
61
|
+
const cachePath = getCachePath(cacheKey)
|
|
62
|
+
try {
|
|
63
|
+
const raw = fs.readFileSync(cachePath, 'utf8')
|
|
64
|
+
const parsed = JSON.parse(raw)
|
|
65
|
+
if (!Object.hasOwn(parsed, 'data')) {
|
|
66
|
+
log.debug('%s cache file has no data field, ignoring', cacheKey)
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
const { timestamp, data } = parsed
|
|
70
|
+
if (Date.now() - timestamp > CACHE_TTL_MS) {
|
|
71
|
+
log.debug('%s cache expired (age: %d ms)', cacheKey, Date.now() - timestamp)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
log.debug('%s cache hit', cacheKey)
|
|
75
|
+
return { data }
|
|
76
|
+
} catch {
|
|
77
|
+
// Cache file missing, corrupt, or unreadable — treat as cache miss
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Writes data to the filesystem cache atomically.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} cacheKey
|
|
85
|
+
* @param {unknown} data
|
|
86
|
+
*/
|
|
87
|
+
function writeToCache (cacheKey, data) {
|
|
88
|
+
if (!cacheKey) return
|
|
89
|
+
const cachePath = getCachePath(cacheKey)
|
|
90
|
+
const tmpPath = cachePath + '.tmp.' + process.pid
|
|
91
|
+
try {
|
|
92
|
+
fs.writeFileSync(tmpPath, JSON.stringify({ timestamp: Date.now(), data }), 'utf8')
|
|
93
|
+
fs.renameSync(tmpPath, cachePath)
|
|
94
|
+
log.debug('Cache written: %s', cachePath)
|
|
95
|
+
} catch (err) {
|
|
96
|
+
log.error('Failed to write cache %s: %s', cacheKey, err.message)
|
|
97
|
+
try { fs.unlinkSync(tmpPath) } catch { /* ignore */ }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Attempts to acquire an exclusive lock using O_CREAT|O_EXCL.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} cacheKey
|
|
105
|
+
* @returns {boolean}
|
|
106
|
+
*/
|
|
107
|
+
function tryAcquireLock (cacheKey) {
|
|
108
|
+
const lockPath = getLockPath(cacheKey)
|
|
109
|
+
try {
|
|
110
|
+
const fd = fs.openSync(lockPath, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY)
|
|
111
|
+
fs.writeSync(fd, String(Date.now()))
|
|
112
|
+
fs.closeSync(fd)
|
|
113
|
+
return true
|
|
114
|
+
} catch {
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Removes the lock file.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} cacheKey
|
|
123
|
+
*/
|
|
124
|
+
function releaseLock (cacheKey) {
|
|
125
|
+
try { fs.unlinkSync(getLockPath(cacheKey)) } catch { /* ignore */ }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Updates the lock file timestamp so waiters know the owner is still alive.
|
|
130
|
+
*
|
|
131
|
+
* @param {string} cacheKey
|
|
132
|
+
*/
|
|
133
|
+
function touchLock (cacheKey) {
|
|
134
|
+
const lockPath = getLockPath(cacheKey)
|
|
135
|
+
const tmpPath = lockPath + '.tmp.' + process.pid
|
|
136
|
+
try {
|
|
137
|
+
fs.writeFileSync(tmpPath, String(Date.now()))
|
|
138
|
+
fs.renameSync(tmpPath, lockPath)
|
|
139
|
+
} catch {
|
|
140
|
+
try { fs.unlinkSync(tmpPath) } catch { /* ignore */ }
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Starts a periodic heartbeat that touches the lock file.
|
|
146
|
+
* Returns a function that stops the heartbeat and releases the lock.
|
|
147
|
+
*
|
|
148
|
+
* @param {string} cacheKey
|
|
149
|
+
* @returns {Function}
|
|
150
|
+
*/
|
|
151
|
+
function startLockHeartbeat (cacheKey) {
|
|
152
|
+
const interval = setInterval(() => touchLock(cacheKey), CACHE_LOCK_HEARTBEAT_MS)
|
|
153
|
+
interval.unref()
|
|
154
|
+
return () => {
|
|
155
|
+
clearInterval(interval)
|
|
156
|
+
try { fs.unlinkSync(getLockPath(cacheKey)) } catch { /* ignore */ }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Checks whether the lock file is stale (older than the lock timeout).
|
|
162
|
+
*
|
|
163
|
+
* @param {string} cacheKey
|
|
164
|
+
* @returns {boolean}
|
|
165
|
+
*/
|
|
166
|
+
function isLockStale (cacheKey) {
|
|
167
|
+
try {
|
|
168
|
+
const content = fs.readFileSync(getLockPath(cacheKey), 'utf8')
|
|
169
|
+
return Date.now() - Number(content) > CACHE_LOCK_TIMEOUT_MS
|
|
170
|
+
} catch {
|
|
171
|
+
return true
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Polls until the cache file appears or the timeout is reached.
|
|
177
|
+
*
|
|
178
|
+
* @param {string} cacheKey
|
|
179
|
+
* @param {Function} fetchFn - function(done) that fetches from the API
|
|
180
|
+
* @param {Function} done - callback(err, ...results)
|
|
181
|
+
*/
|
|
182
|
+
function waitForCache (cacheKey, fetchFn, done) {
|
|
183
|
+
const poll = () => {
|
|
184
|
+
const cached = readFromCache(cacheKey)
|
|
185
|
+
if (cached) {
|
|
186
|
+
return done(null, cached.data)
|
|
187
|
+
}
|
|
188
|
+
if (isLockStale(cacheKey)) {
|
|
189
|
+
log.debug('%s lock is stale, attempting takeover', cacheKey)
|
|
190
|
+
releaseLock(cacheKey)
|
|
191
|
+
if (!tryAcquireLock(cacheKey)) {
|
|
192
|
+
return setTimeout(poll, CACHE_LOCK_POLL_MS)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const cachedAfterTakeover = readFromCache(cacheKey)
|
|
196
|
+
if (cachedAfterTakeover) {
|
|
197
|
+
releaseLock(cacheKey)
|
|
198
|
+
return done(null, cachedAfterTakeover.data)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const stopHeartbeat = startLockHeartbeat(cacheKey)
|
|
202
|
+
return fetchFn((err, ...results) => {
|
|
203
|
+
stopHeartbeat()
|
|
204
|
+
done(err, ...results)
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
setTimeout(poll, CACHE_LOCK_POLL_MS)
|
|
208
|
+
}
|
|
209
|
+
poll()
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Wraps a fetch function with filesystem-based caching and cross-process deduplication.
|
|
214
|
+
*
|
|
215
|
+
* When cache is disabled (env var not set), calls fetchFn directly.
|
|
216
|
+
* When enabled, checks cache → acquires lock → fetches → writes cache → releases lock.
|
|
217
|
+
*
|
|
218
|
+
* @param {string} cacheKey - Unique cache key for this request
|
|
219
|
+
* @param {Function} fetchFn - function(cacheKey, done) that performs the API request.
|
|
220
|
+
* Must call writeToCache(cacheKey, data) on success before calling done(null, data).
|
|
221
|
+
* @param {Function} done - callback(err, ...results)
|
|
222
|
+
*/
|
|
223
|
+
function withCache (cacheKey, fetchFn, done) {
|
|
224
|
+
if (!isCacheEnabled()) {
|
|
225
|
+
return fetchFn(null, done)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Fast path: cache hit
|
|
229
|
+
const cached = readFromCache(cacheKey)
|
|
230
|
+
if (cached) {
|
|
231
|
+
return done(null, cached.data)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Try to become the fetcher (lock owner)
|
|
235
|
+
const isLockOwner = tryAcquireLock(cacheKey)
|
|
236
|
+
|
|
237
|
+
if (!isLockOwner) {
|
|
238
|
+
log.debug('%s lock held by another process, waiting for cache', cacheKey)
|
|
239
|
+
return waitForCache(cacheKey, (cb) => fetchFn(cacheKey, cb), done)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// This process owns the lock — start heartbeat and fetch
|
|
243
|
+
const stopHeartbeat = startLockHeartbeat(cacheKey)
|
|
244
|
+
|
|
245
|
+
fetchFn(cacheKey, (err, ...results) => {
|
|
246
|
+
stopHeartbeat()
|
|
247
|
+
done(err, ...results)
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
module.exports = {
|
|
252
|
+
isCacheEnabled,
|
|
253
|
+
buildCacheKey,
|
|
254
|
+
readFromCache,
|
|
255
|
+
writeToCache,
|
|
256
|
+
withCache,
|
|
257
|
+
getCachePath,
|
|
258
|
+
getLockPath,
|
|
259
|
+
}
|
|
@@ -15,6 +15,8 @@ const {
|
|
|
15
15
|
TELEMETRY_TEST_MANAGEMENT_TESTS_RESPONSE_BYTES,
|
|
16
16
|
} = require('../telemetry')
|
|
17
17
|
|
|
18
|
+
const { buildCacheKey, writeToCache, withCache } = require('../requests/fs-cache')
|
|
19
|
+
|
|
18
20
|
// Calculate the number of tests from the test management tests response, which has a shape like:
|
|
19
21
|
// { module: { suites: { suite: { tests: { testName: { properties: {...} } } } } } }
|
|
20
22
|
function getNumFromTestManagementTests (testManagementTests) {
|
|
@@ -48,6 +50,58 @@ function getTestManagementTests ({
|
|
|
48
50
|
commitHeadSha,
|
|
49
51
|
commitHeadMessage,
|
|
50
52
|
branch,
|
|
53
|
+
}, done) {
|
|
54
|
+
const effectiveSha = commitHeadSha || sha
|
|
55
|
+
const cacheKey = buildCacheKey('test-mgmt', [
|
|
56
|
+
effectiveSha, repositoryUrl, branch,
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
withCache(cacheKey, (activeCacheKey, cb) => {
|
|
60
|
+
fetchFromApi({
|
|
61
|
+
url,
|
|
62
|
+
isEvpProxy,
|
|
63
|
+
evpProxyPrefix,
|
|
64
|
+
isGzipCompatible,
|
|
65
|
+
repositoryUrl,
|
|
66
|
+
commitMessage,
|
|
67
|
+
sha,
|
|
68
|
+
commitHeadSha,
|
|
69
|
+
commitHeadMessage,
|
|
70
|
+
branch,
|
|
71
|
+
cacheKey: activeCacheKey,
|
|
72
|
+
}, cb)
|
|
73
|
+
}, done)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Fetches test management tests from the API and writes the result to cache on success.
|
|
78
|
+
*
|
|
79
|
+
* @param {object} params
|
|
80
|
+
* @param {string} params.url
|
|
81
|
+
* @param {boolean} params.isEvpProxy
|
|
82
|
+
* @param {string} params.evpProxyPrefix
|
|
83
|
+
* @param {boolean} params.isGzipCompatible
|
|
84
|
+
* @param {string} params.repositoryUrl
|
|
85
|
+
* @param {string} [params.commitMessage]
|
|
86
|
+
* @param {string} params.sha
|
|
87
|
+
* @param {string} [params.commitHeadSha]
|
|
88
|
+
* @param {string} [params.commitHeadMessage]
|
|
89
|
+
* @param {string} [params.branch]
|
|
90
|
+
* @param {string | null} params.cacheKey
|
|
91
|
+
* @param {Function} done
|
|
92
|
+
*/
|
|
93
|
+
function fetchFromApi ({
|
|
94
|
+
url,
|
|
95
|
+
isEvpProxy,
|
|
96
|
+
evpProxyPrefix,
|
|
97
|
+
isGzipCompatible,
|
|
98
|
+
repositoryUrl,
|
|
99
|
+
commitMessage,
|
|
100
|
+
sha,
|
|
101
|
+
commitHeadSha,
|
|
102
|
+
commitHeadMessage,
|
|
103
|
+
branch,
|
|
104
|
+
cacheKey,
|
|
51
105
|
}, done) {
|
|
52
106
|
const options = {
|
|
53
107
|
path: '/api/v2/test/libraries/test-management/tests',
|
|
@@ -110,6 +164,8 @@ function getTestManagementTests ({
|
|
|
110
164
|
|
|
111
165
|
log.debug('Test management tests received: %j', testManagementTests)
|
|
112
166
|
|
|
167
|
+
writeToCache(cacheKey, testManagementTests)
|
|
168
|
+
|
|
113
169
|
done(null, testManagementTests)
|
|
114
170
|
} catch (err) {
|
|
115
171
|
done(err)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { GeneratedConfig } from './generated-config-types'
|
|
2
|
+
|
|
3
|
+
type PayloadTaggingRules = ReturnType<typeof import('../payload-tagging/config').appendRules> | []
|
|
4
|
+
|
|
5
|
+
export interface ConfigProperties extends GeneratedConfig {
|
|
6
|
+
cloudPayloadTagging: GeneratedConfig['cloudPayloadTagging'] & {
|
|
7
|
+
requestsEnabled: boolean
|
|
8
|
+
responsesEnabled: boolean
|
|
9
|
+
rules: PayloadTaggingRules
|
|
10
|
+
}
|
|
11
|
+
commitSHA: string | undefined
|
|
12
|
+
debug: boolean
|
|
13
|
+
gcpPubSubPushSubscriptionEnabled: boolean
|
|
14
|
+
instrumentationSource: 'manual' | 'ssi'
|
|
15
|
+
isAzureFunction: boolean
|
|
16
|
+
isCiVisibility: boolean
|
|
17
|
+
isGCPFunction: boolean
|
|
18
|
+
isServiceNameInferred: boolean
|
|
19
|
+
isServiceUserProvided: boolean
|
|
20
|
+
logger: import('../../../../index').TracerOptions['logger'] | undefined
|
|
21
|
+
lookup: NonNullable<import('../../../../index').TracerOptions['lookup']>
|
|
22
|
+
readonly parsedDdTags: Record<string, string>
|
|
23
|
+
plugins: boolean
|
|
24
|
+
repositoryUrl: string | undefined
|
|
25
|
+
rules: import('../../../../index').SamplingRule[]
|
|
26
|
+
sampler: {
|
|
27
|
+
rateLimit: number
|
|
28
|
+
rules: import('../../../../index').SamplingRule[]
|
|
29
|
+
sampleRate: number | undefined
|
|
30
|
+
spanSamplingRules: import('../../../../index').SpanSamplingRule[] | undefined
|
|
31
|
+
}
|
|
32
|
+
stableConfig: {
|
|
33
|
+
fleetEntries: Record<string, string>
|
|
34
|
+
localEntries: Record<string, string>
|
|
35
|
+
warnings: string[] | undefined
|
|
36
|
+
}
|
|
37
|
+
tracePropagationStyle: GeneratedConfig['tracePropagationStyle']
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type Primitive = bigint | boolean | null | number | string | symbol | undefined
|
|
41
|
+
type Terminal = Date | Function | Primitive | RegExp | URL
|
|
42
|
+
|
|
43
|
+
type KnownStringKeys<T> = Extract<{
|
|
44
|
+
[K in keyof T]:
|
|
45
|
+
K extends string
|
|
46
|
+
? string extends K
|
|
47
|
+
? never
|
|
48
|
+
: K
|
|
49
|
+
: never
|
|
50
|
+
}[keyof T], string>
|
|
51
|
+
|
|
52
|
+
type NestedConfigPath<T> = [NonNullable<T>] extends [Terminal]
|
|
53
|
+
? never
|
|
54
|
+
: [NonNullable<T>] extends [readonly unknown[]]
|
|
55
|
+
? never
|
|
56
|
+
: [NonNullable<T>] extends [object]
|
|
57
|
+
? ConfigPathFor<NonNullable<T>>
|
|
58
|
+
: never
|
|
59
|
+
|
|
60
|
+
type ConfigPathFor<T> = {
|
|
61
|
+
[K in KnownStringKeys<T>]:
|
|
62
|
+
| K
|
|
63
|
+
| (NestedConfigPath<T[K]> extends never ? never : `${K}.${NestedConfigPath<T[K]>}`)
|
|
64
|
+
}[KnownStringKeys<T>]
|
|
65
|
+
|
|
66
|
+
type ConfigPathValueFor<T, TPath extends string> =
|
|
67
|
+
TPath extends `${infer TKey}.${infer TRest}`
|
|
68
|
+
? TKey extends KnownStringKeys<T>
|
|
69
|
+
? ConfigPathValueFor<NonNullable<T[TKey]>, TRest>
|
|
70
|
+
: never
|
|
71
|
+
: TPath extends KnownStringKeys<T>
|
|
72
|
+
? T[TPath]
|
|
73
|
+
: never
|
|
74
|
+
|
|
75
|
+
export type ConfigKey = KnownStringKeys<ConfigProperties>
|
|
76
|
+
export type ConfigPath = ConfigPathFor<ConfigProperties>
|
|
77
|
+
export type ConfigPathValue<TPath extends ConfigPath> = ConfigPathValueFor<ConfigProperties, TPath>
|
|
78
|
+
export type ConfigDefaults = Partial<{ [TPath in ConfigPath]: ConfigPathValue<TPath> }>
|