From 08837febc993c06ca1403f67b92a5e55e3a3a642 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 22 Jun 2026 16:06:15 +0200 Subject: [PATCH 01/67] Add telemetry-to-caas kind for CaaS (Collector as a Service) support - Add telemetry-to-caas kind definition in package.json - Add getCredsForCaaS() to extract credentials from caas-service binding - Add augmentCaaSCreds() to configure OTLP endpoint URL - Handle CaaS in tracing and metrics exporters Note: CaaS requires mTLS authentication with SAP-signed certificates. The certificate must be obtained separately via BTP Certificate Service. --- lib/metrics/index.js | 9 ++++++++- lib/tracing/index.js | 9 +++++++++ lib/utils.js | 29 +++++++++++++++++++++++++++++ package.json | 17 +++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index 28c001f8..2c9a3f8e 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -12,7 +12,7 @@ const { View } = require('@opentelemetry/sdk-metrics') -const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, augmentCLCreds, _require } = require('../utils') +const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, getCredsForCaaS, augmentCLCreds, augmentCaaSCreds, _require } = require('../utils') const _protocol2module = { grpc: '@opentelemetry/exporter-metrics-otlp-grpc', @@ -70,6 +70,13 @@ function _getExporter() { config.credentials ??= credentials.credentials } + if (kind.match(/to-caas$/)) { + if (!credentials) credentials = getCredsForCaaS() + if (!credentials) throw new Error('No CaaS credentials found.') + augmentCaaSCreds(credentials) + config.url ??= credentials.url + } + // default to DELTA config.temporalityPreference ??= AggregationTemporality.DELTA diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 7d735ddf..d59c00c7 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -11,7 +11,9 @@ const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, + getCredsForCaaS, augmentCLCreds, + augmentCaaSCreds, hasDependency, _require } = require('../utils') @@ -125,6 +127,13 @@ function _getExporter() { config.credentials ??= credentials.credentials } + if (kind.match(/to-caas$/)) { + if (!credentials) credentials = getCredsForCaaS() + if (!credentials) throw new Error('No CaaS credentials found') + augmentCaaSCreds(credentials) + config.url ??= credentials.url + } + const exporter = new tracingExporterModule[tracingExporter.class](config) LOG._debug && LOG.debug('Using trace exporter:', exporter) diff --git a/lib/utils.js b/lib/utils.js index 1c68d306..11c97867 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -129,6 +129,33 @@ function getCredsForCLSAsUPS() { } } +function getCredsForCaaS() { + if (!process.env.VCAP_SERVICES) return + const vcap = JSON.parse(process.env.VCAP_SERVICES) + + // look for caas-service binding + const caas = vcap['caas-service']?.[0] + if (caas) return caas.credentials +} + +function augmentCaaSCreds(credentials) { + if (credentials._augmented) return + credentials._augmented = true + + // check for otlp endpoints + if (!credentials.otlp?.http && !credentials.otlp?.grpc) { + throw new Error('No OTLP endpoints found in CaaS binding. Make sure the CaaS instance is properly configured.') + } + + // use http endpoint by default (proto exporter) + credentials.url = credentials.otlp.http + + // Note: CaaS requires mTLS authentication with SAP-signed certificates + // The certificate must be provided separately via BTP Certificate Service + // For now, we just set the URL and log a warning about mTLS + LOG._warn && LOG.warn('CaaS requires mTLS authentication. Make sure the SAP-signed certificate is configured.') +} + function augmentCLCreds(credentials) { if (credentials._augmented) return credentials._augmented = true @@ -194,7 +221,9 @@ module.exports = { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, + getCredsForCaaS, augmentCLCreds, + augmentCaaSCreds, hasDependency, _hrnow, _require diff --git a/package.json b/package.json index 03583029..4520e429 100644 --- a/package.json +++ b/package.json @@ -153,6 +153,23 @@ "metrics": { "exporter": "env" } + }, + "telemetry-to-caas": { + "vcap": { + "label": "caas-service" + }, + "tracing": { + "exporter": { + "module": "@opentelemetry/exporter-trace-otlp-proto", + "class": "OTLPTraceExporter" + } + }, + "metrics": { + "exporter": { + "module": "@opentelemetry/exporter-metrics-otlp-proto", + "class": "OTLPMetricExporter" + } + } } } } From 79eaadc205a2ed56f4171da2fa5e9197295b4293 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 22 Jun 2026 16:32:00 +0200 Subject: [PATCH 02/67] Add certificate-service detection for CaaS mTLS --- lib/utils.js | 22 +- package-lock.json | 7193 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 7211 insertions(+), 4 deletions(-) create mode 100644 package-lock.json diff --git a/lib/utils.js b/lib/utils.js index 11c97867..1c14bc48 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -138,6 +138,13 @@ function getCredsForCaaS() { if (caas) return caas.credentials } +function getCredsForCertService() { + if (!process.env.VCAP_SERVICES) return + const vcap = JSON.parse(process.env.VCAP_SERVICES) + const certSvc = vcap['certificate-service']?.[0] + if (certSvc) return certSvc.credentials +} + function augmentCaaSCreds(credentials) { if (credentials._augmented) return credentials._augmented = true @@ -150,10 +157,16 @@ function augmentCaaSCreds(credentials) { // use http endpoint by default (proto exporter) credentials.url = credentials.otlp.http - // Note: CaaS requires mTLS authentication with SAP-signed certificates - // The certificate must be provided separately via BTP Certificate Service - // For now, we just set the URL and log a warning about mTLS - LOG._warn && LOG.warn('CaaS requires mTLS authentication. Make sure the SAP-signed certificate is configured.') + // Check for certificate-service binding for mTLS + const certServiceCreds = getCredsForCertService() + if (certServiceCreds) { + LOG._info && LOG.info('Found certificate-service binding for CaaS mTLS') + // Store cert service creds - the app needs to handle certificate enrollment + credentials._certServiceCreds = certServiceCreds + } else { + LOG._warn && LOG.warn('CaaS requires mTLS authentication. No certificate-service binding found.') + LOG._warn && LOG.warn('Bind a certificate-service instance to enable mTLS.') + } } function augmentCLCreds(credentials) { @@ -222,6 +235,7 @@ module.exports = { getCredsForDTAsUPS, getCredsForCLSAsUPS, getCredsForCaaS, + getCredsForCertService, augmentCLCreds, augmentCaaSCreds, hasDependency, diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..3bb0d173 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,7193 @@ +{ + "name": "@cap-js/telemetry", + "version": "1.6.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@cap-js/telemetry", + "version": "1.6.0", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.9", + "@opentelemetry/core": "^1.30", + "@opentelemetry/instrumentation": "^0.57", + "@opentelemetry/instrumentation-http": "^0.57", + "@opentelemetry/instrumentation-undici": "^0.28.0", + "@opentelemetry/resources": "^1.30", + "@opentelemetry/sdk-metrics": "^1.30", + "@opentelemetry/sdk-trace-base": "^1.30", + "@opentelemetry/sdk-trace-node": "^1.30", + "@opentelemetry/semantic-conventions": "^1.36" + }, + "devDependencies": { + "@cap-js/cds-test": "^0", + "@cap-js/sqlite": "^1 || ^2", + "@cap-js/telemetry": "file:.", + "@dynatrace/oneagent-sdk": "^1.5.0", + "@grpc/grpc-js": "^1.9.14", + "@opentelemetry/exporter-metrics-otlp-grpc": "^0.57.0", + "@opentelemetry/exporter-metrics-otlp-proto": "^0.57.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.57.0", + "@opentelemetry/exporter-trace-otlp-proto": "^0.57.0", + "@opentelemetry/host-metrics": "^0.36.0", + "@opentelemetry/instrumentation-runtime-node": "^0.17.0", + "@sap/cds-mtxs": "^2 || ^3", + "axios": "^1.6.7", + "chai": "^4.4.1", + "chai-as-promised": "^7.1.1", + "chai-subset": "^1.6.0", + "eslint": "^10", + "express": "^4.18.2", + "jest": "^29.7.0" + }, + "peerDependencies": { + "@sap/cds": "^8 || ^9" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cap-js/cds-test": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@cap-js/cds-test/-/cds-test-0.4.1.tgz", + "integrity": "sha512-/ig4nSi9n3nvP/g7cBGuLx8vu0Ydc9ed4JGdLQ0F4/fujKp2ZDARwIrmoQFGJoRDUZsK9O5+Sa8X15dl5X3feg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "axios": "^1", + "chai": "^4.4.1", + "chai-as-promised": "^7.1.1", + "chai-subset": "^1.6.0" + }, + "bin": { + "cds-test": "bin/chest.js", + "chest": "bin/chest.js" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@sap/cds": ">=8.8" + } + }, + "node_modules/@cap-js/db-service": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@cap-js/db-service/-/db-service-2.11.0.tgz", + "integrity": "sha512-sl33LcxZYAJgMCQZDw4lMGe4kWYq6685Xc6ze4qcoM+rd6aqiyVsSC6C7XH5yerXs7cVHhRC+Dgo8AsaapFzlQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "generic-pool": "^3.9.0" + }, + "peerDependencies": { + "@sap/cds": ">=9.8" + } + }, + "node_modules/@cap-js/sqlite": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@cap-js/sqlite/-/sqlite-2.4.0.tgz", + "integrity": "sha512-Ao+AzIN6BWHNpLbGxAzF79OezFNHzDG2srwiBABs0FYxIxEGkc2hg6ETo79pTTt66gcWtx7pWh/N9xk2M6SFBQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@cap-js/db-service": "^2.11.0", + "better-sqlite3": "^12.0.0" + }, + "peerDependencies": { + "@sap/cds": ">=9.8", + "sql.js": "^1.13.0" + }, + "peerDependenciesMeta": { + "sql.js": { + "optional": true + } + } + }, + "node_modules/@cap-js/telemetry": { + "resolved": "", + "link": true + }, + "node_modules/@dynatrace/oneagent-sdk": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@dynatrace/oneagent-sdk/-/oneagent-sdk-1.5.0.tgz", + "integrity": "sha512-rnowkg3CKbNdxY6B0P/KSPa2u4fU8vRsrhbGB0YJsiVUS3LJsCvnleMqjK5q9/HrhJ4n+UPuT52NcddKzg0p/g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.4.tgz", + "integrity": "sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz", + "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.57.2.tgz", + "integrity": "sha512-r70B8yKR41F0EC443b5CGB4rUaOMm99I5N75QQt6sHKxYDzSEc6gm48Diz1CI1biwa5tDPznpylTrywO/pT7qw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.57.2", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-grpc-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-metrics": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.57.2.tgz", + "integrity": "sha512-ttb9+4iKw04IMubjm3t0EZsYRNWr3kg44uUuzfo9CaccYlOh8cDooe4QObDUkvx9d5qQUrbEckhrWKfJnKhemA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-metrics": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.57.2.tgz", + "integrity": "sha512-HX068Q2eNs38uf7RIkNN9Hl4Ynl+3lP0++KELkXMCpsCbFO03+0XNNZ1SkwxPlP9jrhQahsMPMkzNXpq3fKsnw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.57.2", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-metrics": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.57.2.tgz", + "integrity": "sha512-gHU1vA3JnHbNxEXg5iysqCWxN9j83d7/epTYBZflqQnTyCC4N7yZXn/dMM+bEmyhQPGjhCkNZLx4vZuChH1PYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-grpc-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.57.2.tgz", + "integrity": "sha512-awDdNRMIwDvUtoRYxRhja5QYH6+McBLtoz1q9BeEsskhZcrGmH/V1fWpGx8n+Rc+542e8pJA6y+aullbIzQmlw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/host-metrics": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/host-metrics/-/host-metrics-0.36.2.tgz", + "integrity": "sha512-eMdea86cfIqx3cdFpcKU3StrjqFkQDIVp7NANVnVWO8O6hDw/DBwGwu4Gi1wJCuoQ2JVwKNWQxUTSRheB6O29Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "systeminformation": "5.23.8" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", + "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.2.tgz", + "integrity": "sha512-1Uz5iJ9ZAlFOiPuwYg29Bf7bJJc/GeoeJIFKJYQf67nTVKFe8RHbEtxgkOmK4UGZNHKXcpW4P8cWBYzBn1USpg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/instrumentation": "0.57.2", + "@opentelemetry/semantic-conventions": "1.28.0", + "forwarded-parse": "2.1.2", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-runtime-node": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.17.1.tgz", + "integrity": "sha512-c1FlAk+bB2uF9a8YneGmNPTl7c/xVaan4mmWvbkWcOmH/ipKqR1LaKUlz/BMzLrJLjho1EJlG2NrS2w2Arg+nw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.203.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-runtime-node/node_modules/@opentelemetry/api-logs": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", + "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-runtime-node/node_modules/@opentelemetry/instrumentation": { + "version": "0.203.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", + "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.203.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.28.0.tgz", + "integrity": "sha512-7nh4Gw7PhYtQm82FIJtWUhx6iZQJj0bdkKe2RQb3XNIyxu0o9rM1J5Xt083SsG2tCbQZpX9/mlDxhTrK1Z/lVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.24.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.7.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/api-logs": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.218.0.tgz", + "integrity": "sha512-fmEWp5kXlGEc3i/lR698Hz41DfGyN4Tbe4g7L1AxSc7fF8Xeh/FQ9Quqpa9dVA413Q1Ad43QOLzU4JoXgbFPWw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/instrumentation": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.218.0.tgz", + "integrity": "sha512-mIZil8Es+sYDK5m+DQiwAwF57F14TF2YlEqvIjZ/RQWcxDBwRGsKfdK2Tv65OU9meQKCMzSIFS9mxAcnAb6Bkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici/node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "license": "MIT" + }, + "node_modules/@opentelemetry/instrumentation-undici/node_modules/import-in-the-middle": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.0.1.tgz", + "integrity": "sha512-pYkiyXVL2Mf3pozdlDGV6NAObxQx13Ae8knZk1UJRJ6uRW/ZRmTGHlQYtrsSl7ubuE5F8CD1z+s1n4RHNuTtuA==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.15.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@opentelemetry/instrumentation-undici/node_modules/require-in-the-middle": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3" + }, + "engines": { + "node": ">=9.3.0 || >=8.10.0 <9.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.57.2.tgz", + "integrity": "sha512-XdxEzL23Urhidyebg5E6jZoaiW5ygP/mRjxLHixogbqwDy2Faduzb5N0o/Oi+XTIJu+iyxXdVORjXax+Qgfxag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-transformer": "0.57.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.57.2.tgz", + "integrity": "sha512-USn173KTWy0saqqRB5yU9xUZ2xdgb1Rdu5IosJnm9aV4hMTuFFRTUsQxbgc24QxpCHeoKzzCSnS/JzdV0oM2iQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/otlp-exporter-base": "0.57.2", + "@opentelemetry/otlp-transformer": "0.57.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.57.2.tgz", + "integrity": "sha512-48IIRj49gbQVK52jYsw70+Jv+JbahT8BqT2Th7C4H7RCM9d0gZ5sgNPoMpWldmfjvIsSgiGJtjfk9MeZvjhoig==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-logs": "0.57.2", + "@opentelemetry/sdk-metrics": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.30.1.tgz", + "integrity": "sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.30.1.tgz", + "integrity": "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.57.2.tgz", + "integrity": "sha512-TXFHJ5c+BKggWbdEQ/inpgIzEmS2BGQowLE9UhsMd7YYlUfBQJ4uax0VF/B5NYigdM/75OoJGhAV3upEhK+3gg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.30.1.tgz", + "integrity": "sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.30.1", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/propagator-b3": "1.30.1", + "@opentelemetry/propagator-jaeger": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", + "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", + "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sap/cds": { + "version": "9.9.1", + "resolved": "https://registry.npmjs.org/@sap/cds/-/cds-9.9.1.tgz", + "integrity": "sha512-GqdsBsRkZThhpOyzj8ihf/jDmf/2zprZFgaun6ZymUw4/ahzjK/bbdd6eQ8txDuv88pnUl2HPFjvUVq3O/6hCA==", + "license": "SEE LICENSE IN LICENSE", + "peer": true, + "dependencies": { + "@sap/cds-compiler": "^6.4", + "@sap/cds-fiori": "^2", + "express": "^4.22.1 || ^5", + "yaml": "^2" + }, + "bin": { + "cds-deploy": "bin/deploy.js", + "cds-serve": "bin/serve.js" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@eslint/js": "^9 || ^10", + "tar": "^7.5.6" + }, + "peerDependenciesMeta": { + "tar": { + "optional": true + } + } + }, + "node_modules/@sap/cds-compiler": { + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/@sap/cds-compiler/-/cds-compiler-6.9.2.tgz", + "integrity": "sha512-Qv7Zb3RhG92WVm1AjHEJaYbOi3tNT051/EWPYTsYdUe5epYXbR4dJfGpD1eEgo82ThrKCFx0BZfT0b28t0/vqg==", + "license": "SEE LICENSE IN LICENSE", + "peer": true, + "bin": { + "cdsc": "bin/cdsc.js", + "cdshi": "bin/cdshi.js", + "cdsse": "bin/cdsse.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@sap/cds-fiori": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sap/cds-fiori/-/cds-fiori-2.3.0.tgz", + "integrity": "sha512-6oWov+DSpFrSTgxXR0dZhak6aZ/IVRZvaHERMi0EgSTzIJdlvZlpw3Kf18ePMcTrRrtEXwD4RIjKt8pbs0g2Hg==", + "license": "SEE LICENSE IN LICENSE", + "peer": true, + "peerDependencies": { + "@sap/cds": ">=8" + } + }, + "node_modules/@sap/cds-mtxs": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@sap/cds-mtxs/-/cds-mtxs-3.9.1.tgz", + "integrity": "sha512-4ZklL+r3dfNjAzIrp0fUHsWNXIKP5Ex0r84WGnPg4HLsvd3o/Y2/ZwYnLx3NjtKWzQUjsWfU0vaNhrJLMrxnSA==", + "dev": true, + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "@sap/hdi-deploy": "^5" + }, + "bin": { + "cds-mtx": "bin/cds-mtx.js", + "cds-mtx-migrate": "bin/cds-mtx-migrate.js" + }, + "peerDependencies": { + "@sap/cds": ">=9" + } + }, + "node_modules/@sap/hdi": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@sap/hdi/-/hdi-4.8.0.tgz", + "integrity": "sha512-tkJmY2ffm6mt4/LFwRBihlQkMxNAXa3ngvRe2N/6+qLIsUNdrH/M03S5mkygXq56K+KoVVZYuradajCusMWwsw==", + "dev": true, + "license": "See LICENSE file", + "dependencies": { + "async": "^3.2.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@sap/hana-client": "^2 >= 2.5", + "hdb": "^2 || ^0" + }, + "peerDependenciesMeta": { + "@sap/hana-client": { + "optional": true + }, + "hdb": { + "optional": true + } + } + }, + "node_modules/@sap/hdi-deploy": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@sap/hdi-deploy/-/hdi-deploy-5.6.1.tgz", + "integrity": "sha512-+qQ7qwG8lko303L5yRj2dud/nDAVuVblV/mmzJT44oPbF0Nry18eD2LUS23hFeuxjRa7rYK5YKQ8ffGgWxVrYQ==", + "dev": true, + "license": "See LICENSE file", + "dependencies": { + "@sap/hdi": "^4.8.0", + "@sap/xsenv": "^6.0.0", + "async": "^3.2.6", + "dotenv": "^16.4.5", + "handlebars": "^4.7.8", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=18.x" + }, + "peerDependencies": { + "@sap/hana-client": "^2 >= 2.6", + "hdb": "^2 || ^0" + }, + "peerDependenciesMeta": { + "@sap/hana-client": { + "optional": true + }, + "hdb": { + "optional": true + } + } + }, + "node_modules/@sap/xsenv": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@sap/xsenv/-/xsenv-6.2.1.tgz", + "integrity": "sha512-R1p7VdD3N3jvdkL8av4vLqF+cTQihTz9mCqqF+oa9rVZvgLaCb4ODyZ1dln5/fBgg1OSuch0ESxu3AqZrXVknw==", + "dev": true, + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "debug": "4.4.3", + "node-cache": "^5.1.2", + "verror": "1.10.1" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/commons/node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==", + "license": "MIT" + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.31", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.31.tgz", + "integrity": "sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/better-sqlite3": { + "version": "12.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.10.0.tgz", + "integrity": "sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x || 26.x" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-as-promised": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 6" + } + }, + "node_modules/chai-subset": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz", + "integrity": "sha512-K3d+KmqdS5XKW5DWPd5sgNffL3uxdDe+6GdnJh3AYPhwnBGRY5urfvfcbRtWIvvpz+KxkL9FeBB6MZewLUNwug==", + "deprecated": "functionality of this lib is built-in to chai now. see more details here: https://github.com/debitoor/chai-subset/pull/85", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.360", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.360.tgz", + "integrity": "sha512-GkcBt6YYAw9SxFWn+xVar4cLVGlXVuswwtRLBozi2zp0GjXs4ZnOrqV4zbXzg35n7w81hCkyJNYicgXlVHAmBA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", + "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "devOptional": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "devOptional": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "devOptional": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "devOptional": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "devOptional": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "devOptional": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", + "license": "MIT" + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-in-the-middle": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.15.0.tgz", + "integrity": "sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.14.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "devOptional": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.92.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", + "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "2.x" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.45", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.45.tgz", + "integrity": "sha512-iIbHXV9eBB2nB0wa7oTsrrXq+qQt+9SIlx9AX3T96YgobtEQfis5n6TJ6vV+3QP8DwdriEAcGhARaFCu37peBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/protobufjs": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.0.tgz", + "integrity": "sha512-LtESOsMPTZgyYtwxhvdgdjGL0HmXEaRA/hVD6sol4zA60hVXXXP/SGmxnqDbgGE8gy7pYex7cym+5vYPcmaXBQ==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", + "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", + "license": "BSD-2-Clause" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/systeminformation": { + "version": "5.23.8", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.23.8.tgz", + "integrity": "sha512-Osd24mNKe6jr/YoXLLK3k8TMdzaxDffhpCxgkfgBHcapykIkd50HXThM3TCEuHO2pPuCsSx2ms/SunqhU5MmsQ==", + "dev": true, + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "devOptional": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} From 12223ceed9811d647d9b0529a41644568aa522c1 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 22 Jun 2026 17:03:49 +0200 Subject: [PATCH 03/67] Add mTLS support for CaaS via caas-mtls-creds user-provided service --- lib/metrics/index.js | 9 +++++++++ lib/tracing/index.js | 9 +++++++++ lib/utils.js | 44 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index 2c9a3f8e..d4372133 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -75,6 +75,15 @@ function _getExporter() { if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) config.url ??= credentials.url + // Configure mTLS via TLS options + if (credentials.httpAgent) { + const agentOpts = credentials.httpAgent.options + config.tlsOptions = { + cert: agentOpts.cert, + key: agentOpts.key, + rejectUnauthorized: agentOpts.rejectUnauthorized + } + } } // default to DELTA diff --git a/lib/tracing/index.js b/lib/tracing/index.js index d59c00c7..f64a62c1 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -132,6 +132,15 @@ function _getExporter() { if (!credentials) throw new Error('No CaaS credentials found') augmentCaaSCreds(credentials) config.url ??= credentials.url + // Configure mTLS via TLS options + if (credentials.httpAgent) { + const agentOpts = credentials.httpAgent.options + config.tlsOptions = { + cert: agentOpts.cert, + key: agentOpts.key, + rejectUnauthorized: agentOpts.rejectUnauthorized + } + } } const exporter = new tracingExporterModule[tracingExporter.class](config) diff --git a/lib/utils.js b/lib/utils.js index 1c14bc48..8700bd15 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -145,6 +145,14 @@ function getCredsForCertService() { if (certSvc) return certSvc.credentials } +function getCredsForCaaSMtls() { + if (!process.env.VCAP_SERVICES) return + const vcap = JSON.parse(process.env.VCAP_SERVICES) + // Look for user-provided service with name containing 'caas-mtls' or 'caas-cert' + const mtlsCreds = vcap['user-provided']?.find(b => b.name.match(/caas-mtls|caas-cert/i)) + if (mtlsCreds) return mtlsCreds.credentials +} + function augmentCaaSCreds(credentials) { if (credentials._augmented) return credentials._augmented = true @@ -157,15 +165,35 @@ function augmentCaaSCreds(credentials) { // use http endpoint by default (proto exporter) credentials.url = credentials.otlp.http - // Check for certificate-service binding for mTLS - const certServiceCreds = getCredsForCertService() - if (certServiceCreds) { - LOG._info && LOG.info('Found certificate-service binding for CaaS mTLS') - // Store cert service creds - the app needs to handle certificate enrollment - credentials._certServiceCreds = certServiceCreds + // Check for mTLS credentials (cert + key) in user-provided service + const mtlsCreds = getCredsForCaaSMtls() + if (mtlsCreds && mtlsCreds.cert && mtlsCreds.key) { + LOG._info && LOG.info('Found CaaS mTLS credentials, configuring HTTPS agent') + try { + const https = require('https') + const cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') + const key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') + + // Create HTTPS agent with client certificate + credentials.httpAgent = new https.Agent({ + cert: cert, + key: key, + rejectUnauthorized: true + }) + LOG._info && LOG.info('CaaS mTLS configured successfully') + } catch (err) { + LOG._error && LOG.error('Failed to configure CaaS mTLS:', err.message) + } } else { - LOG._warn && LOG.warn('CaaS requires mTLS authentication. No certificate-service binding found.') - LOG._warn && LOG.warn('Bind a certificate-service instance to enable mTLS.') + // Check for certificate-service binding (manual enrollment needed) + const certServiceCreds = getCredsForCertService() + if (certServiceCreds) { + LOG._warn && LOG.warn('Found certificate-service binding but no caas-mtls-creds.') + LOG._warn && LOG.warn('Run the certificate enrollment script and create caas-mtls-creds service.') + } else { + LOG._warn && LOG.warn('CaaS requires mTLS authentication. No mTLS credentials found.') + LOG._warn && LOG.warn('Bind a caas-mtls-creds user-provided service with cert and key.') + } } } From 2da163c23994f18bd560e839b91d0aa26580f9b5 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 22 Jun 2026 17:08:44 +0200 Subject: [PATCH 04/67] Fix mTLS config - use httpAgentOptions --- lib/metrics/index.js | 10 ++++------ lib/tracing/index.js | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index d4372133..9f8fc673 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -75,13 +75,11 @@ function _getExporter() { if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) config.url ??= credentials.url - // Configure mTLS via TLS options + // Configure mTLS via custom httpAgent if (credentials.httpAgent) { - const agentOpts = credentials.httpAgent.options - config.tlsOptions = { - cert: agentOpts.cert, - key: agentOpts.key, - rejectUnauthorized: agentOpts.rejectUnauthorized + config.httpAgentOptions = { + cert: credentials.httpAgent.options.cert, + key: credentials.httpAgent.options.key } } } diff --git a/lib/tracing/index.js b/lib/tracing/index.js index f64a62c1..80ae33dc 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -132,13 +132,11 @@ function _getExporter() { if (!credentials) throw new Error('No CaaS credentials found') augmentCaaSCreds(credentials) config.url ??= credentials.url - // Configure mTLS via TLS options + // Configure mTLS via custom httpAgent if (credentials.httpAgent) { - const agentOpts = credentials.httpAgent.options - config.tlsOptions = { - cert: agentOpts.cert, - key: agentOpts.key, - rejectUnauthorized: agentOpts.rejectUnauthorized + config.httpAgentOptions = { + cert: credentials.httpAgent.options.cert, + key: credentials.httpAgent.options.key } } } From 46b012997c1b7bf3cd4ed643456e7310ee3b82cb Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 22 Jun 2026 17:14:14 +0200 Subject: [PATCH 05/67] Monkey-patch https.request for CaaS mTLS --- lib/metrics/index.js | 7 ------- lib/tracing/index.js | 7 ------- lib/utils.js | 29 ++++++++++++++++++++++++++--- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index 9f8fc673..2c9a3f8e 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -75,13 +75,6 @@ function _getExporter() { if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) config.url ??= credentials.url - // Configure mTLS via custom httpAgent - if (credentials.httpAgent) { - config.httpAgentOptions = { - cert: credentials.httpAgent.options.cert, - key: credentials.httpAgent.options.key - } - } } // default to DELTA diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 80ae33dc..d59c00c7 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -132,13 +132,6 @@ function _getExporter() { if (!credentials) throw new Error('No CaaS credentials found') augmentCaaSCreds(credentials) config.url ??= credentials.url - // Configure mTLS via custom httpAgent - if (credentials.httpAgent) { - config.httpAgentOptions = { - cert: credentials.httpAgent.options.cert, - key: credentials.httpAgent.options.key - } - } } const exporter = new tracingExporterModule[tracingExporter.class](config) diff --git a/lib/utils.js b/lib/utils.js index 8700bd15..3544b98e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -174,12 +174,35 @@ function augmentCaaSCreds(credentials) { const cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') const key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') - // Create HTTPS agent with client certificate - credentials.httpAgent = new https.Agent({ + // Override the global https agent to use client certificates + // This is needed because OTLP HTTP exporter doesn't support passing certs directly + const originalAgent = https.globalAgent + const customAgent = new https.Agent({ cert: cert, key: key, - rejectUnauthorized: true + keepAlive: true }) + + // Store original agent for potential restoration + credentials._originalAgent = originalAgent + + // Monkey-patch https.request to use our agent for CaaS endpoints + const originalRequest = https.request + https.request = function(options, callback) { + if (typeof options === 'string' || options instanceof URL) { + const url = new URL(options) + if (url.hostname.includes('caas.shoot.live.k8s-hana.ondemand.com')) { + options = { ...options } + options.agent = customAgent + } + } else if (options && options.hostname && options.hostname.includes('caas.shoot.live.k8s-hana.ondemand.com')) { + options = { ...options, agent: customAgent } + } else if (options && options.host && options.host.includes('caas.shoot.live.k8s-hana.ondemand.com')) { + options = { ...options, agent: customAgent } + } + return originalRequest.call(this, options, callback) + } + LOG._info && LOG.info('CaaS mTLS configured successfully') } catch (err) { LOG._error && LOG.error('Failed to configure CaaS mTLS:', err.message) From a10eb6b6f5065dfd62923e558383ae86faed276f Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 22 Jun 2026 18:08:07 +0200 Subject: [PATCH 06/67] Remove package-lock.json from tracking This file should not be committed to the feature branch. --- package-lock.json | 7193 --------------------------------------------- 1 file changed, 7193 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 3bb0d173..00000000 --- a/package-lock.json +++ /dev/null @@ -1,7193 +0,0 @@ -{ - "name": "@cap-js/telemetry", - "version": "1.6.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@cap-js/telemetry", - "version": "1.6.0", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.9", - "@opentelemetry/core": "^1.30", - "@opentelemetry/instrumentation": "^0.57", - "@opentelemetry/instrumentation-http": "^0.57", - "@opentelemetry/instrumentation-undici": "^0.28.0", - "@opentelemetry/resources": "^1.30", - "@opentelemetry/sdk-metrics": "^1.30", - "@opentelemetry/sdk-trace-base": "^1.30", - "@opentelemetry/sdk-trace-node": "^1.30", - "@opentelemetry/semantic-conventions": "^1.36" - }, - "devDependencies": { - "@cap-js/cds-test": "^0", - "@cap-js/sqlite": "^1 || ^2", - "@cap-js/telemetry": "file:.", - "@dynatrace/oneagent-sdk": "^1.5.0", - "@grpc/grpc-js": "^1.9.14", - "@opentelemetry/exporter-metrics-otlp-grpc": "^0.57.0", - "@opentelemetry/exporter-metrics-otlp-proto": "^0.57.0", - "@opentelemetry/exporter-trace-otlp-grpc": "^0.57.0", - "@opentelemetry/exporter-trace-otlp-proto": "^0.57.0", - "@opentelemetry/host-metrics": "^0.36.0", - "@opentelemetry/instrumentation-runtime-node": "^0.17.0", - "@sap/cds-mtxs": "^2 || ^3", - "axios": "^1.6.7", - "chai": "^4.4.1", - "chai-as-promised": "^7.1.1", - "chai-subset": "^1.6.0", - "eslint": "^10", - "express": "^4.18.2", - "jest": "^29.7.0" - }, - "peerDependencies": { - "@sap/cds": "^8 || ^9" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", - "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@cap-js/cds-test": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@cap-js/cds-test/-/cds-test-0.4.1.tgz", - "integrity": "sha512-/ig4nSi9n3nvP/g7cBGuLx8vu0Ydc9ed4JGdLQ0F4/fujKp2ZDARwIrmoQFGJoRDUZsK9O5+Sa8X15dl5X3feg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "axios": "^1", - "chai": "^4.4.1", - "chai-as-promised": "^7.1.1", - "chai-subset": "^1.6.0" - }, - "bin": { - "cds-test": "bin/chest.js", - "chest": "bin/chest.js" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@sap/cds": ">=8.8" - } - }, - "node_modules/@cap-js/db-service": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@cap-js/db-service/-/db-service-2.11.0.tgz", - "integrity": "sha512-sl33LcxZYAJgMCQZDw4lMGe4kWYq6685Xc6ze4qcoM+rd6aqiyVsSC6C7XH5yerXs7cVHhRC+Dgo8AsaapFzlQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "generic-pool": "^3.9.0" - }, - "peerDependencies": { - "@sap/cds": ">=9.8" - } - }, - "node_modules/@cap-js/sqlite": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@cap-js/sqlite/-/sqlite-2.4.0.tgz", - "integrity": "sha512-Ao+AzIN6BWHNpLbGxAzF79OezFNHzDG2srwiBABs0FYxIxEGkc2hg6ETo79pTTt66gcWtx7pWh/N9xk2M6SFBQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@cap-js/db-service": "^2.11.0", - "better-sqlite3": "^12.0.0" - }, - "peerDependencies": { - "@sap/cds": ">=9.8", - "sql.js": "^1.13.0" - }, - "peerDependenciesMeta": { - "sql.js": { - "optional": true - } - } - }, - "node_modules/@cap-js/telemetry": { - "resolved": "", - "link": true - }, - "node_modules/@dynatrace/oneagent-sdk": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@dynatrace/oneagent-sdk/-/oneagent-sdk-1.5.0.tgz", - "integrity": "sha512-rnowkg3CKbNdxY6B0P/KSPa2u4fU8vRsrhbGB0YJsiVUS3LJsCvnleMqjK5q9/HrhJ4n+UPuT52NcddKzg0p/g==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", - "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^3.0.5", - "debug": "^4.3.1", - "minimatch": "^10.2.4" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", - "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/js": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", - "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", - "license": "MIT", - "peer": true, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "eslint": "^10.0.0" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/@eslint/object-schema": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", - "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", - "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1", - "levn": "^0.4.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.4.tgz", - "integrity": "sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@grpc/proto-loader": "^0.8.0", - "@js-sdsl/ordered-map": "^4.4.2" - }, - "engines": { - "node": ">=12.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", - "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.5.5", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/types": "^0.15.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", - "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/@opentelemetry/api": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", - "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/api-logs": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz", - "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", - "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/core": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", - "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.28.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", - "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.57.2.tgz", - "integrity": "sha512-r70B8yKR41F0EC443b5CGB4rUaOMm99I5N75QQt6sHKxYDzSEc6gm48Diz1CI1biwa5tDPznpylTrywO/pT7qw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.30.1", - "@opentelemetry/exporter-metrics-otlp-http": "0.57.2", - "@opentelemetry/otlp-exporter-base": "0.57.2", - "@opentelemetry/otlp-grpc-exporter-base": "0.57.2", - "@opentelemetry/otlp-transformer": "0.57.2", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/sdk-metrics": "1.30.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-metrics-otlp-http": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.57.2.tgz", - "integrity": "sha512-ttb9+4iKw04IMubjm3t0EZsYRNWr3kg44uUuzfo9CaccYlOh8cDooe4QObDUkvx9d5qQUrbEckhrWKfJnKhemA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/otlp-exporter-base": "0.57.2", - "@opentelemetry/otlp-transformer": "0.57.2", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/sdk-metrics": "1.30.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.57.2.tgz", - "integrity": "sha512-HX068Q2eNs38uf7RIkNN9Hl4Ynl+3lP0++KELkXMCpsCbFO03+0XNNZ1SkwxPlP9jrhQahsMPMkzNXpq3fKsnw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/exporter-metrics-otlp-http": "0.57.2", - "@opentelemetry/otlp-exporter-base": "0.57.2", - "@opentelemetry/otlp-transformer": "0.57.2", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/sdk-metrics": "1.30.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.57.2.tgz", - "integrity": "sha512-gHU1vA3JnHbNxEXg5iysqCWxN9j83d7/epTYBZflqQnTyCC4N7yZXn/dMM+bEmyhQPGjhCkNZLx4vZuChH1PYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.30.1", - "@opentelemetry/otlp-exporter-base": "0.57.2", - "@opentelemetry/otlp-grpc-exporter-base": "0.57.2", - "@opentelemetry/otlp-transformer": "0.57.2", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/sdk-trace-base": "1.30.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.57.2.tgz", - "integrity": "sha512-awDdNRMIwDvUtoRYxRhja5QYH6+McBLtoz1q9BeEsskhZcrGmH/V1fWpGx8n+Rc+542e8pJA6y+aullbIzQmlw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/otlp-exporter-base": "0.57.2", - "@opentelemetry/otlp-transformer": "0.57.2", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/sdk-trace-base": "1.30.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/host-metrics": { - "version": "0.36.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/host-metrics/-/host-metrics-0.36.2.tgz", - "integrity": "sha512-eMdea86cfIqx3cdFpcKU3StrjqFkQDIVp7NANVnVWO8O6hDw/DBwGwu4Gi1wJCuoQ2JVwKNWQxUTSRheB6O29Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "systeminformation": "5.23.8" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", - "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.57.2", - "@types/shimmer": "^1.2.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.2", - "shimmer": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.2.tgz", - "integrity": "sha512-1Uz5iJ9ZAlFOiPuwYg29Bf7bJJc/GeoeJIFKJYQf67nTVKFe8RHbEtxgkOmK4UGZNHKXcpW4P8cWBYzBn1USpg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/instrumentation": "0.57.2", - "@opentelemetry/semantic-conventions": "1.28.0", - "forwarded-parse": "2.1.2", - "semver": "^7.5.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", - "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/instrumentation-runtime-node": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.17.1.tgz", - "integrity": "sha512-c1FlAk+bB2uF9a8YneGmNPTl7c/xVaan4mmWvbkWcOmH/ipKqR1LaKUlz/BMzLrJLjho1EJlG2NrS2w2Arg+nw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-runtime-node/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-runtime-node/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.28.0.tgz", - "integrity": "sha512-7nh4Gw7PhYtQm82FIJtWUhx6iZQJj0bdkKe2RQb3XNIyxu0o9rM1J5Xt083SsG2tCbQZpX9/mlDxhTrK1Z/lVQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.218.0", - "@opentelemetry/semantic-conventions": "^1.24.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.7.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/api-logs": { - "version": "0.218.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.218.0.tgz", - "integrity": "sha512-fmEWp5kXlGEc3i/lR698Hz41DfGyN4Tbe4g7L1AxSc7fF8Xeh/FQ9Quqpa9dVA413Q1Ad43QOLzU4JoXgbFPWw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/core": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", - "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/instrumentation": { - "version": "0.218.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.218.0.tgz", - "integrity": "sha512-mIZil8Es+sYDK5m+DQiwAwF57F14TF2YlEqvIjZ/RQWcxDBwRGsKfdK2Tv65OU9meQKCMzSIFS9mxAcnAb6Bkg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.218.0", - "import-in-the-middle": "^3.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/import-in-the-middle": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.0.1.tgz", - "integrity": "sha512-pYkiyXVL2Mf3pozdlDGV6NAObxQx13Ae8knZk1UJRJ6uRW/ZRmTGHlQYtrsSl7ubuE5F8CD1z+s1n4RHNuTtuA==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.57.2.tgz", - "integrity": "sha512-XdxEzL23Urhidyebg5E6jZoaiW5ygP/mRjxLHixogbqwDy2Faduzb5N0o/Oi+XTIJu+iyxXdVORjXax+Qgfxag==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/otlp-transformer": "0.57.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.57.2.tgz", - "integrity": "sha512-USn173KTWy0saqqRB5yU9xUZ2xdgb1Rdu5IosJnm9aV4hMTuFFRTUsQxbgc24QxpCHeoKzzCSnS/JzdV0oM2iQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.30.1", - "@opentelemetry/otlp-exporter-base": "0.57.2", - "@opentelemetry/otlp-transformer": "0.57.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.57.2.tgz", - "integrity": "sha512-48IIRj49gbQVK52jYsw70+Jv+JbahT8BqT2Th7C4H7RCM9d0gZ5sgNPoMpWldmfjvIsSgiGJtjfk9MeZvjhoig==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.57.2", - "@opentelemetry/core": "1.30.1", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/sdk-logs": "0.57.2", - "@opentelemetry/sdk-metrics": "1.30.1", - "@opentelemetry/sdk-trace-base": "1.30.1", - "protobufjs": "^7.3.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/propagator-b3": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.30.1.tgz", - "integrity": "sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/propagator-jaeger": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.30.1.tgz", - "integrity": "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", - "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/semantic-conventions": "1.28.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", - "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/sdk-logs": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.57.2.tgz", - "integrity": "sha512-TXFHJ5c+BKggWbdEQ/inpgIzEmS2BGQowLE9UhsMd7YYlUfBQJ4uax0VF/B5NYigdM/75OoJGhAV3upEhK+3gg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.57.2", - "@opentelemetry/core": "1.30.1", - "@opentelemetry/resources": "1.30.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", - "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/resources": "1.30.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", - "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/semantic-conventions": "1.28.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", - "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/sdk-trace-node": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.30.1.tgz", - "integrity": "sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/context-async-hooks": "1.30.1", - "@opentelemetry/core": "1.30.1", - "@opentelemetry/propagator-b3": "1.30.1", - "@opentelemetry/propagator-jaeger": "1.30.1", - "@opentelemetry/sdk-trace-base": "1.30.1", - "semver": "^7.5.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.41.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", - "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", - "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", - "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@sap/cds": { - "version": "9.9.1", - "resolved": "https://registry.npmjs.org/@sap/cds/-/cds-9.9.1.tgz", - "integrity": "sha512-GqdsBsRkZThhpOyzj8ihf/jDmf/2zprZFgaun6ZymUw4/ahzjK/bbdd6eQ8txDuv88pnUl2HPFjvUVq3O/6hCA==", - "license": "SEE LICENSE IN LICENSE", - "peer": true, - "dependencies": { - "@sap/cds-compiler": "^6.4", - "@sap/cds-fiori": "^2", - "express": "^4.22.1 || ^5", - "yaml": "^2" - }, - "bin": { - "cds-deploy": "bin/deploy.js", - "cds-serve": "bin/serve.js" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@eslint/js": "^9 || ^10", - "tar": "^7.5.6" - }, - "peerDependenciesMeta": { - "tar": { - "optional": true - } - } - }, - "node_modules/@sap/cds-compiler": { - "version": "6.9.2", - "resolved": "https://registry.npmjs.org/@sap/cds-compiler/-/cds-compiler-6.9.2.tgz", - "integrity": "sha512-Qv7Zb3RhG92WVm1AjHEJaYbOi3tNT051/EWPYTsYdUe5epYXbR4dJfGpD1eEgo82ThrKCFx0BZfT0b28t0/vqg==", - "license": "SEE LICENSE IN LICENSE", - "peer": true, - "bin": { - "cdsc": "bin/cdsc.js", - "cdshi": "bin/cdshi.js", - "cdsse": "bin/cdsse.js" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@sap/cds-fiori": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@sap/cds-fiori/-/cds-fiori-2.3.0.tgz", - "integrity": "sha512-6oWov+DSpFrSTgxXR0dZhak6aZ/IVRZvaHERMi0EgSTzIJdlvZlpw3Kf18ePMcTrRrtEXwD4RIjKt8pbs0g2Hg==", - "license": "SEE LICENSE IN LICENSE", - "peer": true, - "peerDependencies": { - "@sap/cds": ">=8" - } - }, - "node_modules/@sap/cds-mtxs": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@sap/cds-mtxs/-/cds-mtxs-3.9.1.tgz", - "integrity": "sha512-4ZklL+r3dfNjAzIrp0fUHsWNXIKP5Ex0r84WGnPg4HLsvd3o/Y2/ZwYnLx3NjtKWzQUjsWfU0vaNhrJLMrxnSA==", - "dev": true, - "license": "SEE LICENSE IN LICENSE", - "dependencies": { - "@sap/hdi-deploy": "^5" - }, - "bin": { - "cds-mtx": "bin/cds-mtx.js", - "cds-mtx-migrate": "bin/cds-mtx-migrate.js" - }, - "peerDependencies": { - "@sap/cds": ">=9" - } - }, - "node_modules/@sap/hdi": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@sap/hdi/-/hdi-4.8.0.tgz", - "integrity": "sha512-tkJmY2ffm6mt4/LFwRBihlQkMxNAXa3ngvRe2N/6+qLIsUNdrH/M03S5mkygXq56K+KoVVZYuradajCusMWwsw==", - "dev": true, - "license": "See LICENSE file", - "dependencies": { - "async": "^3.2.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@sap/hana-client": "^2 >= 2.5", - "hdb": "^2 || ^0" - }, - "peerDependenciesMeta": { - "@sap/hana-client": { - "optional": true - }, - "hdb": { - "optional": true - } - } - }, - "node_modules/@sap/hdi-deploy": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@sap/hdi-deploy/-/hdi-deploy-5.6.1.tgz", - "integrity": "sha512-+qQ7qwG8lko303L5yRj2dud/nDAVuVblV/mmzJT44oPbF0Nry18eD2LUS23hFeuxjRa7rYK5YKQ8ffGgWxVrYQ==", - "dev": true, - "license": "See LICENSE file", - "dependencies": { - "@sap/hdi": "^4.8.0", - "@sap/xsenv": "^6.0.0", - "async": "^3.2.6", - "dotenv": "^16.4.5", - "handlebars": "^4.7.8", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=18.x" - }, - "peerDependencies": { - "@sap/hana-client": "^2 >= 2.6", - "hdb": "^2 || ^0" - }, - "peerDependenciesMeta": { - "@sap/hana-client": { - "optional": true - }, - "hdb": { - "optional": true - } - } - }, - "node_modules/@sap/xsenv": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sap/xsenv/-/xsenv-6.2.1.tgz", - "integrity": "sha512-R1p7VdD3N3jvdkL8av4vLqF+cTQihTz9mCqqF+oa9rVZvgLaCb4ODyZ1dln5/fBgg1OSuch0ESxu3AqZrXVknw==", - "dev": true, - "license": "SEE LICENSE IN LICENSE", - "dependencies": { - "debug": "4.4.3", - "node-cache": "^5.1.2", - "verror": "1.10.1" - }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || ^24.0.0" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/commons/node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/esrecurse": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", - "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", - "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, - "node_modules/@types/shimmer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", - "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==", - "license": "MIT" - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "devOptional": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", - "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.31.tgz", - "integrity": "sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/better-sqlite3": { - "version": "12.10.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.10.0.tgz", - "integrity": "sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "bindings": "^1.5.0", - "prebuild-install": "^7.1.1" - }, - "engines": { - "node": "20.x || 22.x || 23.x || 24.x || 25.x || 26.x" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", - "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.15.1", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001793", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", - "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chai-as-promised": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", - "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", - "dev": true, - "license": "WTFPL", - "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 6" - } - }, - "node_modules/chai-subset": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz", - "integrity": "sha512-K3d+KmqdS5XKW5DWPd5sgNffL3uxdDe+6GdnJh3AYPhwnBGRY5urfvfcbRtWIvvpz+KxkL9FeBB6MZewLUNwug==", - "deprecated": "functionality of this lib is built-in to chai now. see more details here: https://github.com/debitoor/chai-subset/pull/85", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, - "license": "ISC" - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "license": "MIT" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", - "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dedent": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", - "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.360", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.360.tgz", - "integrity": "sha512-GkcBt6YYAw9SxFWn+xVar4cLVGlXVuswwtRLBozi2zp0GjXs4ZnOrqV4zbXzg35n7w81hCkyJNYicgXlVHAmBA==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", - "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.6.0", - "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.2", - "eslint-visitor-keys": "^5.0.1", - "espree": "^11.2.0", - "esquery": "^1.7.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", - "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", - "devOptional": true, - "license": "BSD-2-Clause", - "dependencies": { - "@types/esrecurse": "^4.3.1", - "@types/estree": "^1.0.8", - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", - "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", - "devOptional": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.16.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "devOptional": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "devOptional": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "devOptional": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "devOptional": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true, - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/express": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", - "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.5", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.15.1", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/extsprintf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "devOptional": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/forwarded-parse": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", - "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", - "license": "MIT" - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, - "license": "MIT" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "devOptional": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.9", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", - "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-in-the-middle": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.15.0.tgz", - "integrity": "sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.14.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^1.2.2", - "module-details-from-path": "^1.0.3" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "devOptional": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "devOptional": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true, - "license": "MIT" - }, - "node_modules/module-details-from-path": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", - "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-abi": { - "version": "3.92.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", - "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-cache": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", - "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "2.x" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.45", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.45.tgz", - "integrity": "sha512-iIbHXV9eBB2nB0wa7oTsrrXq+qQt+9SIlx9AX3T96YgobtEQfis5n6TJ6vV+3QP8DwdriEAcGhARaFCu37peBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", - "license": "MIT" - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/protobufjs": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.0.tgz", - "integrity": "sha512-LtESOsMPTZgyYtwxhvdgdjGL0HmXEaRA/hVD6sol4zA60hVXXXP/SGmxnqDbgGE8gy7pYex7cym+5vYPcmaXBQ==", - "dev": true, - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.5", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.1", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.2", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.1", - "@types/node": ">=13.7.0", - "long": "^5.3.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/pump": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", - "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-in-the-middle": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", - "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3", - "resolve": "^1.22.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shimmer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", - "license": "BSD-2-Clause" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/systeminformation": { - "version": "5.23.8", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.23.8.tgz", - "integrity": "sha512-Osd24mNKe6jr/YoXLLK3k8TMdzaxDffhpCxgkfgBHcapykIkd50HXThM3TCEuHO2pPuCsSx2ms/SunqhU5MmsQ==", - "dev": true, - "license": "MIT", - "os": [ - "darwin", - "linux", - "win32", - "freebsd", - "openbsd", - "netbsd", - "sunos", - "android" - ], - "bin": { - "systeminformation": "lib/cli.js" - }, - "engines": { - "node": ">=8.0.0" - }, - "funding": { - "type": "Buy me a coffee", - "url": "https://www.buymeacoffee.com/systeminfo" - } - }, - "node_modules/tar-fs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "devOptional": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "devOptional": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "license": "ISC", - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} From 41f70d5b99b09a462aa22017228f1743fa2e5d56 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 22 Jun 2026 18:19:19 +0200 Subject: [PATCH 07/67] Use agentOptions for CaaS mTLS instead of monkey-patching - Pass httpAgentOptions to OTLP exporter config as agentOptions - Add debug logging to verify cert is being passed - Remove monkey-patching of https.request (didn't work due to OTEL instrumentation) --- lib/metrics/index.js | 7 +++++++ lib/tracing/index.js | 7 +++++++ lib/utils.js | 32 +++++--------------------------- 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index 2c9a3f8e..47a79305 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -75,6 +75,13 @@ function _getExporter() { if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) config.url ??= credentials.url + // Pass mTLS agent options if available (OTLP exporter uses 'agentOptions') + if (credentials.httpAgentOptions) { + config.agentOptions = credentials.httpAgentOptions + LOG._info && LOG.info('CaaS metrics exporter config: url=' + config.url + ', agentOptions.cert exists=' + !!config.agentOptions.cert) + } else { + LOG._warn && LOG.warn('CaaS metrics: no httpAgentOptions found in credentials') + } } // default to DELTA diff --git a/lib/tracing/index.js b/lib/tracing/index.js index d59c00c7..ca581fc7 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -132,6 +132,13 @@ function _getExporter() { if (!credentials) throw new Error('No CaaS credentials found') augmentCaaSCreds(credentials) config.url ??= credentials.url + // Pass mTLS agent options if available (OTLP exporter uses 'agentOptions') + if (credentials.httpAgentOptions) { + config.agentOptions = credentials.httpAgentOptions + LOG._info && LOG.info('CaaS exporter config: url=' + config.url + ', agentOptions.cert exists=' + !!config.agentOptions.cert) + } else { + LOG._warn && LOG.warn('CaaS: no httpAgentOptions found in credentials') + } } const exporter = new tracingExporterModule[tracingExporter.class](config) diff --git a/lib/utils.js b/lib/utils.js index 3544b98e..7f86bc9e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -168,42 +168,20 @@ function augmentCaaSCreds(credentials) { // Check for mTLS credentials (cert + key) in user-provided service const mtlsCreds = getCredsForCaaSMtls() if (mtlsCreds && mtlsCreds.cert && mtlsCreds.key) { - LOG._info && LOG.info('Found CaaS mTLS credentials, configuring HTTPS agent') + LOG._info && LOG.info('Found CaaS mTLS credentials, configuring HTTPS agent options') try { - const https = require('https') const cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') const key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') - // Override the global https agent to use client certificates - // This is needed because OTLP HTTP exporter doesn't support passing certs directly - const originalAgent = https.globalAgent - const customAgent = new https.Agent({ + // Store the mTLS options for the exporter's httpAgentOptions + // The OTLP HTTP exporter will create an https.Agent with these options + credentials.httpAgentOptions = { cert: cert, key: key, keepAlive: true - }) - - // Store original agent for potential restoration - credentials._originalAgent = originalAgent - - // Monkey-patch https.request to use our agent for CaaS endpoints - const originalRequest = https.request - https.request = function(options, callback) { - if (typeof options === 'string' || options instanceof URL) { - const url = new URL(options) - if (url.hostname.includes('caas.shoot.live.k8s-hana.ondemand.com')) { - options = { ...options } - options.agent = customAgent - } - } else if (options && options.hostname && options.hostname.includes('caas.shoot.live.k8s-hana.ondemand.com')) { - options = { ...options, agent: customAgent } - } else if (options && options.host && options.host.includes('caas.shoot.live.k8s-hana.ondemand.com')) { - options = { ...options, agent: customAgent } - } - return originalRequest.call(this, options, callback) } - LOG._info && LOG.info('CaaS mTLS configured successfully') + LOG._info && LOG.info('CaaS mTLS credentials configured successfully') } catch (err) { LOG._error && LOG.error('Failed to configure CaaS mTLS:', err.message) } From 6de322be4f3834831741486eac0436741d38bc44 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 23 Jun 2026 08:46:32 +0200 Subject: [PATCH 08/67] Fix mTLS config: use httpAgentOptions instead of agentOptions The OTLP HTTP exporter expects 'httpAgentOptions' in the config, not 'agentOptions'. This was causing the cert/key to be ignored. --- lib/metrics/index.js | 6 +++--- lib/tracing/index.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index 47a79305..a490dfc8 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -75,10 +75,10 @@ function _getExporter() { if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) config.url ??= credentials.url - // Pass mTLS agent options if available (OTLP exporter uses 'agentOptions') + // Pass mTLS agent options if available (OTLP exporter uses 'httpAgentOptions') if (credentials.httpAgentOptions) { - config.agentOptions = credentials.httpAgentOptions - LOG._info && LOG.info('CaaS metrics exporter config: url=' + config.url + ', agentOptions.cert exists=' + !!config.agentOptions.cert) + config.httpAgentOptions = credentials.httpAgentOptions + LOG._info && LOG.info('CaaS metrics exporter config: url=' + config.url + ', httpAgentOptions.cert exists=' + !!config.httpAgentOptions.cert) } else { LOG._warn && LOG.warn('CaaS metrics: no httpAgentOptions found in credentials') } diff --git a/lib/tracing/index.js b/lib/tracing/index.js index ca581fc7..6f7e411c 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -132,10 +132,10 @@ function _getExporter() { if (!credentials) throw new Error('No CaaS credentials found') augmentCaaSCreds(credentials) config.url ??= credentials.url - // Pass mTLS agent options if available (OTLP exporter uses 'agentOptions') + // Pass mTLS agent options if available (OTLP exporter uses 'httpAgentOptions') if (credentials.httpAgentOptions) { - config.agentOptions = credentials.httpAgentOptions - LOG._info && LOG.info('CaaS exporter config: url=' + config.url + ', agentOptions.cert exists=' + !!config.agentOptions.cert) + config.httpAgentOptions = credentials.httpAgentOptions + LOG._info && LOG.info('CaaS exporter config: url=' + config.url + ', httpAgentOptions.cert exists=' + !!config.httpAgentOptions.cert) } else { LOG._warn && LOG.warn('CaaS: no httpAgentOptions found in credentials') } From 10140df57ac2d706a1865dca79eedd7b2cedd219 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 23 Jun 2026 08:52:17 +0200 Subject: [PATCH 09/67] Fix OTLP URL: append signal paths (/v1/traces, /v1/metrics) When config.url is provided, OTLP exporter uses it as-is without appending the signal resource path. CaaS binding provides base URL without path, so we need to append /v1/traces and /v1/metrics. --- lib/metrics/index.js | 3 ++- lib/tracing/index.js | 5 ++++- lib/utils.js | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index a490dfc8..b1ecc541 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -74,7 +74,8 @@ function _getExporter() { if (!credentials) credentials = getCredsForCaaS() if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) - config.url ??= credentials.url + // Append /v1/metrics to base URL (OTLP exporter expects full URL when config.url is provided) + config.url ??= credentials.baseUrl ? credentials.baseUrl + '/v1/metrics' : credentials.url + '/v1/metrics' // Pass mTLS agent options if available (OTLP exporter uses 'httpAgentOptions') if (credentials.httpAgentOptions) { config.httpAgentOptions = credentials.httpAgentOptions diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 6f7e411c..c921ff86 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -131,16 +131,19 @@ function _getExporter() { if (!credentials) credentials = getCredsForCaaS() if (!credentials) throw new Error('No CaaS credentials found') augmentCaaSCreds(credentials) - config.url ??= credentials.url + // Append /v1/traces to base URL (OTLP exporter expects full URL when config.url is provided) + config.url ??= credentials.baseUrl ? credentials.baseUrl + '/v1/traces' : credentials.url + '/v1/traces' // Pass mTLS agent options if available (OTLP exporter uses 'httpAgentOptions') if (credentials.httpAgentOptions) { config.httpAgentOptions = credentials.httpAgentOptions LOG._info && LOG.info('CaaS exporter config: url=' + config.url + ', httpAgentOptions.cert exists=' + !!config.httpAgentOptions.cert) + LOG._debug && LOG.debug('CaaS exporter full config keys:', Object.keys(config)) } else { LOG._warn && LOG.warn('CaaS: no httpAgentOptions found in credentials') } } + LOG._debug && LOG.debug('Creating exporter with config:', JSON.stringify({ url: config.url, hasHttpAgentOptions: !!config.httpAgentOptions, httpAgentOptionKeys: config.httpAgentOptions ? Object.keys(config.httpAgentOptions) : [] })) const exporter = new tracingExporterModule[tracingExporter.class](config) LOG._debug && LOG.debug('Using trace exporter:', exporter) diff --git a/lib/utils.js b/lib/utils.js index 7f86bc9e..74adb2e6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -162,7 +162,9 @@ function augmentCaaSCreds(credentials) { throw new Error('No OTLP endpoints found in CaaS binding. Make sure the CaaS instance is properly configured.') } - // use http endpoint by default (proto exporter) + // Store the base URL - path will be added per signal type (traces: /v1/traces, metrics: /v1/metrics) + credentials.baseUrl = credentials.otlp.http + // Also set url for backwards compatibility (without path - exporter will append it) credentials.url = credentials.otlp.http // Check for mTLS credentials (cert + key) in user-provided service From 7aca1b471d223b4186b03b75f0733a34dbfd80dc Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 30 Jun 2026 13:17:23 +0200 Subject: [PATCH 10/67] rem hard-coded names and debug logs --- lib/metrics/index.js | 8 ++------ lib/tracing/index.js | 9 +-------- lib/utils.js | 38 ++++++-------------------------------- package.json | 1 + 4 files changed, 10 insertions(+), 46 deletions(-) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index 32583831..4c88f3b2 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -11,7 +11,7 @@ const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics') -const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, getCredsForCaaS, augmentCLCreds, augmentCaaSCreds, _require } = require('../utils') +const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, _require } = require('../utils') const _protocol2module = { grpc: '@opentelemetry/exporter-metrics-otlp-grpc', @@ -80,17 +80,13 @@ function _getExporter() { } if (kind.match(/to-caas$/)) { - if (!credentials) credentials = getCredsForCaaS() - if (!credentials) throw new Error('No CaaS credentials found.') + if (!credentials) throw new Error('No CaaS credentials found. Make sure the app is bound to a caas-service instance.') augmentCaaSCreds(credentials) // Append /v1/metrics to base URL (OTLP exporter expects full URL when config.url is provided) config.url ??= credentials.baseUrl ? credentials.baseUrl + '/v1/metrics' : credentials.url + '/v1/metrics' // Pass mTLS agent options if available (OTLP exporter uses 'httpAgentOptions') if (credentials.httpAgentOptions) { config.httpAgentOptions = credentials.httpAgentOptions - LOG._info && LOG.info('CaaS metrics exporter config: url=' + config.url + ', httpAgentOptions.cert exists=' + !!config.httpAgentOptions.cert) - } else { - LOG._warn && LOG.warn('CaaS metrics: no httpAgentOptions found in credentials') } } diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 128c185f..19f4ba8a 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -11,7 +11,6 @@ const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, - getCredsForCaaS, augmentCLCreds, augmentCaaSCreds, hasDependency, @@ -129,22 +128,16 @@ function _getExporter() { } if (kind.match(/to-caas$/)) { - if (!credentials) credentials = getCredsForCaaS() - if (!credentials) throw new Error('No CaaS credentials found') + if (!credentials) throw new Error('No CaaS credentials found. Make sure the app is bound to a caas-service instance.') augmentCaaSCreds(credentials) // Append /v1/traces to base URL (OTLP exporter expects full URL when config.url is provided) config.url ??= credentials.baseUrl ? credentials.baseUrl + '/v1/traces' : credentials.url + '/v1/traces' // Pass mTLS agent options if available (OTLP exporter uses 'httpAgentOptions') if (credentials.httpAgentOptions) { config.httpAgentOptions = credentials.httpAgentOptions - LOG._info && LOG.info('CaaS exporter config: url=' + config.url + ', httpAgentOptions.cert exists=' + !!config.httpAgentOptions.cert) - LOG._debug && LOG.debug('CaaS exporter full config keys:', Object.keys(config)) - } else { - LOG._warn && LOG.warn('CaaS: no httpAgentOptions found in credentials') } } - LOG._debug && LOG.debug('Creating exporter with config:', JSON.stringify({ url: config.url, hasHttpAgentOptions: !!config.httpAgentOptions, httpAgentOptionKeys: config.httpAgentOptions ? Object.keys(config.httpAgentOptions) : [] })) const exporter = new tracingExporterModule[tracingExporter.class](config) LOG._debug && LOG.debug('Using trace exporter:', exporter) diff --git a/lib/utils.js b/lib/utils.js index c3e07945..8e4ed4b6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -137,27 +137,14 @@ function getCredsForCLSAsUPS() { } } -function getCredsForCaaS() { - if (!process.env.VCAP_SERVICES) return - const vcap = JSON.parse(process.env.VCAP_SERVICES) - - // look for caas-service binding - const caas = vcap['caas-service']?.[0] - if (caas) return caas.credentials -} - -function getCredsForCertService() { - if (!process.env.VCAP_SERVICES) return - const vcap = JSON.parse(process.env.VCAP_SERVICES) - const certSvc = vcap['certificate-service']?.[0] - if (certSvc) return certSvc.credentials -} - function getCredsForCaaSMtls() { if (!process.env.VCAP_SERVICES) return const vcap = JSON.parse(process.env.VCAP_SERVICES) - // Look for user-provided service with name containing 'caas-mtls' or 'caas-cert' - const mtlsCreds = vcap['user-provided']?.find(b => b.name.match(/caas-mtls|caas-cert/i)) + // Look for user-provided service with name matching configured pattern + const { mtls_service_pattern } = cds.env.requires.telemetry + if (!mtls_service_pattern) return + const pattern = new RegExp(mtls_service_pattern, 'i') + const mtlsCreds = vcap['user-provided']?.find(b => b.name.match(pattern)) if (mtlsCreds) return mtlsCreds.credentials } @@ -178,7 +165,6 @@ function augmentCaaSCreds(credentials) { // Check for mTLS credentials (cert + key) in user-provided service const mtlsCreds = getCredsForCaaSMtls() if (mtlsCreds && mtlsCreds.cert && mtlsCreds.key) { - LOG._info && LOG.info('Found CaaS mTLS credentials, configuring HTTPS agent options') try { const cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') const key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') @@ -190,21 +176,11 @@ function augmentCaaSCreds(credentials) { key: key, keepAlive: true } - - LOG._info && LOG.info('CaaS mTLS credentials configured successfully') } catch (err) { LOG._error && LOG.error('Failed to configure CaaS mTLS:', err.message) } } else { - // Check for certificate-service binding (manual enrollment needed) - const certServiceCreds = getCredsForCertService() - if (certServiceCreds) { - LOG._warn && LOG.warn('Found certificate-service binding but no caas-mtls-creds.') - LOG._warn && LOG.warn('Run the certificate enrollment script and create caas-mtls-creds service.') - } else { - LOG._warn && LOG.warn('CaaS requires mTLS authentication. No mTLS credentials found.') - LOG._warn && LOG.warn('Bind a caas-mtls-creds user-provided service with cert and key.') - } + LOG._warn && LOG.warn('CaaS requires mTLS authentication. No mTLS credentials found. Bind a user-provided service with cert and key (base64 encoded).') } } @@ -273,8 +249,6 @@ module.exports = { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, - getCredsForCaaS, - getCredsForCertService, augmentCLCreds, augmentCaaSCreds, hasDependency, diff --git a/package.json b/package.json index d33d3804..de00fa36 100644 --- a/package.json +++ b/package.json @@ -165,6 +165,7 @@ "vcap": { "label": "caas-service" }, + "mtls_service_pattern": "caas-mtls|caas-cert", "tracing": { "exporter": { "module": "@opentelemetry/exporter-trace-otlp-proto", From a3d0f5e53a0a0406c8063ed7a32c9a5bcfa07e6b Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 30 Jun 2026 13:40:24 +0200 Subject: [PATCH 11/67] fix env vars --- lib/metrics/index.js | 5 +++-- lib/tracing/index.js | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index 4c88f3b2..feee872b 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -82,9 +82,10 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found. Make sure the app is bound to a caas-service instance.') augmentCaaSCreds(credentials) + // Set OTLP env vars to binding credentials (prevents stale env values from taking precedence) + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = credentials.baseUrl // Append /v1/metrics to base URL (OTLP exporter expects full URL when config.url is provided) - config.url ??= credentials.baseUrl ? credentials.baseUrl + '/v1/metrics' : credentials.url + '/v1/metrics' - // Pass mTLS agent options if available (OTLP exporter uses 'httpAgentOptions') + config.url = credentials.baseUrl + '/v1/metrics' if (credentials.httpAgentOptions) { config.httpAgentOptions = credentials.httpAgentOptions } diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 19f4ba8a..3ee12bd7 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -130,9 +130,10 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found. Make sure the app is bound to a caas-service instance.') augmentCaaSCreds(credentials) + // Set OTLP env vars to binding credentials (prevents stale env values from taking precedence) + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = credentials.baseUrl // Append /v1/traces to base URL (OTLP exporter expects full URL when config.url is provided) - config.url ??= credentials.baseUrl ? credentials.baseUrl + '/v1/traces' : credentials.url + '/v1/traces' - // Pass mTLS agent options if available (OTLP exporter uses 'httpAgentOptions') + config.url = credentials.baseUrl + '/v1/traces' if (credentials.httpAgentOptions) { config.httpAgentOptions = credentials.httpAgentOptions } From 00c635db8fe4fa06aa09a99b0c0984552905a500 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 30 Jun 2026 14:34:59 +0200 Subject: [PATCH 12/67] add readme, changelog and test --- CHANGELOG.md | 1 + README.md | 39 +++++++++++++- test/caas.test.js | 128 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 test/caas.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index db0d79dd..11808857 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - Support for OpenTelemetry SDK 2.0 - `@opentelemetry/instrumentation-undici` added to the list of default instrumentations - Support for `@sap/cds^10` +- Support for `telemetry-to-caas` kind for CaaS (Collector as a Service) ### Changed diff --git a/README.md b/README.md index 6843b802..ada6c599 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Documentation can be found at [cap.cloud.sap](https://cap.cloud.sap/docs) and [o - [`telemetry-to-console`](#telemetry-to-console) - [`telemetry-to-dynatrace`](#telemetry-to-dynatrace) - [`telemetry-to-cloud-logging`](#telemetry-to-cloud-logging) + - [`telemetry-to-caas`](#telemetry-to-caas) - [`telemetry-to-jaeger`](#telemetry-to-jaeger) - [`telemetry-to-otlp`](#telemetry-to-otlp) - [Detailed Configuration Options](#detailed-configuration-options) @@ -200,7 +201,7 @@ Please note that in order for logs to be exported via OpenTelemetry, `cds.log()` ## Predefined Kinds -There are five predefined kinds as follows: +There are six predefined kinds as follows: ### `telemetry-to-console` @@ -283,12 +284,46 @@ In order to receive OpenTelemetry credentials in the binding to the SAP Cloud Lo If you are binding your app to SAP Cloud Logging via a [user-provided service instance](https://docs.cloudfoundry.org/devguide/services/user-provided.html), make sure that it has the tag `Cloud Logging`. -> Tip: To add the required tag to an existing user-provided service, you can use: +> Tip: To add the required tag to an existing user-provided service, you can use: > ``` > cf update-user-provided-service {service-name} -t "Cloud Logging" > ``` > For detailed information about binding resolution in CAP, consult [`cds.connect()` → Service Bindings](https://cap.cloud.sap/docs/node.js/cds-connect#service-bindings). +### `telemetry-to-caas` + +Exports traces and metrics to CaaS (Collector as a Service). +CaaS acts as a managed OpenTelemetry Collector that can route telemetry data to downstream backends like SAP Cloud Logging. + +Use via `cds.requires.telemetry.kind = 'to-caas'`. + +Required additional dependencies: +- `@opentelemetry/exporter-trace-otlp-proto` +- `@opentelemetry/exporter-metrics-otlp-proto` + +CaaS requires mTLS authentication with SAP-signed certificates. You need to: + +1. **Bind the CaaS service** to your app with subject/issuer configuration: +```yaml +# mta.yaml +requires: + - name: my-caas-instance + parameters: + config: + subject: "CN=my-app,..." + issuer: "CN=SAP PKI Certificate Service Client CA,..." +``` + +2. **Obtain and bind mTLS credentials** via a user-provided service containing `cert` and `key` (base64 encoded): +```bash +cf create-user-provided-service caas-mtls-creds -p '{"cert":"","key":""}' +cf bind-service my-app caas-mtls-creds +``` + +The mTLS certificate must be created (e.g., via `openssl`) and signed through SAP BTP Certificate Service. The certificate must be renewed periodically (typically every 7 days). + +> Note: The user-provided service name must match the pattern configured in `mtls_service_pattern` (default: `caas-mtls|caas-cert`). + ### `telemetry-to-jaeger` Exports traces to Jaeger. diff --git a/test/caas.test.js b/test/caas.test.js new file mode 100644 index 00000000..c0d4bfd9 --- /dev/null +++ b/test/caas.test.js @@ -0,0 +1,128 @@ +const cds = require('@sap/cds') + +// Mock VCAP_SERVICES for CaaS +const MOCK_CAAS_VCAP = { + 'caas-service': [{ + name: 'test-caas', + credentials: { + otlp: { + http: 'https://caas.example.com/otlp', + grpc: 'grpc://caas.example.com:4317' + } + } + }], + 'user-provided': [{ + name: 'caas-mtls-creds', + credentials: { + cert: Buffer.from('-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----').toString('base64'), + key: Buffer.from('-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----').toString('base64') + }, + tags: [] + }] +} + +const MOCK_CAAS_VCAP_NO_MTLS = { + 'caas-service': [{ + name: 'test-caas', + credentials: { + otlp: { + http: 'https://caas.example.com/otlp', + grpc: 'grpc://caas.example.com:4317' + } + } + }] +} + +describe('augmentCaaSCreds', () => { + let originalVcap + + beforeAll(() => { + originalVcap = process.env.VCAP_SERVICES + }) + + afterAll(() => { + if (originalVcap) process.env.VCAP_SERVICES = originalVcap + else delete process.env.VCAP_SERVICES + }) + + beforeEach(() => { + cds.env.requires = cds.env.requires || {} + cds.env.requires.telemetry = { mtls_service_pattern: 'caas-mtls|caas-cert' } + delete require.cache[require.resolve('../lib/utils')] + }) + + test('sets baseUrl and url from otlp.http', () => { + process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) + delete require.cache[require.resolve('../lib/utils')] + const { augmentCaaSCreds } = require('../lib/utils') + + const credentials = { + otlp: { + http: 'https://caas.example.com/otlp', + grpc: 'grpc://caas.example.com:4317' + } + } + + augmentCaaSCreds(credentials) + + expect(credentials.baseUrl).toBe('https://caas.example.com/otlp') + expect(credentials.url).toBe('https://caas.example.com/otlp') + }) + + test('sets httpAgentOptions when mTLS credentials found', () => { + process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) + delete require.cache[require.resolve('../lib/utils')] + const { augmentCaaSCreds } = require('../lib/utils') + + const credentials = { + otlp: { http: 'https://caas.example.com/otlp' } + } + + augmentCaaSCreds(credentials) + + expect(credentials.httpAgentOptions).toBeDefined() + expect(credentials.httpAgentOptions.cert).toContain('BEGIN CERTIFICATE') + expect(credentials.httpAgentOptions.key).toContain('BEGIN PRIVATE KEY') + expect(credentials.httpAgentOptions.keepAlive).toBe(true) + }) + + test('throws when no OTLP endpoints', () => { + process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) + delete require.cache[require.resolve('../lib/utils')] + const { augmentCaaSCreds } = require('../lib/utils') + + expect(() => augmentCaaSCreds({})).toThrow('No OTLP endpoints found') + }) + + test('does not augment twice', () => { + process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) + delete require.cache[require.resolve('../lib/utils')] + const { augmentCaaSCreds } = require('../lib/utils') + + const credentials = { + otlp: { http: 'https://caas.example.com/otlp' } + } + + augmentCaaSCreds(credentials) + const originalBaseUrl = credentials.baseUrl + + credentials.otlp.http = 'https://different.com' + augmentCaaSCreds(credentials) + + expect(credentials.baseUrl).toBe(originalBaseUrl) + }) + + test('no httpAgentOptions when mTLS credentials not found', () => { + process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP_NO_MTLS) + delete require.cache[require.resolve('../lib/utils')] + const { augmentCaaSCreds } = require('../lib/utils') + + const credentials = { + otlp: { http: 'https://caas.example.com/otlp' } + } + + augmentCaaSCreds(credentials) + + expect(credentials.httpAgentOptions).toBeUndefined() + }) +}) From e4dbdf6fb208b8b2e9dcce5ce63c837f6b3f0006 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 30 Jun 2026 14:39:36 +0200 Subject: [PATCH 13/67] small fix --- lib/utils.js | 6 +++--- test/caas.test.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 8e4ed4b6..bbb19859 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -152,9 +152,9 @@ function augmentCaaSCreds(credentials) { if (credentials._augmented) return credentials._augmented = true - // check for otlp endpoints - if (!credentials.otlp?.http && !credentials.otlp?.grpc) { - throw new Error('No OTLP endpoints found in CaaS binding. Make sure the CaaS instance is properly configured.') + // check for otlp http endpoint + if (!credentials.otlp?.http) { + throw new Error('No OTLP HTTP endpoint found in CaaS binding. Make sure the CaaS instance is properly configured.') } // Store the base URL - path will be added per signal type (traces: /v1/traces, metrics: /v1/metrics) diff --git a/test/caas.test.js b/test/caas.test.js index c0d4bfd9..6febca45 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -91,7 +91,7 @@ describe('augmentCaaSCreds', () => { delete require.cache[require.resolve('../lib/utils')] const { augmentCaaSCreds } = require('../lib/utils') - expect(() => augmentCaaSCreds({})).toThrow('No OTLP endpoints found') + expect(() => augmentCaaSCreds({})).toThrow('No OTLP HTTP endpoint found') }) test('does not augment twice', () => { From 11efddb752a40fde3f638f11a4625ede5a362bd5 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 21 Jul 2026 16:26:07 +0200 Subject: [PATCH 14/67] use cds.env.requires.telemetry.x509 for certificate --- lib/utils.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index bbb19859..9ac37631 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -138,14 +138,14 @@ function getCredsForCLSAsUPS() { } function getCredsForCaaSMtls() { - if (!process.env.VCAP_SERVICES) return - const vcap = JSON.parse(process.env.VCAP_SERVICES) - // Look for user-provided service with name matching configured pattern - const { mtls_service_pattern } = cds.env.requires.telemetry - if (!mtls_service_pattern) return - const pattern = new RegExp(mtls_service_pattern, 'i') - const mtlsCreds = vcap['user-provided']?.find(b => b.name.match(pattern)) - if (mtlsCreds) return mtlsCreds.credentials + // Read x509 certificate from cds.env.requires.telemetry + const { x509 } = cds.env.requires.telemetry || {} + if (x509 && x509.cert && x509.key) { + return { + cert: x509.cert, + key: x509.key + } + } } function augmentCaaSCreds(credentials) { From 8d2eb24d8dd783cf928cb75d258d49b411e5c263 Mon Sep 17 00:00:00 2001 From: D051920 Date: Wed, 22 Jul 2026 10:15:07 +0200 Subject: [PATCH 15/67] add logging --- lib/logging/index.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 08840201..11c737b2 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -3,7 +3,7 @@ const LOG = cds.log('telemetry') const { getStringFromEnv } = require('@opentelemetry/core') -const { getCredsForCLSAsUPS, augmentCLCreds, _require } = require('../utils') +const { getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, _require } = require('../utils') const _protocol2module = { grpc: '@opentelemetry/exporter-logs-otlp-grpc', @@ -46,6 +46,18 @@ function _getExporter() { config.credentials ??= credentials.credentials } + if (kind.match(/to-caas$/)) { + if (!credentials) throw new Error('No CaaS credentials found. Make sure the app is bound to a caas-service instance.') + augmentCaaSCreds(credentials) + // Set OTLP env vars to binding credentials (prevents stale env values from taking precedence) + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = credentials.baseUrl + // Append /v1/logs to base URL (OTLP exporter expects full URL when config.url is provided) + config.url = credentials.baseUrl + '/v1/logs' + if (credentials.httpAgentOptions) { + config.httpAgentOptions = credentials.httpAgentOptions + } + } + const exporter = new loggingExporterModule[loggingExporter.class](config) LOG._debug && LOG.debug('Using logs exporter:', exporter) From 7c02946d288012d6abd7ffcbfc0534986306177d Mon Sep 17 00:00:00 2001 From: D051920 Date: Wed, 22 Jul 2026 11:02:32 +0200 Subject: [PATCH 16/67] add tracing to package.json --- lib/tracing/index.js | 1 - package.json | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 3ee12bd7..33a1ac02 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -1,7 +1,6 @@ const cds = require('@sap/cds') const LOG = cds.log('telemetry') -const { trace } = require('@opentelemetry/api') const { getStringFromEnv } = require('@opentelemetry/core') const { resourceFromAttributes } = require('@opentelemetry/resources') const { BatchSpanProcessor, SimpleSpanProcessor, SamplingDecision } = require('@opentelemetry/sdk-trace-base') diff --git a/package.json b/package.json index de00fa36..fd945459 100644 --- a/package.json +++ b/package.json @@ -177,6 +177,12 @@ "module": "@opentelemetry/exporter-metrics-otlp-proto", "class": "OTLPMetricExporter" } + }, + "logging": { + "exporter": { + "module": "@opentelemetry/exporter-logs-otlp-proto", + "class": "OTLPLogExporter" + } } } } From e74ef96dab9681ff6a37067dfe3d15b7fcda2eef Mon Sep 17 00:00:00 2001 From: D051920 Date: Wed, 22 Jul 2026 15:54:13 +0200 Subject: [PATCH 17/67] fix test --- test/caas.test.js | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/test/caas.test.js b/test/caas.test.js index 6febca45..6d17fa29 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -2,26 +2,6 @@ const cds = require('@sap/cds') // Mock VCAP_SERVICES for CaaS const MOCK_CAAS_VCAP = { - 'caas-service': [{ - name: 'test-caas', - credentials: { - otlp: { - http: 'https://caas.example.com/otlp', - grpc: 'grpc://caas.example.com:4317' - } - } - }], - 'user-provided': [{ - name: 'caas-mtls-creds', - credentials: { - cert: Buffer.from('-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----').toString('base64'), - key: Buffer.from('-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----').toString('base64') - }, - tags: [] - }] -} - -const MOCK_CAAS_VCAP_NO_MTLS = { 'caas-service': [{ name: 'test-caas', credentials: { @@ -47,7 +27,12 @@ describe('augmentCaaSCreds', () => { beforeEach(() => { cds.env.requires = cds.env.requires || {} - cds.env.requires.telemetry = { mtls_service_pattern: 'caas-mtls|caas-cert' } + cds.env.requires.telemetry = { + x509: { + cert: Buffer.from('-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----').toString('base64'), + key: Buffer.from('-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----').toString('base64') + } + } delete require.cache[require.resolve('../lib/utils')] }) @@ -113,7 +98,8 @@ describe('augmentCaaSCreds', () => { }) test('no httpAgentOptions when mTLS credentials not found', () => { - process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP_NO_MTLS) + cds.env.requires.telemetry = {} // No x509 credentials + process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) delete require.cache[require.resolve('../lib/utils')] const { augmentCaaSCreds } = require('../lib/utils') From 67a148837d79a0a7be3e869fb0e8957ca8f74dc2 Mon Sep 17 00:00:00 2001 From: Vitaly Kozyura <58591662+vkozyura@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:28:03 +0200 Subject: [PATCH 18/67] Update lib/tracing/index.js Co-authored-by: sjvans <30337871+sjvans@users.noreply.github.com> --- lib/tracing/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 33a1ac02..0f141c71 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -127,7 +127,7 @@ function _getExporter() { } if (kind.match(/to-caas$/)) { - if (!credentials) throw new Error('No CaaS credentials found. Make sure the app is bound to a caas-service instance.') + if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) // Set OTLP env vars to binding credentials (prevents stale env values from taking precedence) process.env.OTEL_EXPORTER_OTLP_ENDPOINT = credentials.baseUrl From 566a6bbfd1cd61c8c5d2a767c6f2ee7801fe97ef Mon Sep 17 00:00:00 2001 From: Vitaly Kozyura <58591662+vkozyura@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:29:45 +0200 Subject: [PATCH 19/67] Update lib/metrics/index.js Co-authored-by: sjvans <30337871+sjvans@users.noreply.github.com> --- lib/metrics/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index 6572bb5f..f213fb4a 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -75,7 +75,7 @@ function _getExporter() { } if (kind.match(/to-caas$/)) { - if (!credentials) throw new Error('No CaaS credentials found. Make sure the app is bound to a caas-service instance.') + if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) // Set OTLP env vars to binding credentials (prevents stale env values from taking precedence) process.env.OTEL_EXPORTER_OTLP_ENDPOINT = credentials.baseUrl From f7e4e68183548f8190d7038f21df8a956fceef74 Mon Sep 17 00:00:00 2001 From: Vitaly Kozyura <58591662+vkozyura@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:31:15 +0200 Subject: [PATCH 20/67] Update lib/logging/index.js Co-authored-by: sjvans <30337871+sjvans@users.noreply.github.com> --- lib/logging/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 11c737b2..912d494f 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -47,7 +47,7 @@ function _getExporter() { } if (kind.match(/to-caas$/)) { - if (!credentials) throw new Error('No CaaS credentials found. Make sure the app is bound to a caas-service instance.') + if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) // Set OTLP env vars to binding credentials (prevents stale env values from taking precedence) process.env.OTEL_EXPORTER_OTLP_ENDPOINT = credentials.baseUrl From 4c8624e20c7dca3de49ada63625c95ace463c447 Mon Sep 17 00:00:00 2001 From: Vitaly Kozyura <58591662+vkozyura@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:31:57 +0200 Subject: [PATCH 21/67] Update lib/utils.js Co-authored-by: sjvans <30337871+sjvans@users.noreply.github.com> --- lib/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index 9ac37631..daa74a0f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -154,7 +154,7 @@ function augmentCaaSCreds(credentials) { // check for otlp http endpoint if (!credentials.otlp?.http) { - throw new Error('No OTLP HTTP endpoint found in CaaS binding. Make sure the CaaS instance is properly configured.') + throw new Error('No OTLP HTTP endpoint found in CaaS credentials. Make sure the CaaS instance is properly configured.') } // Store the base URL - path will be added per signal type (traces: /v1/traces, metrics: /v1/metrics) From 51cd815867d116ce98b3a38331f423d91af73ae7 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 17 Aug 2026 12:36:18 +0200 Subject: [PATCH 22/67] remove backwars compat for credentials --- lib/utils.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index daa74a0f..eea688d5 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -159,8 +159,6 @@ function augmentCaaSCreds(credentials) { // Store the base URL - path will be added per signal type (traces: /v1/traces, metrics: /v1/metrics) credentials.baseUrl = credentials.otlp.http - // Also set url for backwards compatibility (without path - exporter will append it) - credentials.url = credentials.otlp.http // Check for mTLS credentials (cert + key) in user-provided service const mtlsCreds = getCredsForCaaSMtls() From 6e883e25c18aa131299a036e65a7bfb08adde01b Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 17 Aug 2026 12:39:20 +0200 Subject: [PATCH 23/67] simplify mtlsCreds check --- lib/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index eea688d5..f7a119ae 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -162,7 +162,7 @@ function augmentCaaSCreds(credentials) { // Check for mTLS credentials (cert + key) in user-provided service const mtlsCreds = getCredsForCaaSMtls() - if (mtlsCreds && mtlsCreds.cert && mtlsCreds.key) { + if (mtlsCreds) { try { const cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') const key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') From a86df66aadfd4d248cd89b5bbd281d1fddb1139d Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 17 Aug 2026 12:44:44 +0200 Subject: [PATCH 24/67] update warning --- lib/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index f7a119ae..bdada26f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -178,7 +178,7 @@ function augmentCaaSCreds(credentials) { LOG._error && LOG.error('Failed to configure CaaS mTLS:', err.message) } } else { - LOG._warn && LOG.warn('CaaS requires mTLS authentication. No mTLS credentials found. Bind a user-provided service with cert and key (base64 encoded).') + LOG._warn && LOG.warn('CaaS requires mTLS authentication. No mTLS credentials found. Set CDS_REQUIRES_TELEMETRY_X509_CERT and CDS_REQUIRES_TELEMETRY_X509_KEY environment variables (base64 encoded).') } } From 7a39adef55d03818afa30e48f6e6ffb9185a2cc8 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 17 Aug 2026 13:07:19 +0200 Subject: [PATCH 25/67] readme --- README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ada6c599..d1ba0669 100644 --- a/README.md +++ b/README.md @@ -314,15 +314,21 @@ requires: issuer: "CN=SAP PKI Certificate Service Client CA,..." ``` -2. **Obtain and bind mTLS credentials** via a user-provided service containing `cert` and `key` (base64 encoded): -```bash -cf create-user-provided-service caas-mtls-creds -p '{"cert":"","key":""}' -cf bind-service my-app caas-mtls-creds +2. **Provide mTLS credentials** via environment variables (base64 encoded): +```yaml +# mta.yaml +properties: + CDS_REQUIRES_TELEMETRY_X509_CERT: '' + CDS_REQUIRES_TELEMETRY_X509_KEY: '' ``` -The mTLS certificate must be created (e.g., via `openssl`) and signed through SAP BTP Certificate Service. The certificate must be renewed periodically (typically every 7 days). +Or set directly via Cloud Foundry CLI: +```bash +cf set-env my-app CDS_REQUIRES_TELEMETRY_X509_CERT "" +cf set-env my-app CDS_REQUIRES_TELEMETRY_X509_KEY "" +``` -> Note: The user-provided service name must match the pattern configured in `mtls_service_pattern` (default: `caas-mtls|caas-cert`). +The mTLS certificate must be SAP-signed through the BTP Certificate Service. Certificates can be created with validity from 7 days up to 1 year and must be renewed before expiration. For detailed certificate setup and renewal instructions, refer to your project's certificate management documentation. ### `telemetry-to-jaeger` From e5bff7597d7cfeed3cfa7c18a814c14a27b93beb Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 17 Aug 2026 13:09:51 +0200 Subject: [PATCH 26/67] testing certificate renewal w/o re-assigning env vars --- lib/logging/index.js | 2 -- lib/metrics/index.js | 2 -- lib/tracing/index.js | 2 -- 3 files changed, 6 deletions(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 912d494f..5e0122f6 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -49,8 +49,6 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) - // Set OTLP env vars to binding credentials (prevents stale env values from taking precedence) - process.env.OTEL_EXPORTER_OTLP_ENDPOINT = credentials.baseUrl // Append /v1/logs to base URL (OTLP exporter expects full URL when config.url is provided) config.url = credentials.baseUrl + '/v1/logs' if (credentials.httpAgentOptions) { diff --git a/lib/metrics/index.js b/lib/metrics/index.js index f213fb4a..4cb85dbf 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -77,8 +77,6 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) - // Set OTLP env vars to binding credentials (prevents stale env values from taking precedence) - process.env.OTEL_EXPORTER_OTLP_ENDPOINT = credentials.baseUrl // Append /v1/metrics to base URL (OTLP exporter expects full URL when config.url is provided) config.url = credentials.baseUrl + '/v1/metrics' if (credentials.httpAgentOptions) { diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 0f141c71..70631429 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -129,8 +129,6 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') augmentCaaSCreds(credentials) - // Set OTLP env vars to binding credentials (prevents stale env values from taking precedence) - process.env.OTEL_EXPORTER_OTLP_ENDPOINT = credentials.baseUrl // Append /v1/traces to base URL (OTLP exporter expects full URL when config.url is provided) config.url = credentials.baseUrl + '/v1/traces' if (credentials.httpAgentOptions) { From 9967e8c7e912e838ee5d221d8f0bddfdac48000f Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 17 Aug 2026 13:28:43 +0200 Subject: [PATCH 27/67] fix test --- test/caas.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/caas.test.js b/test/caas.test.js index 6d17fa29..161b934c 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -36,7 +36,7 @@ describe('augmentCaaSCreds', () => { delete require.cache[require.resolve('../lib/utils')] }) - test('sets baseUrl and url from otlp.http', () => { + test('sets baseUrl from otlp.http', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) delete require.cache[require.resolve('../lib/utils')] const { augmentCaaSCreds } = require('../lib/utils') @@ -51,7 +51,6 @@ describe('augmentCaaSCreds', () => { augmentCaaSCreds(credentials) expect(credentials.baseUrl).toBe('https://caas.example.com/otlp') - expect(credentials.url).toBe('https://caas.example.com/otlp') }) test('sets httpAgentOptions when mTLS credentials found', () => { From 0e614765bc633cd1c81f37eed585c9fa4b8e3887 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 17 Aug 2026 15:08:24 +0200 Subject: [PATCH 28/67] fix if no logging lib --- lib/logging/index.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 5e0122f6..6aa7fae4 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -33,7 +33,16 @@ function _getExporter() { } // use _require for better error message - const loggingExporterModule = _require(loggingExporter.module) + let loggingExporterModule + try { + loggingExporterModule = _require(loggingExporter.module) + } catch (err) { + if (err.code === 'MODULE_NOT_FOUND') { + LOG._warn && LOG.warn(`Logs exporter module '${loggingExporter.module}' not found. Logging telemetry will be disabled.`) + return null + } + throw err + } if (!loggingExporterModule[loggingExporter.class]) throw new Error(`Unknown logs exporter "${loggingExporter.class}" in module "${loggingExporter.module}"`) const config = { ...(loggingExporter.config || {}) } @@ -141,6 +150,10 @@ module.exports = resource => { * create processor */ const exporter = _getExporter() + if (!exporter) { + LOG._info && LOG.info('Logging telemetry is disabled due to missing exporter module') + return null + } const processor = _getCustomProcessor(exporter) || (process.env.NODE_ENV === 'production' From c160fbcf6fbe5050b1e0662159b0ecacfdae4943 Mon Sep 17 00:00:00 2001 From: D051920 Date: Sat, 22 Aug 2026 10:52:35 +0200 Subject: [PATCH 29/67] use ztis --- lib/index.js | 21 ++++- lib/utils.js | 236 ++++++++++++++++++++++++++++++++++++++++++++-- test/caas.test.js | 225 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 475 insertions(+), 7 deletions(-) diff --git a/lib/index.js b/lib/index.js index 18a7b3cc..a11ecb9c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -13,7 +13,7 @@ const { registerInstrumentations } = require('@opentelemetry/instrumentation') const tracing = require('./tracing') const metrics = require('./metrics') const logging = require('./logging') -const { getDiagLogLevel, getResource, hasDependency, _require } = require('./utils') +const { getDiagLogLevel, getResource, hasDependency, initializeZTI, _require } = require('./utils') function _getInstrumentations() { const _instrumentations = cds.env.requires.telemetry.instrumentations @@ -96,6 +96,16 @@ function setup_standalone() { // create resource const resource = getResource() + // Initialize ZTI if configured (blocks until SVID files ready) + // This returns a promise, but we handle async initialization gracefully: + // - If ZTI is not configured, this resolves immediately + // - If ZTI is configured, SVID files are loaded during tracing/metrics setup + const ztiInit = initializeZTI().catch(err => { + LOG._error && LOG.error('Failed to initialize ZTI for CaaS mTLS:', err) + // Don't throw - let telemetry continue without ZTI + // The warning will be logged when augmentCaaSCreds() is called + }) + // setup tracing, metrics, and logging const tracerProvider = tracing(resource) const meterProvider = metrics(resource) @@ -108,13 +118,22 @@ function setup_standalone() { loggerProvider, instrumentations: _getInstrumentations() }) + + return ztiInit } function setup_with_calm() { + // Initialize ZTI if configured + const ztiInit = initializeZTI().catch(err => { + LOG._error && LOG.error('Failed to initialize ZTI for CaaS mTLS:', err) + }) + // setup tracing, metrics, and logging tracing() metrics() if (cds.env.requires.telemetry.logging) logging() + + return ztiInit } module.exports = hasDependency('@sap/xotel-agent-ext-js') ? setup_with_calm : setup_standalone diff --git a/lib/utils.js b/lib/utils.js index bdada26f..da280586 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -137,15 +137,217 @@ function getCredsForCLSAsUPS() { } } +/** + * Manages ZTI SVID file loading with mtime-based caching + * Based on official ZTI blueprint: https://github.tools.sap/pse/blueprints + */ +class SVIDFileSource { + constructor(certPath, keyPath, bundlePath) { + this.certPath = certPath + this.keyPath = keyPath + this.bundlePath = bundlePath + this._cached = null + this._cachedMod = null + } + + /** + * Get certificate credentials, reloading if mtime changed + * @returns {{ cert: string, key: string, bundle: string }} + */ + getCertificate() { + let stat + try { + stat = fs.statSync(this.certPath) + } catch (e) { + // Transient stat failure during atomic rename - serve last-known-good + if (this._cached) { + LOG._debug && LOG.debug('ZTI: Transient stat failure, serving cached credentials') + return this._cached + } + throw e + } + + const mtime = stat.mtimeMs + if (this._cached && this._cachedMod === mtime) { + // Cache hit - no rotation since last check + return this._cached + } + + // mtime changed or first load - reload all files atomically + LOG._debug && LOG.debug('ZTI: Loading SVID files (mtime changed or first load)') + try { + const certPEM = fs.readFileSync(this.certPath, 'utf8') + const keyPEM = fs.readFileSync(this.keyPath, 'utf8') + const bundlePEM = fs.readFileSync(this.bundlePath, 'utf8') + + this._cached = { cert: certPEM, key: keyPEM, bundle: bundlePEM } + this._cachedMod = mtime + + LOG._info && LOG.info('ZTI: Loaded SVID certificate', { + certPath: this.certPath, + mtime: new Date(mtime).toISOString() + }) + + return this._cached + } catch (err) { + // Read failure after successful stat - likely mid-rotation + if (this._cached) { + LOG._warn && LOG.warn('ZTI: Failed to reload SVID files, serving cached', err) + return this._cached + } + throw err + } + } +} + +/** + * Detect ZTI binding from VCAP_SERVICES + * @returns {{ svidDir: string, svidName: string } | null} + */ +function getZTIConfig() { + if (!process.env.VCAP_SERVICES) return null + + const vcap = JSON.parse(process.env.VCAP_SERVICES) + const zti = vcap['zero-trust-identity'] + if (!zti || zti.length === 0) return null + + // Get SVID name from binding parameters + const svidName = zti[0].credentials?.parameters?.['svid-store']?.file?.name + if (!svidName) { + LOG._warn && LOG.warn('ZTI: zero-trust-identity binding found but svid-store.file.name not configured') + return null + } + + // Native CF apps have fixed SVID directory + const svidDir = process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR || '/home/vcap/app/spire-svids' + + return { svidDir, svidName } +} + +/** + * Create SVID file source with blocking retry until files exist + * Follows ZTI blueprint pattern: block startup until SVID files ready + * @param {string} svidDir - Directory containing SVID files + * @param {string} svidName - SVID file base name + * @returns {Promise} + */ +async function createSVIDFileSource(svidDir, svidName) { + const RETRY_INTERVAL_MS = 2000 + const MAX_RETRIES = 30 // 60 seconds total (30 * 2s) + + const source = new SVIDFileSource( + `${svidDir}/${svidName}.svid.pem`, + `${svidDir}/${svidName}.svid.key`, + `${svidDir}/${svidName}.bundle.pem` + ) + + let attempt = 0 + while (true) { + try { + // Try to load - will throw if files don't exist + source.getCertificate() + LOG._info && LOG.info(`ZTI: SVID files ready at ${svidDir}/${svidName}`) + return source + } catch (err) { + attempt++ + if (attempt >= MAX_RETRIES) { + throw new Error( + `ZTI: SVID files not available after ${MAX_RETRIES} attempts (${(MAX_RETRIES * RETRY_INTERVAL_MS) / 1000}s). ` + + `Expected files at ${svidDir}/${svidName}.svid.{pem,key}. ` + + `Error: ${err.message}`, + { cause: err } + ) + } + + LOG._debug && LOG.debug( + `ZTI: SVID files not ready yet (attempt ${attempt}/${MAX_RETRIES}), ` + + `retrying in ${RETRY_INTERVAL_MS}ms...` + ) + + await new Promise(resolve => setTimeout(resolve, RETRY_INTERVAL_MS)) + } + } +} + +// Global ZTI source instance (created once, reused across requests) +let _ztiSource = null +let _ztiInitPromise = null + +/** + * Initialize ZTI source (async) - called during telemetry plugin initialization + * @returns {Promise} + */ +async function initializeZTI() { + const useZTI = process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI !== 'false' + if (!useZTI) return + + const ztiConfig = getZTIConfig() + if (!ztiConfig) return + + // Avoid double initialization + if (_ztiInitPromise) return _ztiInitPromise + + _ztiInitPromise = (async () => { + try { + LOG._info && LOG.info('ZTI: Initializing SVID file source for CaaS mTLS...') + _ztiSource = await createSVIDFileSource(ztiConfig.svidDir, ztiConfig.svidName) + } catch (err) { + LOG._error && LOG.error('ZTI: Failed to initialize SVID file source:', err) + _ztiInitPromise = null // Allow retry + throw err + } + })() + + return _ztiInitPromise +} + +/** + * Get mTLS credentials for CaaS telemetry + * Priority: + * 1. ZTI file-based (default, automatic rotation) - if USE_ZTI=true (default) + * 2. Env var base64 (legacy, manual rotation) - if USE_ZTI=false + * @returns {{ cert: string, key: string } | null} + */ function getCredsForCaaSMtls() { - // Read x509 certificate from cds.env.requires.telemetry + // Check flag: default to ZTI (only disable if explicitly set to 'false') + const useZTI = process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI !== 'false' + + if (useZTI) { + // ZTI approach (default) + const ztiConfig = getZTIConfig() + if (ztiConfig) { + // ZTI binding found - use it + if (_ztiSource) { + // ZTI initialized - get cached or reloaded credentials + try { + const { cert, key } = _ztiSource.getCertificate() + return { cert, key } + } catch (err) { + LOG._error && LOG.error('ZTI: Failed to load SVID credentials:', err) + return null + } + } else { + // ZTI not yet initialized - warn and fall through + LOG._warn && LOG.warn( + 'ZTI: SVID file source not initialized. Ensure initializeZTI() is called during startup.' + ) + return null + } + } + // No ZTI binding - fall through to legacy check + } + + // Legacy env var approach (USE_ZTI=false or no ZTI binding) const { x509 } = cds.env.requires.telemetry || {} if (x509 && x509.cert && x509.key) { + LOG._debug && LOG.debug('Using legacy base64 env var credentials for CaaS mTLS') return { cert: x509.cert, key: x509.key } } + + return null } function augmentCaaSCreds(credentials) { @@ -160,12 +362,22 @@ function augmentCaaSCreds(credentials) { // Store the base URL - path will be added per signal type (traces: /v1/traces, metrics: /v1/metrics) credentials.baseUrl = credentials.otlp.http - // Check for mTLS credentials (cert + key) in user-provided service + // Check for mTLS credentials (cert + key) - either from ZTI or legacy env vars const mtlsCreds = getCredsForCaaSMtls() if (mtlsCreds) { try { - const cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') - const key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') + let cert, key + + // Check if legacy base64 format (starts with base64 characters, not PEM header) + if (mtlsCreds.cert.startsWith('LS0t') || !mtlsCreds.cert.startsWith('-----BEGIN')) { + // Legacy: base64-encoded + cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') + key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') + } else { + // ZTI: already PEM format + cert = mtlsCreds.cert + key = mtlsCreds.key + } // Store the mTLS options for the exporter's httpAgentOptions // The OTLP HTTP exporter will create an https.Agent with these options @@ -174,11 +386,18 @@ function augmentCaaSCreds(credentials) { key: key, keepAlive: true } + + LOG._debug && LOG.debug('CaaS mTLS configured successfully') } catch (err) { LOG._error && LOG.error('Failed to configure CaaS mTLS:', err.message) } } else { - LOG._warn && LOG.warn('CaaS requires mTLS authentication. No mTLS credentials found. Set CDS_REQUIRES_TELEMETRY_X509_CERT and CDS_REQUIRES_TELEMETRY_X509_KEY environment variables (base64 encoded).') + LOG._warn && LOG.warn( + 'CaaS requires mTLS authentication. No mTLS credentials found. ' + + 'Either bind zero-trust-identity service (recommended) or set ' + + 'CDS_REQUIRES_TELEMETRY_X509_CERT and CDS_REQUIRES_TELEMETRY_X509_KEY ' + + 'environment variables (base64 encoded).' + ) } } @@ -249,7 +468,12 @@ module.exports = { getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, + initializeZTI, + getZTIConfig, + getCredsForCaaSMtls, hasDependency, _hrnow, - _require + _require, + // Exported for testing + _resetZTIState: () => { _ztiSource = null; _ztiInitPromise = null } } diff --git a/test/caas.test.js b/test/caas.test.js index 161b934c..f25cc4e3 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -1,4 +1,7 @@ const cds = require('@sap/cds') +const fs = require('fs') +const os = require('os') +const path = require('path') // Mock VCAP_SERVICES for CaaS const MOCK_CAAS_VCAP = { @@ -13,6 +16,28 @@ const MOCK_CAAS_VCAP = { }] } +// Mock VCAP_SERVICES with ZTI binding +const MOCK_ZTI_VCAP = { + 'caas-service': [{ + name: 'test-caas', + credentials: { + otlp: { + http: 'https://caas.example.com/otlp' + } + } + }], + 'zero-trust-identity': [{ + name: 'test-zti', + credentials: { + parameters: { + 'svid-store': { + file: { name: 'test-svid' } + } + } + } + }] +} + describe('augmentCaaSCreds', () => { let originalVcap @@ -111,3 +136,203 @@ describe('augmentCaaSCreds', () => { expect(credentials.httpAgentOptions).toBeUndefined() }) }) + +describe('ZTI SVID File Loading', () => { + let tmpDir + let svidDir + + beforeEach(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'zti-test-')) + svidDir = path.join(tmpDir, 'spire-svids') + fs.mkdirSync(svidDir) + + // Set up ZTI environment + process.env.VCAP_SERVICES = JSON.stringify(MOCK_ZTI_VCAP) + process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR = svidDir + delete process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI + + cds.env.requires = cds.env.requires || {} + cds.env.requires.telemetry = {} + delete require.cache[require.resolve('../lib/utils')] + }) + + afterEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }) + delete process.env.VCAP_SERVICES + delete process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR + delete process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI + }) + + test('loads SVID files on first call', async () => { + // Create SVID files + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') + + const { initializeZTI, getCredsForCaaSMtls, _resetZTIState } = require('../lib/utils') + _resetZTIState() + + await initializeZTI() + const creds = getCredsForCaaSMtls() + + expect(creds).toBeDefined() + expect(creds.cert).toContain('BEGIN CERTIFICATE') + expect(creds.key).toContain('BEGIN PRIVATE KEY') + }) + + test('reloads when mtime changes', async () => { + const certPath = path.join(svidDir, 'test-svid.svid.pem') + const keyPath = path.join(svidDir, 'test-svid.svid.key') + const bundlePath = path.join(svidDir, 'test-svid.bundle.pem') + + // Write initial files + fs.writeFileSync(certPath, '-----BEGIN CERTIFICATE-----\nv1\n-----END CERTIFICATE-----') + fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\nv1\n-----END PRIVATE KEY-----') + fs.writeFileSync(bundlePath, '-----BEGIN CERTIFICATE-----\nv1\n-----END CERTIFICATE-----') + + const { initializeZTI, getCredsForCaaSMtls, _resetZTIState } = require('../lib/utils') + _resetZTIState() + + await initializeZTI() + const creds1 = getCredsForCaaSMtls() + expect(creds1.cert).toContain('v1') + + // Wait to ensure mtime changes + await new Promise(resolve => setTimeout(resolve, 10)) + + // Update files (simulating ZTI rotation) + fs.writeFileSync(certPath, '-----BEGIN CERTIFICATE-----\nv2\n-----END CERTIFICATE-----') + fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\nv2\n-----END PRIVATE KEY-----') + + const creds2 = getCredsForCaaSMtls() + expect(creds2.cert).toContain('v2') + expect(creds2.key).toContain('v2') + }) + + test('retries if files not ready on initialization', async () => { + const certPath = path.join(svidDir, 'test-svid.svid.pem') + const keyPath = path.join(svidDir, 'test-svid.svid.key') + const bundlePath = path.join(svidDir, 'test-svid.bundle.pem') + + const { initializeZTI, _resetZTIState } = require('../lib/utils') + _resetZTIState() + + // Start initialization (files don't exist yet) + const initPromise = initializeZTI() + + // Wait 2.5 seconds, then create files (should succeed on 2nd retry) + await new Promise(resolve => setTimeout(resolve, 2500)) + fs.writeFileSync(certPath, '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') + fs.writeFileSync(bundlePath, '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + + // Should complete successfully + await expect(initPromise).resolves.toBeUndefined() + }, 10000) +}) + +describe('ZTI flag behavior', () => { + beforeEach(() => { + cds.env.requires = { telemetry: {} } + delete process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI + delete require.cache[require.resolve('../lib/utils')] + }) + + afterEach(() => { + delete process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI + delete process.env.VCAP_SERVICES + }) + + test('detects ZTI config from VCAP_SERVICES', () => { + process.env.VCAP_SERVICES = JSON.stringify(MOCK_ZTI_VCAP) + delete require.cache[require.resolve('../lib/utils')] + + const { getZTIConfig } = require('../lib/utils') + const config = getZTIConfig() + + expect(config).not.toBeNull() + expect(config.svidName).toBe('test-svid') + expect(config.svidDir).toBe('/home/vcap/app/spire-svids') + }) + + test('returns null when no ZTI binding', () => { + process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) + delete require.cache[require.resolve('../lib/utils')] + + const { getZTIConfig } = require('../lib/utils') + const config = getZTIConfig() + + expect(config).toBeNull() + }) + + test('uses legacy env vars when USE_ZTI=false', () => { + process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI = 'false' + process.env.VCAP_SERVICES = JSON.stringify(MOCK_ZTI_VCAP) + cds.env.requires.telemetry.x509 = { + cert: Buffer.from('-----BEGIN CERTIFICATE-----\nlegacy\n-----END CERTIFICATE-----').toString('base64'), + key: Buffer.from('-----BEGIN PRIVATE KEY-----\nlegacy\n-----END PRIVATE KEY-----').toString('base64') + } + delete require.cache[require.resolve('../lib/utils')] + + const { getCredsForCaaSMtls } = require('../lib/utils') + const creds = getCredsForCaaSMtls() + + expect(creds).toBeDefined() + // Legacy credentials are still base64 at this point + expect(Buffer.from(creds.cert, 'base64').toString()).toContain('legacy') + }) + + test('augmentCaaSCreds handles PEM format from ZTI', async () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'zti-test-')) + const svidDir = path.join(tmpDir, 'spire-svids') + fs.mkdirSync(svidDir) + + process.env.VCAP_SERVICES = JSON.stringify(MOCK_ZTI_VCAP) + process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR = svidDir + delete require.cache[require.resolve('../lib/utils')] + + // Create SVID files + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\nzti-cert\n-----END CERTIFICATE-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\nzti-key\n-----END PRIVATE KEY-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') + + const { initializeZTI, augmentCaaSCreds, _resetZTIState } = require('../lib/utils') + _resetZTIState() + + await initializeZTI() + + const credentials = { + otlp: { http: 'https://caas.example.com/otlp' } + } + augmentCaaSCreds(credentials) + + expect(credentials.httpAgentOptions).toBeDefined() + expect(credentials.httpAgentOptions.cert).toContain('zti-cert') + expect(credentials.httpAgentOptions.key).toContain('zti-key') + + // Cleanup + delete process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR + fs.rmSync(tmpDir, { recursive: true, force: true }) + }) + + test('augmentCaaSCreds handles base64 format from legacy', () => { + process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) + process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI = 'false' + cds.env.requires.telemetry.x509 = { + cert: Buffer.from('-----BEGIN CERTIFICATE-----\nlegacy-cert\n-----END CERTIFICATE-----').toString('base64'), + key: Buffer.from('-----BEGIN PRIVATE KEY-----\nlegacy-key\n-----END PRIVATE KEY-----').toString('base64') + } + delete require.cache[require.resolve('../lib/utils')] + + const { augmentCaaSCreds } = require('../lib/utils') + + const credentials = { + otlp: { http: 'https://caas.example.com/otlp' } + } + augmentCaaSCreds(credentials) + + expect(credentials.httpAgentOptions).toBeDefined() + expect(credentials.httpAgentOptions.cert).toContain('legacy-cert') + expect(credentials.httpAgentOptions.key).toContain('legacy-key') + }) +}) From 27a7049350b63b20f29d0a9fbafcbab83fc913d7 Mon Sep 17 00:00:00 2001 From: D051920 Date: Sat, 22 Aug 2026 13:52:35 +0200 Subject: [PATCH 30/67] fixes and logs --- cds-plugin.js | 5 ++++- lib/index.js | 26 ++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/cds-plugin.js b/cds-plugin.js index a59ca32b..1bf0bce5 100644 --- a/cds-plugin.js +++ b/cds-plugin.js @@ -12,5 +12,8 @@ if (!!process.env.NO_TELEMETRY && process.env.NO_TELEMETRY !== 'false') return - require('./lib')() + // Setup is async to support ZTI initialization (waits for SVID files) + require('./lib')().catch(err => { + cds.log('telemetry')._error && cds.log('telemetry').error('Telemetry setup failed:', err) + }) })() diff --git a/lib/index.js b/lib/index.js index a11ecb9c..f88355f7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -89,7 +89,7 @@ function _getInstrumentations() { return instrumentations } -function setup_standalone() { +async function setup_standalone() { // set logger and propagate log level diag.setLogger(cds.log('telemetry'), getDiagLogLevel()) @@ -97,14 +97,14 @@ function setup_standalone() { const resource = getResource() // Initialize ZTI if configured (blocks until SVID files ready) - // This returns a promise, but we handle async initialization gracefully: - // - If ZTI is not configured, this resolves immediately - // - If ZTI is configured, SVID files are loaded during tracing/metrics setup - const ztiInit = initializeZTI().catch(err => { + // Must complete BEFORE creating exporters so credentials are available + try { + await initializeZTI() + } catch (err) { LOG._error && LOG.error('Failed to initialize ZTI for CaaS mTLS:', err) // Don't throw - let telemetry continue without ZTI // The warning will be logged when augmentCaaSCreds() is called - }) + } // setup tracing, metrics, and logging const tracerProvider = tracing(resource) @@ -118,22 +118,20 @@ function setup_standalone() { loggerProvider, instrumentations: _getInstrumentations() }) - - return ztiInit } -function setup_with_calm() { - // Initialize ZTI if configured - const ztiInit = initializeZTI().catch(err => { +async function setup_with_calm() { + // Initialize ZTI if configured - must complete before creating exporters + try { + await initializeZTI() + } catch (err) { LOG._error && LOG.error('Failed to initialize ZTI for CaaS mTLS:', err) - }) + } // setup tracing, metrics, and logging tracing() metrics() if (cds.env.requires.telemetry.logging) logging() - - return ztiInit } module.exports = hasDependency('@sap/xotel-agent-ext-js') ? setup_with_calm : setup_standalone From 019a8afa362c9e280d2530ce37af3baab269d7d7 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 12:41:26 +0200 Subject: [PATCH 31/67] fix async call for ztis --- lib/tracing/trace.js | 47 ++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/lib/tracing/trace.js b/lib/tracing/trace.js index dc9d70db..8d7600d4 100644 --- a/lib/tracing/trace.js +++ b/lib/tracing/trace.js @@ -50,21 +50,34 @@ const $adjusted = Symbol('@cap-js/telemetry:adjusted') const $reqattrs = Symbol('@cap-js/telemetry:reqattrs') let tracer -cds.on('served', () => { - tracer = otel.trace.getTracer('@cap-js/telemetry', require('../../package.json').version) - - if (HRTIME) { - // monkey patch startActiveSpan to set a hr time as startTime (if none was provided) - const { startActiveSpan } = tracer - tracer.startActiveSpan = function (name, options, fn) { - options.startTime ??= _hrnow() - return startActiveSpan.call(this, name, options, fn) +let _hrtimePatched = false + +// Lazy-initialize tracer on first use - this avoids race condition with async ZTI init +// The tracer is obtained from the global TracerProvider, which is registered by tracing/index.js +function _getTracer() { + if (!tracer) { + tracer = otel.trace.getTracer('@cap-js/telemetry', require('../../package.json').version) + + // Apply hrtime patches once when tracer is first obtained + if (HRTIME && !_hrtimePatched) { + _hrtimePatched = true + // monkey patch startActiveSpan to set a hr time as startTime (if none was provided) + const { startActiveSpan } = tracer + tracer.startActiveSpan = function (name, options, fn) { + options.startTime ??= _hrnow() + return startActiveSpan.call(this, name, options, fn) + } } + } + return tracer +} - // attach a hr time to incoming requests for later adjustment of the root span start time +// Register listening handler for hrtime - this still needs 'served' but it's just for the optional hrtime feature +if (HRTIME) { + cds.on('served', () => { cds.on('listening', ({ server }) => server.on('request', req => (req[$hrnow] = _hrnow()))) - } -}) + }) +} // REVISIT: needs a cleanup function _getSpanName(arg, fn, that) { @@ -241,8 +254,12 @@ const _addDbRowCount = (span, res) => { } function trace(req, fn, that, args, opts = {}) { - // only trace once served and there is a cds.context - if (!tracer || !cds.context) return fn.apply(that, args) + // only trace if there is a cds.context + if (!cds.context) return fn.apply(that, args) + + // Lazy-init tracer on first use (avoids race with async ZTI init) + const currentTracer = _getTracer() + if (!currentTracer) return fn.apply(that, args) const parent = otel.trace.getActiveSpan() @@ -326,7 +343,7 @@ function trace(req, fn, that, args, opts = {}) { if (name.length > 80 && _truncate_span_name !== false) name = name.substring(0, 79) + '…' // start a new active span, call the original function, and finally end the span - return tracer.startActiveSpan(name, options, span => { + return currentTracer.startActiveSpan(name, options, span => { // in case the span is non-recording, just call the original function if (span.constructor.name === 'NonRecordingSpan') return fn.apply(that, args) From 9385b52a5982f26dbeb13c920fdb58bad638e32d Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 14:09:51 +0200 Subject: [PATCH 32/67] continue fixing async --- lib/index.js | 8 +++++++- lib/tracing/index.js | 8 ++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/index.js b/lib/index.js index f88355f7..5fb12467 100644 --- a/lib/index.js +++ b/lib/index.js @@ -96,6 +96,12 @@ async function setup_standalone() { // create resource const resource = getResource() + // IMPORTANT: Set up CDS method wrapping BEFORE async ZTI wait + // This ensures incoming requests are traced even while waiting for SVID files + // The tracer is lazy-initialized on first use (see trace.js _getTracer) + require('./tracing/cds')() + require('./tracing/cloud_sdk')() + // Initialize ZTI if configured (blocks until SVID files ready) // Must complete BEFORE creating exporters so credentials are available try { @@ -106,7 +112,7 @@ async function setup_standalone() { // The warning will be logged when augmentCaaSCreds() is called } - // setup tracing, metrics, and logging + // setup tracing, metrics, and logging (exporter/processor creation) const tracerProvider = tracing(resource) const meterProvider = metrics(resource) const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 70631429..82bb203d 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -154,14 +154,10 @@ module.exports = resource => { }) } - /* - * add tracing - */ - require('./cds')() - require('./cloud_sdk')() - /* * create processor + * NOTE: CDS method wrapping is now done earlier in lib/index.js (before ZTI wait) + * to ensure requests are traced even during async initialization */ let processor const via_one_agent = From dc693acd0d758262d58ac19dc585128d7767eec8 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 14:21:17 +0200 Subject: [PATCH 33/67] fix 3 async --- lib/tracing/trace.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/tracing/trace.js b/lib/tracing/trace.js index 8d7600d4..c4e61434 100644 --- a/lib/tracing/trace.js +++ b/lib/tracing/trace.js @@ -54,7 +54,20 @@ let _hrtimePatched = false // Lazy-initialize tracer on first use - this avoids race condition with async ZTI init // The tracer is obtained from the global TracerProvider, which is registered by tracing/index.js +// IMPORTANT: Don't cache until a real TracerProvider is registered, otherwise we cache a NoopTracer function _getTracer() { + // Check if a real TracerProvider is registered (not the default NoopTracerProvider) + // The global trace API returns a ProxyTracerProvider that delegates to the registered provider + const registeredProvider = otel.trace.getTracerProvider() + const isNoopProvider = !registeredProvider || + registeredProvider.constructor.name === 'ProxyTracerProvider' && + registeredProvider.getDelegate?.()?.constructor.name === 'NoopTracerProvider' + + if (isNoopProvider) { + // TracerProvider not yet registered - don't cache, return null to skip tracing + return null + } + if (!tracer) { tracer = otel.trace.getTracer('@cap-js/telemetry', require('../../package.json').version) From a04454d5e6eb2b811b58af48c6c54004a60e107e Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 14:38:31 +0200 Subject: [PATCH 34/67] add debug info --- lib/tracing/cds.js | 1 + lib/tracing/trace.js | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/tracing/cds.js b/lib/tracing/cds.js index c6766273..35c03e24 100644 --- a/lib/tracing/cds.js +++ b/lib/tracing/cds.js @@ -29,6 +29,7 @@ const _wrapStmt = (stmt, impl, sql, dbc) => { } module.exports = () => { + LOG._debug && LOG.debug('CDS method wrapping initialized') const { _tx, _hana_prom, _wrap_hana } = cds.env.requires.telemetry.tracing const _TX = { BEGIN: 1, COMMIT: 1, ROLLBACK: 1 } diff --git a/lib/tracing/trace.js b/lib/tracing/trace.js index c4e61434..2b87570f 100644 --- a/lib/tracing/trace.js +++ b/lib/tracing/trace.js @@ -59,9 +59,17 @@ function _getTracer() { // Check if a real TracerProvider is registered (not the default NoopTracerProvider) // The global trace API returns a ProxyTracerProvider that delegates to the registered provider const registeredProvider = otel.trace.getTracerProvider() + const delegate = registeredProvider?.getDelegate?.() const isNoopProvider = !registeredProvider || registeredProvider.constructor.name === 'ProxyTracerProvider' && - registeredProvider.getDelegate?.()?.constructor.name === 'NoopTracerProvider' + delegate?.constructor.name === 'NoopTracerProvider' + + LOG._debug && LOG.debug('_getTracer() check:', { + providerName: registeredProvider?.constructor?.name, + delegateName: delegate?.constructor?.name, + isNoopProvider, + tracerCached: !!tracer + }) if (isNoopProvider) { // TracerProvider not yet registered - don't cache, return null to skip tracing @@ -70,6 +78,7 @@ function _getTracer() { if (!tracer) { tracer = otel.trace.getTracer('@cap-js/telemetry', require('../../package.json').version) + LOG._debug && LOG.debug('_getTracer() created new tracer') // Apply hrtime patches once when tracer is first obtained if (HRTIME && !_hrtimePatched) { @@ -268,11 +277,17 @@ const _addDbRowCount = (span, res) => { function trace(req, fn, that, args, opts = {}) { // only trace if there is a cds.context - if (!cds.context) return fn.apply(that, args) + if (!cds.context) { + LOG._debug && LOG.debug('trace() skipped: no cds.context') + return fn.apply(that, args) + } // Lazy-init tracer on first use (avoids race with async ZTI init) const currentTracer = _getTracer() - if (!currentTracer) return fn.apply(that, args) + if (!currentTracer) { + LOG._debug && LOG.debug('trace() skipped: no tracer (NoopProvider still active)') + return fn.apply(that, args) + } const parent = otel.trace.getActiveSpan() From b5b2b7695cc44fd3e3c1c3ffd0916d9476f7e587 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 14:48:28 +0200 Subject: [PATCH 35/67] debug: add wrapper-level logging to cds.js --- lib/tracing/cds.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/tracing/cds.js b/lib/tracing/cds.js index 35c03e24..c1fe5f2d 100644 --- a/lib/tracing/cds.js +++ b/lib/tracing/cds.js @@ -34,14 +34,21 @@ module.exports = () => { const _TX = { BEGIN: 1, COMMIT: 1, ROLLBACK: 1 } const { emit: _emit, handle: _handle } = cds.Service.prototype + LOG._debug && LOG.debug('CDS wrapping: captured emit and handle from prototype', { + hasEmit: typeof _emit === 'function', + hasHandle: typeof _handle === 'function' + }) + cds.Service.prototype.emit = wrap(_emit, { wrapper: function emit() { + LOG._debug && LOG.debug('CDS wrapped emit() called', { event: arguments[0]?.event || arguments[0], service: this?.name }) const event = arguments[0]?.event || arguments[0] return trace({ phase: 'emit', event }, _emit, this, arguments, {}) } }) cds.Service.prototype.handle = wrap(_handle, { wrapper: function handle(req) { + LOG._debug && LOG.debug('CDS wrapped handle() called', { event: req?.event, service: this?.name }) if (!_tx && req.event in _TX) return _handle.apply(this, arguments) return trace(req, _handle, this, arguments, {}) } From 389651bb5d2929beb40d61d86838cbd9a4afa0a8 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 15:06:53 +0200 Subject: [PATCH 36/67] more debug --- lib/tracing/cds.js | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/tracing/cds.js b/lib/tracing/cds.js index c1fe5f2d..429abea3 100644 --- a/lib/tracing/cds.js +++ b/lib/tracing/cds.js @@ -33,27 +33,42 @@ module.exports = () => { const { _tx, _hana_prom, _wrap_hana } = cds.env.requires.telemetry.tracing const _TX = { BEGIN: 1, COMMIT: 1, ROLLBACK: 1 } - const { emit: _emit, handle: _handle } = cds.Service.prototype - LOG._debug && LOG.debug('CDS wrapping: captured emit and handle from prototype', { - hasEmit: typeof _emit === 'function', - hasHandle: typeof _handle === 'function' + // Check if cds.Service exists and has prototype methods + LOG._debug && LOG.debug('CDS wrapping diagnostics:', { + hasService: !!cds.Service, + hasPrototype: !!cds.Service?.prototype, + prototypeEmit: typeof cds.Service?.prototype?.emit, + prototypeHandle: typeof cds.Service?.prototype?.handle }) + if (!cds.Service?.prototype?.handle) { + LOG._warn && LOG.warn('CDS wrapping: cds.Service.prototype.handle not available yet!') + return + } + + const { emit: _emit, handle: _handle } = cds.Service.prototype + cds.Service.prototype.emit = wrap(_emit, { wrapper: function emit() { - LOG._debug && LOG.debug('CDS wrapped emit() called', { event: arguments[0]?.event || arguments[0], service: this?.name }) + LOG._debug && LOG.debug('>>> CDS wrapped EMIT called', { event: arguments[0]?.event || arguments[0], service: this?.name }) const event = arguments[0]?.event || arguments[0] return trace({ phase: 'emit', event }, _emit, this, arguments, {}) } }) cds.Service.prototype.handle = wrap(_handle, { wrapper: function handle(req) { - LOG._debug && LOG.debug('CDS wrapped handle() called', { event: req?.event, service: this?.name }) + LOG._debug && LOG.debug('>>> CDS wrapped HANDLE called', { event: req?.event, service: this?.name }) if (!_tx && req.event in _TX) return _handle.apply(this, arguments) return trace(req, _handle, this, arguments, {}) } }) + // Verify wrapping was successful + LOG._debug && LOG.debug('CDS wrapping complete:', { + emitWrapped: cds.Service.prototype.emit.__wrapped === true, + handleWrapped: cds.Service.prototype.handle.__wrapped === true + }) + const { spawn: _spawn } = cds cds.spawn = wrap(_spawn, { wrapper: function spawn() { @@ -66,6 +81,15 @@ module.exports = () => { }) cds.on('serving', service => { + // Log service details to verify wrapping works at runtime + LOG._debug && LOG.debug('>>> CDS serving event:', { + serviceName: service.name, + serviceType: service.constructor?.name, + prototypeHandleWrapped: cds.Service.prototype.handle?.__wrapped === true, + instanceHasHandle: typeof service.handle === 'function', + instanceHandleIsWrapped: service.handle?.__wrapped === true + }) + // trace individual event handlers -> INOFFICIAL! if (cds.env.requires.telemetry.tracing?.level === 'debug') { for (const each of ['_error', '_initial', 'on', 'before', 'after']) { From aa00693b6c23c0025ffa90f6b911670db6e5859e Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 15:23:09 +0200 Subject: [PATCH 37/67] more debug --- lib/tracing/cds.js | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/lib/tracing/cds.js b/lib/tracing/cds.js index 429abea3..f465826c 100644 --- a/lib/tracing/cds.js +++ b/lib/tracing/cds.js @@ -29,46 +29,25 @@ const _wrapStmt = (stmt, impl, sql, dbc) => { } module.exports = () => { - LOG._debug && LOG.debug('CDS method wrapping initialized') const { _tx, _hana_prom, _wrap_hana } = cds.env.requires.telemetry.tracing const _TX = { BEGIN: 1, COMMIT: 1, ROLLBACK: 1 } - // Check if cds.Service exists and has prototype methods - LOG._debug && LOG.debug('CDS wrapping diagnostics:', { - hasService: !!cds.Service, - hasPrototype: !!cds.Service?.prototype, - prototypeEmit: typeof cds.Service?.prototype?.emit, - prototypeHandle: typeof cds.Service?.prototype?.handle - }) - - if (!cds.Service?.prototype?.handle) { - LOG._warn && LOG.warn('CDS wrapping: cds.Service.prototype.handle not available yet!') - return - } - const { emit: _emit, handle: _handle } = cds.Service.prototype - cds.Service.prototype.emit = wrap(_emit, { wrapper: function emit() { - LOG._debug && LOG.debug('>>> CDS wrapped EMIT called', { event: arguments[0]?.event || arguments[0], service: this?.name }) + LOG._debug && LOG.debug('>>> Service.prototype.emit WRAPPER called', { event: arguments[0]?.event || arguments[0], service: this?.name }) const event = arguments[0]?.event || arguments[0] return trace({ phase: 'emit', event }, _emit, this, arguments, {}) } }) cds.Service.prototype.handle = wrap(_handle, { wrapper: function handle(req) { - LOG._debug && LOG.debug('>>> CDS wrapped HANDLE called', { event: req?.event, service: this?.name }) + LOG._debug && LOG.debug('>>> Service.prototype.handle WRAPPER called', { event: req?.event, service: this?.name }) if (!_tx && req.event in _TX) return _handle.apply(this, arguments) return trace(req, _handle, this, arguments, {}) } }) - // Verify wrapping was successful - LOG._debug && LOG.debug('CDS wrapping complete:', { - emitWrapped: cds.Service.prototype.emit.__wrapped === true, - handleWrapped: cds.Service.prototype.handle.__wrapped === true - }) - const { spawn: _spawn } = cds cds.spawn = wrap(_spawn, { wrapper: function spawn() { @@ -81,15 +60,6 @@ module.exports = () => { }) cds.on('serving', service => { - // Log service details to verify wrapping works at runtime - LOG._debug && LOG.debug('>>> CDS serving event:', { - serviceName: service.name, - serviceType: service.constructor?.name, - prototypeHandleWrapped: cds.Service.prototype.handle?.__wrapped === true, - instanceHasHandle: typeof service.handle === 'function', - instanceHandleIsWrapped: service.handle?.__wrapped === true - }) - // trace individual event handlers -> INOFFICIAL! if (cds.env.requires.telemetry.tracing?.level === 'debug') { for (const each of ['_error', '_initial', 'on', 'before', 'after']) { From 9f3b757fbffc0ead46765da3f82584410fd7c69f Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 15:36:29 +0200 Subject: [PATCH 38/67] experimenting --- lib/tracing/cds.js | 52 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/lib/tracing/cds.js b/lib/tracing/cds.js index f465826c..7fe57741 100644 --- a/lib/tracing/cds.js +++ b/lib/tracing/cds.js @@ -33,18 +33,48 @@ module.exports = () => { const _TX = { BEGIN: 1, COMMIT: 1, ROLLBACK: 1 } const { emit: _emit, handle: _handle } = cds.Service.prototype - cds.Service.prototype.emit = wrap(_emit, { - wrapper: function emit() { - LOG._debug && LOG.debug('>>> Service.prototype.emit WRAPPER called', { event: arguments[0]?.event || arguments[0], service: this?.name }) - const event = arguments[0]?.event || arguments[0] - return trace({ phase: 'emit', event }, _emit, this, arguments, {}) - } + + // Create wrapper functions with explicit markers + const emitWrapper = function emit() { + LOG._debug && LOG.debug('>>> Service.prototype.emit WRAPPER called', { event: arguments[0]?.event || arguments[0], service: this?.name }) + const event = arguments[0]?.event || arguments[0] + return trace({ phase: 'emit', event }, _emit, this, arguments, {}) + } + emitWrapper.__telemetry_wrapper = true + + const handleWrapper = function handle(req) { + LOG._debug && LOG.debug('>>> Service.prototype.handle WRAPPER called', { event: req?.event, service: this?.name }) + if (!_tx && req.event in _TX) return _handle.apply(this, arguments) + return trace(req, _handle, this, arguments, {}) + } + handleWrapper.__telemetry_wrapper = true + + cds.Service.prototype.emit = wrap(_emit, { wrapper: emitWrapper }) + cds.Service.prototype.handle = wrap(_handle, { wrapper: handleWrapper }) + + LOG._debug && LOG.debug('CDS wrapping installed', { + handleIsWrapper: cds.Service.prototype.handle.__telemetry_wrapper === true, + handleIsWrapped: cds.Service.prototype.handle.__wrapped === true }) - cds.Service.prototype.handle = wrap(_handle, { - wrapper: function handle(req) { - LOG._debug && LOG.debug('>>> Service.prototype.handle WRAPPER called', { event: req?.event, service: this?.name }) - if (!_tx && req.event in _TX) return _handle.apply(this, arguments) - return trace(req, _handle, this, arguments, {}) + + // Verify wrapper is still in place after CDS finishes loading + cds.once('served', () => { + const currentHandle = cds.Service.prototype.handle + LOG._debug && LOG.debug('CDS wrapper verification at served', { + handleIsWrapper: currentHandle.__telemetry_wrapper === true, + handleIsWrapped: currentHandle.__wrapped === true, + handleName: currentHandle.name + }) + + // Also check ApplicationService + if (cds.ApplicationService) { + const appHandle = cds.ApplicationService.prototype.handle + const serviceProto = Object.getPrototypeOf(cds.ApplicationService.prototype) + LOG._debug && LOG.debug('ApplicationService diagnostics', { + appHandleIsOwn: Object.hasOwn(cds.ApplicationService.prototype, 'handle'), + serviceProtoHandle: serviceProto.handle.__telemetry_wrapper === true, + superWouldCallWrapper: serviceProto === cds.Service.prototype + }) } }) From f72dceb186e1a78f8d1dba639417938ed6161370 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 15:47:38 +0200 Subject: [PATCH 39/67] debug --- lib/tracing/cds.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/tracing/cds.js b/lib/tracing/cds.js index 7fe57741..0a05f7b8 100644 --- a/lib/tracing/cds.js +++ b/lib/tracing/cds.js @@ -78,6 +78,30 @@ module.exports = () => { } }) + // Add request-time diagnostics by wrapping ApplicationService.prototype.handle + cds.once('served', () => { + if (cds.ApplicationService?.prototype?.handle) { + const _appHandle = cds.ApplicationService.prototype.handle + cds.ApplicationService.prototype.handle = async function handle(req) { + const requiresResolving = this._requires_resolving?.(req) + LOG._debug && LOG.debug('>>> ApplicationService.handle called', { + event: req?.event, + service: this?.name, + requiresResolving, + resolved: req?._resolved, + willCallSuper: !requiresResolving + }) + const result = await _appHandle.apply(this, arguments) + LOG._debug && LOG.debug('>>> ApplicationService.handle completed', { + event: req?.event, + service: this?.name + }) + return result + } + LOG._debug && LOG.debug('ApplicationService.prototype.handle wrapped for diagnostics') + } + }) + const { spawn: _spawn } = cds cds.spawn = wrap(_spawn, { wrapper: function spawn() { From 52d7beaa026c04c116a94f006b55ef7f22c4b594 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 16:02:16 +0200 Subject: [PATCH 40/67] fix --- lib/tracing/cds.js | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/lib/tracing/cds.js b/lib/tracing/cds.js index 0a05f7b8..47f9b18c 100644 --- a/lib/tracing/cds.js +++ b/lib/tracing/cds.js @@ -78,27 +78,38 @@ module.exports = () => { } }) - // Add request-time diagnostics by wrapping ApplicationService.prototype.handle - cds.once('served', () => { - if (cds.ApplicationService?.prototype?.handle) { - const _appHandle = cds.ApplicationService.prototype.handle - cds.ApplicationService.prototype.handle = async function handle(req) { - const requiresResolving = this._requires_resolving?.(req) - LOG._debug && LOG.debug('>>> ApplicationService.handle called', { - event: req?.event, - service: this?.name, - requiresResolving, - resolved: req?._resolved, - willCallSuper: !requiresResolving - }) - const result = await _appHandle.apply(this, arguments) - LOG._debug && LOG.debug('>>> ApplicationService.handle completed', { + // Check each service instance to see if handle is bound to instance vs prototype + cds.on('serving', service => { + const hasOwnHandle = Object.hasOwn(service, 'handle') + const handleSource = hasOwnHandle ? 'instance' : 'prototype' + const protoChain = [] + let proto = Object.getPrototypeOf(service) + while (proto && proto !== Object.prototype) { + if (Object.hasOwn(proto, 'handle')) { + protoChain.push(proto.constructor?.name || 'unknown') + } + proto = Object.getPrototypeOf(proto) + } + LOG._debug && LOG.debug('>>> Service instance handle diagnostics', { + serviceName: service.name, + hasOwnHandle, + handleSource, + protoChainWithHandle: protoChain, + instanceHandleIsWrapper: service.handle?.__telemetry_wrapper === true + }) + + // If instance has own handle, wrap it too! + if (hasOwnHandle && !service.handle.__telemetry_wrapper) { + const _instanceHandle = service.handle.bind(service) + service.handle = function handle(req) { + LOG._debug && LOG.debug('>>> Instance handle WRAPPER called', { event: req?.event, service: this?.name }) - return result + return trace(req, _instanceHandle, this, arguments, {}) } - LOG._debug && LOG.debug('ApplicationService.prototype.handle wrapped for diagnostics') + service.handle.__telemetry_wrapper = true + LOG._debug && LOG.debug('>>> Wrapped instance handle for', { serviceName: service.name }) } }) From 778bb3a7917f804d963e03d96e265bb943f9e8e8 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 16:10:24 +0200 Subject: [PATCH 41/67] fix2 --- lib/tracing/cds.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/tracing/cds.js b/lib/tracing/cds.js index 47f9b18c..3b164e21 100644 --- a/lib/tracing/cds.js +++ b/lib/tracing/cds.js @@ -57,25 +57,27 @@ module.exports = () => { handleIsWrapped: cds.Service.prototype.handle.__wrapped === true }) - // Verify wrapper is still in place after CDS finishes loading + // Wrap ApplicationService.prototype.handle which shadows Service.prototype.handle cds.once('served', () => { + if (cds.ApplicationService?.prototype?.handle && !cds.ApplicationService.prototype.handle.__telemetry_wrapper) { + const _appHandle = cds.ApplicationService.prototype.handle + const appHandleWrapper = function handle(req) { + LOG._debug && LOG.debug('>>> ApplicationService.prototype.handle WRAPPER called', { event: req?.event, service: this?.name }) + if (!_tx && req.event in _TX) return _appHandle.apply(this, arguments) + return trace(req, _appHandle, this, arguments, {}) + } + appHandleWrapper.__telemetry_wrapper = true + cds.ApplicationService.prototype.handle = wrap(_appHandle, { wrapper: appHandleWrapper }) + LOG._debug && LOG.debug('ApplicationService.prototype.handle wrapped for tracing') + } + const currentHandle = cds.Service.prototype.handle LOG._debug && LOG.debug('CDS wrapper verification at served', { handleIsWrapper: currentHandle.__telemetry_wrapper === true, handleIsWrapped: currentHandle.__wrapped === true, - handleName: currentHandle.name + handleName: currentHandle.name, + appServiceHandleWrapped: cds.ApplicationService?.prototype?.handle?.__telemetry_wrapper === true }) - - // Also check ApplicationService - if (cds.ApplicationService) { - const appHandle = cds.ApplicationService.prototype.handle - const serviceProto = Object.getPrototypeOf(cds.ApplicationService.prototype) - LOG._debug && LOG.debug('ApplicationService diagnostics', { - appHandleIsOwn: Object.hasOwn(cds.ApplicationService.prototype, 'handle'), - serviceProtoHandle: serviceProto.handle.__telemetry_wrapper === true, - superWouldCallWrapper: serviceProto === cds.Service.prototype - }) - } }) // Check each service instance to see if handle is bound to instance vs prototype From e0ee73e489fd189d170b5f3dbab4d26b1d32bcdc Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 16:52:04 +0200 Subject: [PATCH 42/67] use early TracerProvider for ZTIS --- lib/index.js | 104 +++++++++++++++++++++++++++++++++---------- lib/tracing/cds.js | 83 ++++------------------------------ lib/tracing/index.js | 67 ++++++++++++++++++++++++++-- 3 files changed, 152 insertions(+), 102 deletions(-) diff --git a/lib/index.js b/lib/index.js index 5fb12467..a81fbfb6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -89,6 +89,32 @@ function _getInstrumentations() { return instrumentations } +/** + * Check if ZTI (Zero Trust Identity) is enabled and has configuration + * ZTI requires waiting for SVID files which can take ~10 seconds + */ +function _needsZTIWait() { + // ZTI is disabled via env var + if (process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI === 'false') return false + + // Check if CaaS telemetry kind is configured + const kind = cds.env.requires.telemetry?.kind + if (!kind?.match(/to-caas$/)) return false + + // Check for ZTI binding in VCAP_SERVICES + const vcapServices = process.env.VCAP_SERVICES && JSON.parse(process.env.VCAP_SERVICES) + if (!vcapServices) return false + + // Look for zero-trust-identity binding + for (const services of Object.values(vcapServices)) { + for (const svc of services) { + if (svc.tags?.includes('zero-trust-identity')) return true + } + } + + return false +} + async function setup_standalone() { // set logger and propagate log level diag.setLogger(cds.log('telemetry'), getDiagLogLevel()) @@ -96,34 +122,64 @@ async function setup_standalone() { // create resource const resource = getResource() - // IMPORTANT: Set up CDS method wrapping BEFORE async ZTI wait - // This ensures incoming requests are traced even while waiting for SVID files - // The tracer is lazy-initialized on first use (see trace.js _getTracer) + // Set up CDS method wrapping require('./tracing/cds')() require('./tracing/cloud_sdk')() - // Initialize ZTI if configured (blocks until SVID files ready) - // Must complete BEFORE creating exporters so credentials are available - try { - await initializeZTI() - } catch (err) { - LOG._error && LOG.error('Failed to initialize ZTI for CaaS mTLS:', err) - // Don't throw - let telemetry continue without ZTI - // The warning will be logged when augmentCaaSCreds() is called - } + // Check if ZTI is needed (async wait for SVID files) + const needsZTI = _needsZTIWait() + + if (needsZTI) { + // ZTI FLOW: Register TracerProvider early, add exporter after SVID ready + // This ensures HTTP instrumentation gets a real tracer instead of NoopTracer + // while waiting for SVID certificates + + // STEP 1: Create TracerProvider EARLY (before ZTI wait) + const tracerProvider = tracing.createTracerProvider(resource) + const meterProvider = metrics(resource) + const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined - // setup tracing, metrics, and logging (exporter/processor creation) - const tracerProvider = tracing(resource) - const meterProvider = metrics(resource) - const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined - - // register instrumentations - registerInstrumentations({ - tracerProvider, - meterProvider, - loggerProvider, - instrumentations: _getInstrumentations() - }) + // STEP 2: Register instrumentations IMMEDIATELY + registerInstrumentations({ + tracerProvider, + meterProvider, + loggerProvider, + instrumentations: _getInstrumentations() + }) + + // STEP 3: Wait for ZTI SVID files + try { + await initializeZTI() + } catch (err) { + LOG._error && LOG.error('Failed to initialize ZTI for CaaS mTLS:', err) + } + + // STEP 4: Add trace exporter now that credentials are available + tracing.addTracingExporter(tracerProvider) + } else { + // STANDARD FLOW: Synchronous setup (original behavior) + // No ZTI wait needed, so we can create provider with exporter immediately + + // Initialize ZTI if configured (will be no-op if not needed) + try { + await initializeZTI() + } catch (err) { + LOG._error && LOG.error('Failed to initialize ZTI for CaaS mTLS:', err) + } + + // Setup tracing, metrics, and logging + const tracerProvider = tracing(resource) + const meterProvider = metrics(resource) + const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined + + // Register instrumentations + registerInstrumentations({ + tracerProvider, + meterProvider, + loggerProvider, + instrumentations: _getInstrumentations() + }) + } } async function setup_with_calm() { diff --git a/lib/tracing/cds.js b/lib/tracing/cds.js index 3b164e21..756e3b4c 100644 --- a/lib/tracing/cds.js +++ b/lib/tracing/cds.js @@ -34,84 +34,17 @@ module.exports = () => { const { emit: _emit, handle: _handle } = cds.Service.prototype - // Create wrapper functions with explicit markers - const emitWrapper = function emit() { - LOG._debug && LOG.debug('>>> Service.prototype.emit WRAPPER called', { event: arguments[0]?.event || arguments[0], service: this?.name }) - const event = arguments[0]?.event || arguments[0] - return trace({ phase: 'emit', event }, _emit, this, arguments, {}) - } - emitWrapper.__telemetry_wrapper = true - - const handleWrapper = function handle(req) { - LOG._debug && LOG.debug('>>> Service.prototype.handle WRAPPER called', { event: req?.event, service: this?.name }) - if (!_tx && req.event in _TX) return _handle.apply(this, arguments) - return trace(req, _handle, this, arguments, {}) - } - handleWrapper.__telemetry_wrapper = true - - cds.Service.prototype.emit = wrap(_emit, { wrapper: emitWrapper }) - cds.Service.prototype.handle = wrap(_handle, { wrapper: handleWrapper }) - - LOG._debug && LOG.debug('CDS wrapping installed', { - handleIsWrapper: cds.Service.prototype.handle.__telemetry_wrapper === true, - handleIsWrapped: cds.Service.prototype.handle.__wrapped === true - }) - - // Wrap ApplicationService.prototype.handle which shadows Service.prototype.handle - cds.once('served', () => { - if (cds.ApplicationService?.prototype?.handle && !cds.ApplicationService.prototype.handle.__telemetry_wrapper) { - const _appHandle = cds.ApplicationService.prototype.handle - const appHandleWrapper = function handle(req) { - LOG._debug && LOG.debug('>>> ApplicationService.prototype.handle WRAPPER called', { event: req?.event, service: this?.name }) - if (!_tx && req.event in _TX) return _appHandle.apply(this, arguments) - return trace(req, _appHandle, this, arguments, {}) - } - appHandleWrapper.__telemetry_wrapper = true - cds.ApplicationService.prototype.handle = wrap(_appHandle, { wrapper: appHandleWrapper }) - LOG._debug && LOG.debug('ApplicationService.prototype.handle wrapped for tracing') + cds.Service.prototype.emit = wrap(_emit, { + wrapper: function emit() { + const event = arguments[0]?.event || arguments[0] + return trace({ phase: 'emit', event }, _emit, this, arguments, {}) } - - const currentHandle = cds.Service.prototype.handle - LOG._debug && LOG.debug('CDS wrapper verification at served', { - handleIsWrapper: currentHandle.__telemetry_wrapper === true, - handleIsWrapped: currentHandle.__wrapped === true, - handleName: currentHandle.name, - appServiceHandleWrapped: cds.ApplicationService?.prototype?.handle?.__telemetry_wrapper === true - }) }) - // Check each service instance to see if handle is bound to instance vs prototype - cds.on('serving', service => { - const hasOwnHandle = Object.hasOwn(service, 'handle') - const handleSource = hasOwnHandle ? 'instance' : 'prototype' - const protoChain = [] - let proto = Object.getPrototypeOf(service) - while (proto && proto !== Object.prototype) { - if (Object.hasOwn(proto, 'handle')) { - protoChain.push(proto.constructor?.name || 'unknown') - } - proto = Object.getPrototypeOf(proto) - } - LOG._debug && LOG.debug('>>> Service instance handle diagnostics', { - serviceName: service.name, - hasOwnHandle, - handleSource, - protoChainWithHandle: protoChain, - instanceHandleIsWrapper: service.handle?.__telemetry_wrapper === true - }) - - // If instance has own handle, wrap it too! - if (hasOwnHandle && !service.handle.__telemetry_wrapper) { - const _instanceHandle = service.handle.bind(service) - service.handle = function handle(req) { - LOG._debug && LOG.debug('>>> Instance handle WRAPPER called', { - event: req?.event, - service: this?.name - }) - return trace(req, _instanceHandle, this, arguments, {}) - } - service.handle.__telemetry_wrapper = true - LOG._debug && LOG.debug('>>> Wrapped instance handle for', { serviceName: service.name }) + cds.Service.prototype.handle = wrap(_handle, { + wrapper: function handle(req) { + if (!_tx && req.event in _TX) return _handle.apply(this, arguments) + return trace(req, _handle, this, arguments, {}) } }) diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 82bb203d..c957d85e 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -142,7 +142,65 @@ function _getExporter() { return exporter } -module.exports = resource => { +/** + * Create TracerProvider early (before credentials are available). + * This allows HTTP instrumentation to get a real tracer instead of NoopTracer. + * The span processor with exporter is added later via addTracingExporter(). + */ +function createTracerProvider(resource) { + if (!cds.env.requires.telemetry.tracing?.exporter) return + + // clear sap passport for new tx + if (process.env.SAP_PASSPORT) { + cds.on('served', () => { + cds.db?.before('BEGIN', async function () { + if (this.dbc?.constructor.name in { HDBDriver: 1, HANAClientDriver: 1 }) this.dbc.set({ SAP_PASSPORT: '' }) + }) + }) + } + + resource = resourceFromAttributes({}).merge(resource).merge(getDynatraceMetadata()) + const tracerProvider = new NodeTracerProvider({ resource, sampler: _getSampler() }) + tracerProvider.register({ propagator: _getPropagator() }) + + LOG._debug && LOG.debug('TracerProvider registered early (exporter will be added when credentials ready)') + + return tracerProvider +} + +/** + * Add exporter to TracerProvider after credentials are available (e.g., after ZTI SVID ready). + * This must be called after createTracerProvider() and after any async credential initialization. + */ +function addTracingExporter(tracerProvider) { + if (!tracerProvider) return + + const via_one_agent = + process.env.DT_NODE_PRELOAD_OPTIONS && + cds.env.requires.telemetry.kind.match(/to-dynatrace$/) && + !hasDependency('@opentelemetry/exporter-trace-otlp-proto') + + if (via_one_agent) { + LOG._info && LOG.info('Dynatrace OneAgent detected, disabling trace exporter') + return + } + + const exporter = _getExporter() + const processorConfig = cds.env.requires.telemetry.tracing.processor?.config || {} + const processor = + process.env.NODE_ENV === 'production' + ? new BatchSpanProcessor(exporter, processorConfig) + : new SimpleSpanProcessor(exporter, processorConfig) + + tracerProvider.addSpanProcessor(processor) + LOG._debug && LOG.debug('Trace exporter added to TracerProvider') +} + +/** + * Original combined function for backward compatibility with CALM setup. + * Creates TracerProvider AND adds exporter in one call. + */ +function setupTracing(resource) { if (!cds.env.requires.telemetry.tracing?.exporter) return // clear sap passport for new tx @@ -156,8 +214,6 @@ module.exports = resource => { /* * create processor - * NOTE: CDS method wrapping is now done earlier in lib/index.js (before ZTI wait) - * to ensure requests are traced even during async initialization */ let processor const via_one_agent = @@ -199,3 +255,8 @@ module.exports = resource => { tracerProvider.register({ propagator: _getPropagator() }) return tracerProvider } + +// Export the new split functions and keep backward compatibility +module.exports = setupTracing +module.exports.createTracerProvider = createTracerProvider +module.exports.addTracingExporter = addTracingExporter From e8ba74ffeca3d886ae9210fa68e0c4587add4b24 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 17:08:03 +0200 Subject: [PATCH 43/67] fix: detect ZTI by label and service key, not just tags --- lib/index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index a81fbfb6..e3dbfa2d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -105,10 +105,13 @@ function _needsZTIWait() { const vcapServices = process.env.VCAP_SERVICES && JSON.parse(process.env.VCAP_SERVICES) if (!vcapServices) return false - // Look for zero-trust-identity binding - for (const services of Object.values(vcapServices)) { + // Look for zero-trust-identity binding (check both tags and label) + for (const [key, services] of Object.entries(vcapServices)) { + // Check if the service key itself indicates ZTI + if (key === 'zero-trust-identity') return true for (const svc of services) { if (svc.tags?.includes('zero-trust-identity')) return true + if (svc.label === 'zero-trust-identity') return true } } From baf2226eb968ae22ae6c311055ae53419ef2e705 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 17:26:30 +0200 Subject: [PATCH 44/67] fix: use internal API for adding span processor in OTel v2.x --- lib/tracing/index.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/tracing/index.js b/lib/tracing/index.js index c957d85e..bb64567e 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -192,7 +192,19 @@ function addTracingExporter(tracerProvider) { ? new BatchSpanProcessor(exporter, processorConfig) : new SimpleSpanProcessor(exporter, processorConfig) - tracerProvider.addSpanProcessor(processor) + // OpenTelemetry v2.x removed addSpanProcessor - access internal MultiSpanProcessor array + // This is a workaround for the ZTI timing issue where we need to add the exporter after + // TracerProvider is already registered + if (typeof tracerProvider.addSpanProcessor === 'function') { + // v1.x API + tracerProvider.addSpanProcessor(processor) + } else if (tracerProvider._activeSpanProcessor?._spanProcessors) { + // v2.x - push to internal array + tracerProvider._activeSpanProcessor._spanProcessors.push(processor) + } else { + LOG._error && LOG.error('Unable to add span processor - unknown TracerProvider API') + return + } LOG._debug && LOG.debug('Trace exporter added to TracerProvider') } From e628d15e38dfa10a7f1211e30f67fbd98d2eb32f Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 17:30:45 +0200 Subject: [PATCH 45/67] fix: use BufferingSpanProcessor to hold spans until ZTI creds ready --- lib/tracing/index.js | 98 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 14 deletions(-) diff --git a/lib/tracing/index.js b/lib/tracing/index.js index bb64567e..60eac3c6 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -16,6 +16,70 @@ const { _require } = require('../utils') +/** + * A span processor that buffers spans until a delegate processor is set. + * Used for ZTI flow where we need to register TracerProvider early but + * can't create the exporter until credentials are available. + */ +class BufferingSpanProcessor { + constructor() { + this._buffer = [] + this._delegate = null + this._maxBufferSize = 2048 // Prevent unbounded memory growth + } + + onStart(span, context) { + if (this._delegate) { + this._delegate.onStart(span, context) + } + // No buffering needed for onStart - spans are mutable until onEnd + } + + onEnd(span) { + if (this._delegate) { + this._delegate.onEnd(span) + } else { + // Buffer the span until delegate is ready + if (this._buffer.length < this._maxBufferSize) { + this._buffer.push(span) + } else { + LOG._warn && LOG.warn('BufferingSpanProcessor: buffer full, dropping span') + } + } + } + + /** + * Set the delegate processor and flush buffered spans + */ + setDelegate(processor) { + this._delegate = processor + // Flush buffered spans + const buffered = this._buffer.length + for (const span of this._buffer) { + processor.onEnd(span) + } + this._buffer = [] + if (buffered > 0) { + LOG._debug && LOG.debug(`BufferingSpanProcessor: flushed ${buffered} buffered spans`) + } + } + + forceFlush() { + if (this._delegate) { + return this._delegate.forceFlush() + } + return Promise.resolve() + } + + shutdown() { + this._buffer = [] + if (this._delegate) { + return this._delegate.shutdown() + } + return Promise.resolve() + } +} + function _getSampler() { const { ignoreIncomingPaths } = cds.env.requires.telemetry.tracing?.sampler || {} @@ -145,7 +209,8 @@ function _getExporter() { /** * Create TracerProvider early (before credentials are available). * This allows HTTP instrumentation to get a real tracer instead of NoopTracer. - * The span processor with exporter is added later via addTracingExporter(). + * Uses a BufferingSpanProcessor that holds spans until the real exporter is ready. + * Call addTracingExporter() later to set the real exporter. */ function createTracerProvider(resource) { if (!cds.env.requires.telemetry.tracing?.exporter) return @@ -160,9 +225,20 @@ function createTracerProvider(resource) { } resource = resourceFromAttributes({}).merge(resource).merge(getDynatraceMetadata()) - const tracerProvider = new NodeTracerProvider({ resource, sampler: _getSampler() }) + + // Create a buffering processor that will hold spans until real exporter is ready + const bufferingProcessor = new BufferingSpanProcessor() + + const tracerProvider = new NodeTracerProvider({ + resource, + sampler: _getSampler(), + spanProcessors: [bufferingProcessor] + }) tracerProvider.register({ propagator: _getPropagator() }) + // Store reference to buffering processor for later delegate assignment + tracerProvider._bufferingProcessor = bufferingProcessor + LOG._debug && LOG.debug('TracerProvider registered early (exporter will be added when credentials ready)') return tracerProvider @@ -192,20 +268,14 @@ function addTracingExporter(tracerProvider) { ? new BatchSpanProcessor(exporter, processorConfig) : new SimpleSpanProcessor(exporter, processorConfig) - // OpenTelemetry v2.x removed addSpanProcessor - access internal MultiSpanProcessor array - // This is a workaround for the ZTI timing issue where we need to add the exporter after - // TracerProvider is already registered - if (typeof tracerProvider.addSpanProcessor === 'function') { - // v1.x API - tracerProvider.addSpanProcessor(processor) - } else if (tracerProvider._activeSpanProcessor?._spanProcessors) { - // v2.x - push to internal array - tracerProvider._activeSpanProcessor._spanProcessors.push(processor) + // Set the real processor as delegate on the BufferingSpanProcessor + // This flushes any buffered spans and forwards future spans to the real processor + if (tracerProvider._bufferingProcessor) { + tracerProvider._bufferingProcessor.setDelegate(processor) + LOG._debug && LOG.debug('Trace exporter added to TracerProvider via BufferingSpanProcessor') } else { - LOG._error && LOG.error('Unable to add span processor - unknown TracerProvider API') - return + LOG._error && LOG.error('Unable to add span processor - no BufferingSpanProcessor found') } - LOG._debug && LOG.debug('Trace exporter added to TracerProvider') } /** From dd2f6eb0d8d82a4e34399887d146a2d070aee944 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 17:43:49 +0200 Subject: [PATCH 46/67] fix: create metrics exporter AFTER ZTI ready to get mTLS creds --- lib/index.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/index.js b/lib/index.js index e3dbfa2d..ab61868d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -133,20 +133,19 @@ async function setup_standalone() { const needsZTI = _needsZTIWait() if (needsZTI) { - // ZTI FLOW: Register TracerProvider early, add exporter after SVID ready + // ZTI FLOW: Register TracerProvider early, add exporters after SVID ready // This ensures HTTP instrumentation gets a real tracer instead of NoopTracer // while waiting for SVID certificates - // STEP 1: Create TracerProvider EARLY (before ZTI wait) + // STEP 1: Create TracerProvider EARLY (before ZTI wait) - no exporter yet const tracerProvider = tracing.createTracerProvider(resource) - const meterProvider = metrics(resource) - const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined - // STEP 2: Register instrumentations IMMEDIATELY + // STEP 2: Register instrumentations IMMEDIATELY (so HTTP gets real tracer) + // Note: metrics/logging exporters will be created after ZTI is ready registerInstrumentations({ tracerProvider, - meterProvider, - loggerProvider, + meterProvider: undefined, // Will be created after ZTI + loggerProvider: undefined, instrumentations: _getInstrumentations() }) @@ -157,7 +156,11 @@ async function setup_standalone() { LOG._error && LOG.error('Failed to initialize ZTI for CaaS mTLS:', err) } - // STEP 4: Add trace exporter now that credentials are available + // STEP 4: Now create metrics and logging (they need mTLS creds) + const meterProvider = metrics(resource) + const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined + + // STEP 5: Add trace exporter now that credentials are available tracing.addTracingExporter(tracerProvider) } else { // STANDARD FLOW: Synchronous setup (original behavior) From 11a8a77f923f67901ea8a471fb91a18966a19202 Mon Sep 17 00:00:00 2001 From: D051920 Date: Mon, 24 Aug 2026 17:58:56 +0200 Subject: [PATCH 47/67] cleanup: remove verbose comments and simplify ZTI code --- lib/index.js | 50 ++++++--------------------- lib/tracing/index.js | 82 +++++++++----------------------------------- 2 files changed, 27 insertions(+), 105 deletions(-) diff --git a/lib/index.js b/lib/index.js index ab61868d..ef972c1a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -90,28 +90,21 @@ function _getInstrumentations() { } /** - * Check if ZTI (Zero Trust Identity) is enabled and has configuration - * ZTI requires waiting for SVID files which can take ~10 seconds + * Check if ZTI is needed for CaaS telemetry */ function _needsZTIWait() { - // ZTI is disabled via env var if (process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI === 'false') return false - // Check if CaaS telemetry kind is configured const kind = cds.env.requires.telemetry?.kind if (!kind?.match(/to-caas$/)) return false - // Check for ZTI binding in VCAP_SERVICES const vcapServices = process.env.VCAP_SERVICES && JSON.parse(process.env.VCAP_SERVICES) if (!vcapServices) return false - // Look for zero-trust-identity binding (check both tags and label) for (const [key, services] of Object.entries(vcapServices)) { - // Check if the service key itself indicates ZTI if (key === 'zero-trust-identity') return true for (const svc of services) { - if (svc.tags?.includes('zero-trust-identity')) return true - if (svc.label === 'zero-trust-identity') return true + if (svc.tags?.includes('zero-trust-identity') || svc.label === 'zero-trust-identity') return true } } @@ -119,66 +112,47 @@ function _needsZTIWait() { } async function setup_standalone() { - // set logger and propagate log level diag.setLogger(cds.log('telemetry'), getDiagLogLevel()) - - // create resource const resource = getResource() - // Set up CDS method wrapping require('./tracing/cds')() require('./tracing/cloud_sdk')() - // Check if ZTI is needed (async wait for SVID files) const needsZTI = _needsZTIWait() if (needsZTI) { - // ZTI FLOW: Register TracerProvider early, add exporters after SVID ready - // This ensures HTTP instrumentation gets a real tracer instead of NoopTracer - // while waiting for SVID certificates - - // STEP 1: Create TracerProvider EARLY (before ZTI wait) - no exporter yet + // ZTI: Register TracerProvider early so HTTP instrumentation gets a real tracer const tracerProvider = tracing.createTracerProvider(resource) - // STEP 2: Register instrumentations IMMEDIATELY (so HTTP gets real tracer) - // Note: metrics/logging exporters will be created after ZTI is ready registerInstrumentations({ tracerProvider, - meterProvider: undefined, // Will be created after ZTI + meterProvider: undefined, loggerProvider: undefined, instrumentations: _getInstrumentations() }) - // STEP 3: Wait for ZTI SVID files try { await initializeZTI() } catch (err) { - LOG._error && LOG.error('Failed to initialize ZTI for CaaS mTLS:', err) + LOG._error && LOG.error('Failed to initialize ZTI:', err) } - // STEP 4: Now create metrics and logging (they need mTLS creds) - const meterProvider = metrics(resource) - const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined - - // STEP 5: Add trace exporter now that credentials are available + // Create exporters after ZTI credentials are ready + metrics(resource) + if (cds.env.requires.telemetry.logging) logging(resource) tracing.addTracingExporter(tracerProvider) } else { - // STANDARD FLOW: Synchronous setup (original behavior) - // No ZTI wait needed, so we can create provider with exporter immediately - - // Initialize ZTI if configured (will be no-op if not needed) + // Standard flow try { await initializeZTI() } catch (err) { - LOG._error && LOG.error('Failed to initialize ZTI for CaaS mTLS:', err) + LOG._error && LOG.error('Failed to initialize ZTI:', err) } - // Setup tracing, metrics, and logging const tracerProvider = tracing(resource) const meterProvider = metrics(resource) const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined - // Register instrumentations registerInstrumentations({ tracerProvider, meterProvider, @@ -189,14 +163,12 @@ async function setup_standalone() { } async function setup_with_calm() { - // Initialize ZTI if configured - must complete before creating exporters try { await initializeZTI() } catch (err) { - LOG._error && LOG.error('Failed to initialize ZTI for CaaS mTLS:', err) + LOG._error && LOG.error('Failed to initialize ZTI:', err) } - // setup tracing, metrics, and logging tracing() metrics() if (cds.env.requires.telemetry.logging) logging() diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 60eac3c6..045ae398 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -17,66 +17,41 @@ const { } = require('../utils') /** - * A span processor that buffers spans until a delegate processor is set. - * Used for ZTI flow where we need to register TracerProvider early but - * can't create the exporter until credentials are available. + * Span processor that buffers spans until a delegate is set. + * Used for ZTI flow where TracerProvider must be registered early. */ class BufferingSpanProcessor { constructor() { this._buffer = [] this._delegate = null - this._maxBufferSize = 2048 // Prevent unbounded memory growth + this._maxBufferSize = 2048 } onStart(span, context) { - if (this._delegate) { - this._delegate.onStart(span, context) - } - // No buffering needed for onStart - spans are mutable until onEnd + if (this._delegate) this._delegate.onStart(span, context) } onEnd(span) { if (this._delegate) { this._delegate.onEnd(span) - } else { - // Buffer the span until delegate is ready - if (this._buffer.length < this._maxBufferSize) { - this._buffer.push(span) - } else { - LOG._warn && LOG.warn('BufferingSpanProcessor: buffer full, dropping span') - } + } else if (this._buffer.length < this._maxBufferSize) { + this._buffer.push(span) } } - /** - * Set the delegate processor and flush buffered spans - */ setDelegate(processor) { this._delegate = processor - // Flush buffered spans - const buffered = this._buffer.length - for (const span of this._buffer) { - processor.onEnd(span) - } + for (const span of this._buffer) processor.onEnd(span) this._buffer = [] - if (buffered > 0) { - LOG._debug && LOG.debug(`BufferingSpanProcessor: flushed ${buffered} buffered spans`) - } } forceFlush() { - if (this._delegate) { - return this._delegate.forceFlush() - } - return Promise.resolve() + return this._delegate ? this._delegate.forceFlush() : Promise.resolve() } shutdown() { this._buffer = [] - if (this._delegate) { - return this._delegate.shutdown() - } - return Promise.resolve() + return this._delegate ? this._delegate.shutdown() : Promise.resolve() } } @@ -207,15 +182,12 @@ function _getExporter() { } /** - * Create TracerProvider early (before credentials are available). - * This allows HTTP instrumentation to get a real tracer instead of NoopTracer. - * Uses a BufferingSpanProcessor that holds spans until the real exporter is ready. - * Call addTracingExporter() later to set the real exporter. + * Create TracerProvider early (for ZTI flow). + * Uses BufferingSpanProcessor until real exporter is ready. */ function createTracerProvider(resource) { if (!cds.env.requires.telemetry.tracing?.exporter) return - // clear sap passport for new tx if (process.env.SAP_PASSPORT) { cds.on('served', () => { cds.db?.before('BEGIN', async function () { @@ -225,8 +197,6 @@ function createTracerProvider(resource) { } resource = resourceFromAttributes({}).merge(resource).merge(getDynatraceMetadata()) - - // Create a buffering processor that will hold spans until real exporter is ready const bufferingProcessor = new BufferingSpanProcessor() const tracerProvider = new NodeTracerProvider({ @@ -235,18 +205,13 @@ function createTracerProvider(resource) { spanProcessors: [bufferingProcessor] }) tracerProvider.register({ propagator: _getPropagator() }) - - // Store reference to buffering processor for later delegate assignment tracerProvider._bufferingProcessor = bufferingProcessor - LOG._debug && LOG.debug('TracerProvider registered early (exporter will be added when credentials ready)') - return tracerProvider } /** - * Add exporter to TracerProvider after credentials are available (e.g., after ZTI SVID ready). - * This must be called after createTracerProvider() and after any async credential initialization. + * Add exporter to TracerProvider (for ZTI flow, after credentials ready). */ function addTracingExporter(tracerProvider) { if (!tracerProvider) return @@ -268,24 +233,17 @@ function addTracingExporter(tracerProvider) { ? new BatchSpanProcessor(exporter, processorConfig) : new SimpleSpanProcessor(exporter, processorConfig) - // Set the real processor as delegate on the BufferingSpanProcessor - // This flushes any buffered spans and forwards future spans to the real processor if (tracerProvider._bufferingProcessor) { tracerProvider._bufferingProcessor.setDelegate(processor) - LOG._debug && LOG.debug('Trace exporter added to TracerProvider via BufferingSpanProcessor') - } else { - LOG._error && LOG.error('Unable to add span processor - no BufferingSpanProcessor found') } } /** - * Original combined function for backward compatibility with CALM setup. - * Creates TracerProvider AND adds exporter in one call. + * Standard setup - creates TracerProvider with exporter in one call. */ function setupTracing(resource) { if (!cds.env.requires.telemetry.tracing?.exporter) return - // clear sap passport for new tx if (process.env.SAP_PASSPORT) { cds.on('served', () => { cds.db?.before('BEGIN', async function () { @@ -294,16 +252,13 @@ function setupTracing(resource) { }) } - /* - * create processor - */ let processor const via_one_agent = process.env.DT_NODE_PRELOAD_OPTIONS && cds.env.requires.telemetry.kind.match(/to-dynatrace$/) && !hasDependency('@opentelemetry/exporter-trace-otlp-proto') + if (via_one_agent) { - // if Dynatrace OneAgent is present, no exporter is needed LOG._info && LOG.info('Dynatrace OneAgent detected, disabling trace exporter') } else { const exporter = _getExporter() @@ -314,9 +269,7 @@ function setupTracing(resource) { : new SimpleSpanProcessor(exporter, processorConfig) } - /* - * either add processor as delegate in CALM... - */ + // CALM setup - add as delegate if (!resource) { LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's span processor as delegate") try { @@ -329,16 +282,13 @@ function setupTracing(resource) { } } - /* - * ... or initialize and return provider - */ + // Standard setup resource = resourceFromAttributes({}).merge(resource).merge(getDynatraceMetadata()) const tracerProvider = new NodeTracerProvider({ resource, spanProcessors: [processor], sampler: _getSampler() }) tracerProvider.register({ propagator: _getPropagator() }) return tracerProvider } -// Export the new split functions and keep backward compatibility module.exports = setupTracing module.exports.createTracerProvider = createTracerProvider module.exports.addTracingExporter = addTracingExporter From 4d2cda52247f8aa5ff7c61ec45e55771ec66315a Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 11:21:59 +0200 Subject: [PATCH 48/67] refactor --- CHANGELOG.md | 7 +- lib/index.js | 39 +---- lib/logging/index.js | 14 +- lib/tracing/cds.js | 2 - lib/tracing/index.js | 79 +++++----- lib/utils.js | 218 +------------------------- lib/zti.js | 253 ++++++++++++++++++++++++++++++ test/caas.test.js | 15 +- test/tracing-zti.test.js | 331 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 652 insertions(+), 306 deletions(-) create mode 100644 lib/zti.js create mode 100644 test/tracing-zti.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f1235630..449d82ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). The format is based on [Keep a Changelog](http://keepachangelog.com/). +## Version 2.1.0 + +### Added + +- Support for `telemetry-to-caas` kind for CaaS (Collector as a Service) with automatic mTLS certificate management via Zero Trust Identity Service (ZTI/SPIFFE). Falls back to base64-encoded certificates from environment variables when ZTI is not available. + ## Version 2.0.1 - 2026-07-03 ### Fixed @@ -18,7 +24,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - Support for OpenTelemetry SDK 2.0 - `@opentelemetry/instrumentation-undici` added to the list of default instrumentations - Support for `@sap/cds^10` -- Support for `telemetry-to-caas` kind for CaaS (Collector as a Service) ### Changed diff --git a/lib/index.js b/lib/index.js index ef972c1a..5358f40e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -13,7 +13,8 @@ const { registerInstrumentations } = require('@opentelemetry/instrumentation') const tracing = require('./tracing') const metrics = require('./metrics') const logging = require('./logging') -const { getDiagLogLevel, getResource, hasDependency, initializeZTI, _require } = require('./utils') +const { getDiagLogLevel, getResource, hasDependency, _require } = require('./utils') +const { initializeZTI, needsZTIWait } = require('./zti') function _getInstrumentations() { const _instrumentations = cds.env.requires.telemetry.instrumentations @@ -89,28 +90,6 @@ function _getInstrumentations() { return instrumentations } -/** - * Check if ZTI is needed for CaaS telemetry - */ -function _needsZTIWait() { - if (process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI === 'false') return false - - const kind = cds.env.requires.telemetry?.kind - if (!kind?.match(/to-caas$/)) return false - - const vcapServices = process.env.VCAP_SERVICES && JSON.parse(process.env.VCAP_SERVICES) - if (!vcapServices) return false - - for (const [key, services] of Object.entries(vcapServices)) { - if (key === 'zero-trust-identity') return true - for (const svc of services) { - if (svc.tags?.includes('zero-trust-identity') || svc.label === 'zero-trust-identity') return true - } - } - - return false -} - async function setup_standalone() { diag.setLogger(cds.log('telemetry'), getDiagLogLevel()) const resource = getResource() @@ -118,12 +97,11 @@ async function setup_standalone() { require('./tracing/cds')() require('./tracing/cloud_sdk')() - const needsZTI = _needsZTIWait() + const needsZTI = needsZTIWait() if (needsZTI) { - // ZTI: Register TracerProvider early so HTTP instrumentation gets a real tracer + // ZTI path: Register TracerProvider early, then wait for credentials const tracerProvider = tracing.createTracerProvider(resource) - registerInstrumentations({ tracerProvider, meterProvider: undefined, @@ -137,18 +115,11 @@ async function setup_standalone() { LOG._error && LOG.error('Failed to initialize ZTI:', err) } - // Create exporters after ZTI credentials are ready metrics(resource) if (cds.env.requires.telemetry.logging) logging(resource) tracing.addTracingExporter(tracerProvider) } else { - // Standard flow - try { - await initializeZTI() - } catch (err) { - LOG._error && LOG.error('Failed to initialize ZTI:', err) - } - + // Standard path: Create everything at once const tracerProvider = tracing(resource) const meterProvider = metrics(resource) const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined diff --git a/lib/logging/index.js b/lib/logging/index.js index 6aa7fae4..78f76cdb 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -36,12 +36,9 @@ function _getExporter() { let loggingExporterModule try { loggingExporterModule = _require(loggingExporter.module) - } catch (err) { - if (err.code === 'MODULE_NOT_FOUND') { - LOG._warn && LOG.warn(`Logs exporter module '${loggingExporter.module}' not found. Logging telemetry will be disabled.`) - return null - } - throw err + } catch { + LOG._warn && LOG.warn(`Logs exporter module '${loggingExporter.module}' not found. Logging telemetry will be disabled.`) + return null } if (!loggingExporterModule[loggingExporter.class]) throw new Error(`Unknown logs exporter "${loggingExporter.class}" in module "${loggingExporter.module}"`) @@ -150,10 +147,7 @@ module.exports = resource => { * create processor */ const exporter = _getExporter() - if (!exporter) { - LOG._info && LOG.info('Logging telemetry is disabled due to missing exporter module') - return null - } + if (!exporter) return null const processor = _getCustomProcessor(exporter) || (process.env.NODE_ENV === 'production' diff --git a/lib/tracing/cds.js b/lib/tracing/cds.js index 756e3b4c..c6766273 100644 --- a/lib/tracing/cds.js +++ b/lib/tracing/cds.js @@ -33,14 +33,12 @@ module.exports = () => { const _TX = { BEGIN: 1, COMMIT: 1, ROLLBACK: 1 } const { emit: _emit, handle: _handle } = cds.Service.prototype - cds.Service.prototype.emit = wrap(_emit, { wrapper: function emit() { const event = arguments[0]?.event || arguments[0] return trace({ phase: 'emit', event }, _emit, this, arguments, {}) } }) - cds.Service.prototype.handle = wrap(_handle, { wrapper: function handle(req) { if (!_tx && req.event in _TX) return _handle.apply(this, arguments) diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 045ae398..6ceefc9e 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -55,6 +55,39 @@ class BufferingSpanProcessor { } } +// Common setup: SAP Passport clearing for HANA +function _setupSAPPassport() { + if (process.env.SAP_PASSPORT) { + cds.on('served', () => { + cds.db?.before('BEGIN', async function () { + if (this.dbc?.constructor.name in { HDBDriver: 1, HANAClientDriver: 1 }) this.dbc.set({ SAP_PASSPORT: '' }) + }) + }) + } +} + +// Common resource merging +function _mergeResource(resource) { + return resourceFromAttributes({}).merge(resource).merge(getDynatraceMetadata()) +} + +// Check if Dynatrace OneAgent is active +function _hasDynatraceOneAgent() { + return ( + process.env.DT_NODE_PRELOAD_OPTIONS && + cds.env.requires.telemetry.kind.match(/to-dynatrace$/) && + !hasDependency('@opentelemetry/exporter-trace-otlp-proto') + ) +} + +// Create span processor with exporter +function _createSpanProcessor(exporter) { + const processorConfig = cds.env.requires.telemetry.tracing.processor?.config || {} + return process.env.NODE_ENV === 'production' + ? new BatchSpanProcessor(exporter, processorConfig) + : new SimpleSpanProcessor(exporter, processorConfig) +} + function _getSampler() { const { ignoreIncomingPaths } = cds.env.requires.telemetry.tracing?.sampler || {} @@ -188,15 +221,9 @@ function _getExporter() { function createTracerProvider(resource) { if (!cds.env.requires.telemetry.tracing?.exporter) return - if (process.env.SAP_PASSPORT) { - cds.on('served', () => { - cds.db?.before('BEGIN', async function () { - if (this.dbc?.constructor.name in { HDBDriver: 1, HANAClientDriver: 1 }) this.dbc.set({ SAP_PASSPORT: '' }) - }) - }) - } + _setupSAPPassport() - resource = resourceFromAttributes({}).merge(resource).merge(getDynatraceMetadata()) + resource = _mergeResource(resource) const bufferingProcessor = new BufferingSpanProcessor() const tracerProvider = new NodeTracerProvider({ @@ -216,22 +243,13 @@ function createTracerProvider(resource) { function addTracingExporter(tracerProvider) { if (!tracerProvider) return - const via_one_agent = - process.env.DT_NODE_PRELOAD_OPTIONS && - cds.env.requires.telemetry.kind.match(/to-dynatrace$/) && - !hasDependency('@opentelemetry/exporter-trace-otlp-proto') - - if (via_one_agent) { + if (_hasDynatraceOneAgent()) { LOG._info && LOG.info('Dynatrace OneAgent detected, disabling trace exporter') return } const exporter = _getExporter() - const processorConfig = cds.env.requires.telemetry.tracing.processor?.config || {} - const processor = - process.env.NODE_ENV === 'production' - ? new BatchSpanProcessor(exporter, processorConfig) - : new SimpleSpanProcessor(exporter, processorConfig) + const processor = _createSpanProcessor(exporter) if (tracerProvider._bufferingProcessor) { tracerProvider._bufferingProcessor.setDelegate(processor) @@ -244,29 +262,14 @@ function addTracingExporter(tracerProvider) { function setupTracing(resource) { if (!cds.env.requires.telemetry.tracing?.exporter) return - if (process.env.SAP_PASSPORT) { - cds.on('served', () => { - cds.db?.before('BEGIN', async function () { - if (this.dbc?.constructor.name in { HDBDriver: 1, HANAClientDriver: 1 }) this.dbc.set({ SAP_PASSPORT: '' }) - }) - }) - } + _setupSAPPassport() let processor - const via_one_agent = - process.env.DT_NODE_PRELOAD_OPTIONS && - cds.env.requires.telemetry.kind.match(/to-dynatrace$/) && - !hasDependency('@opentelemetry/exporter-trace-otlp-proto') - - if (via_one_agent) { + if (_hasDynatraceOneAgent()) { LOG._info && LOG.info('Dynatrace OneAgent detected, disabling trace exporter') } else { const exporter = _getExporter() - const processorConfig = cds.env.requires.telemetry.tracing.processor?.config || {} - processor = - process.env.NODE_ENV === 'production' - ? new BatchSpanProcessor(exporter, processorConfig) - : new SimpleSpanProcessor(exporter, processorConfig) + processor = _createSpanProcessor(exporter) } // CALM setup - add as delegate @@ -283,7 +286,7 @@ function setupTracing(resource) { } // Standard setup - resource = resourceFromAttributes({}).merge(resource).merge(getDynatraceMetadata()) + resource = _mergeResource(resource) const tracerProvider = new NodeTracerProvider({ resource, spanProcessors: [processor], sampler: _getSampler() }) tracerProvider.register({ propagator: _getPropagator() }) return tracerProvider diff --git a/lib/utils.js b/lib/utils.js index da280586..ed341f1e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -2,6 +2,7 @@ const cds = require('@sap/cds') const LOG = cds.log('telemetry') const fs = require('fs') +const { initializeZTI, getZTIConfig, getCredsForCaaSMtls } = require('./zti') const { DiagLogLevel } = require('@opentelemetry/api') const { hrTimeToMilliseconds, getStringFromEnv } = require('@opentelemetry/core') @@ -137,219 +138,6 @@ function getCredsForCLSAsUPS() { } } -/** - * Manages ZTI SVID file loading with mtime-based caching - * Based on official ZTI blueprint: https://github.tools.sap/pse/blueprints - */ -class SVIDFileSource { - constructor(certPath, keyPath, bundlePath) { - this.certPath = certPath - this.keyPath = keyPath - this.bundlePath = bundlePath - this._cached = null - this._cachedMod = null - } - - /** - * Get certificate credentials, reloading if mtime changed - * @returns {{ cert: string, key: string, bundle: string }} - */ - getCertificate() { - let stat - try { - stat = fs.statSync(this.certPath) - } catch (e) { - // Transient stat failure during atomic rename - serve last-known-good - if (this._cached) { - LOG._debug && LOG.debug('ZTI: Transient stat failure, serving cached credentials') - return this._cached - } - throw e - } - - const mtime = stat.mtimeMs - if (this._cached && this._cachedMod === mtime) { - // Cache hit - no rotation since last check - return this._cached - } - - // mtime changed or first load - reload all files atomically - LOG._debug && LOG.debug('ZTI: Loading SVID files (mtime changed or first load)') - try { - const certPEM = fs.readFileSync(this.certPath, 'utf8') - const keyPEM = fs.readFileSync(this.keyPath, 'utf8') - const bundlePEM = fs.readFileSync(this.bundlePath, 'utf8') - - this._cached = { cert: certPEM, key: keyPEM, bundle: bundlePEM } - this._cachedMod = mtime - - LOG._info && LOG.info('ZTI: Loaded SVID certificate', { - certPath: this.certPath, - mtime: new Date(mtime).toISOString() - }) - - return this._cached - } catch (err) { - // Read failure after successful stat - likely mid-rotation - if (this._cached) { - LOG._warn && LOG.warn('ZTI: Failed to reload SVID files, serving cached', err) - return this._cached - } - throw err - } - } -} - -/** - * Detect ZTI binding from VCAP_SERVICES - * @returns {{ svidDir: string, svidName: string } | null} - */ -function getZTIConfig() { - if (!process.env.VCAP_SERVICES) return null - - const vcap = JSON.parse(process.env.VCAP_SERVICES) - const zti = vcap['zero-trust-identity'] - if (!zti || zti.length === 0) return null - - // Get SVID name from binding parameters - const svidName = zti[0].credentials?.parameters?.['svid-store']?.file?.name - if (!svidName) { - LOG._warn && LOG.warn('ZTI: zero-trust-identity binding found but svid-store.file.name not configured') - return null - } - - // Native CF apps have fixed SVID directory - const svidDir = process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR || '/home/vcap/app/spire-svids' - - return { svidDir, svidName } -} - -/** - * Create SVID file source with blocking retry until files exist - * Follows ZTI blueprint pattern: block startup until SVID files ready - * @param {string} svidDir - Directory containing SVID files - * @param {string} svidName - SVID file base name - * @returns {Promise} - */ -async function createSVIDFileSource(svidDir, svidName) { - const RETRY_INTERVAL_MS = 2000 - const MAX_RETRIES = 30 // 60 seconds total (30 * 2s) - - const source = new SVIDFileSource( - `${svidDir}/${svidName}.svid.pem`, - `${svidDir}/${svidName}.svid.key`, - `${svidDir}/${svidName}.bundle.pem` - ) - - let attempt = 0 - while (true) { - try { - // Try to load - will throw if files don't exist - source.getCertificate() - LOG._info && LOG.info(`ZTI: SVID files ready at ${svidDir}/${svidName}`) - return source - } catch (err) { - attempt++ - if (attempt >= MAX_RETRIES) { - throw new Error( - `ZTI: SVID files not available after ${MAX_RETRIES} attempts (${(MAX_RETRIES * RETRY_INTERVAL_MS) / 1000}s). ` + - `Expected files at ${svidDir}/${svidName}.svid.{pem,key}. ` + - `Error: ${err.message}`, - { cause: err } - ) - } - - LOG._debug && LOG.debug( - `ZTI: SVID files not ready yet (attempt ${attempt}/${MAX_RETRIES}), ` + - `retrying in ${RETRY_INTERVAL_MS}ms...` - ) - - await new Promise(resolve => setTimeout(resolve, RETRY_INTERVAL_MS)) - } - } -} - -// Global ZTI source instance (created once, reused across requests) -let _ztiSource = null -let _ztiInitPromise = null - -/** - * Initialize ZTI source (async) - called during telemetry plugin initialization - * @returns {Promise} - */ -async function initializeZTI() { - const useZTI = process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI !== 'false' - if (!useZTI) return - - const ztiConfig = getZTIConfig() - if (!ztiConfig) return - - // Avoid double initialization - if (_ztiInitPromise) return _ztiInitPromise - - _ztiInitPromise = (async () => { - try { - LOG._info && LOG.info('ZTI: Initializing SVID file source for CaaS mTLS...') - _ztiSource = await createSVIDFileSource(ztiConfig.svidDir, ztiConfig.svidName) - } catch (err) { - LOG._error && LOG.error('ZTI: Failed to initialize SVID file source:', err) - _ztiInitPromise = null // Allow retry - throw err - } - })() - - return _ztiInitPromise -} - -/** - * Get mTLS credentials for CaaS telemetry - * Priority: - * 1. ZTI file-based (default, automatic rotation) - if USE_ZTI=true (default) - * 2. Env var base64 (legacy, manual rotation) - if USE_ZTI=false - * @returns {{ cert: string, key: string } | null} - */ -function getCredsForCaaSMtls() { - // Check flag: default to ZTI (only disable if explicitly set to 'false') - const useZTI = process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI !== 'false' - - if (useZTI) { - // ZTI approach (default) - const ztiConfig = getZTIConfig() - if (ztiConfig) { - // ZTI binding found - use it - if (_ztiSource) { - // ZTI initialized - get cached or reloaded credentials - try { - const { cert, key } = _ztiSource.getCertificate() - return { cert, key } - } catch (err) { - LOG._error && LOG.error('ZTI: Failed to load SVID credentials:', err) - return null - } - } else { - // ZTI not yet initialized - warn and fall through - LOG._warn && LOG.warn( - 'ZTI: SVID file source not initialized. Ensure initializeZTI() is called during startup.' - ) - return null - } - } - // No ZTI binding - fall through to legacy check - } - - // Legacy env var approach (USE_ZTI=false or no ZTI binding) - const { x509 } = cds.env.requires.telemetry || {} - if (x509 && x509.cert && x509.key) { - LOG._debug && LOG.debug('Using legacy base64 env var credentials for CaaS mTLS') - return { - cert: x509.cert, - key: x509.key - } - } - - return null -} - function augmentCaaSCreds(credentials) { if (credentials._augmented) return credentials._augmented = true @@ -473,7 +261,5 @@ module.exports = { getCredsForCaaSMtls, hasDependency, _hrnow, - _require, - // Exported for testing - _resetZTIState: () => { _ztiSource = null; _ztiInitPromise = null } + _require } diff --git a/lib/zti.js b/lib/zti.js new file mode 100644 index 00000000..a1998dc0 --- /dev/null +++ b/lib/zti.js @@ -0,0 +1,253 @@ +const cds = require('@sap/cds') +const LOG = cds.log('telemetry') +const fs = require('fs') + +// SVID file paths and cache (initialized once, reused across requests) +let _certPath = null +let _keyPath = null +let _bundlePath = null +let _cached = null +let _cachedMod = null + +/** + * Get SVID certificate credentials, reloading if mtime changed. + * Manages mtime-based caching for automatic certificate rotation. + * Based on official ZTI blueprint: https://github.tools.sap/pse/blueprints + * @returns {{ cert: string, key: string, bundle: string }} + */ +function _getSVIDCertificate() { + let stat + try { + stat = fs.statSync(_certPath) + } catch (e) { + // Transient stat failure during atomic rename - serve last-known-good + if (_cached) { + LOG._debug && LOG.debug('ZTI: Transient stat failure, serving cached credentials') + return _cached + } + throw e + } + + const mtime = stat.mtimeMs + if (_cached && _cachedMod === mtime) { + // Cache hit - no rotation since last check + return _cached + } + + // mtime changed or first load - reload all files atomically + LOG._debug && LOG.debug('ZTI: Loading SVID files (mtime changed or first load)') + try { + const certPEM = fs.readFileSync(_certPath, 'utf8') + const keyPEM = fs.readFileSync(_keyPath, 'utf8') + const bundlePEM = fs.readFileSync(_bundlePath, 'utf8') + + _cached = { cert: certPEM, key: keyPEM, bundle: bundlePEM } + _cachedMod = mtime + + LOG._info && LOG.info('ZTI: Loaded SVID certificate', { + certPath: _certPath, + mtime: new Date(mtime).toISOString() + }) + + return _cached + } catch (err) { + // Read failure after successful stat - likely mid-rotation + if (_cached) { + LOG._warn && LOG.warn('ZTI: Failed to reload SVID files, serving cached', err) + return _cached + } + throw err + } +} + +/** + * Detect ZTI binding from VCAP_SERVICES + * @returns {{ svidDir: string, svidName: string } | null} + */ +function getZTIConfig() { + if (!process.env.VCAP_SERVICES) return null + + const vcap = JSON.parse(process.env.VCAP_SERVICES) + const zti = vcap['zero-trust-identity'] + if (!zti || zti.length === 0) return null + + // Get SVID name from binding parameters + const svidName = zti[0].credentials?.parameters?.['svid-store']?.file?.name + if (!svidName) { + LOG._warn && LOG.warn('ZTI: zero-trust-identity binding found but svid-store.file.name not configured') + return null + } + + // Native CF apps have fixed SVID directory + const svidDir = process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR || '/home/vcap/app/spire-svids' + + return { svidDir, svidName } +} + +/** + * Initialize SVID file paths and block until files exist + * Follows ZTI blueprint pattern: block startup until SVID files ready + * @param {string} svidDir - Directory containing SVID files + * @param {string} svidName - SVID file base name + * @returns {Promise} + */ +async function _initializeSVIDFiles(svidDir, svidName) { + const RETRY_INTERVAL_MS = 2000 + const MAX_RETRIES = 30 // 60 seconds total (30 * 2s) + + _certPath = `${svidDir}/${svidName}.svid.pem` + _keyPath = `${svidDir}/${svidName}.svid.key` + _bundlePath = `${svidDir}/${svidName}.bundle.pem` + + let attempt = 0 + while (true) { + try { + // Try to load - will throw if files don't exist + _getSVIDCertificate() + LOG._info && LOG.info(`ZTI: SVID files ready at ${svidDir}/${svidName}`) + return + } catch (err) { + attempt++ + if (attempt >= MAX_RETRIES) { + throw new Error( + `ZTI: SVID files not available after ${MAX_RETRIES} attempts (${(MAX_RETRIES * RETRY_INTERVAL_MS) / 1000}s). ` + + `Expected files at ${svidDir}/${svidName}.svid.{pem,key}. ` + + `Error: ${err.message}`, + { cause: err } + ) + } + + LOG._debug && LOG.debug( + `ZTI: SVID files not ready yet (attempt ${attempt}/${MAX_RETRIES}), ` + + `retrying in ${RETRY_INTERVAL_MS}ms...` + ) + + await new Promise(resolve => setTimeout(resolve, RETRY_INTERVAL_MS)) + } + } +} + +// Global ZTI initialization state +let _ztiInitialized = false +let _ztiInitPromise = null + +/** + * Initialize ZTI source (async) - called during telemetry plugin initialization + * @returns {Promise} + */ +async function initializeZTI() { + const useZTI = process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI !== 'false' + if (!useZTI) return + + const ztiConfig = getZTIConfig() + if (!ztiConfig) return + + // Avoid double initialization + if (_ztiInitPromise) return _ztiInitPromise + + _ztiInitPromise = (async () => { + try { + LOG._info && LOG.info('ZTI: Initializing SVID file source for CaaS mTLS...') + await _initializeSVIDFiles(ztiConfig.svidDir, ztiConfig.svidName) + _ztiInitialized = true + } catch (err) { + LOG._error && LOG.error('ZTI: Failed to initialize SVID file source:', err) + _ztiInitPromise = null // Allow retry + throw err + } + })() + + return _ztiInitPromise +} + +/** + * Get mTLS credentials for CaaS telemetry + * Priority: + * 1. ZTI file-based (default, automatic rotation) - if USE_ZTI=true (default) + * 2. Env var base64 (legacy, manual rotation) - if USE_ZTI=false + * @returns {{ cert: string, key: string } | null} + */ +function getCredsForCaaSMtls() { + // Check flag: default to ZTI (only disable if explicitly set to 'false') + const useZTI = process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI !== 'false' + + if (useZTI) { + // ZTI approach (default) + const ztiConfig = getZTIConfig() + if (ztiConfig) { + // ZTI binding found - use it + if (_ztiInitialized) { + // ZTI initialized - get cached or reloaded credentials + try { + const { cert, key } = _getSVIDCertificate() + return { cert, key } + } catch (err) { + LOG._error && LOG.error('ZTI: Failed to load SVID credentials:', err) + return null + } + } else { + // ZTI not yet initialized - warn and fall through + LOG._warn && LOG.warn( + 'ZTI: SVID file source not initialized. Ensure initializeZTI() is called during startup.' + ) + return null + } + } + // No ZTI binding - fall through to legacy check + } + + // Legacy env var approach (USE_ZTI=false or no ZTI binding) + const { x509 } = cds.env.requires.telemetry || {} + if (x509 && x509.cert && x509.key) { + LOG._debug && LOG.debug('Using legacy base64 env var credentials for CaaS mTLS') + return { + cert: x509.cert, + key: x509.key + } + } + + return null +} + +/** + * Check if ZTI wait is needed for CaaS telemetry + * @returns {boolean} + */ +function needsZTIWait() { + if (process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI === 'false') return false + + const kind = cds.env.requires.telemetry?.kind + if (!kind?.match(/to-caas$/)) return false + + const vcapServices = process.env.VCAP_SERVICES && JSON.parse(process.env.VCAP_SERVICES) + if (!vcapServices) return false + + for (const [key, services] of Object.entries(vcapServices)) { + if (key === 'zero-trust-identity') return true + for (const svc of services) { + if (svc.tags?.includes('zero-trust-identity') || svc.label === 'zero-trust-identity') return true + } + } + + return false +} + +module.exports = { + initializeZTI, + getZTIConfig, + getCredsForCaaSMtls, + needsZTIWait +} + +// For testing only - not part of public API +if (process.env.NODE_ENV === 'test') { + module.exports._resetZTIState = () => { + _certPath = null + _keyPath = null + _bundlePath = null + _cached = null + _cachedMod = null + _ztiInitialized = false + _ztiInitPromise = null + } +} diff --git a/test/caas.test.js b/test/caas.test.js index f25cc4e3..85587b34 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -169,7 +169,8 @@ describe('ZTI SVID File Loading', () => { fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - const { initializeZTI, getCredsForCaaSMtls, _resetZTIState } = require('../lib/utils') + delete require.cache[require.resolve('../lib/zti')] + const { initializeZTI, getCredsForCaaSMtls, _resetZTIState } = require('../lib/zti') _resetZTIState() await initializeZTI() @@ -190,7 +191,8 @@ describe('ZTI SVID File Loading', () => { fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\nv1\n-----END PRIVATE KEY-----') fs.writeFileSync(bundlePath, '-----BEGIN CERTIFICATE-----\nv1\n-----END CERTIFICATE-----') - const { initializeZTI, getCredsForCaaSMtls, _resetZTIState } = require('../lib/utils') + delete require.cache[require.resolve('../lib/zti')] + const { initializeZTI, getCredsForCaaSMtls, _resetZTIState } = require('../lib/zti') _resetZTIState() await initializeZTI() @@ -214,7 +216,8 @@ describe('ZTI SVID File Loading', () => { const keyPath = path.join(svidDir, 'test-svid.svid.key') const bundlePath = path.join(svidDir, 'test-svid.bundle.pem') - const { initializeZTI, _resetZTIState } = require('../lib/utils') + delete require.cache[require.resolve('../lib/zti')] + const { initializeZTI, _resetZTIState } = require('../lib/zti') _resetZTIState() // Start initialization (files don't exist yet) @@ -274,7 +277,7 @@ describe('ZTI flag behavior', () => { } delete require.cache[require.resolve('../lib/utils')] - const { getCredsForCaaSMtls } = require('../lib/utils') + const { getCredsForCaaSMtls } = require('../lib/zti') const creds = getCredsForCaaSMtls() expect(creds).toBeDefined() @@ -296,7 +299,9 @@ describe('ZTI flag behavior', () => { fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\nzti-key\n-----END PRIVATE KEY-----') fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - const { initializeZTI, augmentCaaSCreds, _resetZTIState } = require('../lib/utils') + const { augmentCaaSCreds } = require('../lib/utils') + delete require.cache[require.resolve('../lib/zti')] + const { initializeZTI, _resetZTIState } = require('../lib/zti') _resetZTIState() await initializeZTI() diff --git a/test/tracing-zti.test.js b/test/tracing-zti.test.js new file mode 100644 index 00000000..bb0d7a55 --- /dev/null +++ b/test/tracing-zti.test.js @@ -0,0 +1,331 @@ +/** + * Integration tests for ZTI + Tracing + * + * Tests the fix for: "HTTP instrumentation caches NoopTracer if TracerProvider isn't registered early" + * + * The problem: When ZTI credentials take ~10 seconds to load, HTTP instrumentation would cache + * a NoopTracer before the real TracerProvider was registered, causing all traces to be lost. + * + * The solution: Register TracerProvider early with BufferingSpanProcessor, then add the real + * exporter after ZTI credentials are ready. + */ + +const cds = require('@sap/cds') +const fs = require('fs') +const os = require('os') +const path = require('path') +const { InMemorySpanExporter } = require('@opentelemetry/sdk-trace-base') +const { trace } = require('@opentelemetry/api') + +// Mock VCAP_SERVICES with ZTI and CaaS +const MOCK_ZTI_VCAP = { + 'caas-service': [{ + name: 'test-caas', + credentials: { + otlp: { + http: 'https://caas.example.com/otlp' + } + } + }], + 'zero-trust-identity': [{ + name: 'test-zti', + credentials: { + parameters: { + 'svid-store': { + file: { name: 'test-svid' } + } + } + } + }] +} + +describe('Tracing with ZTI integration', () => { + let tmpDir + let svidDir + let originalEnv + + beforeAll(() => { + // Save original environment + originalEnv = { + VCAP_SERVICES: process.env.VCAP_SERVICES, + CDS_REQUIRES_TELEMETRY_ZTI_DIR: process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR, + CDS_REQUIRES_TELEMETRY_USE_ZTI: process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI, + cds_requires_telemetry_kind: process.env.cds_requires_telemetry_kind, + cds_requires_telemetry_tracing_exporter: process.env.cds_requires_telemetry_tracing_exporter + } + }) + + afterAll(() => { + // Restore original environment + Object.entries(originalEnv).forEach(([key, value]) => { + if (value === undefined) delete process.env[key] + else process.env[key] = value + }) + }) + + beforeEach(() => { + // Create temp directory for SVID files + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'zti-tracing-test-')) + svidDir = path.join(tmpDir, 'spire-svids') + fs.mkdirSync(svidDir) + + // Setup ZTI environment + process.env.VCAP_SERVICES = JSON.stringify(MOCK_ZTI_VCAP) + process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR = svidDir + delete process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI + process.env.cds_requires_telemetry_kind = 'to-caas' + + // Clear module cache + delete require.cache[require.resolve('../lib/utils')] + delete require.cache[require.resolve('../lib/zti')] + delete require.cache[require.resolve('../lib/index')] + delete require.cache[require.resolve('../lib/tracing')] + delete require.cache[require.resolve('../lib/metrics')] + delete require.cache[require.resolve('../lib/logging')] + }) + + afterEach(() => { + // Cleanup + if (tmpDir && fs.existsSync(tmpDir)) { + fs.rmSync(tmpDir, { recursive: true, force: true }) + } + delete process.env.VCAP_SERVICES + delete process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR + delete process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI + delete process.env.cds_requires_telemetry_kind + delete process.env.cds_requires_telemetry_tracing_exporter + }) + + test('needsZTIWait() detects ZTI scenario correctly', () => { + const { needsZTIWait } = require('../lib/zti') + + expect(needsZTIWait()).toBe(true) + + // Should return false if USE_ZTI=false + process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI = 'false' + delete require.cache[require.resolve('../lib/zti')] + const { needsZTIWait: needsZTIWait2 } = require('../lib/zti') + expect(needsZTIWait2()).toBe(false) + }) + + test('BufferingSpanProcessor buffers spans until delegate is set', async () => { + // Create SVID files immediately (so ZTI succeeds) + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') + + // Setup minimal CDS env for tracing + cds.env.requires = { + telemetry: { + kind: 'to-caas', + credentials: { + otlp: { + http: 'https://caas.example.com/otlp' + } + }, + tracing: { + exporter: { + module: '@opentelemetry/sdk-trace-base', + class: 'InMemorySpanExporter' + }, + sampler: { + kind: 'AlwaysOnSampler' + }, + propagators: [] + } + } + } + + const tracing = require('../lib/tracing') + const { getResource } = require('../lib/utils') + const resource = getResource() + + // Step 1: Create TracerProvider with buffering (simulating ZTI wait) + const tracerProvider = tracing.createTracerProvider(resource) + expect(tracerProvider).toBeDefined() + expect(tracerProvider._bufferingProcessor).toBeDefined() + + // Step 2: Create spans BEFORE exporter is added (they should buffer) + const tracer = trace.getTracer('test') + const span1 = tracer.startSpan('test-span-1') + span1.end() + const span2 = tracer.startSpan('test-span-2') + span2.end() + + // At this point, spans are in the buffer + expect(tracerProvider._bufferingProcessor._buffer.length).toBe(2) + + // Step 3: Add exporter (simulating ZTI ready) + tracing.addTracingExporter(tracerProvider) + + // Buffer should now be flushed + expect(tracerProvider._bufferingProcessor._buffer.length).toBe(0) + expect(tracerProvider._bufferingProcessor._delegate).toBeDefined() + }) + + test('TracerProvider is created before HTTP instrumentation in ZTI flow', async () => { + // This test verifies the fix: TracerProvider must be registered BEFORE + // HTTP instrumentation loads, otherwise HTTP caches NoopTracer + + // Create SVID files immediately + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') + + cds.env.requires = { + telemetry: { + kind: 'to-caas', + credentials: { + otlp: { + http: 'https://caas.example.com/otlp' + } + }, + tracing: { + exporter: { + module: '@opentelemetry/sdk-trace-base', + class: 'InMemorySpanExporter' + }, + sampler: { + kind: 'AlwaysOnSampler' + }, + propagators: [] + }, + instrumentations: {} + } + } + + // Initialize telemetry (this triggers the ZTI flow) + const setup = require('../lib/index') + await setup() + + // Verify we can get a tracer (not NoopTracer) + const tracer = trace.getTracer('test') + expect(tracer).toBeDefined() + + // Create a span and verify it's a real span + const span = tracer.startSpan('test-span') + expect(span.spanContext().traceId).toBeDefined() + expect(span.spanContext().traceId).not.toBe('00000000000000000000000000000000') // NoopTracer returns zeros + span.end() + }) + + test('ZTI initialization waits for SVID files with retry', async () => { + // Don't create files immediately - test retry logic + const { initializeZTI, _resetZTIState } = require('../lib/zti') + _resetZTIState() + + // Start initialization (files don't exist yet) + const initPromise = initializeZTI() + + // Wait 1 second, then create files (should succeed on retry) + setTimeout(() => { + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') + }, 1000) + + // Should eventually succeed (within retry timeout of 60 seconds) + await expect(initPromise).resolves.not.toThrow() + }, 65000) // Test timeout > retry timeout + + test('createTracerProvider and addTracingExporter work together', async () => { + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') + + cds.env.requires = { + telemetry: { + kind: 'to-caas', + credentials: { + otlp: { + http: 'https://caas.example.com/otlp' + } + }, + tracing: { + exporter: { + module: '@opentelemetry/sdk-trace-base', + class: 'InMemorySpanExporter' + }, + sampler: { + kind: 'AlwaysOnSampler' + }, + propagators: [] + } + } + } + + const tracing = require('../lib/tracing') + const { getResource } = require('../lib/utils') + const { initializeZTI } = require('../lib/zti') + const resource = getResource() + + // Simulate ZTI flow + // 1. Create TracerProvider early + const tracerProvider = tracing.createTracerProvider(resource) + expect(tracerProvider).toBeDefined() + + // 2. Wait for ZTI + await initializeZTI() + + // 3. Add exporter + tracing.addTracingExporter(tracerProvider) + + // Verify the buffering processor got a delegate + expect(tracerProvider._bufferingProcessor._delegate).toBeDefined() + }) + + test('standard flow works without ZTI', async () => { + // Remove ZTI from VCAP_SERVICES + process.env.VCAP_SERVICES = JSON.stringify({ + 'caas-service': [{ + name: 'test-caas', + credentials: { + otlp: { + http: 'https://caas.example.com/otlp' + } + } + }] + }) + + cds.env.requires = { + telemetry: { + kind: 'to-caas', + credentials: { + otlp: { + http: 'https://caas.example.com/otlp' + } + }, + x509: { + cert: Buffer.from('-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----').toString('base64'), + key: Buffer.from('-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----').toString('base64') + }, + tracing: { + exporter: { + module: '@opentelemetry/sdk-trace-base', + class: 'InMemorySpanExporter' + }, + sampler: { + kind: 'AlwaysOnSampler' + }, + propagators: [] + }, + instrumentations: {} + } + } + + delete require.cache[require.resolve('../lib/zti')] + const { needsZTIWait } = require('../lib/zti') + + // Should NOT need ZTI wait (no ZTI binding) + expect(needsZTIWait()).toBe(false) + + // Standard setup should work + const setup = require('../lib/index') + await setup() + + const tracer = trace.getTracer('test') + const span = tracer.startSpan('test-span') + expect(span.spanContext().traceId).toBeDefined() + span.end() + }) +}) From 47ff244c2c591b87d799d4ea776c6e47ddade9c4 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 12:22:08 +0200 Subject: [PATCH 49/67] fix after refactoring --- lib/index.js | 24 +++++++++++--- test/tracing-zti.test.js | 69 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/lib/index.js b/lib/index.js index 5358f40e..23872abc 100644 --- a/lib/index.js +++ b/lib/index.js @@ -100,13 +100,17 @@ async function setup_standalone() { const needsZTI = needsZTIWait() if (needsZTI) { - // ZTI path: Register TracerProvider early, then wait for credentials + // ZTI path: Register TracerProvider early so HTTP instrumentation gets a real tracer, + // then wait for credentials before creating exporters const tracerProvider = tracing.createTracerProvider(resource) + const instrumentations = _getInstrumentations() + + // First registration: TracerProvider only - this ensures HTTP instrumentation doesn't cache NoopTracer registerInstrumentations({ tracerProvider, meterProvider: undefined, loggerProvider: undefined, - instrumentations: _getInstrumentations() + instrumentations }) try { @@ -115,8 +119,20 @@ async function setup_standalone() { LOG._error && LOG.error('Failed to initialize ZTI:', err) } - metrics(resource) - if (cds.env.requires.telemetry.logging) logging(resource) + // Create metrics and logging providers AFTER ZTI credentials are ready + const meterProvider = metrics(resource) + const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined + + // Update instrumentations with the real providers for metrics and logging + for (const instrumentation of instrumentations) { + if (meterProvider && instrumentation.setMeterProvider) { + instrumentation.setMeterProvider(meterProvider) + } + if (loggerProvider && instrumentation.setLoggerProvider) { + instrumentation.setLoggerProvider(loggerProvider) + } + } + tracing.addTracingExporter(tracerProvider) } else { // Standard path: Create everything at once diff --git a/test/tracing-zti.test.js b/test/tracing-zti.test.js index bb0d7a55..b40cf906 100644 --- a/test/tracing-zti.test.js +++ b/test/tracing-zti.test.js @@ -14,7 +14,6 @@ const cds = require('@sap/cds') const fs = require('fs') const os = require('os') const path = require('path') -const { InMemorySpanExporter } = require('@opentelemetry/sdk-trace-base') const { trace } = require('@opentelemetry/api') // Mock VCAP_SERVICES with ZTI and CaaS @@ -274,6 +273,74 @@ describe('Tracing with ZTI integration', () => { expect(tracerProvider._bufferingProcessor._delegate).toBeDefined() }) + test('ZTI flow updates instrumentations with meterProvider and loggerProvider', async () => { + // This test verifies the fix for: instrumentations not receiving meterProvider/loggerProvider + // when created before ZTI credentials are ready. + // + // The problem: In the ZTI path, registerInstrumentations() was called with + // meterProvider: undefined and loggerProvider: undefined BEFORE those providers + // were created, causing metrics and logs to not be exported. + + // Create SVID files immediately + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') + + cds.env.requires = { + telemetry: { + kind: 'to-caas', + credentials: { + otlp: { http: 'https://caas.example.com/otlp' } + }, + tracing: { + exporter: { + module: '@opentelemetry/sdk-trace-base', + class: 'InMemorySpanExporter' + }, + sampler: { kind: 'AlwaysOnSampler' }, + propagators: [] + }, + metrics: { + exporter: { + module: '@opentelemetry/sdk-metrics', + class: 'InMemoryMetricExporter' + }, + config: {} + }, + logging: { + exporter: { + module: '@opentelemetry/sdk-logs', + class: 'InMemoryLogRecordExporter' + } + }, + instrumentations: {} + } + } + + // Verify ZTI is needed + delete require.cache[require.resolve('../lib/zti')] + const { needsZTIWait, _resetZTIState } = require('../lib/zti') + _resetZTIState() + expect(needsZTIWait()).toBe(true) + + // Run setup + delete require.cache[require.resolve('../lib/index')] + const setup = require('../lib/index') + await setup() + + // Verify meterProvider was set globally (this is what the fix ensures) + const { metrics } = require('@opentelemetry/api') + const meterProvider = metrics.getMeterProvider() + expect(meterProvider).toBeDefined() + expect(meterProvider.constructor.name).not.toBe('NoopMeterProvider') + + // Verify loggerProvider was set globally + const { logs } = require('@opentelemetry/api-logs') + const loggerProvider = logs.getLoggerProvider() + expect(loggerProvider).toBeDefined() + expect(loggerProvider.constructor.name).not.toBe('NoopLoggerProvider') + }) + test('standard flow works without ZTI', async () => { // Remove ZTI from VCAP_SERVICES process.env.VCAP_SERVICES = JSON.stringify({ From b8a3282cef9360723cfde07c3ad13c72c90f24d0 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 12:38:01 +0200 Subject: [PATCH 50/67] fix logging --- lib/logging/index.js | 38 ++++++++++++++++++--- test/caas.test.js | 81 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 78f76cdb..eceaa256 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -11,13 +11,41 @@ const _protocol2module = { 'http/json': '@opentelemetry/exporter-logs-otlp-http' } +/** + * Get the logging exporter config, handling `logging: true` shorthand. + * When `logging: true`, looks up the exporter from the kind's configuration. + */ +function _getLoggingConfig() { + const { kind, logging } = cds.env.requires.telemetry + + // If logging is an object with exporter, use it directly + if (logging && typeof logging === 'object' && logging.exporter) { + return logging + } + + // If logging is true (shorthand), look up from kind config + if (logging === true && kind) { + const kinds = cds.env.requires.kinds || {} + const kindConfig = kinds[kind] + if (kindConfig?.logging?.exporter) { + return kindConfig.logging + } + } + + return null +} + function _getExporter() { + const loggingConfig = _getLoggingConfig() + if (!loggingConfig?.exporter) return null + let { kind, - logging: { exporter: loggingExporter }, credentials } = cds.env.requires.telemetry + let loggingExporter = loggingConfig.exporter + // for kind telemetry-to-otlp based on env vars if (loggingExporter === 'env') { let protocol = @@ -69,9 +97,8 @@ function _getExporter() { } function _getCustomProcessor(exporter) { - let { - logging: { processor: loggingProcessor } - } = cds.env.requires.telemetry + const loggingConfig = _getLoggingConfig() + const loggingProcessor = loggingConfig?.processor if (!loggingProcessor) return @@ -87,7 +114,8 @@ function _getCustomProcessor(exporter) { } module.exports = resource => { - if (!cds.env.requires.telemetry.logging?.exporter) return + const loggingConfig = _getLoggingConfig() + if (!loggingConfig?.exporter) return const { logs, SeverityNumber } = require('@opentelemetry/api-logs') const { LoggerProvider, BatchLogRecordProcessor, SimpleLogRecordProcessor } = require('@opentelemetry/sdk-logs') diff --git a/test/caas.test.js b/test/caas.test.js index 85587b34..bff8a50d 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -341,3 +341,84 @@ describe('ZTI flag behavior', () => { expect(credentials.httpAgentOptions.key).toContain('legacy-key') }) }) + +describe('logging: true shorthand', () => { + beforeEach(() => { + delete require.cache[require.resolve('../lib/logging')] + }) + + afterEach(() => { + delete process.env.VCAP_SERVICES + }) + + test('resolves exporter from kind config when logging: true', () => { + cds.env.requires = { + telemetry: { + kind: 'telemetry-to-caas', + logging: true + }, + kinds: { + 'telemetry-to-caas': { + logging: { + exporter: { + module: '@opentelemetry/exporter-logs-otlp-proto', + class: 'OTLPLogExporter' + } + } + } + } + } + + // Access the internal _getLoggingConfig via module loading behavior + // We test indirectly by checking that logging module doesn't return early + const loggingModule = require('../lib/logging') + + // The module should not return undefined when logging: true with kind config + // We can't easily call it without side effects, but we can verify the fix by + // checking that it tries to load the exporter module + expect(cds.env.requires.telemetry.logging).toBe(true) + expect(cds.env.requires.kinds['telemetry-to-caas'].logging.exporter).toBeDefined() + }) + + test('returns null when logging: true but no kind config', () => { + cds.env.requires = { + telemetry: { + kind: 'unknown-kind', + logging: true + }, + kinds: {} + } + + delete require.cache[require.resolve('../lib/logging')] + const loggingModule = require('../lib/logging') + + // Module export is a function, calling it with resource should return early (undefined) + // because no exporter config is available + const result = loggingModule({}) + expect(result).toBeUndefined() + }) + + test('uses logging.exporter directly when provided as object', () => { + // Use a non-CaaS kind to avoid needing credentials + cds.env.requires = { + telemetry: { + kind: 'telemetry-to-console', + logging: { + exporter: { + module: '@opentelemetry/sdk-logs', + class: 'InMemoryLogRecordExporter' + } + } + }, + kinds: {} + } + + delete require.cache[require.resolve('../lib/logging')] + const loggingModule = require('../lib/logging') + + // Should use the direct exporter config, not look up from kind + // This will succeed and create a logger provider + const result = loggingModule({}) + expect(result).toBeDefined() + }) +}) From ddc952b54301bce2764299e084ac0f337db47b74 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 13:02:21 +0200 Subject: [PATCH 51/67] debug: add logging to _getLoggingConfig --- lib/logging/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/logging/index.js b/lib/logging/index.js index eceaa256..0f258f62 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -18,8 +18,11 @@ const _protocol2module = { function _getLoggingConfig() { const { kind, logging } = cds.env.requires.telemetry + LOG._debug && LOG.debug('_getLoggingConfig called', { kind, logging, loggingType: typeof logging }) + // If logging is an object with exporter, use it directly if (logging && typeof logging === 'object' && logging.exporter) { + LOG._debug && LOG.debug('Using logging config from telemetry.logging object') return logging } @@ -27,11 +30,13 @@ function _getLoggingConfig() { if (logging === true && kind) { const kinds = cds.env.requires.kinds || {} const kindConfig = kinds[kind] + LOG._debug && LOG.debug('Looking up logging config from kind', { kind, hasKindConfig: !!kindConfig, hasLoggingExporter: !!kindConfig?.logging?.exporter }) if (kindConfig?.logging?.exporter) { return kindConfig.logging } } + LOG._debug && LOG.debug('_getLoggingConfig returning null') return null } From 3e52ca9bfcbb38c682acf9a90d5a1a1423085ec9 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 13:14:38 +0200 Subject: [PATCH 52/67] fix 2 logging --- lib/logging/index.js | 21 ++++++++++++++------- test/caas.test.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 0f258f62..b8ef2107 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -18,11 +18,8 @@ const _protocol2module = { function _getLoggingConfig() { const { kind, logging } = cds.env.requires.telemetry - LOG._debug && LOG.debug('_getLoggingConfig called', { kind, logging, loggingType: typeof logging }) - // If logging is an object with exporter, use it directly if (logging && typeof logging === 'object' && logging.exporter) { - LOG._debug && LOG.debug('Using logging config from telemetry.logging object') return logging } @@ -30,13 +27,11 @@ function _getLoggingConfig() { if (logging === true && kind) { const kinds = cds.env.requires.kinds || {} const kindConfig = kinds[kind] - LOG._debug && LOG.debug('Looking up logging config from kind', { kind, hasKindConfig: !!kindConfig, hasLoggingExporter: !!kindConfig?.logging?.exporter }) if (kindConfig?.logging?.exporter) { return kindConfig.logging } } - LOG._debug && LOG.debug('_getLoggingConfig returning null') return null } @@ -126,7 +121,11 @@ module.exports = resource => { const { LoggerProvider, BatchLogRecordProcessor, SimpleLogRecordProcessor } = require('@opentelemetry/sdk-logs') // setup logs interception via cds.log.format - cds.on('served', () => { + let _logInterceptionSetup = false + const setupLogInterception = () => { + if (_logInterceptionSetup) return + _logInterceptionSetup = true + const loggerProvider = logs.getLoggerProvider() const loggers = {} @@ -174,7 +173,15 @@ module.exports = resource => { // replace format function of existing loggers for (const each in cds.log.loggers) cds.log.loggers[each].setFormat(format) - }) + } + + // In ZTI flow, 'served' may have already fired before logging module is initialized + // (because await initializeZTI() takes time while cds server starts). + // We register for 'served' for normal flow, but also call setupLogInterception() + // immediately to handle the case where 'served' already fired. + // The guard ensures it only runs once. + cds.on('served', setupLogInterception) + setupLogInterception() /* * create processor diff --git a/test/caas.test.js b/test/caas.test.js index bff8a50d..916aa421 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -351,6 +351,40 @@ describe('logging: true shorthand', () => { delete process.env.VCAP_SERVICES }) + test('sets up log interception immediately for ZTI timing (served already fired)', () => { + // Simulate ZTI scenario: logging module is initialized AFTER cds.on('served') has fired + // This happens because await initializeZTI() takes time while the server starts + cds.env.requires = { + telemetry: { + kind: 'telemetry-to-console', + logging: { + exporter: { + module: '@opentelemetry/sdk-logs', + class: 'InMemoryLogRecordExporter' + } + } + }, + kinds: {} + } + + // Save original cds.log.format + const originalFormat = cds.log.format + + delete require.cache[require.resolve('../lib/logging')] + const loggingModule = require('../lib/logging') + + // Call logging module - simulates what happens after ZTI init completes + const result = loggingModule({}) + expect(result).toBeDefined() + + // The format function should be replaced immediately, not waiting for 'served' + // This is the key fix - in ZTI flow, 'served' has already fired + expect(cds.log.format).not.toBe(originalFormat) + + // Cleanup + cds.log.format = originalFormat + }) + test('resolves exporter from kind config when logging: true', () => { cds.env.requires = { telemetry: { From 152b33e8ffc6ea4ff7c72483aff0c01ecd0726aa Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 13:25:16 +0200 Subject: [PATCH 53/67] fix 3 logging --- lib/logging/index.js | 67 ++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index b8ef2107..59ae5d3a 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -120,14 +120,46 @@ module.exports = resource => { const { logs, SeverityNumber } = require('@opentelemetry/api-logs') const { LoggerProvider, BatchLogRecordProcessor, SimpleLogRecordProcessor } = require('@opentelemetry/sdk-logs') + /* + * create processor and provider FIRST (before setting up log interception) + */ + const exporter = _getExporter() + if (!exporter) return null + const processor = + _getCustomProcessor(exporter) || + (process.env.NODE_ENV === 'production' + ? new BatchLogRecordProcessor(exporter) + : new SimpleLogRecordProcessor(exporter)) + + /* + * either add processor as delegate in CALM... + */ + if (!resource) { + LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") + try { + const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') + getCompositeLogRecordProcessor().addDelegate(processor) + return + } catch (error) { + LOG.error('Failed to add log processor as delegate:', error) + throw error + } + } + + /* + * ... or initialize provider and set it globally + */ + const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) + logs.setGlobalLoggerProvider(loggerProvider) + // setup logs interception via cds.log.format + // MUST be done AFTER LoggerProvider is set globally, otherwise logs.getLoggerProvider() + // returns NoopLoggerProvider let _logInterceptionSetup = false const setupLogInterception = () => { if (_logInterceptionSetup) return _logInterceptionSetup = true - const loggerProvider = logs.getLoggerProvider() - const loggers = {} const l2s = { 1: 'ERROR', 2: 'WARN', 3: 'INFO', 4: 'DEBUG', 5: 'TRACE' } @@ -183,36 +215,5 @@ module.exports = resource => { cds.on('served', setupLogInterception) setupLogInterception() - /* - * create processor - */ - const exporter = _getExporter() - if (!exporter) return null - const processor = - _getCustomProcessor(exporter) || - (process.env.NODE_ENV === 'production' - ? new BatchLogRecordProcessor(exporter) - : new SimpleLogRecordProcessor(exporter)) - - /* - * either add processor as delegate in CALM... - */ - if (!resource) { - LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") - try { - const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') - getCompositeLogRecordProcessor().addDelegate(processor) - return - } catch (error) { - LOG.error('Failed to add log processor as delegate:', error) - throw error - } - } - - /* - * ... or initialize and return provider - */ - const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) - logs.setGlobalLoggerProvider(loggerProvider) return loggerProvider } From 331fe754a95b106877fe56368b6d5ad4bff87846 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 13:41:32 +0200 Subject: [PATCH 54/67] revert fixes logging --- lib/logging/index.js | 127 ++++++++++++++++--------------------------- test/caas.test.js | 81 --------------------------- 2 files changed, 46 insertions(+), 162 deletions(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 59ae5d3a..3ddaa038 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -11,41 +11,13 @@ const _protocol2module = { 'http/json': '@opentelemetry/exporter-logs-otlp-http' } -/** - * Get the logging exporter config, handling `logging: true` shorthand. - * When `logging: true`, looks up the exporter from the kind's configuration. - */ -function _getLoggingConfig() { - const { kind, logging } = cds.env.requires.telemetry - - // If logging is an object with exporter, use it directly - if (logging && typeof logging === 'object' && logging.exporter) { - return logging - } - - // If logging is true (shorthand), look up from kind config - if (logging === true && kind) { - const kinds = cds.env.requires.kinds || {} - const kindConfig = kinds[kind] - if (kindConfig?.logging?.exporter) { - return kindConfig.logging - } - } - - return null -} - function _getExporter() { - const loggingConfig = _getLoggingConfig() - if (!loggingConfig?.exporter) return null - let { kind, + logging: { exporter: loggingExporter }, credentials } = cds.env.requires.telemetry - let loggingExporter = loggingConfig.exporter - // for kind telemetry-to-otlp based on env vars if (loggingExporter === 'env') { let protocol = @@ -64,9 +36,12 @@ function _getExporter() { let loggingExporterModule try { loggingExporterModule = _require(loggingExporter.module) - } catch { - LOG._warn && LOG.warn(`Logs exporter module '${loggingExporter.module}' not found. Logging telemetry will be disabled.`) - return null + } catch (err) { + if (err.code === 'MODULE_NOT_FOUND') { + LOG._warn && LOG.warn(`Logs exporter module '${loggingExporter.module}' not found. Logging telemetry will be disabled.`) + return null + } + throw err } if (!loggingExporterModule[loggingExporter.class]) throw new Error(`Unknown logs exporter "${loggingExporter.class}" in module "${loggingExporter.module}"`) @@ -97,8 +72,9 @@ function _getExporter() { } function _getCustomProcessor(exporter) { - const loggingConfig = _getLoggingConfig() - const loggingProcessor = loggingConfig?.processor + let { + logging: { processor: loggingProcessor } + } = cds.env.requires.telemetry if (!loggingProcessor) return @@ -114,51 +90,14 @@ function _getCustomProcessor(exporter) { } module.exports = resource => { - const loggingConfig = _getLoggingConfig() - if (!loggingConfig?.exporter) return + if (!cds.env.requires.telemetry.logging?.exporter) return const { logs, SeverityNumber } = require('@opentelemetry/api-logs') const { LoggerProvider, BatchLogRecordProcessor, SimpleLogRecordProcessor } = require('@opentelemetry/sdk-logs') - /* - * create processor and provider FIRST (before setting up log interception) - */ - const exporter = _getExporter() - if (!exporter) return null - const processor = - _getCustomProcessor(exporter) || - (process.env.NODE_ENV === 'production' - ? new BatchLogRecordProcessor(exporter) - : new SimpleLogRecordProcessor(exporter)) - - /* - * either add processor as delegate in CALM... - */ - if (!resource) { - LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") - try { - const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') - getCompositeLogRecordProcessor().addDelegate(processor) - return - } catch (error) { - LOG.error('Failed to add log processor as delegate:', error) - throw error - } - } - - /* - * ... or initialize provider and set it globally - */ - const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) - logs.setGlobalLoggerProvider(loggerProvider) - // setup logs interception via cds.log.format - // MUST be done AFTER LoggerProvider is set globally, otherwise logs.getLoggerProvider() - // returns NoopLoggerProvider - let _logInterceptionSetup = false - const setupLogInterception = () => { - if (_logInterceptionSetup) return - _logInterceptionSetup = true + cds.on('served', () => { + const loggerProvider = logs.getLoggerProvider() const loggers = {} const l2s = { 1: 'ERROR', 2: 'WARN', 3: 'INFO', 4: 'DEBUG', 5: 'TRACE' } @@ -205,15 +144,41 @@ module.exports = resource => { // replace format function of existing loggers for (const each in cds.log.loggers) cds.log.loggers[each].setFormat(format) + }) + + /* + * create processor + */ + const exporter = _getExporter() + if (!exporter) { + LOG._info && LOG.info('Logging telemetry is disabled due to missing exporter module') + return null } + const processor = + _getCustomProcessor(exporter) || + (process.env.NODE_ENV === 'production' + ? new BatchLogRecordProcessor({ exporter }) + : new SimpleLogRecordProcessor({ exporter })) - // In ZTI flow, 'served' may have already fired before logging module is initialized - // (because await initializeZTI() takes time while cds server starts). - // We register for 'served' for normal flow, but also call setupLogInterception() - // immediately to handle the case where 'served' already fired. - // The guard ensures it only runs once. - cds.on('served', setupLogInterception) - setupLogInterception() + /* + * either add processor as delegate in CALM... + */ + if (!resource) { + LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") + try { + const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') + getCompositeLogRecordProcessor().addDelegate(processor) + return + } catch (error) { + LOG.error('Failed to add log processor as delegate:', error) + throw error + } + } + /* + * ... or initialize and return provider + */ + const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) + logs.setGlobalLoggerProvider(loggerProvider) return loggerProvider } diff --git a/test/caas.test.js b/test/caas.test.js index 916aa421..5abad1e7 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -351,87 +351,6 @@ describe('logging: true shorthand', () => { delete process.env.VCAP_SERVICES }) - test('sets up log interception immediately for ZTI timing (served already fired)', () => { - // Simulate ZTI scenario: logging module is initialized AFTER cds.on('served') has fired - // This happens because await initializeZTI() takes time while the server starts - cds.env.requires = { - telemetry: { - kind: 'telemetry-to-console', - logging: { - exporter: { - module: '@opentelemetry/sdk-logs', - class: 'InMemoryLogRecordExporter' - } - } - }, - kinds: {} - } - - // Save original cds.log.format - const originalFormat = cds.log.format - - delete require.cache[require.resolve('../lib/logging')] - const loggingModule = require('../lib/logging') - - // Call logging module - simulates what happens after ZTI init completes - const result = loggingModule({}) - expect(result).toBeDefined() - - // The format function should be replaced immediately, not waiting for 'served' - // This is the key fix - in ZTI flow, 'served' has already fired - expect(cds.log.format).not.toBe(originalFormat) - - // Cleanup - cds.log.format = originalFormat - }) - - test('resolves exporter from kind config when logging: true', () => { - cds.env.requires = { - telemetry: { - kind: 'telemetry-to-caas', - logging: true - }, - kinds: { - 'telemetry-to-caas': { - logging: { - exporter: { - module: '@opentelemetry/exporter-logs-otlp-proto', - class: 'OTLPLogExporter' - } - } - } - } - } - - // Access the internal _getLoggingConfig via module loading behavior - // We test indirectly by checking that logging module doesn't return early - const loggingModule = require('../lib/logging') - - // The module should not return undefined when logging: true with kind config - // We can't easily call it without side effects, but we can verify the fix by - // checking that it tries to load the exporter module - expect(cds.env.requires.telemetry.logging).toBe(true) - expect(cds.env.requires.kinds['telemetry-to-caas'].logging.exporter).toBeDefined() - }) - - test('returns null when logging: true but no kind config', () => { - cds.env.requires = { - telemetry: { - kind: 'unknown-kind', - logging: true - }, - kinds: {} - } - - delete require.cache[require.resolve('../lib/logging')] - const loggingModule = require('../lib/logging') - - // Module export is a function, calling it with resource should return early (undefined) - // because no exporter config is available - const result = loggingModule({}) - expect(result).toBeUndefined() - }) - test('uses logging.exporter directly when provided as object', () => { // Use a non-CaaS kind to avoid needing credentials cds.env.requires = { From 21f4646d4bfa78c6ab0db863851731476ea17bd0 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 14:23:17 +0200 Subject: [PATCH 55/67] fix logging again --- lib/logging/index.js | 83 +++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 3ddaa038..06f33a53 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -95,9 +95,50 @@ module.exports = resource => { const { logs, SeverityNumber } = require('@opentelemetry/api-logs') const { LoggerProvider, BatchLogRecordProcessor, SimpleLogRecordProcessor } = require('@opentelemetry/sdk-logs') - // setup logs interception via cds.log.format - cds.on('served', () => { - const loggerProvider = logs.getLoggerProvider() + /* + * create processor + */ + const exporter = _getExporter() + if (!exporter) { + LOG._info && LOG.info('Logging telemetry is disabled due to missing exporter module') + return null + } + const processor = + _getCustomProcessor(exporter) || + (process.env.NODE_ENV === 'production' + ? new BatchLogRecordProcessor({ exporter }) + : new SimpleLogRecordProcessor({ exporter })) + + /* + * either add processor as delegate in CALM... + */ + if (!resource) { + LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") + try { + const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') + getCompositeLogRecordProcessor().addDelegate(processor) + return + } catch (error) { + LOG.error('Failed to add log processor as delegate:', error) + throw error + } + } + + /* + * ... or initialize and return provider + */ + const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) + logs.setGlobalLoggerProvider(loggerProvider) + + /* + * setup logs interception via cds.log.format + * Must be done AFTER LoggerProvider is set globally. + * In ZTI flow, 'served' may have already fired, so we also call immediately. + */ + let _logInterceptionSetup = false + const setupLogInterception = () => { + if (_logInterceptionSetup) return + _logInterceptionSetup = true const loggers = {} const l2s = { 1: 'ERROR', 2: 'WARN', 3: 'INFO', 4: 'DEBUG', 5: 'TRACE' } @@ -144,41 +185,11 @@ module.exports = resource => { // replace format function of existing loggers for (const each in cds.log.loggers) cds.log.loggers[each].setFormat(format) - }) - - /* - * create processor - */ - const exporter = _getExporter() - if (!exporter) { - LOG._info && LOG.info('Logging telemetry is disabled due to missing exporter module') - return null } - const processor = - _getCustomProcessor(exporter) || - (process.env.NODE_ENV === 'production' - ? new BatchLogRecordProcessor({ exporter }) - : new SimpleLogRecordProcessor({ exporter })) - /* - * either add processor as delegate in CALM... - */ - if (!resource) { - LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") - try { - const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') - getCompositeLogRecordProcessor().addDelegate(processor) - return - } catch (error) { - LOG.error('Failed to add log processor as delegate:', error) - throw error - } - } + // Register for 'served' event (normal flow) and also call immediately (ZTI flow where 'served' already fired) + cds.on('served', setupLogInterception) + setupLogInterception() - /* - * ... or initialize and return provider - */ - const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) - logs.setGlobalLoggerProvider(loggerProvider) return loggerProvider } From 0873484999fd3e3b710b3935d3c3d1c01b954905 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 14:59:24 +0200 Subject: [PATCH 56/67] revert not needed changes in logging --- lib/logging/index.js | 91 ++++++++++++++++++-------------------------- test/caas.test.js | 34 ----------------- 2 files changed, 37 insertions(+), 88 deletions(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 06f33a53..78f76cdb 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -36,12 +36,9 @@ function _getExporter() { let loggingExporterModule try { loggingExporterModule = _require(loggingExporter.module) - } catch (err) { - if (err.code === 'MODULE_NOT_FOUND') { - LOG._warn && LOG.warn(`Logs exporter module '${loggingExporter.module}' not found. Logging telemetry will be disabled.`) - return null - } - throw err + } catch { + LOG._warn && LOG.warn(`Logs exporter module '${loggingExporter.module}' not found. Logging telemetry will be disabled.`) + return null } if (!loggingExporterModule[loggingExporter.class]) throw new Error(`Unknown logs exporter "${loggingExporter.class}" in module "${loggingExporter.module}"`) @@ -95,50 +92,9 @@ module.exports = resource => { const { logs, SeverityNumber } = require('@opentelemetry/api-logs') const { LoggerProvider, BatchLogRecordProcessor, SimpleLogRecordProcessor } = require('@opentelemetry/sdk-logs') - /* - * create processor - */ - const exporter = _getExporter() - if (!exporter) { - LOG._info && LOG.info('Logging telemetry is disabled due to missing exporter module') - return null - } - const processor = - _getCustomProcessor(exporter) || - (process.env.NODE_ENV === 'production' - ? new BatchLogRecordProcessor({ exporter }) - : new SimpleLogRecordProcessor({ exporter })) - - /* - * either add processor as delegate in CALM... - */ - if (!resource) { - LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") - try { - const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') - getCompositeLogRecordProcessor().addDelegate(processor) - return - } catch (error) { - LOG.error('Failed to add log processor as delegate:', error) - throw error - } - } - - /* - * ... or initialize and return provider - */ - const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) - logs.setGlobalLoggerProvider(loggerProvider) - - /* - * setup logs interception via cds.log.format - * Must be done AFTER LoggerProvider is set globally. - * In ZTI flow, 'served' may have already fired, so we also call immediately. - */ - let _logInterceptionSetup = false - const setupLogInterception = () => { - if (_logInterceptionSetup) return - _logInterceptionSetup = true + // setup logs interception via cds.log.format + cds.on('served', () => { + const loggerProvider = logs.getLoggerProvider() const loggers = {} const l2s = { 1: 'ERROR', 2: 'WARN', 3: 'INFO', 4: 'DEBUG', 5: 'TRACE' } @@ -185,11 +141,38 @@ module.exports = resource => { // replace format function of existing loggers for (const each in cds.log.loggers) cds.log.loggers[each].setFormat(format) - } + }) - // Register for 'served' event (normal flow) and also call immediately (ZTI flow where 'served' already fired) - cds.on('served', setupLogInterception) - setupLogInterception() + /* + * create processor + */ + const exporter = _getExporter() + if (!exporter) return null + const processor = + _getCustomProcessor(exporter) || + (process.env.NODE_ENV === 'production' + ? new BatchLogRecordProcessor(exporter) + : new SimpleLogRecordProcessor(exporter)) + /* + * either add processor as delegate in CALM... + */ + if (!resource) { + LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") + try { + const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') + getCompositeLogRecordProcessor().addDelegate(processor) + return + } catch (error) { + LOG.error('Failed to add log processor as delegate:', error) + throw error + } + } + + /* + * ... or initialize and return provider + */ + const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) + logs.setGlobalLoggerProvider(loggerProvider) return loggerProvider } diff --git a/test/caas.test.js b/test/caas.test.js index 5abad1e7..85587b34 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -341,37 +341,3 @@ describe('ZTI flag behavior', () => { expect(credentials.httpAgentOptions.key).toContain('legacy-key') }) }) - -describe('logging: true shorthand', () => { - beforeEach(() => { - delete require.cache[require.resolve('../lib/logging')] - }) - - afterEach(() => { - delete process.env.VCAP_SERVICES - }) - - test('uses logging.exporter directly when provided as object', () => { - // Use a non-CaaS kind to avoid needing credentials - cds.env.requires = { - telemetry: { - kind: 'telemetry-to-console', - logging: { - exporter: { - module: '@opentelemetry/sdk-logs', - class: 'InMemoryLogRecordExporter' - } - } - }, - kinds: {} - } - - delete require.cache[require.resolve('../lib/logging')] - const loggingModule = require('../lib/logging') - - // Should use the direct exporter config, not look up from kind - // This will succeed and create a logger provider - const result = loggingModule({}) - expect(result).toBeDefined() - }) -}) From 6412f191936b99a1fffe749a2f3246aaf9caafe9 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 15:06:54 +0200 Subject: [PATCH 57/67] fixing logging part 2 --- lib/logging/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 78f76cdb..230c2249 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -151,8 +151,8 @@ module.exports = resource => { const processor = _getCustomProcessor(exporter) || (process.env.NODE_ENV === 'production' - ? new BatchLogRecordProcessor(exporter) - : new SimpleLogRecordProcessor(exporter)) + ? new BatchLogRecordProcessor({ exporter }) + : new SimpleLogRecordProcessor({ exporter })) /* * either add processor as delegate in CALM... From fe1f47468cb6ee4eea0f9323e0c305bf0166774f Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 15:15:57 +0200 Subject: [PATCH 58/67] continue fixing logging --- lib/logging/index.js | 74 ++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 230c2249..c6dffc05 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -92,9 +92,45 @@ module.exports = resource => { const { logs, SeverityNumber } = require('@opentelemetry/api-logs') const { LoggerProvider, BatchLogRecordProcessor, SimpleLogRecordProcessor } = require('@opentelemetry/sdk-logs') + /* + * create processor + */ + const exporter = _getExporter() + if (!exporter) return null + const processor = + _getCustomProcessor(exporter) || + (process.env.NODE_ENV === 'production' + ? new BatchLogRecordProcessor({ exporter }) + : new SimpleLogRecordProcessor({ exporter })) + + /* + * either add processor as delegate in CALM... + */ + if (!resource) { + LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") + try { + const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') + getCompositeLogRecordProcessor().addDelegate(processor) + return + } catch (error) { + LOG.error('Failed to add log processor as delegate:', error) + throw error + } + } + + /* + * ... or initialize and return provider + */ + const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) + logs.setGlobalLoggerProvider(loggerProvider) + // setup logs interception via cds.log.format - cds.on('served', () => { - const loggerProvider = logs.getLoggerProvider() + // Must be done AFTER LoggerProvider is set globally. + // In ZTI flow, 'served' may have already fired, so we also call immediately. + let _logInterceptionSetup = false + const setupLogInterception = () => { + if (_logInterceptionSetup) return + _logInterceptionSetup = true const loggers = {} const l2s = { 1: 'ERROR', 2: 'WARN', 3: 'INFO', 4: 'DEBUG', 5: 'TRACE' } @@ -141,38 +177,10 @@ module.exports = resource => { // replace format function of existing loggers for (const each in cds.log.loggers) cds.log.loggers[each].setFormat(format) - }) - - /* - * create processor - */ - const exporter = _getExporter() - if (!exporter) return null - const processor = - _getCustomProcessor(exporter) || - (process.env.NODE_ENV === 'production' - ? new BatchLogRecordProcessor({ exporter }) - : new SimpleLogRecordProcessor({ exporter })) - - /* - * either add processor as delegate in CALM... - */ - if (!resource) { - LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") - try { - const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') - getCompositeLogRecordProcessor().addDelegate(processor) - return - } catch (error) { - LOG.error('Failed to add log processor as delegate:', error) - throw error - } } - /* - * ... or initialize and return provider - */ - const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) - logs.setGlobalLoggerProvider(loggerProvider) + cds.on('served', setupLogInterception) + setupLogInterception() + return loggerProvider } From 6bf599ef19ddaaadf6dabbb7dc23dc3a348adabc Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 16:18:45 +0200 Subject: [PATCH 59/67] small refactoring --- lib/zti.js | 81 ++++++++------------------- test/caas.test.js | 117 ++++++++++++++++++++------------------- test/tracing-zti.test.js | 30 +++++----- 3 files changed, 98 insertions(+), 130 deletions(-) diff --git a/lib/zti.js b/lib/zti.js index a1998dc0..bce66fbd 100644 --- a/lib/zti.js +++ b/lib/zti.js @@ -2,23 +2,23 @@ const cds = require('@sap/cds') const LOG = cds.log('telemetry') const fs = require('fs') -// SVID file paths and cache (initialized once, reused across requests) -let _certPath = null -let _keyPath = null -let _bundlePath = null -let _cached = null -let _cachedMod = null +// Retry settings for waiting on SVID files during startup +const ZTI_RETRY_INTERVAL_MS = 2000 +const ZTI_MAX_RETRIES = 30 // 60 seconds total + +// SVID paths and cached credentials (initialized once, reused across requests) +let _paths = null // { cert, key, bundle } - file paths +let _cached = null // { cert, key, bundle, mtime } - cached credentials /** * Get SVID certificate credentials, reloading if mtime changed. * Manages mtime-based caching for automatic certificate rotation. - * Based on official ZTI blueprint: https://github.tools.sap/pse/blueprints * @returns {{ cert: string, key: string, bundle: string }} */ function _getSVIDCertificate() { let stat try { - stat = fs.statSync(_certPath) + stat = fs.statSync(_paths.cert) } catch (e) { // Transient stat failure during atomic rename - serve last-known-good if (_cached) { @@ -29,26 +29,19 @@ function _getSVIDCertificate() { } const mtime = stat.mtimeMs - if (_cached && _cachedMod === mtime) { + if (_cached?.mtime === mtime) { // Cache hit - no rotation since last check return _cached } // mtime changed or first load - reload all files atomically - LOG._debug && LOG.debug('ZTI: Loading SVID files (mtime changed or first load)') try { - const certPEM = fs.readFileSync(_certPath, 'utf8') - const keyPEM = fs.readFileSync(_keyPath, 'utf8') - const bundlePEM = fs.readFileSync(_bundlePath, 'utf8') - - _cached = { cert: certPEM, key: keyPEM, bundle: bundlePEM } - _cachedMod = mtime - - LOG._info && LOG.info('ZTI: Loaded SVID certificate', { - certPath: _certPath, - mtime: new Date(mtime).toISOString() - }) - + _cached = { + cert: fs.readFileSync(_paths.cert, 'utf8'), + key: fs.readFileSync(_paths.key, 'utf8'), + bundle: fs.readFileSync(_paths.bundle, 'utf8'), + mtime + } return _cached } catch (err) { // Read failure after successful stat - likely mid-rotation @@ -92,37 +85,22 @@ function getZTIConfig() { * @returns {Promise} */ async function _initializeSVIDFiles(svidDir, svidName) { - const RETRY_INTERVAL_MS = 2000 - const MAX_RETRIES = 30 // 60 seconds total (30 * 2s) - - _certPath = `${svidDir}/${svidName}.svid.pem` - _keyPath = `${svidDir}/${svidName}.svid.key` - _bundlePath = `${svidDir}/${svidName}.bundle.pem` + _paths = { + cert: `${svidDir}/${svidName}.svid.pem`, + key: `${svidDir}/${svidName}.svid.key`, + bundle: `${svidDir}/${svidName}.bundle.pem` + } let attempt = 0 while (true) { try { - // Try to load - will throw if files don't exist _getSVIDCertificate() - LOG._info && LOG.info(`ZTI: SVID files ready at ${svidDir}/${svidName}`) return } catch (err) { - attempt++ - if (attempt >= MAX_RETRIES) { - throw new Error( - `ZTI: SVID files not available after ${MAX_RETRIES} attempts (${(MAX_RETRIES * RETRY_INTERVAL_MS) / 1000}s). ` + - `Expected files at ${svidDir}/${svidName}.svid.{pem,key}. ` + - `Error: ${err.message}`, - { cause: err } - ) + if (++attempt >= ZTI_MAX_RETRIES) { + throw new Error(`ZTI: SVID files not available at ${_paths.cert} after ${ZTI_MAX_RETRIES * ZTI_RETRY_INTERVAL_MS / 1000}s`, { cause: err }) } - - LOG._debug && LOG.debug( - `ZTI: SVID files not ready yet (attempt ${attempt}/${MAX_RETRIES}), ` + - `retrying in ${RETRY_INTERVAL_MS}ms...` - ) - - await new Promise(resolve => setTimeout(resolve, RETRY_INTERVAL_MS)) + await new Promise(resolve => setTimeout(resolve, ZTI_RETRY_INTERVAL_MS)) } } } @@ -238,16 +216,3 @@ module.exports = { getCredsForCaaSMtls, needsZTIWait } - -// For testing only - not part of public API -if (process.env.NODE_ENV === 'test') { - module.exports._resetZTIState = () => { - _certPath = null - _keyPath = null - _bundlePath = null - _cached = null - _cachedMod = null - _ztiInitialized = false - _ztiInitPromise = null - } -} diff --git a/test/caas.test.js b/test/caas.test.js index 85587b34..8aafbdde 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -153,7 +153,6 @@ describe('ZTI SVID File Loading', () => { cds.env.requires = cds.env.requires || {} cds.env.requires.telemetry = {} - delete require.cache[require.resolve('../lib/utils')] }) afterEach(() => { @@ -169,12 +168,15 @@ describe('ZTI SVID File Loading', () => { fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - delete require.cache[require.resolve('../lib/zti')] - const { initializeZTI, getCredsForCaaSMtls, _resetZTIState } = require('../lib/zti') - _resetZTIState() - - await initializeZTI() - const creds = getCredsForCaaSMtls() + let creds + // jest.isolateModulesAsync gives each test a fresh zti module with reset internal state + // (cached credentials, init promise, file paths). Without isolation, state from previous + // tests would leak and cause failures when tests run in sequence. + await jest.isolateModulesAsync(async () => { + const { initializeZTI, getCredsForCaaSMtls } = require('../lib/zti') + await initializeZTI() + creds = getCredsForCaaSMtls() + }) expect(creds).toBeDefined() expect(creds.cert).toContain('BEGIN CERTIFICATE') @@ -191,22 +193,24 @@ describe('ZTI SVID File Loading', () => { fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\nv1\n-----END PRIVATE KEY-----') fs.writeFileSync(bundlePath, '-----BEGIN CERTIFICATE-----\nv1\n-----END CERTIFICATE-----') - delete require.cache[require.resolve('../lib/zti')] - const { initializeZTI, getCredsForCaaSMtls, _resetZTIState } = require('../lib/zti') - _resetZTIState() + let creds1, creds2 + await jest.isolateModulesAsync(async () => { + const { initializeZTI, getCredsForCaaSMtls } = require('../lib/zti') - await initializeZTI() - const creds1 = getCredsForCaaSMtls() - expect(creds1.cert).toContain('v1') + await initializeZTI() + creds1 = getCredsForCaaSMtls() + + // Wait to ensure mtime changes + await new Promise(resolve => setTimeout(resolve, 10)) - // Wait to ensure mtime changes - await new Promise(resolve => setTimeout(resolve, 10)) + // Update files (simulating ZTI rotation) + fs.writeFileSync(certPath, '-----BEGIN CERTIFICATE-----\nv2\n-----END CERTIFICATE-----') + fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\nv2\n-----END PRIVATE KEY-----') - // Update files (simulating ZTI rotation) - fs.writeFileSync(certPath, '-----BEGIN CERTIFICATE-----\nv2\n-----END CERTIFICATE-----') - fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\nv2\n-----END PRIVATE KEY-----') + creds2 = getCredsForCaaSMtls() + }) - const creds2 = getCredsForCaaSMtls() + expect(creds1.cert).toContain('v1') expect(creds2.cert).toContain('v2') expect(creds2.key).toContain('v2') }) @@ -216,21 +220,21 @@ describe('ZTI SVID File Loading', () => { const keyPath = path.join(svidDir, 'test-svid.svid.key') const bundlePath = path.join(svidDir, 'test-svid.bundle.pem') - delete require.cache[require.resolve('../lib/zti')] - const { initializeZTI, _resetZTIState } = require('../lib/zti') - _resetZTIState() + await jest.isolateModulesAsync(async () => { + const { initializeZTI } = require('../lib/zti') - // Start initialization (files don't exist yet) - const initPromise = initializeZTI() + // Start initialization (files don't exist yet) + const initPromise = initializeZTI() - // Wait 2.5 seconds, then create files (should succeed on 2nd retry) - await new Promise(resolve => setTimeout(resolve, 2500)) - fs.writeFileSync(certPath, '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') - fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') - fs.writeFileSync(bundlePath, '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + // Wait 2.5 seconds, then create files (should succeed on 2nd retry) + await new Promise(resolve => setTimeout(resolve, 2500)) + fs.writeFileSync(certPath, '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') + fs.writeFileSync(bundlePath, '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') - // Should complete successfully - await expect(initPromise).resolves.toBeUndefined() + // Should complete successfully + await expect(initPromise).resolves.toBeUndefined() + }) }, 10000) }) @@ -248,24 +252,26 @@ describe('ZTI flag behavior', () => { test('detects ZTI config from VCAP_SERVICES', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_ZTI_VCAP) - delete require.cache[require.resolve('../lib/utils')] - const { getZTIConfig } = require('../lib/utils') - const config = getZTIConfig() + jest.isolateModules(() => { + const { getZTIConfig } = require('../lib/utils') + const config = getZTIConfig() - expect(config).not.toBeNull() - expect(config.svidName).toBe('test-svid') - expect(config.svidDir).toBe('/home/vcap/app/spire-svids') + expect(config).not.toBeNull() + expect(config.svidName).toBe('test-svid') + expect(config.svidDir).toBe('/home/vcap/app/spire-svids') + }) }) test('returns null when no ZTI binding', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) - delete require.cache[require.resolve('../lib/utils')] - const { getZTIConfig } = require('../lib/utils') - const config = getZTIConfig() + jest.isolateModules(() => { + const { getZTIConfig } = require('../lib/utils') + const config = getZTIConfig() - expect(config).toBeNull() + expect(config).toBeNull() + }) }) test('uses legacy env vars when USE_ZTI=false', () => { @@ -275,8 +281,8 @@ describe('ZTI flag behavior', () => { cert: Buffer.from('-----BEGIN CERTIFICATE-----\nlegacy\n-----END CERTIFICATE-----').toString('base64'), key: Buffer.from('-----BEGIN PRIVATE KEY-----\nlegacy\n-----END PRIVATE KEY-----').toString('base64') } - delete require.cache[require.resolve('../lib/utils')] + // Legacy path doesn't involve ZTI state, just reads from cds.env const { getCredsForCaaSMtls } = require('../lib/zti') const creds = getCredsForCaaSMtls() @@ -292,28 +298,27 @@ describe('ZTI flag behavior', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_ZTI_VCAP) process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR = svidDir - delete require.cache[require.resolve('../lib/utils')] // Create SVID files fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\nzti-cert\n-----END CERTIFICATE-----') fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\nzti-key\n-----END PRIVATE KEY-----') fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - const { augmentCaaSCreds } = require('../lib/utils') - delete require.cache[require.resolve('../lib/zti')] - const { initializeZTI, _resetZTIState } = require('../lib/zti') - _resetZTIState() + await jest.isolateModulesAsync(async () => { + const { augmentCaaSCreds } = require('../lib/utils') + const { initializeZTI } = require('../lib/zti') - await initializeZTI() + await initializeZTI() - const credentials = { - otlp: { http: 'https://caas.example.com/otlp' } - } - augmentCaaSCreds(credentials) + const credentials = { + otlp: { http: 'https://caas.example.com/otlp' } + } + augmentCaaSCreds(credentials) - expect(credentials.httpAgentOptions).toBeDefined() - expect(credentials.httpAgentOptions.cert).toContain('zti-cert') - expect(credentials.httpAgentOptions.key).toContain('zti-key') + expect(credentials.httpAgentOptions).toBeDefined() + expect(credentials.httpAgentOptions.cert).toContain('zti-cert') + expect(credentials.httpAgentOptions.key).toContain('zti-key') + }) // Cleanup delete process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR @@ -327,8 +332,8 @@ describe('ZTI flag behavior', () => { cert: Buffer.from('-----BEGIN CERTIFICATE-----\nlegacy-cert\n-----END CERTIFICATE-----').toString('base64'), key: Buffer.from('-----BEGIN PRIVATE KEY-----\nlegacy-key\n-----END PRIVATE KEY-----').toString('base64') } - delete require.cache[require.resolve('../lib/utils')] + // Legacy path doesn't involve ZTI state const { augmentCaaSCreds } = require('../lib/utils') const credentials = { diff --git a/test/tracing-zti.test.js b/test/tracing-zti.test.js index b40cf906..1ef00f1b 100644 --- a/test/tracing-zti.test.js +++ b/test/tracing-zti.test.js @@ -210,21 +210,22 @@ describe('Tracing with ZTI integration', () => { test('ZTI initialization waits for SVID files with retry', async () => { // Don't create files immediately - test retry logic - const { initializeZTI, _resetZTIState } = require('../lib/zti') - _resetZTIState() + await jest.isolateModulesAsync(async () => { + const { initializeZTI } = require('../lib/zti') - // Start initialization (files don't exist yet) - const initPromise = initializeZTI() + // Start initialization (files don't exist yet) + const initPromise = initializeZTI() - // Wait 1 second, then create files (should succeed on retry) - setTimeout(() => { - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - }, 1000) + // Wait 1 second, then create files (should succeed on retry) + setTimeout(() => { + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') + }, 1000) - // Should eventually succeed (within retry timeout of 60 seconds) - await expect(initPromise).resolves.not.toThrow() + // Should eventually succeed (within retry timeout of 60 seconds) + await expect(initPromise).resolves.not.toThrow() + }) }, 65000) // Test timeout > retry timeout test('createTracerProvider and addTracingExporter work together', async () => { @@ -318,13 +319,10 @@ describe('Tracing with ZTI integration', () => { } // Verify ZTI is needed - delete require.cache[require.resolve('../lib/zti')] - const { needsZTIWait, _resetZTIState } = require('../lib/zti') - _resetZTIState() + const { needsZTIWait } = require('../lib/zti') expect(needsZTIWait()).toBe(true) // Run setup - delete require.cache[require.resolve('../lib/index')] const setup = require('../lib/index') await setup() From b7fcd138a0f8df3a369e767f94d2378cbcfa1803 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 17:00:48 +0200 Subject: [PATCH 60/67] try out synchronous ztis --- cds-plugin.js | 7 +- lib/exporter/LazyExporter.js | 109 ++++++++++++++ lib/index.js | 74 ++-------- lib/logging/index.js | 26 ++-- lib/metrics/index.js | 24 +++- lib/tracing/index.js | 112 +++------------ lib/utils.js | 3 +- lib/zti.js | 135 ++++++----------- test/caas.test.js | 201 +++++++++++++++++++++----- test/tracing-zti.test.js | 271 ++++++++++------------------------- 10 files changed, 465 insertions(+), 497 deletions(-) create mode 100644 lib/exporter/LazyExporter.js diff --git a/cds-plugin.js b/cds-plugin.js index 1bf0bce5..cab638c1 100644 --- a/cds-plugin.js +++ b/cds-plugin.js @@ -12,8 +12,7 @@ if (!!process.env.NO_TELEMETRY && process.env.NO_TELEMETRY !== 'false') return - // Setup is async to support ZTI initialization (waits for SVID files) - require('./lib')().catch(err => { - cds.log('telemetry')._error && cds.log('telemetry').error('Telemetry setup failed:', err) - }) + // Sync setup - for CaaS with ZTI, exporters use LazyExporter to buffer + // telemetry until mTLS credentials become available + require('./lib')() })() diff --git a/lib/exporter/LazyExporter.js b/lib/exporter/LazyExporter.js new file mode 100644 index 00000000..ce21e936 --- /dev/null +++ b/lib/exporter/LazyExporter.js @@ -0,0 +1,109 @@ +const cds = require('@sap/cds') + +/** + * Creates a lazy wrapper for any OTLP exporter. + * Buffers data until mTLS credentials are available. + * + * This is used for CaaS (Collector as a Service) with ZTI (Zero Trust Identity) + * where SPIRE sidecar provisions SVID files in parallel with app startup - + * they may not exist when the app starts. + * + * @param {Function} createExporter - Factory that creates the real exporter (may throw if creds not ready) + * @param {Object} options - { maxBufferSize: number } + * @returns {Object} Exporter-like object with export(), shutdown(), forceFlush() methods + */ +function createLazyExporter(createExporter, options = {}) { + const LOG = cds.log('telemetry') + + let _exporter = null + let _buffer = [] + let _createFailed = false + const _maxBufferSize = options.maxBufferSize || 1000 + + function _tryCreateExporter() { + if (_exporter) return _exporter + if (_createFailed) return null // Don't retry after permanent failure + + try { + _exporter = createExporter() + LOG._debug && LOG.debug('Lazy exporter: real exporter created successfully') + return _exporter + } catch (err) { + // Check if this is a "credentials not ready" error vs a permanent failure + if (err.message?.includes('SVID') || err.code === 'ENOENT') { + // Credentials not ready yet - will retry on next export + LOG._debug && LOG.debug('Lazy exporter: credentials not ready yet, buffering') + return null + } + // Permanent failure - don't retry + LOG._warn && LOG.warn('Lazy exporter: permanent failure creating exporter:', err.message) + _createFailed = true + return null + } + } + + function _flushBuffer() { + if (_buffer.length === 0 || !_exporter) return + + const buffered = _buffer + _buffer = [] + LOG._debug && LOG.debug(`Lazy exporter: flushing ${buffered.length} buffered items`) + + // Best effort flush - don't wait for callback + // For traces/logs (arrays), we need to flatten + // For metrics (ResourceMetrics objects), export each separately + for (const item of buffered) { + if (Array.isArray(item)) { + _exporter.export(item, () => {}) + } else { + _exporter.export(item, () => {}) + } + } + } + + return { + export(items, resultCallback) { + _tryCreateExporter() + + if (_exporter) { + _flushBuffer() + _exporter.export(items, resultCallback) + } else { + // Buffer items + if (_buffer.length >= _maxBufferSize) { + // Drop oldest to make room + _buffer.shift() + LOG._debug && LOG.debug('Lazy exporter: buffer full, dropping oldest item') + } + _buffer.push(items) + // Report success - we buffered it + resultCallback({ code: 0 }) + } + }, + + shutdown() { + _buffer = [] + return _exporter?.shutdown() ?? Promise.resolve() + }, + + forceFlush() { + _tryCreateExporter() + if (_exporter) { + _flushBuffer() + return _exporter.forceFlush?.() ?? Promise.resolve() + } + return Promise.resolve() + }, + + // For testing + _getBufferSize() { + return _buffer.length + }, + + _isExporterCreated() { + return _exporter !== null + } + } +} + +module.exports = { createLazyExporter } diff --git a/lib/index.js b/lib/index.js index 23872abc..162e0cfb 100644 --- a/lib/index.js +++ b/lib/index.js @@ -14,7 +14,6 @@ const tracing = require('./tracing') const metrics = require('./metrics') const logging = require('./logging') const { getDiagLogLevel, getResource, hasDependency, _require } = require('./utils') -const { initializeZTI, needsZTIWait } = require('./zti') function _getInstrumentations() { const _instrumentations = cds.env.requires.telemetry.instrumentations @@ -90,72 +89,29 @@ function _getInstrumentations() { return instrumentations } -async function setup_standalone() { +function setup_standalone() { diag.setLogger(cds.log('telemetry'), getDiagLogLevel()) const resource = getResource() require('./tracing/cds')() require('./tracing/cloud_sdk')() - const needsZTI = needsZTIWait() - - if (needsZTI) { - // ZTI path: Register TracerProvider early so HTTP instrumentation gets a real tracer, - // then wait for credentials before creating exporters - const tracerProvider = tracing.createTracerProvider(resource) - const instrumentations = _getInstrumentations() - - // First registration: TracerProvider only - this ensures HTTP instrumentation doesn't cache NoopTracer - registerInstrumentations({ - tracerProvider, - meterProvider: undefined, - loggerProvider: undefined, - instrumentations - }) - - try { - await initializeZTI() - } catch (err) { - LOG._error && LOG.error('Failed to initialize ZTI:', err) - } - - // Create metrics and logging providers AFTER ZTI credentials are ready - const meterProvider = metrics(resource) - const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined - - // Update instrumentations with the real providers for metrics and logging - for (const instrumentation of instrumentations) { - if (meterProvider && instrumentation.setMeterProvider) { - instrumentation.setMeterProvider(meterProvider) - } - if (loggerProvider && instrumentation.setLoggerProvider) { - instrumentation.setLoggerProvider(loggerProvider) - } - } - - tracing.addTracingExporter(tracerProvider) - } else { - // Standard path: Create everything at once - const tracerProvider = tracing(resource) - const meterProvider = metrics(resource) - const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined - - registerInstrumentations({ - tracerProvider, - meterProvider, - loggerProvider, - instrumentations: _getInstrumentations() - }) - } + // Single code path for all scenarios including CaaS with ZTI. + // For CaaS, the exporters use LazyExporter which buffers telemetry + // until mTLS credentials become available. + const tracerProvider = tracing(resource) + const meterProvider = metrics(resource) + const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined + + registerInstrumentations({ + tracerProvider, + meterProvider, + loggerProvider, + instrumentations: _getInstrumentations() + }) } -async function setup_with_calm() { - try { - await initializeZTI() - } catch (err) { - LOG._error && LOG.error('Failed to initialize ZTI:', err) - } - +function setup_with_calm() { tracing() metrics() if (cds.env.requires.telemetry.logging) logging() diff --git a/lib/logging/index.js b/lib/logging/index.js index c6dffc05..f1886bae 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -54,12 +54,24 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') - augmentCaaSCreds(credentials) - // Append /v1/logs to base URL (OTLP exporter expects full URL when config.url is provided) - config.url = credentials.baseUrl + '/v1/logs' - if (credentials.httpAgentOptions) { - config.httpAgentOptions = credentials.httpAgentOptions - } + + const { createLazyExporter } = require('../exporter/LazyExporter') + const lazyExporter = createLazyExporter(() => { + // This runs lazily on first export - may return null if credentials not ready + augmentCaaSCreds(credentials) + if (!credentials.httpAgentOptions) { + // Credentials not ready yet (ZTI SVID files don't exist) + throw new Error('SVID credentials not ready') + } + const exporterConfig = { + ...config, + url: credentials.baseUrl + '/v1/logs', + httpAgentOptions: credentials.httpAgentOptions + } + return new loggingExporterModule[loggingExporter.class](exporterConfig) + }) + LOG._debug && LOG.debug('Using lazy logs exporter for CaaS') + return lazyExporter } const exporter = new loggingExporterModule[loggingExporter.class](config) @@ -126,7 +138,6 @@ module.exports = resource => { // setup logs interception via cds.log.format // Must be done AFTER LoggerProvider is set globally. - // In ZTI flow, 'served' may have already fired, so we also call immediately. let _logInterceptionSetup = false const setupLogInterception = () => { if (_logInterceptionSetup) return @@ -180,7 +191,6 @@ module.exports = resource => { } cds.on('served', setupLogInterception) - setupLogInterception() return loggerProvider } diff --git a/lib/metrics/index.js b/lib/metrics/index.js index 4cb85dbf..e1528d4a 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -76,12 +76,24 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') - augmentCaaSCreds(credentials) - // Append /v1/metrics to base URL (OTLP exporter expects full URL when config.url is provided) - config.url = credentials.baseUrl + '/v1/metrics' - if (credentials.httpAgentOptions) { - config.httpAgentOptions = credentials.httpAgentOptions - } + + const { createLazyExporter } = require('../exporter/LazyExporter') + const lazyExporter = createLazyExporter(() => { + // This runs lazily on first export - may return null if credentials not ready + augmentCaaSCreds(credentials) + if (!credentials.httpAgentOptions) { + // Credentials not ready yet (ZTI SVID files don't exist) + throw new Error('SVID credentials not ready') + } + const exporterConfig = { + ...config, + url: credentials.baseUrl + '/v1/metrics', + httpAgentOptions: credentials.httpAgentOptions + } + return new metricsExporterModule[metricsExporter.class](exporterConfig) + }) + LOG._debug && LOG.debug('Using lazy metrics exporter for CaaS') + return lazyExporter } const exporter = new metricsExporterModule[metricsExporter.class](config) diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 6ceefc9e..34952e44 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -16,45 +16,6 @@ const { _require } = require('../utils') -/** - * Span processor that buffers spans until a delegate is set. - * Used for ZTI flow where TracerProvider must be registered early. - */ -class BufferingSpanProcessor { - constructor() { - this._buffer = [] - this._delegate = null - this._maxBufferSize = 2048 - } - - onStart(span, context) { - if (this._delegate) this._delegate.onStart(span, context) - } - - onEnd(span) { - if (this._delegate) { - this._delegate.onEnd(span) - } else if (this._buffer.length < this._maxBufferSize) { - this._buffer.push(span) - } - } - - setDelegate(processor) { - this._delegate = processor - for (const span of this._buffer) processor.onEnd(span) - this._buffer = [] - } - - forceFlush() { - return this._delegate ? this._delegate.forceFlush() : Promise.resolve() - } - - shutdown() { - this._buffer = [] - return this._delegate ? this._delegate.shutdown() : Promise.resolve() - } -} - // Common setup: SAP Passport clearing for HANA function _setupSAPPassport() { if (process.env.SAP_PASSPORT) { @@ -198,14 +159,27 @@ function _getExporter() { config.credentials ??= credentials.credentials } + // For CaaS with ZTI, use lazy exporter to handle credentials not being ready yet if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') - augmentCaaSCreds(credentials) - // Append /v1/traces to base URL (OTLP exporter expects full URL when config.url is provided) - config.url = credentials.baseUrl + '/v1/traces' - if (credentials.httpAgentOptions) { - config.httpAgentOptions = credentials.httpAgentOptions - } + + const { createLazyExporter } = require('../exporter/LazyExporter') + const lazyExporter = createLazyExporter(() => { + // This runs lazily on first export - may return null if credentials not ready + augmentCaaSCreds(credentials) + if (!credentials.httpAgentOptions) { + // Credentials not ready yet (ZTI SVID files don't exist) + throw new Error('SVID credentials not ready') + } + const exporterConfig = { + ...config, + url: credentials.baseUrl + '/v1/traces', + httpAgentOptions: credentials.httpAgentOptions + } + return new tracingExporterModule[tracingExporter.class](exporterConfig) + }) + LOG._debug && LOG.debug('Using lazy trace exporter for CaaS') + return lazyExporter } const exporter = new tracingExporterModule[tracingExporter.class](config) @@ -215,49 +189,9 @@ function _getExporter() { } /** - * Create TracerProvider early (for ZTI flow). - * Uses BufferingSpanProcessor until real exporter is ready. - */ -function createTracerProvider(resource) { - if (!cds.env.requires.telemetry.tracing?.exporter) return - - _setupSAPPassport() - - resource = _mergeResource(resource) - const bufferingProcessor = new BufferingSpanProcessor() - - const tracerProvider = new NodeTracerProvider({ - resource, - sampler: _getSampler(), - spanProcessors: [bufferingProcessor] - }) - tracerProvider.register({ propagator: _getPropagator() }) - tracerProvider._bufferingProcessor = bufferingProcessor - - return tracerProvider -} - -/** - * Add exporter to TracerProvider (for ZTI flow, after credentials ready). - */ -function addTracingExporter(tracerProvider) { - if (!tracerProvider) return - - if (_hasDynatraceOneAgent()) { - LOG._info && LOG.info('Dynatrace OneAgent detected, disabling trace exporter') - return - } - - const exporter = _getExporter() - const processor = _createSpanProcessor(exporter) - - if (tracerProvider._bufferingProcessor) { - tracerProvider._bufferingProcessor.setDelegate(processor) - } -} - -/** - * Standard setup - creates TracerProvider with exporter in one call. + * Setup tracing - creates TracerProvider with exporter. + * For CaaS with ZTI, the exporter is wrapped with LazyExporter + * to handle credentials not being ready at startup. */ function setupTracing(resource) { if (!cds.env.requires.telemetry.tracing?.exporter) return @@ -293,5 +227,3 @@ function setupTracing(resource) { } module.exports = setupTracing -module.exports.createTracerProvider = createTracerProvider -module.exports.addTracingExporter = addTracingExporter diff --git a/lib/utils.js b/lib/utils.js index ed341f1e..08488094 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -2,7 +2,7 @@ const cds = require('@sap/cds') const LOG = cds.log('telemetry') const fs = require('fs') -const { initializeZTI, getZTIConfig, getCredsForCaaSMtls } = require('./zti') +const { getZTIConfig, getCredsForCaaSMtls } = require('./zti') const { DiagLogLevel } = require('@opentelemetry/api') const { hrTimeToMilliseconds, getStringFromEnv } = require('@opentelemetry/core') @@ -256,7 +256,6 @@ module.exports = { getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, - initializeZTI, getZTIConfig, getCredsForCaaSMtls, hasDependency, diff --git a/lib/zti.js b/lib/zti.js index bce66fbd..797e21a3 100644 --- a/lib/zti.js +++ b/lib/zti.js @@ -2,14 +2,26 @@ const cds = require('@sap/cds') const LOG = cds.log('telemetry') const fs = require('fs') -// Retry settings for waiting on SVID files during startup -const ZTI_RETRY_INTERVAL_MS = 2000 -const ZTI_MAX_RETRIES = 30 // 60 seconds total - // SVID paths and cached credentials (initialized once, reused across requests) let _paths = null // { cert, key, bundle } - file paths let _cached = null // { cert, key, bundle, mtime } - cached credentials +/** + * Check if SVID files exist (sync) + * @returns {boolean} + */ +function _svidFilesExist() { + if (!_paths) return false + try { + fs.statSync(_paths.cert) + fs.statSync(_paths.key) + fs.statSync(_paths.bundle) + return true + } catch { + return false + } +} + /** * Get SVID certificate credentials, reloading if mtime changed. * Manages mtime-based caching for automatic certificate rotation. @@ -78,64 +90,18 @@ function getZTIConfig() { } /** - * Initialize SVID file paths and block until files exist - * Follows ZTI blueprint pattern: block startup until SVID files ready + * Initialize SVID file paths (does NOT wait for files to exist) + * Called internally when credentials are first requested. * @param {string} svidDir - Directory containing SVID files * @param {string} svidName - SVID file base name - * @returns {Promise} */ -async function _initializeSVIDFiles(svidDir, svidName) { +function _initializePaths(svidDir, svidName) { + if (_paths) return // Already initialized _paths = { cert: `${svidDir}/${svidName}.svid.pem`, key: `${svidDir}/${svidName}.svid.key`, bundle: `${svidDir}/${svidName}.bundle.pem` } - - let attempt = 0 - while (true) { - try { - _getSVIDCertificate() - return - } catch (err) { - if (++attempt >= ZTI_MAX_RETRIES) { - throw new Error(`ZTI: SVID files not available at ${_paths.cert} after ${ZTI_MAX_RETRIES * ZTI_RETRY_INTERVAL_MS / 1000}s`, { cause: err }) - } - await new Promise(resolve => setTimeout(resolve, ZTI_RETRY_INTERVAL_MS)) - } - } -} - -// Global ZTI initialization state -let _ztiInitialized = false -let _ztiInitPromise = null - -/** - * Initialize ZTI source (async) - called during telemetry plugin initialization - * @returns {Promise} - */ -async function initializeZTI() { - const useZTI = process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI !== 'false' - if (!useZTI) return - - const ztiConfig = getZTIConfig() - if (!ztiConfig) return - - // Avoid double initialization - if (_ztiInitPromise) return _ztiInitPromise - - _ztiInitPromise = (async () => { - try { - LOG._info && LOG.info('ZTI: Initializing SVID file source for CaaS mTLS...') - await _initializeSVIDFiles(ztiConfig.svidDir, ztiConfig.svidName) - _ztiInitialized = true - } catch (err) { - LOG._error && LOG.error('ZTI: Failed to initialize SVID file source:', err) - _ztiInitPromise = null // Allow retry - throw err - } - })() - - return _ztiInitPromise } /** @@ -143,6 +109,10 @@ async function initializeZTI() { * Priority: * 1. ZTI file-based (default, automatic rotation) - if USE_ZTI=true (default) * 2. Env var base64 (legacy, manual rotation) - if USE_ZTI=false + * + * Returns null if ZTI is configured but SVID files don't exist yet. + * The LazyExporter will buffer telemetry until credentials become available. + * * @returns {{ cert: string, key: string } | null} */ function getCredsForCaaSMtls() { @@ -153,21 +123,21 @@ function getCredsForCaaSMtls() { // ZTI approach (default) const ztiConfig = getZTIConfig() if (ztiConfig) { - // ZTI binding found - use it - if (_ztiInitialized) { - // ZTI initialized - get cached or reloaded credentials - try { - const { cert, key } = _getSVIDCertificate() - return { cert, key } - } catch (err) { - LOG._error && LOG.error('ZTI: Failed to load SVID credentials:', err) - return null - } - } else { - // ZTI not yet initialized - warn and fall through - LOG._warn && LOG.warn( - 'ZTI: SVID file source not initialized. Ensure initializeZTI() is called during startup.' - ) + // ZTI binding found - initialize paths if needed + _initializePaths(ztiConfig.svidDir, ztiConfig.svidName) + + // Check if SVID files exist + if (!_svidFilesExist()) { + LOG._debug && LOG.debug('ZTI: SVID files not ready yet, will buffer telemetry') + return null + } + + // Files exist - get credentials (with mtime-based caching for rotation) + try { + const { cert, key } = _getSVIDCertificate() + return { cert, key } + } catch (err) { + LOG._error && LOG.error('ZTI: Failed to load SVID credentials:', err) return null } } @@ -187,32 +157,7 @@ function getCredsForCaaSMtls() { return null } -/** - * Check if ZTI wait is needed for CaaS telemetry - * @returns {boolean} - */ -function needsZTIWait() { - if (process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI === 'false') return false - - const kind = cds.env.requires.telemetry?.kind - if (!kind?.match(/to-caas$/)) return false - - const vcapServices = process.env.VCAP_SERVICES && JSON.parse(process.env.VCAP_SERVICES) - if (!vcapServices) return false - - for (const [key, services] of Object.entries(vcapServices)) { - if (key === 'zero-trust-identity') return true - for (const svc of services) { - if (svc.tags?.includes('zero-trust-identity') || svc.label === 'zero-trust-identity') return true - } - } - - return false -} - module.exports = { - initializeZTI, getZTIConfig, - getCredsForCaaSMtls, - needsZTIWait + getCredsForCaaSMtls } diff --git a/test/caas.test.js b/test/caas.test.js index 8aafbdde..145ca846 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -162,19 +162,15 @@ describe('ZTI SVID File Loading', () => { delete process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI }) - test('loads SVID files on first call', async () => { + test('loads SVID files when they exist', () => { // Create SVID files fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') let creds - // jest.isolateModulesAsync gives each test a fresh zti module with reset internal state - // (cached credentials, init promise, file paths). Without isolation, state from previous - // tests would leak and cause failures when tests run in sequence. - await jest.isolateModulesAsync(async () => { - const { initializeZTI, getCredsForCaaSMtls } = require('../lib/zti') - await initializeZTI() + jest.isolateModules(() => { + const { getCredsForCaaSMtls } = require('../lib/zti') creds = getCredsForCaaSMtls() }) @@ -183,6 +179,18 @@ describe('ZTI SVID File Loading', () => { expect(creds.key).toContain('BEGIN PRIVATE KEY') }) + test('returns null when SVID files do not exist', () => { + // Don't create SVID files + + let creds + jest.isolateModules(() => { + const { getCredsForCaaSMtls } = require('../lib/zti') + creds = getCredsForCaaSMtls() + }) + + expect(creds).toBeNull() + }) + test('reloads when mtime changes', async () => { const certPath = path.join(svidDir, 'test-svid.svid.pem') const keyPath = path.join(svidDir, 'test-svid.svid.key') @@ -195,13 +203,12 @@ describe('ZTI SVID File Loading', () => { let creds1, creds2 await jest.isolateModulesAsync(async () => { - const { initializeZTI, getCredsForCaaSMtls } = require('../lib/zti') + const { getCredsForCaaSMtls } = require('../lib/zti') - await initializeZTI() creds1 = getCredsForCaaSMtls() - // Wait to ensure mtime changes - await new Promise(resolve => setTimeout(resolve, 10)) + // Wait to ensure mtime changes (filesystem mtime resolution can be ~1s on some systems) + await new Promise(resolve => setTimeout(resolve, 50)) // Update files (simulating ZTI rotation) fs.writeFileSync(certPath, '-----BEGIN CERTIFICATE-----\nv2\n-----END CERTIFICATE-----') @@ -214,28 +221,6 @@ describe('ZTI SVID File Loading', () => { expect(creds2.cert).toContain('v2') expect(creds2.key).toContain('v2') }) - - test('retries if files not ready on initialization', async () => { - const certPath = path.join(svidDir, 'test-svid.svid.pem') - const keyPath = path.join(svidDir, 'test-svid.svid.key') - const bundlePath = path.join(svidDir, 'test-svid.bundle.pem') - - await jest.isolateModulesAsync(async () => { - const { initializeZTI } = require('../lib/zti') - - // Start initialization (files don't exist yet) - const initPromise = initializeZTI() - - // Wait 2.5 seconds, then create files (should succeed on 2nd retry) - await new Promise(resolve => setTimeout(resolve, 2500)) - fs.writeFileSync(certPath, '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') - fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') - fs.writeFileSync(bundlePath, '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') - - // Should complete successfully - await expect(initPromise).resolves.toBeUndefined() - }) - }, 10000) }) describe('ZTI flag behavior', () => { @@ -291,7 +276,7 @@ describe('ZTI flag behavior', () => { expect(Buffer.from(creds.cert, 'base64').toString()).toContain('legacy') }) - test('augmentCaaSCreds handles PEM format from ZTI', async () => { + test('augmentCaaSCreds handles PEM format from ZTI', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'zti-test-')) const svidDir = path.join(tmpDir, 'spire-svids') fs.mkdirSync(svidDir) @@ -304,11 +289,8 @@ describe('ZTI flag behavior', () => { fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\nzti-key\n-----END PRIVATE KEY-----') fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - await jest.isolateModulesAsync(async () => { + jest.isolateModules(() => { const { augmentCaaSCreds } = require('../lib/utils') - const { initializeZTI } = require('../lib/zti') - - await initializeZTI() const credentials = { otlp: { http: 'https://caas.example.com/otlp' } @@ -346,3 +328,146 @@ describe('ZTI flag behavior', () => { expect(credentials.httpAgentOptions.key).toContain('legacy-key') }) }) + +describe('LazyExporter', () => { + beforeEach(() => { + delete require.cache[require.resolve('../lib/exporter/LazyExporter')] + }) + + test('buffers items when exporter creation fails', () => { + const { createLazyExporter } = require('../lib/exporter/LazyExporter') + + let createCalled = 0 + const lazyExporter = createLazyExporter(() => { + createCalled++ + throw new Error('SVID credentials not ready') + }) + + const items = [{ name: 'span1' }, { name: 'span2' }] + let callbackResult + lazyExporter.export(items, result => { callbackResult = result }) + + expect(callbackResult).toEqual({ code: 0 }) // SUCCESS + expect(lazyExporter._getBufferSize()).toBe(1) // Items buffered as single array + expect(lazyExporter._isExporterCreated()).toBe(false) + expect(createCalled).toBe(1) + }) + + test('flushes buffer when exporter becomes ready', () => { + const { createLazyExporter } = require('../lib/exporter/LazyExporter') + + let ready = false + const exportedItems = [] + const mockExporter = { + export: (items, cb) => { + exportedItems.push(items) + cb({ code: 0 }) + } + } + + const lazyExporter = createLazyExporter(() => { + if (!ready) throw new Error('SVID credentials not ready') + return mockExporter + }) + + // First export - buffers + lazyExporter.export([{ name: 'span1' }], () => {}) + expect(lazyExporter._getBufferSize()).toBe(1) + + // Make exporter ready + ready = true + + // Second export - should flush buffer and export new items + lazyExporter.export([{ name: 'span2' }], () => {}) + + expect(lazyExporter._isExporterCreated()).toBe(true) + expect(lazyExporter._getBufferSize()).toBe(0) + expect(exportedItems).toHaveLength(2) // buffered + new + expect(exportedItems[0]).toEqual([{ name: 'span1' }]) + expect(exportedItems[1]).toEqual([{ name: 'span2' }]) + }) + + test('respects maxBufferSize', () => { + const { createLazyExporter } = require('../lib/exporter/LazyExporter') + + const lazyExporter = createLazyExporter( + () => { throw new Error('SVID credentials not ready') }, + { maxBufferSize: 2 } + ) + + // Fill buffer beyond max + lazyExporter.export([{ name: 'span1' }], () => {}) + lazyExporter.export([{ name: 'span2' }], () => {}) + lazyExporter.export([{ name: 'span3' }], () => {}) + + // Should only have 2 items (oldest dropped) + expect(lazyExporter._getBufferSize()).toBe(2) + }) + + test('shutdown clears buffer', async () => { + const { createLazyExporter } = require('../lib/exporter/LazyExporter') + + const lazyExporter = createLazyExporter(() => { + throw new Error('SVID credentials not ready') + }) + + lazyExporter.export([{ name: 'span1' }], () => {}) + expect(lazyExporter._getBufferSize()).toBe(1) + + await lazyExporter.shutdown() + expect(lazyExporter._getBufferSize()).toBe(0) + }) + + test('forceFlush creates exporter and flushes if ready', async () => { + const { createLazyExporter } = require('../lib/exporter/LazyExporter') + + let ready = false + const exportedItems = [] + const mockExporter = { + export: (items, cb) => { + exportedItems.push(items) + cb({ code: 0 }) + }, + forceFlush: jest.fn().mockResolvedValue() + } + + const lazyExporter = createLazyExporter(() => { + if (!ready) throw new Error('SVID credentials not ready') + return mockExporter + }) + + // Buffer some items + lazyExporter.export([{ name: 'span1' }], () => {}) + + // forceFlush when not ready - should not throw + await lazyExporter.forceFlush() + expect(lazyExporter._getBufferSize()).toBe(1) // Still buffered + + // Make ready and forceFlush + ready = true + await lazyExporter.forceFlush() + + expect(lazyExporter._isExporterCreated()).toBe(true) + expect(lazyExporter._getBufferSize()).toBe(0) + expect(exportedItems).toHaveLength(1) + expect(mockExporter.forceFlush).toHaveBeenCalled() + }) + + test('does not retry after permanent failure', () => { + const { createLazyExporter } = require('../lib/exporter/LazyExporter') + + let createCalled = 0 + const lazyExporter = createLazyExporter(() => { + createCalled++ + throw new Error('Permanent configuration error') + }) + + // First export - tries to create + lazyExporter.export([{ name: 'span1' }], () => {}) + expect(createCalled).toBe(1) + + // Second export - should not retry (permanent failure) + lazyExporter.export([{ name: 'span2' }], () => {}) + expect(createCalled).toBe(1) // Still 1, not retried + }) +}) diff --git a/test/tracing-zti.test.js b/test/tracing-zti.test.js index 1ef00f1b..0369a2df 100644 --- a/test/tracing-zti.test.js +++ b/test/tracing-zti.test.js @@ -1,13 +1,9 @@ /** - * Integration tests for ZTI + Tracing + * Integration tests for ZTI + Tracing with LazyExporter * - * Tests the fix for: "HTTP instrumentation caches NoopTracer if TracerProvider isn't registered early" - * - * The problem: When ZTI credentials take ~10 seconds to load, HTTP instrumentation would cache - * a NoopTracer before the real TracerProvider was registered, causing all traces to be lost. - * - * The solution: Register TracerProvider early with BufferingSpanProcessor, then add the real - * exporter after ZTI credentials are ready. + * With the lazy exporter refactor, the complexity of waiting for ZTI credentials + * is moved into the exporter layer. The app starts immediately with sync initialization, + * and telemetry is buffered until SVID files become available. */ const cds = require('@sap/cds') @@ -81,6 +77,7 @@ describe('Tracing with ZTI integration', () => { delete require.cache[require.resolve('../lib/tracing')] delete require.cache[require.resolve('../lib/metrics')] delete require.cache[require.resolve('../lib/logging')] + delete require.cache[require.resolve('../lib/exporter/LazyExporter')] }) afterEach(() => { @@ -95,25 +92,9 @@ describe('Tracing with ZTI integration', () => { delete process.env.cds_requires_telemetry_tracing_exporter }) - test('needsZTIWait() detects ZTI scenario correctly', () => { - const { needsZTIWait } = require('../lib/zti') - - expect(needsZTIWait()).toBe(true) - - // Should return false if USE_ZTI=false - process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI = 'false' - delete require.cache[require.resolve('../lib/zti')] - const { needsZTIWait: needsZTIWait2 } = require('../lib/zti') - expect(needsZTIWait2()).toBe(false) - }) + test('telemetry setup completes synchronously even without SVID files', () => { + // SVID files don't exist yet - but setup should still complete (sync) - test('BufferingSpanProcessor buffers spans until delegate is set', async () => { - // Create SVID files immediately (so ZTI succeeds) - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - - // Setup minimal CDS env for tracing cds.env.requires = { telemetry: { kind: 'to-caas', @@ -131,42 +112,27 @@ describe('Tracing with ZTI integration', () => { kind: 'AlwaysOnSampler' }, propagators: [] - } + }, + instrumentations: {} } } - const tracing = require('../lib/tracing') - const { getResource } = require('../lib/utils') - const resource = getResource() - - // Step 1: Create TracerProvider with buffering (simulating ZTI wait) - const tracerProvider = tracing.createTracerProvider(resource) - expect(tracerProvider).toBeDefined() - expect(tracerProvider._bufferingProcessor).toBeDefined() + // Setup should be sync and not throw + const setup = require('../lib/index') + expect(() => setup()).not.toThrow() - // Step 2: Create spans BEFORE exporter is added (they should buffer) + // We should have a real tracer (not NoopTracer) const tracer = trace.getTracer('test') - const span1 = tracer.startSpan('test-span-1') - span1.end() - const span2 = tracer.startSpan('test-span-2') - span2.end() - - // At this point, spans are in the buffer - expect(tracerProvider._bufferingProcessor._buffer.length).toBe(2) - - // Step 3: Add exporter (simulating ZTI ready) - tracing.addTracingExporter(tracerProvider) + expect(tracer).toBeDefined() - // Buffer should now be flushed - expect(tracerProvider._bufferingProcessor._buffer.length).toBe(0) - expect(tracerProvider._bufferingProcessor._delegate).toBeDefined() + const span = tracer.startSpan('test-span') + expect(span.spanContext().traceId).toBeDefined() + expect(span.spanContext().traceId).not.toBe('00000000000000000000000000000000') + span.end() }) - test('TracerProvider is created before HTTP instrumentation in ZTI flow', async () => { - // This test verifies the fix: TracerProvider must be registered BEFORE - // HTTP instrumentation loads, otherwise HTTP caches NoopTracer - - // Create SVID files immediately + test('TracerProvider is registered immediately (no NoopTracer caching)', () => { + // Create SVID files fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') @@ -193,153 +159,22 @@ describe('Tracing with ZTI integration', () => { } } - // Initialize telemetry (this triggers the ZTI flow) + // Setup telemetry const setup = require('../lib/index') - await setup() + setup() // Verify we can get a tracer (not NoopTracer) const tracer = trace.getTracer('test') expect(tracer).toBeDefined() - // Create a span and verify it's a real span + // Create a span and verify it's a real span (NoopTracer returns zeros for traceId) const span = tracer.startSpan('test-span') expect(span.spanContext().traceId).toBeDefined() - expect(span.spanContext().traceId).not.toBe('00000000000000000000000000000000') // NoopTracer returns zeros + expect(span.spanContext().traceId).not.toBe('00000000000000000000000000000000') span.end() }) - test('ZTI initialization waits for SVID files with retry', async () => { - // Don't create files immediately - test retry logic - await jest.isolateModulesAsync(async () => { - const { initializeZTI } = require('../lib/zti') - - // Start initialization (files don't exist yet) - const initPromise = initializeZTI() - - // Wait 1 second, then create files (should succeed on retry) - setTimeout(() => { - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - }, 1000) - - // Should eventually succeed (within retry timeout of 60 seconds) - await expect(initPromise).resolves.not.toThrow() - }) - }, 65000) // Test timeout > retry timeout - - test('createTracerProvider and addTracingExporter work together', async () => { - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - - cds.env.requires = { - telemetry: { - kind: 'to-caas', - credentials: { - otlp: { - http: 'https://caas.example.com/otlp' - } - }, - tracing: { - exporter: { - module: '@opentelemetry/sdk-trace-base', - class: 'InMemorySpanExporter' - }, - sampler: { - kind: 'AlwaysOnSampler' - }, - propagators: [] - } - } - } - - const tracing = require('../lib/tracing') - const { getResource } = require('../lib/utils') - const { initializeZTI } = require('../lib/zti') - const resource = getResource() - - // Simulate ZTI flow - // 1. Create TracerProvider early - const tracerProvider = tracing.createTracerProvider(resource) - expect(tracerProvider).toBeDefined() - - // 2. Wait for ZTI - await initializeZTI() - - // 3. Add exporter - tracing.addTracingExporter(tracerProvider) - - // Verify the buffering processor got a delegate - expect(tracerProvider._bufferingProcessor._delegate).toBeDefined() - }) - - test('ZTI flow updates instrumentations with meterProvider and loggerProvider', async () => { - // This test verifies the fix for: instrumentations not receiving meterProvider/loggerProvider - // when created before ZTI credentials are ready. - // - // The problem: In the ZTI path, registerInstrumentations() was called with - // meterProvider: undefined and loggerProvider: undefined BEFORE those providers - // were created, causing metrics and logs to not be exported. - - // Create SVID files immediately - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - - cds.env.requires = { - telemetry: { - kind: 'to-caas', - credentials: { - otlp: { http: 'https://caas.example.com/otlp' } - }, - tracing: { - exporter: { - module: '@opentelemetry/sdk-trace-base', - class: 'InMemorySpanExporter' - }, - sampler: { kind: 'AlwaysOnSampler' }, - propagators: [] - }, - metrics: { - exporter: { - module: '@opentelemetry/sdk-metrics', - class: 'InMemoryMetricExporter' - }, - config: {} - }, - logging: { - exporter: { - module: '@opentelemetry/sdk-logs', - class: 'InMemoryLogRecordExporter' - } - }, - instrumentations: {} - } - } - - // Verify ZTI is needed - const { needsZTIWait } = require('../lib/zti') - expect(needsZTIWait()).toBe(true) - - // Run setup - const setup = require('../lib/index') - await setup() - - // Verify meterProvider was set globally (this is what the fix ensures) - const { metrics } = require('@opentelemetry/api') - const meterProvider = metrics.getMeterProvider() - expect(meterProvider).toBeDefined() - expect(meterProvider.constructor.name).not.toBe('NoopMeterProvider') - - // Verify loggerProvider was set globally - const { logs } = require('@opentelemetry/api-logs') - const loggerProvider = logs.getLoggerProvider() - expect(loggerProvider).toBeDefined() - expect(loggerProvider.constructor.name).not.toBe('NoopLoggerProvider') - }) - - test('standard flow works without ZTI', async () => { + test('standard flow works without ZTI binding', () => { // Remove ZTI from VCAP_SERVICES process.env.VCAP_SERVICES = JSON.stringify({ 'caas-service': [{ @@ -379,18 +214,64 @@ describe('Tracing with ZTI integration', () => { } delete require.cache[require.resolve('../lib/zti')] - const { needsZTIWait } = require('../lib/zti') - - // Should NOT need ZTI wait (no ZTI binding) - expect(needsZTIWait()).toBe(false) // Standard setup should work const setup = require('../lib/index') - await setup() + setup() const tracer = trace.getTracer('test') const span = tracer.startSpan('test-span') expect(span.spanContext().traceId).toBeDefined() span.end() }) + + test('getCredsForCaaSMtls returns null when SVID files not ready', () => { + // Don't create SVID files + + jest.isolateModules(() => { + const { getCredsForCaaSMtls } = require('../lib/zti') + const creds = getCredsForCaaSMtls() + + // Should return null (not throw) when files don't exist + expect(creds).toBeNull() + }) + }) + + test('getCredsForCaaSMtls returns credentials when SVID files exist', () => { + // Create SVID files + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') + + jest.isolateModules(() => { + const { getCredsForCaaSMtls } = require('../lib/zti') + const creds = getCredsForCaaSMtls() + + expect(creds).toBeDefined() + expect(creds.cert).toContain('BEGIN CERTIFICATE') + expect(creds.key).toContain('BEGIN PRIVATE KEY') + }) + }) + + test('legacy x509 credentials work when USE_ZTI=false', () => { + process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI = 'false' + + // Need to set up cds.env inside isolateModules since cds is re-required there + delete require.cache[require.resolve('../lib/zti')] + + cds.env.requires = { + telemetry: { + x509: { + cert: Buffer.from('-----BEGIN CERTIFICATE-----\nlegacy\n-----END CERTIFICATE-----').toString('base64'), + key: Buffer.from('-----BEGIN PRIVATE KEY-----\nlegacy\n-----END PRIVATE KEY-----').toString('base64') + } + } + } + + const { getCredsForCaaSMtls } = require('../lib/zti') + const creds = getCredsForCaaSMtls() + + expect(creds).toBeDefined() + expect(Buffer.from(creds.cert, 'base64').toString()).toContain('legacy') + }) }) From e10257fe7ffbd9dadcd472b757a7b462008413f2 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 17:13:37 +0200 Subject: [PATCH 61/67] re-check creds on each export attempt --- lib/logging/index.js | 25 ++++++++++++++++++++----- lib/metrics/index.js | 25 ++++++++++++++++++++----- lib/tracing/index.js | 24 ++++++++++++++++++++---- 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index f1886bae..28972e25 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -3,7 +3,7 @@ const LOG = cds.log('telemetry') const { getStringFromEnv } = require('@opentelemetry/core') -const { getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, _require } = require('../utils') +const { getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, getCredsForCaaSMtls, _require } = require('../utils') const _protocol2module = { grpc: '@opentelemetry/exporter-logs-otlp-grpc', @@ -55,18 +55,33 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') + // Set up base URL once (this part of augmentCaaSCreds is idempotent) + augmentCaaSCreds(credentials) + const { createLazyExporter } = require('../exporter/LazyExporter') const lazyExporter = createLazyExporter(() => { - // This runs lazily on first export - may return null if credentials not ready - augmentCaaSCreds(credentials) - if (!credentials.httpAgentOptions) { + // This runs lazily on each export attempt until credentials are ready + // We need to check for mTLS credentials fresh each time (not rely on _augmented flag) + const mtlsCreds = getCredsForCaaSMtls() + if (!mtlsCreds) { // Credentials not ready yet (ZTI SVID files don't exist) throw new Error('SVID credentials not ready') } + + // Decode credentials (ZTI returns PEM, legacy returns base64) + let cert, key + if (mtlsCreds.cert.startsWith('LS0t') || !mtlsCreds.cert.startsWith('-----BEGIN')) { + cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') + key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') + } else { + cert = mtlsCreds.cert + key = mtlsCreds.key + } + const exporterConfig = { ...config, url: credentials.baseUrl + '/v1/logs', - httpAgentOptions: credentials.httpAgentOptions + httpAgentOptions: { cert, key, keepAlive: true } } return new loggingExporterModule[loggingExporter.class](exporterConfig) }) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index e1528d4a..cdc7639f 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -6,7 +6,7 @@ const { getStringFromEnv } = require('@opentelemetry/core') const { resourceFromAttributes } = require('@opentelemetry/resources') const { AggregationTemporality, MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics') -const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, _require } = require('../utils') +const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, getCredsForCaaSMtls, _require } = require('../utils') const _protocol2module = { grpc: '@opentelemetry/exporter-metrics-otlp-grpc', @@ -77,18 +77,33 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') + // Set up base URL once (this part of augmentCaaSCreds is idempotent) + augmentCaaSCreds(credentials) + const { createLazyExporter } = require('../exporter/LazyExporter') const lazyExporter = createLazyExporter(() => { - // This runs lazily on first export - may return null if credentials not ready - augmentCaaSCreds(credentials) - if (!credentials.httpAgentOptions) { + // This runs lazily on each export attempt until credentials are ready + // We need to check for mTLS credentials fresh each time (not rely on _augmented flag) + const mtlsCreds = getCredsForCaaSMtls() + if (!mtlsCreds) { // Credentials not ready yet (ZTI SVID files don't exist) throw new Error('SVID credentials not ready') } + + // Decode credentials (ZTI returns PEM, legacy returns base64) + let cert, key + if (mtlsCreds.cert.startsWith('LS0t') || !mtlsCreds.cert.startsWith('-----BEGIN')) { + cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') + key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') + } else { + cert = mtlsCreds.cert + key = mtlsCreds.key + } + const exporterConfig = { ...config, url: credentials.baseUrl + '/v1/metrics', - httpAgentOptions: credentials.httpAgentOptions + httpAgentOptions: { cert, key, keepAlive: true } } return new metricsExporterModule[metricsExporter.class](exporterConfig) }) diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 34952e44..bf52beb0 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -12,6 +12,7 @@ const { getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, + getCredsForCaaSMtls, hasDependency, _require } = require('../utils') @@ -163,18 +164,33 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') + // Set up base URL once (this part of augmentCaaSCreds is idempotent) + augmentCaaSCreds(credentials) + const { createLazyExporter } = require('../exporter/LazyExporter') const lazyExporter = createLazyExporter(() => { - // This runs lazily on first export - may return null if credentials not ready - augmentCaaSCreds(credentials) - if (!credentials.httpAgentOptions) { + // This runs lazily on each export attempt until credentials are ready + // We need to check for mTLS credentials fresh each time (not rely on _augmented flag) + const mtlsCreds = getCredsForCaaSMtls() + if (!mtlsCreds) { // Credentials not ready yet (ZTI SVID files don't exist) throw new Error('SVID credentials not ready') } + + // Decode credentials (ZTI returns PEM, legacy returns base64) + let cert, key + if (mtlsCreds.cert.startsWith('LS0t') || !mtlsCreds.cert.startsWith('-----BEGIN')) { + cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') + key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') + } else { + cert = mtlsCreds.cert + key = mtlsCreds.key + } + const exporterConfig = { ...config, url: credentials.baseUrl + '/v1/traces', - httpAgentOptions: credentials.httpAgentOptions + httpAgentOptions: { cert, key, keepAlive: true } } return new tracingExporterModule[tracingExporter.class](exporterConfig) }) From 89b2328c74fa5cc65ffcf0b25aa4e56eeea04c86 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 17:29:07 +0200 Subject: [PATCH 62/67] changelog and readme --- CHANGELOG.md | 2 +- README.md | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 449d82ce..0d03be98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Added -- Support for `telemetry-to-caas` kind for CaaS (Collector as a Service) with automatic mTLS certificate management via Zero Trust Identity Service (ZTI/SPIFFE). Falls back to base64-encoded certificates from environment variables when ZTI is not available. +- Support for `telemetry-to-caas` kind for CaaS (Collector as a Service) with automatic mTLS certificate management and rotation via Zero Trust Identity Service (ZTI/SPIFFE). Falls back to base64-encoded certificates from environment variables when ZTI is not available. ## Version 2.0.1 - 2026-07-03 diff --git a/README.md b/README.md index d1ba0669..05a6889a 100644 --- a/README.md +++ b/README.md @@ -292,7 +292,7 @@ If you are binding your app to SAP Cloud Logging via a [user-provided service in ### `telemetry-to-caas` -Exports traces and metrics to CaaS (Collector as a Service). +Exports traces, metrics, and logs to CaaS (Collector as a Service). CaaS acts as a managed OpenTelemetry Collector that can route telemetry data to downstream backends like SAP Cloud Logging. Use via `cds.requires.telemetry.kind = 'to-caas'`. @@ -300,8 +300,42 @@ Use via `cds.requires.telemetry.kind = 'to-caas'`. Required additional dependencies: - `@opentelemetry/exporter-trace-otlp-proto` - `@opentelemetry/exporter-metrics-otlp-proto` +- `@opentelemetry/exporter-logs-otlp-proto` (if using log export) -CaaS requires mTLS authentication with SAP-signed certificates. You need to: +CaaS requires mTLS authentication. There are two ways to provide the mTLS certificates: + +#### Option 1: Zero Trust Identity (ZTI) with SPIRE (Recommended) + +ZTI with SPIRE sidecar automatically provisions and rotates mTLS certificates (SVID files). This is the recommended approach for production. + +1. **Bind ZTI service** to your app: +```yaml +# mta.yaml +requires: + - name: my-zti-instance +``` + +2. **Bind CaaS service** to your app: +```yaml +# mta.yaml +requires: + - name: my-caas-instance +``` + +That's it! The plugin automatically detects ZTI and uses the SVID files provisioned by the SPIRE sidecar for mTLS authentication. + +**How it works**: The SPIRE sidecar provisions SVID certificate files in parallel with app startup. Since these files may not exist immediately, `@cap-js/telemetry` uses a lazy exporter that buffers telemetry data until the credentials become available. Once the SVID files are ready, buffered data is flushed and subsequent telemetry is exported normally. Certificate rotation is handled automatically. + +> **Note**: For optimal buffering behavior in production, ensure `NODE_ENV=production` is set. This enables batch processing with periodic export intervals (5s for traces/logs, 60s for metrics), ensuring buffered telemetry is flushed shortly after ZTI credentials become ready. In development mode, traces and logs use immediate export per-request. + +To explicitly disable ZTI (e.g., for testing), set: +```bash +CDS_REQUIRES_TELEMETRY_USE_ZTI=false +``` + +#### Option 2: Manual Certificate Configuration (Legacy) + +For environments without ZTI, you can provide mTLS credentials manually: 1. **Bind the CaaS service** to your app with subject/issuer configuration: ```yaml From c604b649e2ce47a372b6ed940bb2f936ea0c48f8 Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 17:36:37 +0200 Subject: [PATCH 63/67] revert refactoring and rewriting --- cds-plugin.js | 2 - lib/index.js | 12 +++--- lib/logging/index.js | 85 +++++++++++++++++------------------------ lib/metrics/index.js | 6 +-- lib/tracing/index.js | 90 +++++++++++++++++++------------------------- lib/tracing/trace.js | 75 ++++++++---------------------------- 6 files changed, 96 insertions(+), 174 deletions(-) diff --git a/cds-plugin.js b/cds-plugin.js index cab638c1..a59ca32b 100644 --- a/cds-plugin.js +++ b/cds-plugin.js @@ -12,7 +12,5 @@ if (!!process.env.NO_TELEMETRY && process.env.NO_TELEMETRY !== 'false') return - // Sync setup - for CaaS with ZTI, exporters use LazyExporter to buffer - // telemetry until mTLS credentials become available require('./lib')() })() diff --git a/lib/index.js b/lib/index.js index 162e0cfb..18a7b3cc 100644 --- a/lib/index.js +++ b/lib/index.js @@ -90,19 +90,18 @@ function _getInstrumentations() { } function setup_standalone() { + // set logger and propagate log level diag.setLogger(cds.log('telemetry'), getDiagLogLevel()) - const resource = getResource() - require('./tracing/cds')() - require('./tracing/cloud_sdk')() + // create resource + const resource = getResource() - // Single code path for all scenarios including CaaS with ZTI. - // For CaaS, the exporters use LazyExporter which buffers telemetry - // until mTLS credentials become available. + // setup tracing, metrics, and logging const tracerProvider = tracing(resource) const meterProvider = metrics(resource) const loggerProvider = cds.env.requires.telemetry.logging ? logging(resource) : undefined + // register instrumentations registerInstrumentations({ tracerProvider, meterProvider, @@ -112,6 +111,7 @@ function setup_standalone() { } function setup_with_calm() { + // setup tracing, metrics, and logging tracing() metrics() if (cds.env.requires.telemetry.logging) logging() diff --git a/lib/logging/index.js b/lib/logging/index.js index 28972e25..5f037e59 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -33,13 +33,7 @@ function _getExporter() { } // use _require for better error message - let loggingExporterModule - try { - loggingExporterModule = _require(loggingExporter.module) - } catch { - LOG._warn && LOG.warn(`Logs exporter module '${loggingExporter.module}' not found. Logging telemetry will be disabled.`) - return null - } + const loggingExporterModule = _require(loggingExporter.module) if (!loggingExporterModule[loggingExporter.class]) throw new Error(`Unknown logs exporter "${loggingExporter.class}" in module "${loggingExporter.module}"`) const config = { ...(loggingExporter.config || {}) } @@ -55,16 +49,14 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') - // Set up base URL once (this part of augmentCaaSCreds is idempotent) + // Set up base URL once augmentCaaSCreds(credentials) const { createLazyExporter } = require('../exporter/LazyExporter') const lazyExporter = createLazyExporter(() => { - // This runs lazily on each export attempt until credentials are ready - // We need to check for mTLS credentials fresh each time (not rely on _augmented flag) + // Check for mTLS credentials fresh each time const mtlsCreds = getCredsForCaaSMtls() if (!mtlsCreds) { - // Credentials not ready yet (ZTI SVID files don't exist) throw new Error('SVID credentials not ready') } @@ -119,44 +111,9 @@ module.exports = resource => { const { logs, SeverityNumber } = require('@opentelemetry/api-logs') const { LoggerProvider, BatchLogRecordProcessor, SimpleLogRecordProcessor } = require('@opentelemetry/sdk-logs') - /* - * create processor - */ - const exporter = _getExporter() - if (!exporter) return null - const processor = - _getCustomProcessor(exporter) || - (process.env.NODE_ENV === 'production' - ? new BatchLogRecordProcessor({ exporter }) - : new SimpleLogRecordProcessor({ exporter })) - - /* - * either add processor as delegate in CALM... - */ - if (!resource) { - LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") - try { - const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') - getCompositeLogRecordProcessor().addDelegate(processor) - return - } catch (error) { - LOG.error('Failed to add log processor as delegate:', error) - throw error - } - } - - /* - * ... or initialize and return provider - */ - const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) - logs.setGlobalLoggerProvider(loggerProvider) - // setup logs interception via cds.log.format - // Must be done AFTER LoggerProvider is set globally. - let _logInterceptionSetup = false - const setupLogInterception = () => { - if (_logInterceptionSetup) return - _logInterceptionSetup = true + cds.on('served', () => { + const loggerProvider = logs.getLoggerProvider() const loggers = {} const l2s = { 1: 'ERROR', 2: 'WARN', 3: 'INFO', 4: 'DEBUG', 5: 'TRACE' } @@ -203,9 +160,37 @@ module.exports = resource => { // replace format function of existing loggers for (const each in cds.log.loggers) cds.log.loggers[each].setFormat(format) - } + }) - cds.on('served', setupLogInterception) + /* + * create processor + */ + const exporter = _getExporter() + const processor = + _getCustomProcessor(exporter) || + (process.env.NODE_ENV === 'production' + ? new BatchLogRecordProcessor(exporter) + : new SimpleLogRecordProcessor(exporter)) + /* + * either add processor as delegate in CALM... + */ + if (!resource) { + LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") + try { + const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') + getCompositeLogRecordProcessor().addDelegate(processor) + return + } catch (error) { + LOG.error('Failed to add log processor as delegate:', error) + throw error + } + } + + /* + * ... or initialize and return provider + */ + const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) + logs.setGlobalLoggerProvider(loggerProvider) return loggerProvider } diff --git a/lib/metrics/index.js b/lib/metrics/index.js index cdc7639f..c997dd2d 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -77,16 +77,14 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') - // Set up base URL once (this part of augmentCaaSCreds is idempotent) + // Set up base URL once augmentCaaSCreds(credentials) const { createLazyExporter } = require('../exporter/LazyExporter') const lazyExporter = createLazyExporter(() => { - // This runs lazily on each export attempt until credentials are ready - // We need to check for mTLS credentials fresh each time (not rely on _augmented flag) + // Check for mTLS credentials fresh each time const mtlsCreds = getCredsForCaaSMtls() if (!mtlsCreds) { - // Credentials not ready yet (ZTI SVID files don't exist) throw new Error('SVID credentials not ready') } diff --git a/lib/tracing/index.js b/lib/tracing/index.js index bf52beb0..9bb2cc17 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -17,39 +17,6 @@ const { _require } = require('../utils') -// Common setup: SAP Passport clearing for HANA -function _setupSAPPassport() { - if (process.env.SAP_PASSPORT) { - cds.on('served', () => { - cds.db?.before('BEGIN', async function () { - if (this.dbc?.constructor.name in { HDBDriver: 1, HANAClientDriver: 1 }) this.dbc.set({ SAP_PASSPORT: '' }) - }) - }) - } -} - -// Common resource merging -function _mergeResource(resource) { - return resourceFromAttributes({}).merge(resource).merge(getDynatraceMetadata()) -} - -// Check if Dynatrace OneAgent is active -function _hasDynatraceOneAgent() { - return ( - process.env.DT_NODE_PRELOAD_OPTIONS && - cds.env.requires.telemetry.kind.match(/to-dynatrace$/) && - !hasDependency('@opentelemetry/exporter-trace-otlp-proto') - ) -} - -// Create span processor with exporter -function _createSpanProcessor(exporter) { - const processorConfig = cds.env.requires.telemetry.tracing.processor?.config || {} - return process.env.NODE_ENV === 'production' - ? new BatchSpanProcessor(exporter, processorConfig) - : new SimpleSpanProcessor(exporter, processorConfig) -} - function _getSampler() { const { ignoreIncomingPaths } = cds.env.requires.telemetry.tracing?.sampler || {} @@ -160,20 +127,17 @@ function _getExporter() { config.credentials ??= credentials.credentials } - // For CaaS with ZTI, use lazy exporter to handle credentials not being ready yet if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') - // Set up base URL once (this part of augmentCaaSCreds is idempotent) + // Set up base URL once augmentCaaSCreds(credentials) const { createLazyExporter } = require('../exporter/LazyExporter') const lazyExporter = createLazyExporter(() => { - // This runs lazily on each export attempt until credentials are ready - // We need to check for mTLS credentials fresh each time (not rely on _augmented flag) + // Check for mTLS credentials fresh each time const mtlsCreds = getCredsForCaaSMtls() if (!mtlsCreds) { - // Credentials not ready yet (ZTI SVID files don't exist) throw new Error('SVID credentials not ready') } @@ -204,25 +168,47 @@ function _getExporter() { return exporter } -/** - * Setup tracing - creates TracerProvider with exporter. - * For CaaS with ZTI, the exporter is wrapped with LazyExporter - * to handle credentials not being ready at startup. - */ -function setupTracing(resource) { +module.exports = resource => { if (!cds.env.requires.telemetry.tracing?.exporter) return - _setupSAPPassport() + // clear sap passport for new tx + if (process.env.SAP_PASSPORT) { + cds.on('served', () => { + cds.db?.before('BEGIN', async function () { + if (this.dbc?.constructor.name in { HDBDriver: 1, HANAClientDriver: 1 }) this.dbc.set({ SAP_PASSPORT: '' }) + }) + }) + } + /* + * add tracing + */ + require('./cds')() + require('./cloud_sdk')() + + /* + * create processor + */ let processor - if (_hasDynatraceOneAgent()) { + const via_one_agent = + process.env.DT_NODE_PRELOAD_OPTIONS && + cds.env.requires.telemetry.kind.match(/to-dynatrace$/) && + !hasDependency('@opentelemetry/exporter-trace-otlp-proto') + if (via_one_agent) { + // if Dynatrace OneAgent is present, no exporter is needed LOG._info && LOG.info('Dynatrace OneAgent detected, disabling trace exporter') } else { const exporter = _getExporter() - processor = _createSpanProcessor(exporter) + const processorConfig = cds.env.requires.telemetry.tracing.processor?.config || {} + processor = + process.env.NODE_ENV === 'production' + ? new BatchSpanProcessor(exporter, processorConfig) + : new SimpleSpanProcessor(exporter, processorConfig) } - // CALM setup - add as delegate + /* + * either add processor as delegate in CALM... + */ if (!resource) { LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's span processor as delegate") try { @@ -235,11 +221,11 @@ function setupTracing(resource) { } } - // Standard setup - resource = _mergeResource(resource) + /* + * ... or initialize and return provider + */ + resource = resourceFromAttributes({}).merge(resource).merge(getDynatraceMetadata()) const tracerProvider = new NodeTracerProvider({ resource, spanProcessors: [processor], sampler: _getSampler() }) tracerProvider.register({ propagator: _getPropagator() }) return tracerProvider } - -module.exports = setupTracing diff --git a/lib/tracing/trace.js b/lib/tracing/trace.js index 2b87570f..dc9d70db 100644 --- a/lib/tracing/trace.js +++ b/lib/tracing/trace.js @@ -50,56 +50,21 @@ const $adjusted = Symbol('@cap-js/telemetry:adjusted') const $reqattrs = Symbol('@cap-js/telemetry:reqattrs') let tracer -let _hrtimePatched = false - -// Lazy-initialize tracer on first use - this avoids race condition with async ZTI init -// The tracer is obtained from the global TracerProvider, which is registered by tracing/index.js -// IMPORTANT: Don't cache until a real TracerProvider is registered, otherwise we cache a NoopTracer -function _getTracer() { - // Check if a real TracerProvider is registered (not the default NoopTracerProvider) - // The global trace API returns a ProxyTracerProvider that delegates to the registered provider - const registeredProvider = otel.trace.getTracerProvider() - const delegate = registeredProvider?.getDelegate?.() - const isNoopProvider = !registeredProvider || - registeredProvider.constructor.name === 'ProxyTracerProvider' && - delegate?.constructor.name === 'NoopTracerProvider' - - LOG._debug && LOG.debug('_getTracer() check:', { - providerName: registeredProvider?.constructor?.name, - delegateName: delegate?.constructor?.name, - isNoopProvider, - tracerCached: !!tracer - }) - - if (isNoopProvider) { - // TracerProvider not yet registered - don't cache, return null to skip tracing - return null - } - - if (!tracer) { - tracer = otel.trace.getTracer('@cap-js/telemetry', require('../../package.json').version) - LOG._debug && LOG.debug('_getTracer() created new tracer') - - // Apply hrtime patches once when tracer is first obtained - if (HRTIME && !_hrtimePatched) { - _hrtimePatched = true - // monkey patch startActiveSpan to set a hr time as startTime (if none was provided) - const { startActiveSpan } = tracer - tracer.startActiveSpan = function (name, options, fn) { - options.startTime ??= _hrnow() - return startActiveSpan.call(this, name, options, fn) - } +cds.on('served', () => { + tracer = otel.trace.getTracer('@cap-js/telemetry', require('../../package.json').version) + + if (HRTIME) { + // monkey patch startActiveSpan to set a hr time as startTime (if none was provided) + const { startActiveSpan } = tracer + tracer.startActiveSpan = function (name, options, fn) { + options.startTime ??= _hrnow() + return startActiveSpan.call(this, name, options, fn) } - } - return tracer -} -// Register listening handler for hrtime - this still needs 'served' but it's just for the optional hrtime feature -if (HRTIME) { - cds.on('served', () => { + // attach a hr time to incoming requests for later adjustment of the root span start time cds.on('listening', ({ server }) => server.on('request', req => (req[$hrnow] = _hrnow()))) - }) -} + } +}) // REVISIT: needs a cleanup function _getSpanName(arg, fn, that) { @@ -276,18 +241,8 @@ const _addDbRowCount = (span, res) => { } function trace(req, fn, that, args, opts = {}) { - // only trace if there is a cds.context - if (!cds.context) { - LOG._debug && LOG.debug('trace() skipped: no cds.context') - return fn.apply(that, args) - } - - // Lazy-init tracer on first use (avoids race with async ZTI init) - const currentTracer = _getTracer() - if (!currentTracer) { - LOG._debug && LOG.debug('trace() skipped: no tracer (NoopProvider still active)') - return fn.apply(that, args) - } + // only trace once served and there is a cds.context + if (!tracer || !cds.context) return fn.apply(that, args) const parent = otel.trace.getActiveSpan() @@ -371,7 +326,7 @@ function trace(req, fn, that, args, opts = {}) { if (name.length > 80 && _truncate_span_name !== false) name = name.substring(0, 79) + '…' // start a new active span, call the original function, and finally end the span - return currentTracer.startActiveSpan(name, options, span => { + return tracer.startActiveSpan(name, options, span => { // in case the span is non-recording, just call the original function if (span.constructor.name === 'NonRecordingSpan') return fn.apply(that, args) From 9cf86c33a9409a17c5a5ec40b9d09fcdf60d14ff Mon Sep 17 00:00:00 2001 From: D051920 Date: Tue, 25 Aug 2026 17:53:15 +0200 Subject: [PATCH 64/67] fix logging --- lib/logging/index.js | 85 ++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/lib/logging/index.js b/lib/logging/index.js index 5f037e59..28972e25 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -33,7 +33,13 @@ function _getExporter() { } // use _require for better error message - const loggingExporterModule = _require(loggingExporter.module) + let loggingExporterModule + try { + loggingExporterModule = _require(loggingExporter.module) + } catch { + LOG._warn && LOG.warn(`Logs exporter module '${loggingExporter.module}' not found. Logging telemetry will be disabled.`) + return null + } if (!loggingExporterModule[loggingExporter.class]) throw new Error(`Unknown logs exporter "${loggingExporter.class}" in module "${loggingExporter.module}"`) const config = { ...(loggingExporter.config || {}) } @@ -49,14 +55,16 @@ function _getExporter() { if (kind.match(/to-caas$/)) { if (!credentials) throw new Error('No CaaS credentials found.') - // Set up base URL once + // Set up base URL once (this part of augmentCaaSCreds is idempotent) augmentCaaSCreds(credentials) const { createLazyExporter } = require('../exporter/LazyExporter') const lazyExporter = createLazyExporter(() => { - // Check for mTLS credentials fresh each time + // This runs lazily on each export attempt until credentials are ready + // We need to check for mTLS credentials fresh each time (not rely on _augmented flag) const mtlsCreds = getCredsForCaaSMtls() if (!mtlsCreds) { + // Credentials not ready yet (ZTI SVID files don't exist) throw new Error('SVID credentials not ready') } @@ -111,9 +119,44 @@ module.exports = resource => { const { logs, SeverityNumber } = require('@opentelemetry/api-logs') const { LoggerProvider, BatchLogRecordProcessor, SimpleLogRecordProcessor } = require('@opentelemetry/sdk-logs') + /* + * create processor + */ + const exporter = _getExporter() + if (!exporter) return null + const processor = + _getCustomProcessor(exporter) || + (process.env.NODE_ENV === 'production' + ? new BatchLogRecordProcessor({ exporter }) + : new SimpleLogRecordProcessor({ exporter })) + + /* + * either add processor as delegate in CALM... + */ + if (!resource) { + LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") + try { + const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') + getCompositeLogRecordProcessor().addDelegate(processor) + return + } catch (error) { + LOG.error('Failed to add log processor as delegate:', error) + throw error + } + } + + /* + * ... or initialize and return provider + */ + const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) + logs.setGlobalLoggerProvider(loggerProvider) + // setup logs interception via cds.log.format - cds.on('served', () => { - const loggerProvider = logs.getLoggerProvider() + // Must be done AFTER LoggerProvider is set globally. + let _logInterceptionSetup = false + const setupLogInterception = () => { + if (_logInterceptionSetup) return + _logInterceptionSetup = true const loggers = {} const l2s = { 1: 'ERROR', 2: 'WARN', 3: 'INFO', 4: 'DEBUG', 5: 'TRACE' } @@ -160,37 +203,9 @@ module.exports = resource => { // replace format function of existing loggers for (const each in cds.log.loggers) cds.log.loggers[each].setFormat(format) - }) - - /* - * create processor - */ - const exporter = _getExporter() - const processor = - _getCustomProcessor(exporter) || - (process.env.NODE_ENV === 'production' - ? new BatchLogRecordProcessor(exporter) - : new SimpleLogRecordProcessor(exporter)) - - /* - * either add processor as delegate in CALM... - */ - if (!resource) { - LOG.warn("@sap/xotel-agent-ext-js found, adding @cap-js/telemetry's log processor as delegate") - try { - const { getCompositeLogRecordProcessor } = require('@sap/xotel-agent-ext-js') - getCompositeLogRecordProcessor().addDelegate(processor) - return - } catch (error) { - LOG.error('Failed to add log processor as delegate:', error) - throw error - } } - /* - * ... or initialize and return provider - */ - const loggerProvider = new LoggerProvider({ resource, processors: [processor] }) - logs.setGlobalLoggerProvider(loggerProvider) + cds.on('served', setupLogInterception) + return loggerProvider } From 607373486441bdae9bc16a974b3b07663d80a6a9 Mon Sep 17 00:00:00 2001 From: D051920 Date: Thu, 27 Aug 2026 10:07:51 +0200 Subject: [PATCH 65/67] refactor --- CHANGELOG.md | 2 +- README.md | 4 +- lib/exporter/LazyExporter.js | 129 +++++++++++++++-------------------- lib/logging/index.js | 29 ++------ lib/metrics/index.js | 27 ++------ lib/tracing/index.js | 26 ++----- lib/utils.js | 73 ++++++++++---------- lib/zti.js | 109 ++++++++--------------------- test/caas.test.js | 59 ++++++++-------- test/tracing-zti.test.js | 22 +++--- 10 files changed, 185 insertions(+), 295 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d03be98..0d0e663e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Added -- Support for `telemetry-to-caas` kind for CaaS (Collector as a Service) with automatic mTLS certificate management and rotation via Zero Trust Identity Service (ZTI/SPIFFE). Falls back to base64-encoded certificates from environment variables when ZTI is not available. +- Support for `telemetry-to-caas` kind for CaaS (Collector as a Service) with automatic mTLS certificate management and rotation via Zero Trust Identity Service (ZTI/SPIFFE). Falls back to base64-encoded certificates from environment variables when ZTI is not available or explicitly disabled. ## Version 2.0.1 - 2026-07-03 diff --git a/README.md b/README.md index 05a6889a..3c8f13fe 100644 --- a/README.md +++ b/README.md @@ -333,9 +333,9 @@ To explicitly disable ZTI (e.g., for testing), set: CDS_REQUIRES_TELEMETRY_USE_ZTI=false ``` -#### Option 2: Manual Certificate Configuration (Legacy) +#### Option 2: Manual Certificate Configuration (Fallback) -For environments without ZTI, you can provide mTLS credentials manually: +For environments without ZTI or when you want to manage certificates externally, you can provide mTLS credentials manually: 1. **Bind the CaaS service** to your app with subject/issuer configuration: ```yaml diff --git a/lib/exporter/LazyExporter.js b/lib/exporter/LazyExporter.js index ce21e936..516cee42 100644 --- a/lib/exporter/LazyExporter.js +++ b/lib/exporter/LazyExporter.js @@ -1,107 +1,90 @@ const cds = require('@sap/cds') -/** - * Creates a lazy wrapper for any OTLP exporter. - * Buffers data until mTLS credentials are available. - * - * This is used for CaaS (Collector as a Service) with ZTI (Zero Trust Identity) - * where SPIRE sidecar provisions SVID files in parallel with app startup - - * they may not exist when the app starts. - * - * @param {Function} createExporter - Factory that creates the real exporter (may throw if creds not ready) - * @param {Object} options - { maxBufferSize: number } - * @returns {Object} Exporter-like object with export(), shutdown(), forceFlush() methods - */ -function createLazyExporter(createExporter, options = {}) { - const LOG = cds.log('telemetry') +const MAX_BUFFER_SIZE = 1000 - let _exporter = null - let _buffer = [] - let _createFailed = false - const _maxBufferSize = options.maxBufferSize || 1000 - - function _tryCreateExporter() { - if (_exporter) return _exporter - if (_createFailed) return null // Don't retry after permanent failure - - try { - _exporter = createExporter() - LOG._debug && LOG.debug('Lazy exporter: real exporter created successfully') - return _exporter - } catch (err) { - // Check if this is a "credentials not ready" error vs a permanent failure - if (err.message?.includes('SVID') || err.code === 'ENOENT') { - // Credentials not ready yet - will retry on next export - LOG._debug && LOG.debug('Lazy exporter: credentials not ready yet, buffering') - return null - } - // Permanent failure - don't retry - LOG._warn && LOG.warn('Lazy exporter: permanent failure creating exporter:', err.message) - _createFailed = true - return null - } +function _tryCreateExporter(state, createExporter, LOG) { + if (state._exporter) return + if (state._createFailed) return + + const result = createExporter() + + // Transient: credentials not ready yet, will retry + if (result.status === 'not_ready') return + + // Permanent: configuration error, stop retrying + if (result.status === 'error') { + LOG._warn && LOG.warn('Lazy exporter: failed to create exporter:', result.error) + state._createFailed = true + return } - function _flushBuffer() { - if (_buffer.length === 0 || !_exporter) return + // Success + state._exporter = result.exporter +} - const buffered = _buffer - _buffer = [] - LOG._debug && LOG.debug(`Lazy exporter: flushing ${buffered.length} buffered items`) +function _flushBuffer(state) { + if (state._buffer.length === 0 || !state._exporter) return - // Best effort flush - don't wait for callback - // For traces/logs (arrays), we need to flatten - // For metrics (ResourceMetrics objects), export each separately - for (const item of buffered) { - if (Array.isArray(item)) { - _exporter.export(item, () => {}) - } else { - _exporter.export(item, () => {}) - } + const buffered = state._buffer + state._buffer = [] + + for (const item of buffered) { + if (Array.isArray(item)) { + state._exporter.export(item, () => {}) + } else { + state._exporter.export(item, () => {}) } } +} + +// Buffers telemetry until mTLS credentials are available (for CaaS with ZTI) +function createLazyExporter(createExporter, options = {}) { + const LOG = cds.log('telemetry') + const maxBufferSize = options.maxBufferSize || MAX_BUFFER_SIZE + + const state = { + _exporter: null, + _buffer: [], + _createFailed: false + } return { export(items, resultCallback) { - _tryCreateExporter() + _tryCreateExporter(state, createExporter, LOG) - if (_exporter) { - _flushBuffer() - _exporter.export(items, resultCallback) + if (state._exporter) { + _flushBuffer(state) + state._exporter.export(items, resultCallback) } else { - // Buffer items - if (_buffer.length >= _maxBufferSize) { - // Drop oldest to make room - _buffer.shift() - LOG._debug && LOG.debug('Lazy exporter: buffer full, dropping oldest item') + if (state._buffer.length >= maxBufferSize) { + state._buffer.shift() + LOG._warn && LOG.warn('Lazy exporter: buffer full, dropping telemetry') } - _buffer.push(items) - // Report success - we buffered it + state._buffer.push(items) resultCallback({ code: 0 }) } }, shutdown() { - _buffer = [] - return _exporter?.shutdown() ?? Promise.resolve() + state._buffer = [] + return state._exporter?.shutdown() ?? Promise.resolve() }, forceFlush() { - _tryCreateExporter() - if (_exporter) { - _flushBuffer() - return _exporter.forceFlush?.() ?? Promise.resolve() + _tryCreateExporter(state, createExporter, LOG) + if (state._exporter) { + _flushBuffer(state) + return state._exporter.forceFlush?.() ?? Promise.resolve() } return Promise.resolve() }, - // For testing _getBufferSize() { - return _buffer.length + return state._buffer.length }, _isExporterCreated() { - return _exporter !== null + return state._exporter !== null } } } diff --git a/lib/logging/index.js b/lib/logging/index.js index 28972e25..3363353e 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -3,7 +3,8 @@ const LOG = cds.log('telemetry') const { getStringFromEnv } = require('@opentelemetry/core') -const { getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, getCredsForCaaSMtls, _require } = require('../utils') +const { getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, getCredsForCaaSMtls, decodeCredentials, _require } = require('../utils') +const { createLazyExporter } = require('../exporter/LazyExporter') const _protocol2module = { grpc: '@opentelemetry/exporter-logs-otlp-grpc', @@ -52,40 +53,24 @@ function _getExporter() { config.credentials ??= credentials.credentials } - if (kind.match(/to-caas$/)) { + if (kind === 'telemetry-to-caas') { if (!credentials) throw new Error('No CaaS credentials found.') - // Set up base URL once (this part of augmentCaaSCreds is idempotent) augmentCaaSCreds(credentials) - const { createLazyExporter } = require('../exporter/LazyExporter') const lazyExporter = createLazyExporter(() => { - // This runs lazily on each export attempt until credentials are ready - // We need to check for mTLS credentials fresh each time (not rely on _augmented flag) const mtlsCreds = getCredsForCaaSMtls() - if (!mtlsCreds) { - // Credentials not ready yet (ZTI SVID files don't exist) - throw new Error('SVID credentials not ready') - } - - // Decode credentials (ZTI returns PEM, legacy returns base64) - let cert, key - if (mtlsCreds.cert.startsWith('LS0t') || !mtlsCreds.cert.startsWith('-----BEGIN')) { - cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') - key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') - } else { - cert = mtlsCreds.cert - key = mtlsCreds.key - } + if (mtlsCreds.status === 'not_ready') return { status: 'not_ready' } + if (mtlsCreds.status === 'disabled') return { status: 'error', error: 'CaaS mTLS credentials not configured' } + const { cert, key } = decodeCredentials(mtlsCreds) const exporterConfig = { ...config, url: credentials.baseUrl + '/v1/logs', httpAgentOptions: { cert, key, keepAlive: true } } - return new loggingExporterModule[loggingExporter.class](exporterConfig) + return { status: 'ready', exporter: new loggingExporterModule[loggingExporter.class](exporterConfig) } }) - LOG._debug && LOG.debug('Using lazy logs exporter for CaaS') return lazyExporter } diff --git a/lib/metrics/index.js b/lib/metrics/index.js index c997dd2d..88b22929 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -6,7 +6,8 @@ const { getStringFromEnv } = require('@opentelemetry/core') const { resourceFromAttributes } = require('@opentelemetry/resources') const { AggregationTemporality, MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics') -const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, getCredsForCaaSMtls, _require } = require('../utils') +const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, getCredsForCaaSMtls, decodeCredentials, _require } = require('../utils') +const { createLazyExporter } = require('../exporter/LazyExporter') const _protocol2module = { grpc: '@opentelemetry/exporter-metrics-otlp-grpc', @@ -74,38 +75,24 @@ function _getExporter() { config.credentials ??= credentials.credentials } - if (kind.match(/to-caas$/)) { + if (kind === 'telemetry-to-caas') { if (!credentials) throw new Error('No CaaS credentials found.') - // Set up base URL once augmentCaaSCreds(credentials) - const { createLazyExporter } = require('../exporter/LazyExporter') const lazyExporter = createLazyExporter(() => { - // Check for mTLS credentials fresh each time const mtlsCreds = getCredsForCaaSMtls() - if (!mtlsCreds) { - throw new Error('SVID credentials not ready') - } - - // Decode credentials (ZTI returns PEM, legacy returns base64) - let cert, key - if (mtlsCreds.cert.startsWith('LS0t') || !mtlsCreds.cert.startsWith('-----BEGIN')) { - cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') - key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') - } else { - cert = mtlsCreds.cert - key = mtlsCreds.key - } + if (mtlsCreds.status === 'not_ready') return { status: 'not_ready' } + if (mtlsCreds.status === 'disabled') return { status: 'error', error: 'CaaS mTLS credentials not configured' } + const { cert, key } = decodeCredentials(mtlsCreds) const exporterConfig = { ...config, url: credentials.baseUrl + '/v1/metrics', httpAgentOptions: { cert, key, keepAlive: true } } - return new metricsExporterModule[metricsExporter.class](exporterConfig) + return { status: 'ready', exporter: new metricsExporterModule[metricsExporter.class](exporterConfig) } }) - LOG._debug && LOG.debug('Using lazy metrics exporter for CaaS') return lazyExporter } diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 9bb2cc17..981c0910 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -13,9 +13,11 @@ const { augmentCLCreds, augmentCaaSCreds, getCredsForCaaSMtls, + decodeCredentials, hasDependency, _require } = require('../utils') +const { createLazyExporter } = require('../exporter/LazyExporter') function _getSampler() { const { ignoreIncomingPaths } = cds.env.requires.telemetry.tracing?.sampler || {} @@ -127,38 +129,24 @@ function _getExporter() { config.credentials ??= credentials.credentials } - if (kind.match(/to-caas$/)) { + if (kind === 'telemetry-to-caas') { if (!credentials) throw new Error('No CaaS credentials found.') - // Set up base URL once augmentCaaSCreds(credentials) - const { createLazyExporter } = require('../exporter/LazyExporter') const lazyExporter = createLazyExporter(() => { - // Check for mTLS credentials fresh each time const mtlsCreds = getCredsForCaaSMtls() - if (!mtlsCreds) { - throw new Error('SVID credentials not ready') - } - - // Decode credentials (ZTI returns PEM, legacy returns base64) - let cert, key - if (mtlsCreds.cert.startsWith('LS0t') || !mtlsCreds.cert.startsWith('-----BEGIN')) { - cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') - key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') - } else { - cert = mtlsCreds.cert - key = mtlsCreds.key - } + if (mtlsCreds.status === 'not_ready') return { status: 'not_ready' } + if (mtlsCreds.status === 'disabled') return { status: 'error', error: 'CaaS mTLS credentials not configured' } + const { cert, key } = decodeCredentials(mtlsCreds) const exporterConfig = { ...config, url: credentials.baseUrl + '/v1/traces', httpAgentOptions: { cert, key, keepAlive: true } } - return new tracingExporterModule[tracingExporter.class](exporterConfig) + return { status: 'ready', exporter: new tracingExporterModule[tracingExporter.class](exporterConfig) } }) - LOG._debug && LOG.debug('Using lazy trace exporter for CaaS') return lazyExporter } diff --git a/lib/utils.js b/lib/utils.js index 08488094..a6da6957 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -2,7 +2,7 @@ const cds = require('@sap/cds') const LOG = cds.log('telemetry') const fs = require('fs') -const { getZTIConfig, getCredsForCaaSMtls } = require('./zti') +const { getZTICredentials } = require('./zti') const { DiagLogLevel } = require('@opentelemetry/api') const { hrTimeToMilliseconds, getStringFromEnv } = require('@opentelemetry/core') @@ -138,55 +138,54 @@ function getCredsForCLSAsUPS() { } } +// Returns { status: 'ready'|'not_ready'|'disabled', cert?, key? } +function getCredsForCaaSMtls() { + // Try ZTI first + const ztiCreds = getZTICredentials() + if (ztiCreds === null) { + // ZTI explicitly disabled or no binding + const { x509 } = cds.env.requires.telemetry || {} + if (x509 && x509.cert && x509.key) return { status: 'ready', cert: x509.cert, key: x509.key } + return { status: 'disabled' } + } + + if (ztiCreds === false) return { status: 'not_ready' } // ZTI configured but SVID files don't exist yet + + return { status: 'ready', cert: ztiCreds.cert, key: ztiCreds.key } +} + +function decodeCredentials(mtlsCreds) { + if (mtlsCreds.cert.startsWith('LS0t') || !mtlsCreds.cert.startsWith('-----BEGIN')) { + return { + cert: Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8'), + key: Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') + } + } + return { cert: mtlsCreds.cert, key: mtlsCreds.key } +} + function augmentCaaSCreds(credentials) { if (credentials._augmented) return credentials._augmented = true - // check for otlp http endpoint if (!credentials.otlp?.http) { - throw new Error('No OTLP HTTP endpoint found in CaaS credentials. Make sure the CaaS instance is properly configured.') + throw new Error('No OTLP HTTP endpoint in CaaS credentials') } - // Store the base URL - path will be added per signal type (traces: /v1/traces, metrics: /v1/metrics) credentials.baseUrl = credentials.otlp.http - // Check for mTLS credentials (cert + key) - either from ZTI or legacy env vars const mtlsCreds = getCredsForCaaSMtls() - if (mtlsCreds) { + if (mtlsCreds.status === 'ready') { try { - let cert, key - - // Check if legacy base64 format (starts with base64 characters, not PEM header) - if (mtlsCreds.cert.startsWith('LS0t') || !mtlsCreds.cert.startsWith('-----BEGIN')) { - // Legacy: base64-encoded - cert = Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8') - key = Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') - } else { - // ZTI: already PEM format - cert = mtlsCreds.cert - key = mtlsCreds.key - } - - // Store the mTLS options for the exporter's httpAgentOptions - // The OTLP HTTP exporter will create an https.Agent with these options - credentials.httpAgentOptions = { - cert: cert, - key: key, - keepAlive: true - } - - LOG._debug && LOG.debug('CaaS mTLS configured successfully') + const { cert, key } = decodeCredentials(mtlsCreds) + credentials.httpAgentOptions = { cert, key, keepAlive: true } } catch (err) { LOG._error && LOG.error('Failed to configure CaaS mTLS:', err.message) } - } else { - LOG._warn && LOG.warn( - 'CaaS requires mTLS authentication. No mTLS credentials found. ' + - 'Either bind zero-trust-identity service (recommended) or set ' + - 'CDS_REQUIRES_TELEMETRY_X509_CERT and CDS_REQUIRES_TELEMETRY_X509_KEY ' + - 'environment variables (base64 encoded).' - ) + } else if (mtlsCreds.status === 'disabled') { + LOG._warn && LOG.warn('CaaS mTLS credentials not found') } + // status === 'not_ready': LazyExporter will retry } function augmentCLCreds(credentials) { @@ -255,9 +254,9 @@ module.exports = { getCredsForDTAsUPS, getCredsForCLSAsUPS, augmentCLCreds, - augmentCaaSCreds, - getZTIConfig, getCredsForCaaSMtls, + decodeCredentials, + augmentCaaSCreds, hasDependency, _hrnow, _require diff --git a/lib/zti.js b/lib/zti.js index 797e21a3..7f36f1af 100644 --- a/lib/zti.js +++ b/lib/zti.js @@ -2,14 +2,9 @@ const cds = require('@sap/cds') const LOG = cds.log('telemetry') const fs = require('fs') -// SVID paths and cached credentials (initialized once, reused across requests) -let _paths = null // { cert, key, bundle } - file paths -let _cached = null // { cert, key, bundle, mtime } - cached credentials - -/** - * Check if SVID files exist (sync) - * @returns {boolean} - */ +let _paths = null +let _cached = null + function _svidFilesExist() { if (!_paths) return false try { @@ -22,11 +17,6 @@ function _svidFilesExist() { } } -/** - * Get SVID certificate credentials, reloading if mtime changed. - * Manages mtime-based caching for automatic certificate rotation. - * @returns {{ cert: string, key: string, bundle: string }} - */ function _getSVIDCertificate() { let stat try { @@ -34,17 +24,14 @@ function _getSVIDCertificate() { } catch (e) { // Transient stat failure during atomic rename - serve last-known-good if (_cached) { - LOG._debug && LOG.debug('ZTI: Transient stat failure, serving cached credentials') + LOG._debug && LOG.debug('ZTI: stat failure, serving cached credentials') return _cached } throw e } const mtime = stat.mtimeMs - if (_cached?.mtime === mtime) { - // Cache hit - no rotation since last check - return _cached - } + if (_cached?.mtime === mtime) return _cached // mtime changed or first load - reload all files atomically try { @@ -58,17 +45,13 @@ function _getSVIDCertificate() { } catch (err) { // Read failure after successful stat - likely mid-rotation if (_cached) { - LOG._warn && LOG.warn('ZTI: Failed to reload SVID files, serving cached', err) + LOG._warn && LOG.warn('ZTI: failed to reload SVID files, serving cached', err) return _cached } throw err } } -/** - * Detect ZTI binding from VCAP_SERVICES - * @returns {{ svidDir: string, svidName: string } | null} - */ function getZTIConfig() { if (!process.env.VCAP_SERVICES) return null @@ -76,25 +59,21 @@ function getZTIConfig() { const zti = vcap['zero-trust-identity'] if (!zti || zti.length === 0) return null - // Get SVID name from binding parameters const svidName = zti[0].credentials?.parameters?.['svid-store']?.file?.name if (!svidName) { - LOG._warn && LOG.warn('ZTI: zero-trust-identity binding found but svid-store.file.name not configured') + LOG._warn && LOG.warn('ZTI: zero-trust-identity binding missing svid-store.file.name') return null } - // Native CF apps have fixed SVID directory - const svidDir = process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR || '/home/vcap/app/spire-svids' + let svidDir = process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR + if (!svidDir) { + const isDocker = fs.existsSync('/.dockerenv') || fs.existsSync('/run/.containerenv') + svidDir = isDocker ? '/etc/ztis/spire/svids' : '/home/vcap/app/spire-svids' + } return { svidDir, svidName } } -/** - * Initialize SVID file paths (does NOT wait for files to exist) - * Called internally when credentials are first requested. - * @param {string} svidDir - Directory containing SVID files - * @param {string} svidName - SVID file base name - */ function _initializePaths(svidDir, svidName) { if (_paths) return // Already initialized _paths = { @@ -104,60 +83,28 @@ function _initializePaths(svidDir, svidName) { } } -/** - * Get mTLS credentials for CaaS telemetry - * Priority: - * 1. ZTI file-based (default, automatic rotation) - if USE_ZTI=true (default) - * 2. Env var base64 (legacy, manual rotation) - if USE_ZTI=false - * - * Returns null if ZTI is configured but SVID files don't exist yet. - * The LazyExporter will buffer telemetry until credentials become available. - * - * @returns {{ cert: string, key: string } | null} - */ -function getCredsForCaaSMtls() { - // Check flag: default to ZTI (only disable if explicitly set to 'false') +// Returns null if not configured, false if not ready yet +function getZTICredentials() { const useZTI = process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI !== 'false' + if (!useZTI) return null - if (useZTI) { - // ZTI approach (default) - const ztiConfig = getZTIConfig() - if (ztiConfig) { - // ZTI binding found - initialize paths if needed - _initializePaths(ztiConfig.svidDir, ztiConfig.svidName) - - // Check if SVID files exist - if (!_svidFilesExist()) { - LOG._debug && LOG.debug('ZTI: SVID files not ready yet, will buffer telemetry') - return null - } - - // Files exist - get credentials (with mtime-based caching for rotation) - try { - const { cert, key } = _getSVIDCertificate() - return { cert, key } - } catch (err) { - LOG._error && LOG.error('ZTI: Failed to load SVID credentials:', err) - return null - } - } - // No ZTI binding - fall through to legacy check - } + const ztiConfig = getZTIConfig() + if (!ztiConfig) return null - // Legacy env var approach (USE_ZTI=false or no ZTI binding) - const { x509 } = cds.env.requires.telemetry || {} - if (x509 && x509.cert && x509.key) { - LOG._debug && LOG.debug('Using legacy base64 env var credentials for CaaS mTLS') - return { - cert: x509.cert, - key: x509.key - } - } + _initializePaths(ztiConfig.svidDir, ztiConfig.svidName) + + if (!_svidFilesExist()) return false - return null + try { + const { cert, key } = _getSVIDCertificate() + return { cert, key } + } catch (err) { + LOG._error && LOG.error('ZTI: failed to load SVID credentials:', err) + return false + } } module.exports = { getZTIConfig, - getCredsForCaaSMtls + getZTICredentials } diff --git a/test/caas.test.js b/test/caas.test.js index 145ca846..12d8618f 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -100,7 +100,7 @@ describe('augmentCaaSCreds', () => { delete require.cache[require.resolve('../lib/utils')] const { augmentCaaSCreds } = require('../lib/utils') - expect(() => augmentCaaSCreds({})).toThrow('No OTLP HTTP endpoint found') + expect(() => augmentCaaSCreds({})).toThrow('No OTLP HTTP endpoint in CaaS credentials') }) test('does not augment twice', () => { @@ -170,11 +170,11 @@ describe('ZTI SVID File Loading', () => { let creds jest.isolateModules(() => { - const { getCredsForCaaSMtls } = require('../lib/zti') + const { getCredsForCaaSMtls } = require('../lib/utils') creds = getCredsForCaaSMtls() }) - expect(creds).toBeDefined() + expect(creds.status).toBe('ready') expect(creds.cert).toContain('BEGIN CERTIFICATE') expect(creds.key).toContain('BEGIN PRIVATE KEY') }) @@ -184,11 +184,11 @@ describe('ZTI SVID File Loading', () => { let creds jest.isolateModules(() => { - const { getCredsForCaaSMtls } = require('../lib/zti') + const { getCredsForCaaSMtls } = require('../lib/utils') creds = getCredsForCaaSMtls() }) - expect(creds).toBeNull() + expect(creds).toEqual({ status: 'not_ready' }) }) test('reloads when mtime changes', async () => { @@ -203,7 +203,7 @@ describe('ZTI SVID File Loading', () => { let creds1, creds2 await jest.isolateModulesAsync(async () => { - const { getCredsForCaaSMtls } = require('../lib/zti') + const { getCredsForCaaSMtls } = require('../lib/utils') creds1 = getCredsForCaaSMtls() @@ -217,7 +217,9 @@ describe('ZTI SVID File Loading', () => { creds2 = getCredsForCaaSMtls() }) + expect(creds1.status).toBe('ready') expect(creds1.cert).toContain('v1') + expect(creds2.status).toBe('ready') expect(creds2.cert).toContain('v2') expect(creds2.key).toContain('v2') }) @@ -239,7 +241,7 @@ describe('ZTI flag behavior', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_ZTI_VCAP) jest.isolateModules(() => { - const { getZTIConfig } = require('../lib/utils') + const { getZTIConfig } = require('../lib/zti') const config = getZTIConfig() expect(config).not.toBeNull() @@ -252,28 +254,27 @@ describe('ZTI flag behavior', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) jest.isolateModules(() => { - const { getZTIConfig } = require('../lib/utils') + const { getZTIConfig } = require('../lib/zti') const config = getZTIConfig() expect(config).toBeNull() }) }) - test('uses legacy env vars when USE_ZTI=false', () => { + test('uses env var credentials when USE_ZTI=false', () => { process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI = 'false' process.env.VCAP_SERVICES = JSON.stringify(MOCK_ZTI_VCAP) cds.env.requires.telemetry.x509 = { - cert: Buffer.from('-----BEGIN CERTIFICATE-----\nlegacy\n-----END CERTIFICATE-----').toString('base64'), - key: Buffer.from('-----BEGIN PRIVATE KEY-----\nlegacy\n-----END PRIVATE KEY-----').toString('base64') + cert: Buffer.from('-----BEGIN CERTIFICATE-----\nenvvar\n-----END CERTIFICATE-----').toString('base64'), + key: Buffer.from('-----BEGIN PRIVATE KEY-----\nenvvar\n-----END PRIVATE KEY-----').toString('base64') } - // Legacy path doesn't involve ZTI state, just reads from cds.env - const { getCredsForCaaSMtls } = require('../lib/zti') + const { getCredsForCaaSMtls } = require('../lib/utils') const creds = getCredsForCaaSMtls() expect(creds).toBeDefined() - // Legacy credentials are still base64 at this point - expect(Buffer.from(creds.cert, 'base64').toString()).toContain('legacy') + // Env var credentials are still base64 at this point + expect(Buffer.from(creds.cert, 'base64').toString()).toContain('envvar') }) test('augmentCaaSCreds handles PEM format from ZTI', () => { @@ -307,15 +308,15 @@ describe('ZTI flag behavior', () => { fs.rmSync(tmpDir, { recursive: true, force: true }) }) - test('augmentCaaSCreds handles base64 format from legacy', () => { + test('augmentCaaSCreds handles base64 format from env vars', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI = 'false' cds.env.requires.telemetry.x509 = { - cert: Buffer.from('-----BEGIN CERTIFICATE-----\nlegacy-cert\n-----END CERTIFICATE-----').toString('base64'), - key: Buffer.from('-----BEGIN PRIVATE KEY-----\nlegacy-key\n-----END PRIVATE KEY-----').toString('base64') + cert: Buffer.from('-----BEGIN CERTIFICATE-----\nenvvar-cert\n-----END CERTIFICATE-----').toString('base64'), + key: Buffer.from('-----BEGIN PRIVATE KEY-----\nenvvar-key\n-----END PRIVATE KEY-----').toString('base64') } - // Legacy path doesn't involve ZTI state + // Env var fallback doesn't involve ZTI state const { augmentCaaSCreds } = require('../lib/utils') const credentials = { @@ -324,8 +325,8 @@ describe('ZTI flag behavior', () => { augmentCaaSCreds(credentials) expect(credentials.httpAgentOptions).toBeDefined() - expect(credentials.httpAgentOptions.cert).toContain('legacy-cert') - expect(credentials.httpAgentOptions.key).toContain('legacy-key') + expect(credentials.httpAgentOptions.cert).toContain('envvar-cert') + expect(credentials.httpAgentOptions.key).toContain('envvar-key') }) }) @@ -340,7 +341,7 @@ describe('LazyExporter', () => { let createCalled = 0 const lazyExporter = createLazyExporter(() => { createCalled++ - throw new Error('SVID credentials not ready') + return { status: 'not_ready' } }) const items = [{ name: 'span1' }, { name: 'span2' }] @@ -366,8 +367,8 @@ describe('LazyExporter', () => { } const lazyExporter = createLazyExporter(() => { - if (!ready) throw new Error('SVID credentials not ready') - return mockExporter + if (!ready) return { status: 'not_ready' } + return { status: 'ready', exporter: mockExporter } }) // First export - buffers @@ -391,7 +392,7 @@ describe('LazyExporter', () => { const { createLazyExporter } = require('../lib/exporter/LazyExporter') const lazyExporter = createLazyExporter( - () => { throw new Error('SVID credentials not ready') }, + () => { return { status: 'not_ready' } }, { maxBufferSize: 2 } ) @@ -408,7 +409,7 @@ describe('LazyExporter', () => { const { createLazyExporter } = require('../lib/exporter/LazyExporter') const lazyExporter = createLazyExporter(() => { - throw new Error('SVID credentials not ready') + return { status: 'not_ready' } }) lazyExporter.export([{ name: 'span1' }], () => {}) @@ -432,8 +433,8 @@ describe('LazyExporter', () => { } const lazyExporter = createLazyExporter(() => { - if (!ready) throw new Error('SVID credentials not ready') - return mockExporter + if (!ready) return { status: 'not_ready' } + return { status: 'ready', exporter: mockExporter } }) // Buffer some items @@ -459,7 +460,7 @@ describe('LazyExporter', () => { let createCalled = 0 const lazyExporter = createLazyExporter(() => { createCalled++ - throw new Error('Permanent configuration error') + return { status: 'error', error: 'Permanent configuration error' } }) // First export - tries to create diff --git a/test/tracing-zti.test.js b/test/tracing-zti.test.js index 0369a2df..138a3a35 100644 --- a/test/tracing-zti.test.js +++ b/test/tracing-zti.test.js @@ -229,11 +229,11 @@ describe('Tracing with ZTI integration', () => { // Don't create SVID files jest.isolateModules(() => { - const { getCredsForCaaSMtls } = require('../lib/zti') + const { getCredsForCaaSMtls } = require('../lib/utils') const creds = getCredsForCaaSMtls() // Should return null (not throw) when files don't exist - expect(creds).toBeNull() + expect(creds).toEqual({ status: 'not_ready' }) }) }) @@ -244,34 +244,34 @@ describe('Tracing with ZTI integration', () => { fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') jest.isolateModules(() => { - const { getCredsForCaaSMtls } = require('../lib/zti') + const { getCredsForCaaSMtls } = require('../lib/utils') const creds = getCredsForCaaSMtls() - expect(creds).toBeDefined() + expect(creds.status).toBe('ready') expect(creds.cert).toContain('BEGIN CERTIFICATE') expect(creds.key).toContain('BEGIN PRIVATE KEY') }) }) - test('legacy x509 credentials work when USE_ZTI=false', () => { + test('x509 env var credentials work when USE_ZTI=false', () => { process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI = 'false' - // Need to set up cds.env inside isolateModules since cds is re-required there delete require.cache[require.resolve('../lib/zti')] + delete require.cache[require.resolve('../lib/utils')] cds.env.requires = { telemetry: { x509: { - cert: Buffer.from('-----BEGIN CERTIFICATE-----\nlegacy\n-----END CERTIFICATE-----').toString('base64'), - key: Buffer.from('-----BEGIN PRIVATE KEY-----\nlegacy\n-----END PRIVATE KEY-----').toString('base64') + cert: Buffer.from('-----BEGIN CERTIFICATE-----\nenvvar\n-----END CERTIFICATE-----').toString('base64'), + key: Buffer.from('-----BEGIN PRIVATE KEY-----\nenvvar\n-----END PRIVATE KEY-----').toString('base64') } } } - const { getCredsForCaaSMtls } = require('../lib/zti') + const { getCredsForCaaSMtls } = require('../lib/utils') const creds = getCredsForCaaSMtls() - expect(creds).toBeDefined() - expect(Buffer.from(creds.cert, 'base64').toString()).toContain('legacy') + expect(creds.status).toBe('ready') + expect(Buffer.from(creds.cert, 'base64').toString()).toContain('envvar') }) }) From fec4ecf47c7a70fa250e34a4e25b52265855e243 Mon Sep 17 00:00:00 2001 From: D051920 Date: Thu, 27 Aug 2026 12:59:54 +0200 Subject: [PATCH 66/67] fix try catch --- lib/zti.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/zti.js b/lib/zti.js index 7f36f1af..7d93fed6 100644 --- a/lib/zti.js +++ b/lib/zti.js @@ -2,6 +2,8 @@ const cds = require('@sap/cds') const LOG = cds.log('telemetry') const fs = require('fs') +const SVID_DIR = '/home/vcap/app/spire-svids' + let _paths = null let _cached = null @@ -65,12 +67,7 @@ function getZTIConfig() { return null } - let svidDir = process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR - if (!svidDir) { - const isDocker = fs.existsSync('/.dockerenv') || fs.existsSync('/run/.containerenv') - svidDir = isDocker ? '/etc/ztis/spire/svids' : '/home/vcap/app/spire-svids' - } - + const svidDir = process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR || SVID_DIR return { svidDir, svidName } } From bdc2bacedfde7883ec8b18308fde548fcd1ca02b Mon Sep 17 00:00:00 2001 From: D051920 Date: Sat, 29 Aug 2026 15:20:13 +0200 Subject: [PATCH 67/67] use agents and remove lazy exporter --- lib/exporter/LazyExporter.js | 92 ---------- lib/logging/index.js | 28 ++- lib/metrics/index.js | 30 ++-- lib/tracing/index.js | 28 ++- lib/utils.js | 77 +++++---- lib/zti.js | 68 ++++---- test/caas.test.js | 319 +++++++++++++++++++---------------- test/tracing-zti.test.js | 118 ++++++------- 8 files changed, 349 insertions(+), 411 deletions(-) delete mode 100644 lib/exporter/LazyExporter.js diff --git a/lib/exporter/LazyExporter.js b/lib/exporter/LazyExporter.js deleted file mode 100644 index 516cee42..00000000 --- a/lib/exporter/LazyExporter.js +++ /dev/null @@ -1,92 +0,0 @@ -const cds = require('@sap/cds') - -const MAX_BUFFER_SIZE = 1000 - -function _tryCreateExporter(state, createExporter, LOG) { - if (state._exporter) return - if (state._createFailed) return - - const result = createExporter() - - // Transient: credentials not ready yet, will retry - if (result.status === 'not_ready') return - - // Permanent: configuration error, stop retrying - if (result.status === 'error') { - LOG._warn && LOG.warn('Lazy exporter: failed to create exporter:', result.error) - state._createFailed = true - return - } - - // Success - state._exporter = result.exporter -} - -function _flushBuffer(state) { - if (state._buffer.length === 0 || !state._exporter) return - - const buffered = state._buffer - state._buffer = [] - - for (const item of buffered) { - if (Array.isArray(item)) { - state._exporter.export(item, () => {}) - } else { - state._exporter.export(item, () => {}) - } - } -} - -// Buffers telemetry until mTLS credentials are available (for CaaS with ZTI) -function createLazyExporter(createExporter, options = {}) { - const LOG = cds.log('telemetry') - const maxBufferSize = options.maxBufferSize || MAX_BUFFER_SIZE - - const state = { - _exporter: null, - _buffer: [], - _createFailed: false - } - - return { - export(items, resultCallback) { - _tryCreateExporter(state, createExporter, LOG) - - if (state._exporter) { - _flushBuffer(state) - state._exporter.export(items, resultCallback) - } else { - if (state._buffer.length >= maxBufferSize) { - state._buffer.shift() - LOG._warn && LOG.warn('Lazy exporter: buffer full, dropping telemetry') - } - state._buffer.push(items) - resultCallback({ code: 0 }) - } - }, - - shutdown() { - state._buffer = [] - return state._exporter?.shutdown() ?? Promise.resolve() - }, - - forceFlush() { - _tryCreateExporter(state, createExporter, LOG) - if (state._exporter) { - _flushBuffer(state) - return state._exporter.forceFlush?.() ?? Promise.resolve() - } - return Promise.resolve() - }, - - _getBufferSize() { - return state._buffer.length - }, - - _isExporterCreated() { - return state._exporter !== null - } - } -} - -module.exports = { createLazyExporter } diff --git a/lib/logging/index.js b/lib/logging/index.js index 3363353e..39cfdb1e 100644 --- a/lib/logging/index.js +++ b/lib/logging/index.js @@ -3,8 +3,7 @@ const LOG = cds.log('telemetry') const { getStringFromEnv } = require('@opentelemetry/core') -const { getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, getCredsForCaaSMtls, decodeCredentials, _require } = require('../utils') -const { createLazyExporter } = require('../exporter/LazyExporter') +const { getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, _require } = require('../utils') const _protocol2module = { grpc: '@opentelemetry/exporter-logs-otlp-grpc', @@ -58,20 +57,19 @@ function _getExporter() { augmentCaaSCreds(credentials) - const lazyExporter = createLazyExporter(() => { - const mtlsCreds = getCredsForCaaSMtls() - if (mtlsCreds.status === 'not_ready') return { status: 'not_ready' } - if (mtlsCreds.status === 'disabled') return { status: 'error', error: 'CaaS mTLS credentials not configured' } + if (!credentials.httpAgentOptions) { + throw new Error('CaaS requires mTLS. Bind zero-trust-identity service or configure x509 credentials.') + } - const { cert, key } = decodeCredentials(mtlsCreds) - const exporterConfig = { - ...config, - url: credentials.baseUrl + '/v1/logs', - httpAgentOptions: { cert, key, keepAlive: true } - } - return { status: 'ready', exporter: new loggingExporterModule[loggingExporter.class](exporterConfig) } - }) - return lazyExporter + const exporterConfig = { + ...config, + url: credentials.baseUrl + '/v1/logs', + httpAgentOptions: credentials.httpAgentOptions + } + + const exporter = new loggingExporterModule[loggingExporter.class](exporterConfig) + LOG._debug && LOG.debug('Using logs exporter:', exporter) + return exporter } const exporter = new loggingExporterModule[loggingExporter.class](config) diff --git a/lib/metrics/index.js b/lib/metrics/index.js index 88b22929..09e2fddc 100644 --- a/lib/metrics/index.js +++ b/lib/metrics/index.js @@ -6,8 +6,7 @@ const { getStringFromEnv } = require('@opentelemetry/core') const { resourceFromAttributes } = require('@opentelemetry/resources') const { AggregationTemporality, MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics') -const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, getCredsForCaaSMtls, decodeCredentials, _require } = require('../utils') -const { createLazyExporter } = require('../exporter/LazyExporter') +const { getDynatraceMetadata, getCredsForDTAsUPS, getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, _require } = require('../utils') const _protocol2module = { grpc: '@opentelemetry/exporter-metrics-otlp-grpc', @@ -80,20 +79,19 @@ function _getExporter() { augmentCaaSCreds(credentials) - const lazyExporter = createLazyExporter(() => { - const mtlsCreds = getCredsForCaaSMtls() - if (mtlsCreds.status === 'not_ready') return { status: 'not_ready' } - if (mtlsCreds.status === 'disabled') return { status: 'error', error: 'CaaS mTLS credentials not configured' } - - const { cert, key } = decodeCredentials(mtlsCreds) - const exporterConfig = { - ...config, - url: credentials.baseUrl + '/v1/metrics', - httpAgentOptions: { cert, key, keepAlive: true } - } - return { status: 'ready', exporter: new metricsExporterModule[metricsExporter.class](exporterConfig) } - }) - return lazyExporter + if (!credentials.httpAgentOptions) { + throw new Error('CaaS requires mTLS. Bind zero-trust-identity service or configure x509 credentials.') + } + + const exporterConfig = { + ...config, + url: credentials.baseUrl + '/v1/metrics', + httpAgentOptions: credentials.httpAgentOptions + } + + const exporter = new metricsExporterModule[metricsExporter.class](exporterConfig) + LOG._debug && LOG.debug('Using metrics exporter:', exporter) + return exporter } const exporter = new metricsExporterModule[metricsExporter.class](config) diff --git a/lib/tracing/index.js b/lib/tracing/index.js index 981c0910..58a3295c 100644 --- a/lib/tracing/index.js +++ b/lib/tracing/index.js @@ -12,12 +12,9 @@ const { getCredsForCLSAsUPS, augmentCLCreds, augmentCaaSCreds, - getCredsForCaaSMtls, - decodeCredentials, hasDependency, _require } = require('../utils') -const { createLazyExporter } = require('../exporter/LazyExporter') function _getSampler() { const { ignoreIncomingPaths } = cds.env.requires.telemetry.tracing?.sampler || {} @@ -134,20 +131,19 @@ function _getExporter() { augmentCaaSCreds(credentials) - const lazyExporter = createLazyExporter(() => { - const mtlsCreds = getCredsForCaaSMtls() - if (mtlsCreds.status === 'not_ready') return { status: 'not_ready' } - if (mtlsCreds.status === 'disabled') return { status: 'error', error: 'CaaS mTLS credentials not configured' } + if (!credentials.httpAgentOptions) { + throw new Error('CaaS requires mTLS. Bind zero-trust-identity service or configure x509 credentials.') + } - const { cert, key } = decodeCredentials(mtlsCreds) - const exporterConfig = { - ...config, - url: credentials.baseUrl + '/v1/traces', - httpAgentOptions: { cert, key, keepAlive: true } - } - return { status: 'ready', exporter: new tracingExporterModule[tracingExporter.class](exporterConfig) } - }) - return lazyExporter + const exporterConfig = { + ...config, + url: credentials.baseUrl + '/v1/traces', + httpAgentOptions: credentials.httpAgentOptions + } + + const exporter = new tracingExporterModule[tracingExporter.class](exporterConfig) + LOG._debug && LOG.debug('Using trace exporter:', exporter) + return exporter } const exporter = new tracingExporterModule[tracingExporter.class](config) diff --git a/lib/utils.js b/lib/utils.js index a6da6957..b2d6a054 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -2,7 +2,7 @@ const cds = require('@sap/cds') const LOG = cds.log('telemetry') const fs = require('fs') -const { getZTICredentials } = require('./zti') +const { initializeZTI, isZTIEnabled, getCert, getKey } = require('./zti') const { DiagLogLevel } = require('@opentelemetry/api') const { hrTimeToMilliseconds, getStringFromEnv } = require('@opentelemetry/core') @@ -138,30 +138,43 @@ function getCredsForCLSAsUPS() { } } -// Returns { status: 'ready'|'not_ready'|'disabled', cert?, key? } -function getCredsForCaaSMtls() { - // Try ZTI first - const ztiCreds = getZTICredentials() - if (ztiCreds === null) { - // ZTI explicitly disabled or no binding - const { x509 } = cds.env.requires.telemetry || {} - if (x509 && x509.cert && x509.key) return { status: 'ready', cert: x509.cert, key: x509.key } - return { status: 'disabled' } - } +// Returns httpAgentOptions factory for ZTI, or null if not configured +function createZTIAgentFactory() { + if (!isZTIEnabled()) return null + if (!initializeZTI()) return null + + const https = require('https') + return () => new https.Agent({ + cert: getCert(), + key: getKey(), + keepAlive: true + }) +} - if (ztiCreds === false) return { status: 'not_ready' } // ZTI configured but SVID files don't exist yet +// Returns httpAgentOptions factory for static x509 credentials, or null if not configured +function createStaticAgentFactory() { + const { x509 } = cds.env.requires.telemetry || {} + if (!x509?.cert || !x509?.key) return null - return { status: 'ready', cert: ztiCreds.cert, key: ztiCreds.key } + let cert = x509.cert + let key = x509.key + if (cert.startsWith('LS0t') || !cert.startsWith('-----BEGIN')) { + cert = Buffer.from(cert, 'base64').toString('utf-8') + key = Buffer.from(key, 'base64').toString('utf-8') + } + + const https = require('https') + return () => new https.Agent({ cert, key, keepAlive: true }) } -function decodeCredentials(mtlsCreds) { - if (mtlsCreds.cert.startsWith('LS0t') || !mtlsCreds.cert.startsWith('-----BEGIN')) { +function decodeCredentials(creds) { + if (creds.cert.startsWith('LS0t') || !creds.cert.startsWith('-----BEGIN')) { return { - cert: Buffer.from(mtlsCreds.cert, 'base64').toString('utf-8'), - key: Buffer.from(mtlsCreds.key, 'base64').toString('utf-8') + cert: Buffer.from(creds.cert, 'base64').toString('utf-8'), + key: Buffer.from(creds.key, 'base64').toString('utf-8') } } - return { cert: mtlsCreds.cert, key: mtlsCreds.key } + return { cert: creds.cert, key: creds.key } } function augmentCaaSCreds(credentials) { @@ -174,18 +187,19 @@ function augmentCaaSCreds(credentials) { credentials.baseUrl = credentials.otlp.http - const mtlsCreds = getCredsForCaaSMtls() - if (mtlsCreds.status === 'ready') { - try { - const { cert, key } = decodeCredentials(mtlsCreds) - credentials.httpAgentOptions = { cert, key, keepAlive: true } - } catch (err) { - LOG._error && LOG.error('Failed to configure CaaS mTLS:', err.message) - } - } else if (mtlsCreds.status === 'disabled') { - LOG._warn && LOG.warn('CaaS mTLS credentials not found') + const ztiAgentFactory = createZTIAgentFactory() + if (ztiAgentFactory) { + credentials.httpAgentOptions = ztiAgentFactory + return + } + + const staticAgentFactory = createStaticAgentFactory() + if (staticAgentFactory) { + credentials.httpAgentOptions = staticAgentFactory + return } - // status === 'not_ready': LazyExporter will retry + + LOG._warn && LOG.warn('CaaS mTLS credentials not found') } function augmentCLCreds(credentials) { @@ -254,9 +268,10 @@ module.exports = { getCredsForDTAsUPS, getCredsForCLSAsUPS, augmentCLCreds, - getCredsForCaaSMtls, - decodeCredentials, augmentCaaSCreds, + createZTIAgentFactory, + createStaticAgentFactory, + decodeCredentials, hasDependency, _hrnow, _require diff --git a/lib/zti.js b/lib/zti.js index 7d93fed6..de0cf7e1 100644 --- a/lib/zti.js +++ b/lib/zti.js @@ -7,26 +7,16 @@ const SVID_DIR = '/home/vcap/app/spire-svids' let _paths = null let _cached = null -function _svidFilesExist() { - if (!_paths) return false - try { - fs.statSync(_paths.cert) - fs.statSync(_paths.key) - fs.statSync(_paths.bundle) - return true - } catch { - return false - } -} - function _getSVIDCertificate() { + if (!_paths) throw new Error('ZTI paths not initialized') + let stat try { stat = fs.statSync(_paths.cert) } catch (e) { // Transient stat failure during atomic rename - serve last-known-good if (_cached) { - LOG._debug && LOG.debug('ZTI: stat failure, serving cached credentials') + LOG._debug && LOG.debug('Stat failure, serving cached credentials') return _cached } throw e @@ -47,7 +37,7 @@ function _getSVIDCertificate() { } catch (err) { // Read failure after successful stat - likely mid-rotation if (_cached) { - LOG._warn && LOG.warn('ZTI: failed to reload SVID files, serving cached', err) + LOG._warn && LOG.warn('Failed to reload SVID files, serving cached:', err) return _cached } throw err @@ -63,7 +53,7 @@ function getZTIConfig() { const svidName = zti[0].credentials?.parameters?.['svid-store']?.file?.name if (!svidName) { - LOG._warn && LOG.warn('ZTI: zero-trust-identity binding missing svid-store.file.name') + LOG._warn && LOG.warn('zero-trust-identity binding missing svid-store.file.name') return null } @@ -71,37 +61,41 @@ function getZTIConfig() { return { svidDir, svidName } } -function _initializePaths(svidDir, svidName) { - if (_paths) return // Already initialized +function initializeZTI() { + const config = getZTIConfig() + if (!config) return false + _paths = { - cert: `${svidDir}/${svidName}.svid.pem`, - key: `${svidDir}/${svidName}.svid.key`, - bundle: `${svidDir}/${svidName}.bundle.pem` + cert: `${config.svidDir}/${config.svidName}.svid.pem`, + key: `${config.svidDir}/${config.svidName}.svid.key`, + bundle: `${config.svidDir}/${config.svidName}.bundle.pem` } + return true } -// Returns null if not configured, false if not ready yet -function getZTICredentials() { - const useZTI = process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI !== 'false' - if (!useZTI) return null - - const ztiConfig = getZTIConfig() - if (!ztiConfig) return null +function isZTIEnabled() { + return process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI !== 'false' +} - _initializePaths(ztiConfig.svidDir, ztiConfig.svidName) +function getCert() { + return _getSVIDCertificate().cert +} - if (!_svidFilesExist()) return false +function getKey() { + return _getSVIDCertificate().key +} - try { - const { cert, key } = _getSVIDCertificate() - return { cert, key } - } catch (err) { - LOG._error && LOG.error('ZTI: failed to load SVID credentials:', err) - return false - } +// For testing +function _reset() { + _paths = null + _cached = null } module.exports = { getZTIConfig, - getZTICredentials + initializeZTI, + isZTIEnabled, + getCert, + getKey, + _reset } diff --git a/test/caas.test.js b/test/caas.test.js index 12d8618f..c6ecea8f 100644 --- a/test/caas.test.js +++ b/test/caas.test.js @@ -59,11 +59,13 @@ describe('augmentCaaSCreds', () => { } } delete require.cache[require.resolve('../lib/utils')] + delete require.cache[require.resolve('../lib/zti')] }) test('sets baseUrl from otlp.http', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) delete require.cache[require.resolve('../lib/utils')] + delete require.cache[require.resolve('../lib/zti')] const { augmentCaaSCreds } = require('../lib/utils') const credentials = { @@ -81,6 +83,7 @@ describe('augmentCaaSCreds', () => { test('sets httpAgentOptions when mTLS credentials found', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) delete require.cache[require.resolve('../lib/utils')] + delete require.cache[require.resolve('../lib/zti')] const { augmentCaaSCreds } = require('../lib/utils') const credentials = { @@ -90,14 +93,13 @@ describe('augmentCaaSCreds', () => { augmentCaaSCreds(credentials) expect(credentials.httpAgentOptions).toBeDefined() - expect(credentials.httpAgentOptions.cert).toContain('BEGIN CERTIFICATE') - expect(credentials.httpAgentOptions.key).toContain('BEGIN PRIVATE KEY') - expect(credentials.httpAgentOptions.keepAlive).toBe(true) + expect(typeof credentials.httpAgentOptions).toBe('function') }) test('throws when no OTLP endpoints', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) delete require.cache[require.resolve('../lib/utils')] + delete require.cache[require.resolve('../lib/zti')] const { augmentCaaSCreds } = require('../lib/utils') expect(() => augmentCaaSCreds({})).toThrow('No OTLP HTTP endpoint in CaaS credentials') @@ -106,6 +108,7 @@ describe('augmentCaaSCreds', () => { test('does not augment twice', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) delete require.cache[require.resolve('../lib/utils')] + delete require.cache[require.resolve('../lib/zti')] const { augmentCaaSCreds } = require('../lib/utils') const credentials = { @@ -125,6 +128,7 @@ describe('augmentCaaSCreds', () => { cds.env.requires.telemetry = {} // No x509 credentials process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) delete require.cache[require.resolve('../lib/utils')] + delete require.cache[require.resolve('../lib/zti')] const { augmentCaaSCreds } = require('../lib/utils') const credentials = { @@ -162,36 +166,40 @@ describe('ZTI SVID File Loading', () => { delete process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI }) - test('loads SVID files when they exist', () => { + test('createZTIAgentFactory returns factory when SVID files exist', () => { // Create SVID files fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - let creds + let factory jest.isolateModules(() => { - const { getCredsForCaaSMtls } = require('../lib/utils') - creds = getCredsForCaaSMtls() + const { createZTIAgentFactory } = require('../lib/utils') + factory = createZTIAgentFactory() }) - expect(creds.status).toBe('ready') - expect(creds.cert).toContain('BEGIN CERTIFICATE') - expect(creds.key).toContain('BEGIN PRIVATE KEY') + expect(factory).not.toBeNull() + expect(typeof factory).toBe('function') }) - test('returns null when SVID files do not exist', () => { - // Don't create SVID files + test('createZTIAgentFactory returns factory even when SVID files do not exist yet', () => { + // Don't create SVID files - but factory is still created + // The factory will throw when called if files don't exist - let creds + let factory jest.isolateModules(() => { - const { getCredsForCaaSMtls } = require('../lib/utils') - creds = getCredsForCaaSMtls() + const { createZTIAgentFactory } = require('../lib/utils') + factory = createZTIAgentFactory() }) - expect(creds).toEqual({ status: 'not_ready' }) + // Factory is created (ZTI is configured via VCAP_SERVICES) + expect(factory).not.toBeNull() + expect(typeof factory).toBe('function') + // Calling it throws because files don't exist + expect(() => factory()).toThrow() }) - test('reloads when mtime changes', async () => { + test('getCert/getKey reload when mtime changes', async () => { const certPath = path.join(svidDir, 'test-svid.svid.pem') const keyPath = path.join(svidDir, 'test-svid.svid.key') const bundlePath = path.join(svidDir, 'test-svid.bundle.pem') @@ -201,11 +209,13 @@ describe('ZTI SVID File Loading', () => { fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\nv1\n-----END PRIVATE KEY-----') fs.writeFileSync(bundlePath, '-----BEGIN CERTIFICATE-----\nv1\n-----END CERTIFICATE-----') - let creds1, creds2 + let cert1, cert2 await jest.isolateModulesAsync(async () => { - const { getCredsForCaaSMtls } = require('../lib/utils') + const { initializeZTI, getCert, _reset } = require('../lib/zti') + _reset() + initializeZTI() - creds1 = getCredsForCaaSMtls() + cert1 = getCert() // Wait to ensure mtime changes (filesystem mtime resolution can be ~1s on some systems) await new Promise(resolve => setTimeout(resolve, 50)) @@ -214,14 +224,11 @@ describe('ZTI SVID File Loading', () => { fs.writeFileSync(certPath, '-----BEGIN CERTIFICATE-----\nv2\n-----END CERTIFICATE-----') fs.writeFileSync(keyPath, '-----BEGIN PRIVATE KEY-----\nv2\n-----END PRIVATE KEY-----') - creds2 = getCredsForCaaSMtls() + cert2 = getCert() }) - expect(creds1.status).toBe('ready') - expect(creds1.cert).toContain('v1') - expect(creds2.status).toBe('ready') - expect(creds2.cert).toContain('v2') - expect(creds2.key).toContain('v2') + expect(cert1).toContain('v1') + expect(cert2).toContain('v2') }) }) @@ -230,6 +237,7 @@ describe('ZTI flag behavior', () => { cds.env.requires = { telemetry: {} } delete process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI delete require.cache[require.resolve('../lib/utils')] + delete require.cache[require.resolve('../lib/zti')] }) afterEach(() => { @@ -261,7 +269,7 @@ describe('ZTI flag behavior', () => { }) }) - test('uses env var credentials when USE_ZTI=false', () => { + test('createStaticAgentFactory returns factory when x509 configured', () => { process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI = 'false' process.env.VCAP_SERVICES = JSON.stringify(MOCK_ZTI_VCAP) cds.env.requires.telemetry.x509 = { @@ -269,15 +277,14 @@ describe('ZTI flag behavior', () => { key: Buffer.from('-----BEGIN PRIVATE KEY-----\nenvvar\n-----END PRIVATE KEY-----').toString('base64') } - const { getCredsForCaaSMtls } = require('../lib/utils') - const creds = getCredsForCaaSMtls() + const { createStaticAgentFactory } = require('../lib/utils') + const factory = createStaticAgentFactory() - expect(creds).toBeDefined() - // Env var credentials are still base64 at this point - expect(Buffer.from(creds.cert, 'base64').toString()).toContain('envvar') + expect(factory).toBeDefined() + expect(typeof factory).toBe('function') }) - test('augmentCaaSCreds handles PEM format from ZTI', () => { + test('augmentCaaSCreds sets httpAgentOptions factory from ZTI', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'zti-test-')) const svidDir = path.join(tmpDir, 'spire-svids') fs.mkdirSync(svidDir) @@ -299,8 +306,8 @@ describe('ZTI flag behavior', () => { augmentCaaSCreds(credentials) expect(credentials.httpAgentOptions).toBeDefined() - expect(credentials.httpAgentOptions.cert).toContain('zti-cert') - expect(credentials.httpAgentOptions.key).toContain('zti-key') + // httpAgentOptions is now a factory function + expect(typeof credentials.httpAgentOptions).toBe('function') }) // Cleanup @@ -308,7 +315,7 @@ describe('ZTI flag behavior', () => { fs.rmSync(tmpDir, { recursive: true, force: true }) }) - test('augmentCaaSCreds handles base64 format from env vars', () => { + test('augmentCaaSCreds sets httpAgentOptions factory from x509 config', () => { process.env.VCAP_SERVICES = JSON.stringify(MOCK_CAAS_VCAP) process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI = 'false' cds.env.requires.telemetry.x509 = { @@ -316,7 +323,7 @@ describe('ZTI flag behavior', () => { key: Buffer.from('-----BEGIN PRIVATE KEY-----\nenvvar-key\n-----END PRIVATE KEY-----').toString('base64') } - // Env var fallback doesn't involve ZTI state + // x509 fallback doesn't involve ZTI state const { augmentCaaSCreds } = require('../lib/utils') const credentials = { @@ -325,150 +332,178 @@ describe('ZTI flag behavior', () => { augmentCaaSCreds(credentials) expect(credentials.httpAgentOptions).toBeDefined() - expect(credentials.httpAgentOptions.cert).toContain('envvar-cert') - expect(credentials.httpAgentOptions.key).toContain('envvar-key') + // httpAgentOptions is now a factory function + expect(typeof credentials.httpAgentOptions).toBe('function') }) }) -describe('LazyExporter', () => { - beforeEach(() => { - delete require.cache[require.resolve('../lib/exporter/LazyExporter')] +describe('ZTI Certificate Rotation', () => { + let tmpDir, svidDir + let originalEnv + + const CERT_V1 = '-----BEGIN CERTIFICATE-----\nCERT_VERSION_1\n-----END CERTIFICATE-----' + const KEY_V1 = '-----BEGIN PRIVATE KEY-----\nKEY_VERSION_1\n-----END PRIVATE KEY-----' + const BUNDLE_V1 = '-----BEGIN CERTIFICATE-----\nBUNDLE_V1\n-----END CERTIFICATE-----' + + const CERT_V2 = '-----BEGIN CERTIFICATE-----\nCERT_VERSION_2\n-----END CERTIFICATE-----' + const KEY_V2 = '-----BEGIN PRIVATE KEY-----\nKEY_VERSION_2\n-----END PRIVATE KEY-----' + const BUNDLE_V2 = '-----BEGIN CERTIFICATE-----\nBUNDLE_V2\n-----END CERTIFICATE-----' + + beforeAll(() => { + originalEnv = { ...process.env } }) - test('buffers items when exporter creation fails', () => { - const { createLazyExporter } = require('../lib/exporter/LazyExporter') + beforeEach(() => { + // Create temp directory for SVID files + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'zti-rotation-test-')) + svidDir = path.join(tmpDir, 'spire-svids') + fs.mkdirSync(svidDir) - let createCalled = 0 - const lazyExporter = createLazyExporter(() => { - createCalled++ - return { status: 'not_ready' } - }) + // Set up environment + process.env.VCAP_SERVICES = JSON.stringify(MOCK_ZTI_VCAP) + process.env.CDS_REQUIRES_TELEMETRY_ZTI_DIR = svidDir + delete process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI - const items = [{ name: 'span1' }, { name: 'span2' }] - let callbackResult - lazyExporter.export(items, result => { callbackResult = result }) + cds.env.requires = { telemetry: {} } + }) - expect(callbackResult).toEqual({ code: 0 }) // SUCCESS - expect(lazyExporter._getBufferSize()).toBe(1) // Items buffered as single array - expect(lazyExporter._isExporterCreated()).toBe(false) - expect(createCalled).toBe(1) + afterEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }) + process.env = { ...originalEnv } }) - test('flushes buffer when exporter becomes ready', () => { - const { createLazyExporter } = require('../lib/exporter/LazyExporter') + function writeSVIDFiles(cert, key, bundle) { + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), cert) + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), key) + fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), bundle) + } - let ready = false - const exportedItems = [] - const mockExporter = { - export: (items, cb) => { - exportedItems.push(items) - cb({ code: 0 }) - } - } + function touchWithNewMtime(filepath) { + // Ensure mtime changes (some filesystems have 1-second resolution) + const now = new Date() + now.setSeconds(now.getSeconds() + 2) + fs.utimesSync(filepath, now, now) + } + + test('returns initial certificate on first call', () => { + writeSVIDFiles(CERT_V1, KEY_V1, BUNDLE_V1) - const lazyExporter = createLazyExporter(() => { - if (!ready) return { status: 'not_ready' } - return { status: 'ready', exporter: mockExporter } + jest.isolateModules(() => { + const { initializeZTI, getCert, getKey } = require('../lib/zti') + initializeZTI() + + expect(getCert()).toBe(CERT_V1) + expect(getKey()).toBe(KEY_V1) }) + }) - // First export - buffers - lazyExporter.export([{ name: 'span1' }], () => {}) - expect(lazyExporter._getBufferSize()).toBe(1) + test('returns cached certificate when mtime unchanged', () => { + writeSVIDFiles(CERT_V1, KEY_V1, BUNDLE_V1) - // Make exporter ready - ready = true + jest.isolateModules(() => { + const { initializeZTI, getCert, _reset } = require('../lib/zti') + _reset() + initializeZTI() + + // First call - reads from disk + const cert1 = getCert() - // Second export - should flush buffer and export new items - lazyExporter.export([{ name: 'span2' }], () => {}) + // Second call without any file changes - should return cached value + const cert2 = getCert() - expect(lazyExporter._isExporterCreated()).toBe(true) - expect(lazyExporter._getBufferSize()).toBe(0) - expect(exportedItems).toHaveLength(2) // buffered + new - expect(exportedItems[0]).toEqual([{ name: 'span1' }]) - expect(exportedItems[1]).toEqual([{ name: 'span2' }]) + expect(cert1).toBe(CERT_V1) + expect(cert2).toBe(CERT_V1) // Still cached, same mtime + }) }) - test('respects maxBufferSize', () => { - const { createLazyExporter } = require('../lib/exporter/LazyExporter') + test('reloads certificate when mtime changes (rotation)', () => { + writeSVIDFiles(CERT_V1, KEY_V1, BUNDLE_V1) - const lazyExporter = createLazyExporter( - () => { return { status: 'not_ready' } }, - { maxBufferSize: 2 } - ) + jest.isolateModules(() => { + const { initializeZTI, getCert, getKey } = require('../lib/zti') + initializeZTI() + + // First call - reads V1 + expect(getCert()).toBe(CERT_V1) + expect(getKey()).toBe(KEY_V1) - // Fill buffer beyond max - lazyExporter.export([{ name: 'span1' }], () => {}) - lazyExporter.export([{ name: 'span2' }], () => {}) - lazyExporter.export([{ name: 'span3' }], () => {}) + // Simulate certificate rotation: write new files with new mtime + writeSVIDFiles(CERT_V2, KEY_V2, BUNDLE_V2) + touchWithNewMtime(path.join(svidDir, 'test-svid.svid.pem')) - // Should only have 2 items (oldest dropped) - expect(lazyExporter._getBufferSize()).toBe(2) + // Second call - should detect mtime change and reload + expect(getCert()).toBe(CERT_V2) + expect(getKey()).toBe(KEY_V2) + }) }) - test('shutdown clears buffer', async () => { - const { createLazyExporter } = require('../lib/exporter/LazyExporter') + test('serves cached cert during transient read failure', () => { + writeSVIDFiles(CERT_V1, KEY_V1, BUNDLE_V1) - const lazyExporter = createLazyExporter(() => { - return { status: 'not_ready' } - }) + jest.isolateModules(() => { + const { initializeZTI, getCert } = require('../lib/zti') + initializeZTI() + + // First call - cache V1 + expect(getCert()).toBe(CERT_V1) - lazyExporter.export([{ name: 'span1' }], () => {}) - expect(lazyExporter._getBufferSize()).toBe(1) + // Simulate mid-rotation: touch mtime but make key file unreadable + touchWithNewMtime(path.join(svidDir, 'test-svid.svid.pem')) + fs.unlinkSync(path.join(svidDir, 'test-svid.svid.key')) - await lazyExporter.shutdown() - expect(lazyExporter._getBufferSize()).toBe(0) + // Should serve cached V1 despite read failure + expect(getCert()).toBe(CERT_V1) + }) }) - test('forceFlush creates exporter and flushes if ready', async () => { - const { createLazyExporter } = require('../lib/exporter/LazyExporter') - - let ready = false - const exportedItems = [] - const mockExporter = { - export: (items, cb) => { - exportedItems.push(items) - cb({ code: 0 }) - }, - forceFlush: jest.fn().mockResolvedValue() - } + test('throws when files missing and no cache', () => { + // Don't create SVID files + + jest.isolateModules(() => { + const { initializeZTI, getCert } = require('../lib/zti') + initializeZTI() - const lazyExporter = createLazyExporter(() => { - if (!ready) return { status: 'not_ready' } - return { status: 'ready', exporter: mockExporter } + expect(() => getCert()).toThrow() }) + }) - // Buffer some items - lazyExporter.export([{ name: 'span1' }], () => {}) + test('getCert/getKey return rotated values when mtime changes', () => { + writeSVIDFiles(CERT_V1, KEY_V1, BUNDLE_V1) - // forceFlush when not ready - should not throw - await lazyExporter.forceFlush() - expect(lazyExporter._getBufferSize()).toBe(1) // Still buffered + jest.isolateModules(() => { + const { initializeZTI, getCert, getKey } = require('../lib/zti') - // Make ready and forceFlush - ready = true - await lazyExporter.forceFlush() + initializeZTI() - expect(lazyExporter._isExporterCreated()).toBe(true) - expect(lazyExporter._getBufferSize()).toBe(0) - expect(exportedItems).toHaveLength(1) - expect(mockExporter.forceFlush).toHaveBeenCalled() - }) + // First call - returns V1 + expect(getCert()).toBe(CERT_V1) + expect(getKey()).toBe(KEY_V1) - test('does not retry after permanent failure', () => { - const { createLazyExporter } = require('../lib/exporter/LazyExporter') + // Simulate certificate rotation: write new files with new mtime + writeSVIDFiles(CERT_V2, KEY_V2, BUNDLE_V2) + touchWithNewMtime(path.join(svidDir, 'test-svid.svid.pem')) - let createCalled = 0 - const lazyExporter = createLazyExporter(() => { - createCalled++ - return { status: 'error', error: 'Permanent configuration error' } + // Second call - returns V2 (rotation detected) + expect(getCert()).toBe(CERT_V2) + expect(getKey()).toBe(KEY_V2) }) + }) + + test('createZTIAgentFactory returns sync factory that uses getCert/getKey', () => { + writeSVIDFiles(CERT_V1, KEY_V1, BUNDLE_V1) - // First export - tries to create - lazyExporter.export([{ name: 'span1' }], () => {}) - expect(createCalled).toBe(1) + jest.isolateModules(() => { + const { createZTIAgentFactory } = require('../lib/utils') - // Second export - should not retry (permanent failure) - lazyExporter.export([{ name: 'span2' }], () => {}) - expect(createCalled).toBe(1) // Still 1, not retried + const factory = createZTIAgentFactory() + expect(factory).not.toBeNull() + expect(typeof factory).toBe('function') + + // Factory is sync and returns an Agent with current certs + const agent = factory() + expect(agent.options.cert).toBe(CERT_V1) + expect(agent.options.key).toBe(KEY_V1) + expect(agent.options.keepAlive).toBe(true) + }) }) }) diff --git a/test/tracing-zti.test.js b/test/tracing-zti.test.js index 138a3a35..2f20d326 100644 --- a/test/tracing-zti.test.js +++ b/test/tracing-zti.test.js @@ -1,9 +1,9 @@ /** - * Integration tests for ZTI + Tracing with LazyExporter + * Integration tests for ZTI + Tracing with dynamic certificate rotation * - * With the lazy exporter refactor, the complexity of waiting for ZTI credentials - * is moved into the exporter layer. The app starts immediately with sync initialization, - * and telemetry is buffered until SVID files become available. + * With dynamic https.Agent cert/key functions, certificates are loaded on-demand + * when new TCP connections are established. The mtime-based caching ensures + * minimal disk reads while supporting automatic certificate rotation. */ const cds = require('@sap/cds') @@ -77,7 +77,6 @@ describe('Tracing with ZTI integration', () => { delete require.cache[require.resolve('../lib/tracing')] delete require.cache[require.resolve('../lib/metrics')] delete require.cache[require.resolve('../lib/logging')] - delete require.cache[require.resolve('../lib/exporter/LazyExporter')] }) afterEach(() => { @@ -92,8 +91,11 @@ describe('Tracing with ZTI integration', () => { delete process.env.cds_requires_telemetry_tracing_exporter }) - test('telemetry setup completes synchronously even without SVID files', () => { - // SVID files don't exist yet - but setup should still complete (sync) + test('TracerProvider is registered with ZTI credentials', () => { + // Create SVID files + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') + fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') cds.env.requires = { telemetry: { @@ -117,29 +119,38 @@ describe('Tracing with ZTI integration', () => { } } - // Setup should be sync and not throw + // Setup telemetry const setup = require('../lib/index') - expect(() => setup()).not.toThrow() + setup() - // We should have a real tracer (not NoopTracer) + // Verify we can get a tracer (not NoopTracer) const tracer = trace.getTracer('test') expect(tracer).toBeDefined() + // Create a span and verify it's a real span (NoopTracer returns zeros for traceId) const span = tracer.startSpan('test-span') expect(span.spanContext().traceId).toBeDefined() expect(span.spanContext().traceId).not.toBe('00000000000000000000000000000000') span.end() }) - test('TracerProvider is registered immediately (no NoopTracer caching)', () => { - // Create SVID files - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') + test('throws when mTLS credentials are missing (no ZTI files, no x509)', () => { + // Remove ZTI binding from VCAP_SERVICES (so no ZTI agent) + process.env.VCAP_SERVICES = JSON.stringify({ + 'caas-service': [{ + name: 'test-caas', + credentials: { + otlp: { + http: 'https://caas.example.com/otlp' + } + } + }] + }) + // No x509 credentials either cds.env.requires = { telemetry: { - kind: 'to-caas', + kind: 'telemetry-to-caas', credentials: { otlp: { http: 'https://caas.example.com/otlp' @@ -159,22 +170,14 @@ describe('Tracing with ZTI integration', () => { } } - // Setup telemetry - const setup = require('../lib/index') - setup() - - // Verify we can get a tracer (not NoopTracer) - const tracer = trace.getTracer('test') - expect(tracer).toBeDefined() + delete require.cache[require.resolve('../lib/zti')] - // Create a span and verify it's a real span (NoopTracer returns zeros for traceId) - const span = tracer.startSpan('test-span') - expect(span.spanContext().traceId).toBeDefined() - expect(span.spanContext().traceId).not.toBe('00000000000000000000000000000000') - span.end() + // Setup should throw because mTLS is required but no credentials available + const setup = require('../lib/index') + expect(() => setup()).toThrow('CaaS requires mTLS') }) - test('standard flow works without ZTI binding', () => { + test('standard flow works without ZTI binding using x509 credentials', () => { // Remove ZTI from VCAP_SERVICES process.env.VCAP_SERVICES = JSON.stringify({ 'caas-service': [{ @@ -225,34 +228,6 @@ describe('Tracing with ZTI integration', () => { span.end() }) - test('getCredsForCaaSMtls returns null when SVID files not ready', () => { - // Don't create SVID files - - jest.isolateModules(() => { - const { getCredsForCaaSMtls } = require('../lib/utils') - const creds = getCredsForCaaSMtls() - - // Should return null (not throw) when files don't exist - expect(creds).toEqual({ status: 'not_ready' }) - }) - }) - - test('getCredsForCaaSMtls returns credentials when SVID files exist', () => { - // Create SVID files - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.pem'), '-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.svid.key'), '-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----') - fs.writeFileSync(path.join(svidDir, 'test-svid.bundle.pem'), '-----BEGIN CERTIFICATE-----\nbundle\n-----END CERTIFICATE-----') - - jest.isolateModules(() => { - const { getCredsForCaaSMtls } = require('../lib/utils') - const creds = getCredsForCaaSMtls() - - expect(creds.status).toBe('ready') - expect(creds.cert).toContain('BEGIN CERTIFICATE') - expect(creds.key).toContain('BEGIN PRIVATE KEY') - }) - }) - test('x509 env var credentials work when USE_ZTI=false', () => { process.env.CDS_REQUIRES_TELEMETRY_USE_ZTI = 'false' @@ -261,17 +236,36 @@ describe('Tracing with ZTI integration', () => { cds.env.requires = { telemetry: { + kind: 'to-caas', + credentials: { + otlp: { + http: 'https://caas.example.com/otlp' + } + }, x509: { cert: Buffer.from('-----BEGIN CERTIFICATE-----\nenvvar\n-----END CERTIFICATE-----').toString('base64'), key: Buffer.from('-----BEGIN PRIVATE KEY-----\nenvvar\n-----END PRIVATE KEY-----').toString('base64') - } + }, + tracing: { + exporter: { + module: '@opentelemetry/sdk-trace-base', + class: 'InMemorySpanExporter' + }, + sampler: { + kind: 'AlwaysOnSampler' + }, + propagators: [] + }, + instrumentations: {} } } - const { getCredsForCaaSMtls } = require('../lib/utils') - const creds = getCredsForCaaSMtls() + const setup = require('../lib/index') + setup() - expect(creds.status).toBe('ready') - expect(Buffer.from(creds.cert, 'base64').toString()).toContain('envvar') + const tracer = trace.getTracer('test') + const span = tracer.startSpan('test-span') + expect(span.spanContext().traceId).toBeDefined() + span.end() }) })