From e8c30c86f0bae98d5a90fb6894380fad7b3cfeb3 Mon Sep 17 00:00:00 2001 From: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:13:05 +0200 Subject: [PATCH] Frontend: Support tracing with Faro (#91237) Move code from previous PR --- conf/sample.ini | 3 + .../setup-grafana/configure-grafana/_index.md | 4 + package.json | 1 + packages/grafana-data/src/types/config.ts | 1 + packages/grafana-runtime/src/config.ts | 1 + .../setting_grafana_javascript_agent.go | 2 + .../GrafanaJavascriptAgentBackend.test.ts | 1 + .../GrafanaJavascriptAgentBackend.ts | 7 + yarn.lock | 201 +++++++++++++++++- 9 files changed, 214 insertions(+), 7 deletions(-) diff --git a/conf/sample.ini b/conf/sample.ini index d6cdfb751d5..751dc2ba146 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -1107,6 +1107,9 @@ # Should webvitals instrumentation be enabled, only affects Grafana Javascript Agent ;instrumentations_webvitals_enabled = false +# Should tracing instrumentation be enabled, only affects Grafana Javascript Agent +; instrumentations_tracing_enabled = false + # Api Key, only applies to Grafana Javascript Agent provider ;api_key = testApiKey diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index dc9d24f5666..7f0257e95ed 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -1490,6 +1490,10 @@ Turn on console instrumentation. Only affects Grafana Javascript Agent Turn on webvitals instrumentation. Only affects Grafana Javascript Agent +### instrumentations_tracing_enabled + +Turns on tracing instrumentation. Only affects Grafana Javascript Agent. + ### api_key If `custom_endpoint` required authentication, you can set the api key here. Only relevant for Grafana Javascript Agent provider. diff --git a/package.json b/package.json index 38430370281..96746d0548c 100644 --- a/package.json +++ b/package.json @@ -253,6 +253,7 @@ "@grafana/experimental": "1.7.13", "@grafana/faro-core": "^1.3.6", "@grafana/faro-web-sdk": "^1.3.6", + "@grafana/faro-web-tracing": "^1.8.2", "@grafana/flamegraph": "workspace:*", "@grafana/google-sdk": "0.1.2", "@grafana/lezer-logql": "0.2.6", diff --git a/packages/grafana-data/src/types/config.ts b/packages/grafana-data/src/types/config.ts index aa327bbf113..1cf37071e23 100644 --- a/packages/grafana-data/src/types/config.ts +++ b/packages/grafana-data/src/types/config.ts @@ -62,6 +62,7 @@ export interface GrafanaJavascriptAgentConfig { errorInstrumentalizationEnabled: boolean; consoleInstrumentalizationEnabled: boolean; webVitalsInstrumentalizationEnabled: boolean; + tracingInstrumentalizationEnabled: boolean; apiKey: string; } diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index 45f92e05a43..868a3d8a8fb 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -117,6 +117,7 @@ export class GrafanaBootConfig implements GrafanaConfig { errorInstrumentalizationEnabled: true, consoleInstrumentalizationEnabled: false, webVitalsInstrumentalizationEnabled: false, + tracingInstrumentalizationEnabled: false, }; pluginCatalogURL = 'https://grafana.com/grafana/plugins/'; pluginAdminEnabled = true; diff --git a/pkg/setting/setting_grafana_javascript_agent.go b/pkg/setting/setting_grafana_javascript_agent.go index a623bd242ae..0a58b26995a 100644 --- a/pkg/setting/setting_grafana_javascript_agent.go +++ b/pkg/setting/setting_grafana_javascript_agent.go @@ -8,6 +8,7 @@ type GrafanaJavascriptAgent struct { ErrorInstrumentalizationEnabled bool `json:"errorInstrumentalizationEnabled"` ConsoleInstrumentalizationEnabled bool `json:"consoleInstrumentalizationEnabled"` WebVitalsInstrumentalizationEnabled bool `json:"webVitalsInstrumentalizationEnabled"` + TracingInstrumentalizationEnabled bool `json:"tracingInstrumentalizationEnabled"` InternalLoggerLevel int `json:"internalLoggerLevel"` ApiKey string `json:"apiKey"` } @@ -22,6 +23,7 @@ func (cfg *Cfg) readGrafanaJavascriptAgentConfig() { ErrorInstrumentalizationEnabled: raw.Key("instrumentations_errors_enabled").MustBool(true), ConsoleInstrumentalizationEnabled: raw.Key("instrumentations_console_enabled").MustBool(true), WebVitalsInstrumentalizationEnabled: raw.Key("instrumentations_webvitals_enabled").MustBool(true), + TracingInstrumentalizationEnabled: raw.Key("instrumentations_tracing_enabled").MustBool(true), InternalLoggerLevel: raw.Key("internal_logger_level").MustInt(0), ApiKey: raw.Key("api_key").String(), } diff --git a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts index fd3631eec78..79001934c54 100644 --- a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts +++ b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.test.ts @@ -68,6 +68,7 @@ describe('GrafanaJavascriptAgentEchoBackend', () => { errorInstrumentalizationEnabled: true, consoleInstrumentalizationEnabled: true, webVitalsInstrumentalizationEnabled: true, + tracingInstrumentalizationEnabled: true, customEndpoint: '/log-grafana-javascript-agent', user: { email: 'darth.vader@sith.glx', diff --git a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts index c20b8ec34f1..abf06039856 100644 --- a/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts +++ b/public/app/core/services/echo/backends/grafana-javascript-agent/GrafanaJavascriptAgentBackend.ts @@ -10,6 +10,7 @@ import { FetchTransport, type Instrumentation, } from '@grafana/faro-web-sdk'; +import { TracingInstrumentation } from '@grafana/faro-web-tracing'; import { EchoBackend, EchoEvent, EchoEventType } from '@grafana/runtime'; import { EchoSrvTransport } from './EchoSrvTransport'; @@ -22,6 +23,7 @@ export interface GrafanaJavascriptAgentBackendOptions extends BrowserConfig { errorInstrumentalizationEnabled: boolean; consoleInstrumentalizationEnabled: boolean; webVitalsInstrumentalizationEnabled: boolean; + tracingInstrumentalizationEnabled: boolean; } export class GrafanaJavascriptAgentBackend @@ -49,6 +51,9 @@ export class GrafanaJavascriptAgentBackend if (options.webVitalsInstrumentalizationEnabled) { instrumentations.push(new WebVitalsInstrumentation()); } + if (options.tracingInstrumentalizationEnabled) { + instrumentations.push(new TracingInstrumentation()); + } // session instrumentation must be added! instrumentations.push(new SessionInstrumentation()); @@ -58,6 +63,7 @@ export class GrafanaJavascriptAgentBackend globalObjectKey: options.globalObjectKey || 'faro', preventGlobalExposure: options.preventGlobalExposure || false, app: { + name: 'grafana-frontend', version: options.buildInfo.version, environment: options.buildInfo.env, }, @@ -68,6 +74,7 @@ export class GrafanaJavascriptAgentBackend 'ResizeObserver loop completed', 'Non-Error exception captured with keys', ], + ignoreUrls: [new RegExp(`/*${options.customEndpoint}/`), /frontend-metrics/], sessionTracking: { persistent: true, }, diff --git a/yarn.lock b/yarn.lock index 7d519001947..67de12f66e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3215,7 +3215,7 @@ __metadata: languageName: node linkType: hard -"@grafana/faro-web-sdk@npm:1.8.2, @grafana/faro-web-sdk@npm:^1.3.6": +"@grafana/faro-web-sdk@npm:1.8.2, @grafana/faro-web-sdk@npm:^1.3.6, @grafana/faro-web-sdk@npm:^1.8.2": version: 1.8.2 resolution: "@grafana/faro-web-sdk@npm:1.8.2" dependencies: @@ -3226,6 +3226,26 @@ __metadata: languageName: node linkType: hard +"@grafana/faro-web-tracing@npm:^1.8.2": + version: 1.8.2 + resolution: "@grafana/faro-web-tracing@npm:1.8.2" + dependencies: + "@grafana/faro-web-sdk": "npm:^1.8.2" + "@opentelemetry/api": "npm:^1.9.0" + "@opentelemetry/context-zone": "npm:1.21.0" + "@opentelemetry/core": "npm:^1.25.0" + "@opentelemetry/exporter-trace-otlp-http": "npm:^0.52.0" + "@opentelemetry/instrumentation": "npm:^0.52.0" + "@opentelemetry/instrumentation-fetch": "npm:^0.52.0" + "@opentelemetry/instrumentation-xml-http-request": "npm:^0.52.0" + "@opentelemetry/otlp-transformer": "npm:^0.52.0" + "@opentelemetry/resources": "npm:^1.25.0" + "@opentelemetry/sdk-trace-web": "npm:^1.25.0" + "@opentelemetry/semantic-conventions": "npm:^1.25.0" + checksum: 10/6dd5d07b887de71f37176d01c68602677ebfb9fd9dbc4d6aa2456ec95fd9ceda7556ca71571f4cbaa59b126baebb8424cf34f977c6f3c10cbb3afab1692ebfc4 + languageName: node + linkType: hard + "@grafana/flamegraph@workspace:*, @grafana/flamegraph@workspace:packages/grafana-flamegraph": version: 0.0.0-use.local resolution: "@grafana/flamegraph@workspace:packages/grafana-flamegraph" @@ -5318,6 +5338,26 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/context-zone-peer-dep@npm:1.21.0": + version: 1.21.0 + resolution: "@opentelemetry/context-zone-peer-dep@npm:1.21.0" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.8.0" + zone.js: ^0.10.2 || ^0.11.0 || ^0.13.0 + checksum: 10/e89bfb8b5dd35ef86fd67f6bab9abd0d5c17027611b3d75f01419d9267a7ee8a1976af9198feabf8ea5f199badee7c0c5b34b2b3f3820fd5409aedc28c1abfc0 + languageName: node + linkType: hard + +"@opentelemetry/context-zone@npm:1.21.0": + version: 1.21.0 + resolution: "@opentelemetry/context-zone@npm:1.21.0" + dependencies: + "@opentelemetry/context-zone-peer-dep": "npm:1.21.0" + zone.js: "npm:^0.11.0" + checksum: 10/7b925ad7a4d9087b1d5ea39042cb8a9e30c7b21c675e9e470531e80579c891e80faec822bd70384a44f49a73284145150c0f01a205ff7958ce1eb2ae8fc93881 + languageName: node + linkType: hard + "@opentelemetry/core@npm:0.25.0": version: 0.25.0 resolution: "@opentelemetry/core@npm:0.25.0" @@ -5330,7 +5370,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/core@npm:1.25.1": +"@opentelemetry/core@npm:1.25.1, @opentelemetry/core@npm:^1.25.0": version: 1.25.1 resolution: "@opentelemetry/core@npm:1.25.1" dependencies: @@ -5356,7 +5396,78 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/otlp-transformer@npm:^0.52.0": +"@opentelemetry/exporter-trace-otlp-http@npm:^0.52.0": + version: 0.52.1 + resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.52.1" + dependencies: + "@opentelemetry/core": "npm:1.25.1" + "@opentelemetry/otlp-exporter-base": "npm:0.52.1" + "@opentelemetry/otlp-transformer": "npm:0.52.1" + "@opentelemetry/resources": "npm:1.25.1" + "@opentelemetry/sdk-trace-base": "npm:1.25.1" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/12580244204ac156c0a5d059e275a8d9f1fd5b705d425b6e1c6b3045740d157f5d55190e88c430a528cdc5fa92ad8f3ae4fb157e253f9026462728b189d6b6c3 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-fetch@npm:^0.52.0": + version: 0.52.1 + resolution: "@opentelemetry/instrumentation-fetch@npm:0.52.1" + dependencies: + "@opentelemetry/core": "npm:1.25.1" + "@opentelemetry/instrumentation": "npm:0.52.1" + "@opentelemetry/sdk-trace-web": "npm:1.25.1" + "@opentelemetry/semantic-conventions": "npm:1.25.1" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/0d21aa1c3170d77fe6e09f540304ba22d728a9ce0160e81934ab26c47545289aaca81c2f2dd435d5564c11d9a835e86641ff4bcdf6fd6185b6bd5a3edc0e04fa + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-xml-http-request@npm:^0.52.0": + version: 0.52.1 + resolution: "@opentelemetry/instrumentation-xml-http-request@npm:0.52.1" + dependencies: + "@opentelemetry/core": "npm:1.25.1" + "@opentelemetry/instrumentation": "npm:0.52.1" + "@opentelemetry/sdk-trace-web": "npm:1.25.1" + "@opentelemetry/semantic-conventions": "npm:1.25.1" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/02833d5d940a455979df7e02f425e4eae12f8f45e00ab59c85830be58e4e0a28294391f2cb1bc0b0fe29dc68df2f0945794e6d20b4ecf6409f6a24569b6080d7 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation@npm:0.52.1, @opentelemetry/instrumentation@npm:^0.52.0": + version: 0.52.1 + resolution: "@opentelemetry/instrumentation@npm:0.52.1" + dependencies: + "@opentelemetry/api-logs": "npm:0.52.1" + "@types/shimmer": "npm:^1.0.2" + import-in-the-middle: "npm:^1.8.1" + require-in-the-middle: "npm:^7.1.1" + semver: "npm:^7.5.2" + shimmer: "npm:^1.2.1" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/87761bd593f2b905d88d0531a3a2a7f4b0186334ae413b4c172a86bd4de0fd6d2f906a1bfd9dd7bd172a228a44fa7a680f5802a1570dfe2fadad0768e80bd7a8 + languageName: node + linkType: hard + +"@opentelemetry/otlp-exporter-base@npm:0.52.1": + version: 0.52.1 + resolution: "@opentelemetry/otlp-exporter-base@npm:0.52.1" + dependencies: + "@opentelemetry/core": "npm:1.25.1" + "@opentelemetry/otlp-transformer": "npm:0.52.1" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 10/0fbe164e04b05a7ba230f5532ecfbf1bb2156e2a7330090e1880c5b1db05f89ed4e7a89a67b8297415389975045377efbfb82410685b666581eaa6782c353450 + languageName: node + linkType: hard + +"@opentelemetry/otlp-transformer@npm:0.52.1, @opentelemetry/otlp-transformer@npm:^0.52.0": version: 0.52.1 resolution: "@opentelemetry/otlp-transformer@npm:0.52.1" dependencies: @@ -5385,7 +5496,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/resources@npm:1.25.1": +"@opentelemetry/resources@npm:1.25.1, @opentelemetry/resources@npm:^1.25.0": version: 1.25.1 resolution: "@opentelemetry/resources@npm:1.25.1" dependencies: @@ -5464,6 +5575,19 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/sdk-trace-web@npm:1.25.1, @opentelemetry/sdk-trace-web@npm:^1.25.0": + version: 1.25.1 + resolution: "@opentelemetry/sdk-trace-web@npm:1.25.1" + dependencies: + "@opentelemetry/core": "npm:1.25.1" + "@opentelemetry/sdk-trace-base": "npm:1.25.1" + "@opentelemetry/semantic-conventions": "npm:1.25.1" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 10/0c12cac81b612b361704d20264c663fafb1ccfb43f3ab08c1c8ddccbdc009c02de4dd8f89129799344a6095767a3949e86bf38e4c4222992a432c2c352f77ba0 + languageName: node + linkType: hard + "@opentelemetry/semantic-conventions@npm:0.25.0": version: 0.25.0 resolution: "@opentelemetry/semantic-conventions@npm:0.25.0" @@ -5471,7 +5595,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:1.25.1": +"@opentelemetry/semantic-conventions@npm:1.25.1, @opentelemetry/semantic-conventions@npm:^1.25.0": version: 1.25.1 resolution: "@opentelemetry/semantic-conventions@npm:1.25.1" checksum: 10/d84745a9e21a451560a293b4e6f996ee7c67bb983a7ec05408c23d207c6fc8b73a0af9c1ebea26e3acb4f0e3405ea7eb0d6bdf9adad9f954d60829bbb48ea307 @@ -9290,6 +9414,13 @@ __metadata: languageName: node linkType: hard +"@types/shimmer@npm:^1.0.2": + version: 1.2.0 + resolution: "@types/shimmer@npm:1.2.0" + checksum: 10/f081a31d826ce7bfe8cc7ba8129d2b1dffae44fd580eba4fcf741237646c4c2494ae6de2cada4b7713d138f35f4bc512dbf01311d813dee82020f97d7d8c491c + languageName: node + linkType: hard + "@types/sinonjs__fake-timers@npm:8.1.1": version: 8.1.1 resolution: "@types/sinonjs__fake-timers@npm:8.1.1" @@ -10368,6 +10499,15 @@ __metadata: languageName: node linkType: hard +"acorn-import-attributes@npm:^1.9.5": + version: 1.9.5 + resolution: "acorn-import-attributes@npm:1.9.5" + peerDependencies: + acorn: ^8 + checksum: 10/8bfbfbb6e2467b9b47abb4d095df717ab64fce2525da65eabee073e85e7975fb3a176b6c8bba17c99a7d8ede283a10a590272304eb54a93c4aa1af9790d47a8b + languageName: node + linkType: hard + "acorn-jsx@npm:^5.3.1, acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" @@ -12167,7 +12307,7 @@ __metadata: languageName: node linkType: hard -"cjs-module-lexer@npm:^1.0.0, cjs-module-lexer@npm:^1.2.3": +"cjs-module-lexer@npm:^1.0.0, cjs-module-lexer@npm:^1.2.2, cjs-module-lexer@npm:^1.2.3": version: 1.3.1 resolution: "cjs-module-lexer@npm:1.3.1" checksum: 10/6629188d5ce74b57e5dce2222db851b5496a8d65b533a05957fb24089a3cec8d769378013c375a954c5a0f7522cde6a36d5a65bfd88f5575cb2de3176046fa8e @@ -17166,6 +17306,7 @@ __metadata: "@grafana/experimental": "npm:1.7.13" "@grafana/faro-core": "npm:^1.3.6" "@grafana/faro-web-sdk": "npm:^1.3.6" + "@grafana/faro-web-tracing": "npm:^1.8.2" "@grafana/flamegraph": "workspace:*" "@grafana/google-sdk": "npm:0.1.2" "@grafana/lezer-logql": "npm:0.2.6" @@ -18311,6 +18452,18 @@ __metadata: languageName: node linkType: hard +"import-in-the-middle@npm:^1.8.1": + version: 1.11.0 + resolution: "import-in-the-middle@npm:1.11.0" + dependencies: + acorn: "npm:^8.8.2" + acorn-import-attributes: "npm:^1.9.5" + cjs-module-lexer: "npm:^1.2.2" + module-details-from-path: "npm:^1.0.3" + checksum: 10/e6f79c9de3f1c1907856fb48b99cd2273c5f9d78eb72124ddd142382e41b6bdf1f64c028ced9e5dbfd015f282e6e3b48bd1f53dd0452e2f0a26436ee42b005d8 + languageName: node + linkType: hard + "import-local@npm:3.1.0, import-local@npm:^3.0.2": version: 3.1.0 resolution: "import-local@npm:3.1.0" @@ -22233,6 +22386,13 @@ __metadata: languageName: node linkType: hard +"module-details-from-path@npm:^1.0.3": + version: 1.0.3 + resolution: "module-details-from-path@npm:1.0.3" + checksum: 10/f93226e9154fc8cb91f4609b639167ec7ad9155b30be4924d9717656648a3ae5f181d4e2338434d4c5afc7b5f4c10dd3b64109e5b89a4be70b20a25ba3573d54 + languageName: node + linkType: hard + "moment-timezone@npm:0.5.45": version: 0.5.45 resolution: "moment-timezone@npm:0.5.45" @@ -26506,6 +26666,17 @@ __metadata: languageName: node linkType: hard +"require-in-the-middle@npm:^7.1.1": + version: 7.4.0 + resolution: "require-in-the-middle@npm:7.4.0" + dependencies: + debug: "npm:^4.3.5" + module-details-from-path: "npm:^1.0.3" + resolve: "npm:^1.22.8" + checksum: 10/0ca30ad6a6183423f38599709fc8a670682db85b581a66cb31ea31342e8ba2ce7dca44ee29e8cfe4fb59ffcb0c2b0f9b77d44a10cdc7535c7c2907028e53afbf + languageName: node + linkType: hard + "requires-port@npm:^1.0.0": version: 1.0.0 resolution: "requires-port@npm:1.0.0" @@ -27149,7 +27320,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.6.3, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2": +"semver@npm:7.6.3, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2": version: 7.6.3 resolution: "semver@npm:7.6.3" bin: @@ -27331,6 +27502,13 @@ __metadata: languageName: node linkType: hard +"shimmer@npm:^1.2.1": + version: 1.2.1 + resolution: "shimmer@npm:1.2.1" + checksum: 10/aa0d6252ad1c682a4fdfda69e541be987f7a265ac7b00b1208e5e48cc68dc55f293955346ea4c71a169b7324b82c70f8400b3d3d2d60b2a7519f0a3522423250 + languageName: node + linkType: hard + "should-equal@npm:^2.0.0": version: 2.0.0 resolution: "should-equal@npm:2.0.0" @@ -31122,6 +31300,15 @@ __metadata: languageName: node linkType: hard +"zone.js@npm:^0.11.0": + version: 0.11.8 + resolution: "zone.js@npm:0.11.8" + dependencies: + tslib: "npm:^2.3.0" + checksum: 10/643c71eb9dc9a09510ab0d5c27ee4402f21d09b1aad1d5e1e0b7e1b98e621686e3d82a59d2646a91d655e5d2d4471e43068494dfef4aac16170b67ef46212446 + languageName: node + linkType: hard + "zwitch@npm:^2.0.0": version: 2.0.4 resolution: "zwitch@npm:2.0.4"