diff --git a/.env b/.env index 2b6f0d3ae..9e62f00b8 100644 --- a/.env +++ b/.env @@ -61,6 +61,7 @@ DEMO_PACKAGES_REACT_PATH=packages/react DEMO_PACKAGES_WEB_SDK_PATH=packages/web-sdk DEMO_PACKAGES_WEB_TRACING_PATH=packages/web-tracing DEMO_PACKAGES_FETCH_INSTRUMENTATION_PATH=experimental/instrumentation-fetch +DEMO_PACKAGES_XHR_INSTRUMENTATION_PATH=experimental/instrumentation-xhr DEMO_PORT=5173 DEMO_PORT_HMR=24678 DEMO_SERVER_AGENT_HOST= diff --git a/demo/tsconfig.json b/demo/tsconfig.json index 13dd55bcb..2a69b713f 100644 --- a/demo/tsconfig.json +++ b/demo/tsconfig.json @@ -19,6 +19,7 @@ { "path": "../packages/react/tsconfig.esm.json" }, { "path": "../packages/web-sdk/tsconfig.esm.json" }, { "path": "../packages/web-tracing/tsconfig.esm.json" }, - { "path": "../experimental/instrumentation-fetch/tsconfig.esm.json" } + { "path": "../experimental/instrumentation-fetch/tsconfig.esm.json" }, + { "path": "../experimental/instrumentation-xhr/tsconfig.esm.json" } ] } diff --git a/docker-compose.yaml b/docker-compose.yaml index 6e70d9b67..e9e031159 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -59,7 +59,7 @@ services: - '${AGENT_CONFIG_PATH_LOCAL}:${AGENT_CONFIG_PATH}' - 'demo_demo_logs:${AGENT_LOGS_PATH}' entrypoint: - - '/bin/grafana-agent' + - '/bin/agent' - '-config.file=${AGENT_CONFIG_PATH}/${AGENT_CONFIG_FILE}' - '-config.expand-env' - '-config.enable-read-api' @@ -110,6 +110,8 @@ services: - 'demo_web_tracing_node_modules:${DEMO_WORKSPACE_PATH}/${DEMO_PACKAGES_WEB_TRACING_PATH}/node_modules' - 'demo_fetch_instrumentation_dist:${DEMO_WORKSPACE_PATH}/${DEMO_PACKAGES_FETCH_INSTRUMENTATION_PATH}/dist' - 'demo_fetch_instrumentation_node_modules:${DEMO_WORKSPACE_PATH}/${DEMO_PACKAGES_FETCH_INSTRUMENTATION_PATH}/node_modules' + - 'demo_xhr_instrumentation_dist:${DEMO_WORKSPACE_PATH}/${DEMO_PACKAGES_XHR_INSTRUMENTATION_PATH}/dist' + - 'demo_xhr_instrumentation_node_modules:${DEMO_WORKSPACE_PATH}/${DEMO_PACKAGES_XHR_INSTRUMENTATION_PATH}/node_modules' build: context: '.' args: @@ -121,6 +123,7 @@ services: DEMO_PACKAGES_WEB_SDK_PATH: '${DEMO_PACKAGES_WEB_SDK_PATH}' DEMO_PACKAGES_WEB_TRACING_PATH: '${DEMO_PACKAGES_WEB_TRACING_PATH}' DEMO_PACKAGES_FETCH_INSTRUMENTATION_PATH: '${DEMO_PACKAGES_FETCH_INSTRUMENTATION_PATH}' + DEMO_PACKAGES_XHR_INSTRUMENTATION_PATH: '${DEMO_PACKAGES_XHR_INSTRUMENTATION_PATH}' DEMO_PORT: '${DEMO_PORT}' DEMO_PORT_HMR: '${DEMO_PORT_HMR}' ports: @@ -152,3 +155,5 @@ volumes: demo_web_tracing_node_modules: demo_fetch_instrumentation_dist: demo_fetch_instrumentation_node_modules: + demo_xhr_instrumentation_dist: + demo_xhr_instrumentation_node_modules: diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 7a16c7a69..649e8a3d0 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -2,6 +2,14 @@ ## Next +### FetchInstrumentation + +- Change: fetch instrumentation log attribute "statusText" is now "status_text" + +### XHRInstrumentation + +- Added support for `XMLHttpRequest` instrumentation. + ## 1.1.4 ### FaroOtlpHttpTransport diff --git a/experimental/instrumentation-fetch/README.md b/experimental/instrumentation-fetch/README.md index aa9a09f5d..99f79310c 100644 --- a/experimental/instrumentation-fetch/README.md +++ b/experimental/instrumentation-fetch/README.md @@ -7,6 +7,9 @@ Use at your own risk.❗️ ## Installation and Usage +❗️*Warning*: This package is not interoperable with `@opentelemetry/instrumentation-fetch`. +Use one or the other❗️ + Add the instrumentation as outlined below. The instrumentation send the following events alongside respective request/response data like HTTP headers and other response properties like status codes the requests url and more. diff --git a/experimental/instrumentation-fetch/package.json b/experimental/instrumentation-fetch/package.json index fc2963526..29758a3aa 100644 --- a/experimental/instrumentation-fetch/package.json +++ b/experimental/instrumentation-fetch/package.json @@ -47,8 +47,10 @@ "url": "https://github.com/grafana/faro-web-sdk/issues" }, "homepage": "https://github.com/grafana/faro-web-sdk", + "dependencies": { + "@grafana/faro-core": "^1.2.0" + }, "devDependencies": { - "@grafana/faro-core": "^1.2.0", "@remix-run/web-fetch": "^4.3.4" }, "publishConfig": { diff --git a/experimental/instrumentation-fetch/src/constants.ts b/experimental/instrumentation-fetch/src/constants.ts index 03d0ca111..62983f523 100644 --- a/experimental/instrumentation-fetch/src/constants.ts +++ b/experimental/instrumentation-fetch/src/constants.ts @@ -3,7 +3,7 @@ interface StringResponse { ok: string; redirected: string; status: string; - statusText: string; + status_text: string; type: string; url: string; } @@ -18,7 +18,7 @@ export const responseProperties = (response: Partial): StringResponse ok: response.ok?.toString() ?? '', redirected: response.redirected?.toString() ?? '', status: response.status?.toString() ?? '', - statusText: response.statusText?.toString() ?? '', + status_text: response.statusText?.toString() ?? '', type: response.type?.toString() ?? '', url: response.url?.toString() ?? '', }; diff --git a/experimental/instrumentation-xhr/.browserslistrc b/experimental/instrumentation-xhr/.browserslistrc new file mode 100644 index 000000000..8528fd685 --- /dev/null +++ b/experimental/instrumentation-xhr/.browserslistrc @@ -0,0 +1 @@ +supports es6-module \ No newline at end of file diff --git a/experimental/instrumentation-xhr/README.md b/experimental/instrumentation-xhr/README.md new file mode 100644 index 000000000..5ae75224e --- /dev/null +++ b/experimental/instrumentation-xhr/README.md @@ -0,0 +1,36 @@ +# @grafana/instrumentation-xhr + +Faro instrumentation of the JavaScript +[XMLHttpRequest (XHR)](https://developer.mozilla.org/en-US/docs/Glossary/XMLHttpRequest) API + +❗️*Warning*: this package is experimental and may be subject to frequent and breaking changes. +Use at your own risk.❗️ + +## Installation and Usage + +❗️*Warning*: This package is not interoperable with `@opentelemetry/instrumentation-xml-http-request`. +Use one or the other❗️ + +```ts +// index.ts +import { XHRInstrumentation } from '@grafana/faro-instrumentation-xhr'; +import { getWebInstrumentations, initializeFaro } from '@grafana/faro-react'; + +initializeFaro({ + // see the full set of options in the @grafana/faro-core README.md + instrumentations: [ + // Load the default Web instrumentations + ...getWebInstrumentations(), + // Add XHR instrumentation + new XHRInstrumentation({ + // specify ignoredUrls to prevent telemetry data from being sent to Faro when making requests to those URLs + ignoredUrls: [/^https:\/\/www\.google-analytics\.com\/collect/], + }), + ], +}); + +// myApi.ts +const req = new XMLHttpRequest(); +req.open('GET', '...'); +req.send(); // use XHR as normal - telemetry data is sent to your Faro endpoint +``` diff --git a/experimental/instrumentation-xhr/jest.config.js b/experimental/instrumentation-xhr/jest.config.js new file mode 100644 index 000000000..504f356cd --- /dev/null +++ b/experimental/instrumentation-xhr/jest.config.js @@ -0,0 +1,15 @@ +const { jestBaseConfig } = require('../../jest.config.base.js'); + +module.exports = { + ...jestBaseConfig, + roots: ['experimental/instrumentation-xhr/src'], + testEnvironment: 'jsdom', + moduleNameMapper: { + '^@remix-run/router$': '/index.ts', + '^@remix-run/web-blob$': require.resolve('@remix-run/web-blob'), + '^@remix-run/web-fetch$': require.resolve('@remix-run/web-fetch'), + '^@remix-run/web-form-data$': require.resolve('@remix-run/web-form-data'), + '^@remix-run/web-stream$': require.resolve('@remix-run/web-stream'), + '^@web3-storage/multipart-parser$': require.resolve('@web3-storage/multipart-parser'), + }, +}; diff --git a/experimental/instrumentation-xhr/package.json b/experimental/instrumentation-xhr/package.json new file mode 100644 index 000000000..e508bd58e --- /dev/null +++ b/experimental/instrumentation-xhr/package.json @@ -0,0 +1,62 @@ +{ + "name": "@grafana/faro-instrumentation-xhr", + "version": "1.2.0", + "description": "Faro XHR auto-instrumentation package", + "keywords": [ + "observability", + "apm", + "rum", + "logs", + "traces", + "metrics", + "XMLHttpRequest", + "xhr" + ], + "main": "./dist/cjs/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts", + "scripts": { + "test": "jest", + "start": "yarn watch", + "build": "run-s build:*", + "build:compile": "run-p build:compile:*", + "build:compile:cjs": "tsc --build tsconfig.cjs.json", + "build:compile:esm": "tsc --build tsconfig.esm.json", + "build:compile:bundle": "run-s build:compile:bundle:*", + "build:compile:bundle:create": "rollup -c ./rollup.config.js", + "build:compile:bundle:remove-extras": "rimraf dist/bundle/dist", + "watch": "run-s watch:compile", + "watch:compile": "yarn build:compile:cjs -w", + "clean": "rimraf dist/ yarn-error.log", + "quality": "run-s quality:*", + "quality:test": "jest", + "quality:format": "prettier --cache --cache-location=../../.cache/prettier/instrumentationXHR --ignore-path ../../.prettierignore -w \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"", + "quality:lint": "run-s quality:lint:*", + "quality:lint:eslint": "eslint --cache --cache-location ../../.cache/eslint/instrumentationXHR --ignore-path ../../.eslintignore \"./**/*.{js,jsx,ts,tsx}\"", + "quality:lint:prettier": "prettier --cache --cache-location=../../.cache/prettier/instrumentationXHR --ignore-path ../../.prettierignore -c \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"", + "quality:lint:md": "markdownlint README.md", + "quality:circular-deps": "madge --circular ." + }, + "dependencies": { + "@grafana/faro-core": "^1.2.0" + }, + "devDependencies": { + "@remix-run/web-fetch": "^4.3.4" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/grafana/faro-web-sdk.git", + "directory": "experimental/instrumentation-xhr" + }, + "publishConfig": { + "access": "public" + }, + "author": "Grafana Labs", + "license": "Apache-2.0", + "files": [ + ".browserslistrc", + "dist", + "README.md", + "LICENSE" + ] +} diff --git a/experimental/instrumentation-xhr/rollup.config.js b/experimental/instrumentation-xhr/rollup.config.js new file mode 100644 index 000000000..9268a3d76 --- /dev/null +++ b/experimental/instrumentation-xhr/rollup.config.js @@ -0,0 +1,3 @@ +const { getRollupConfigBase } = require('../../rollup.config.base.js'); + +module.exports = getRollupConfigBase('instrumentationXHR'); diff --git a/experimental/instrumentation-xhr/src/index.ts b/experimental/instrumentation-xhr/src/index.ts new file mode 100644 index 000000000..1a7cd2334 --- /dev/null +++ b/experimental/instrumentation-xhr/src/index.ts @@ -0,0 +1,3 @@ +export { XHRInstrumentation } from './instrumentation'; + +export { parseXHREvent } from './utils'; diff --git a/experimental/instrumentation-xhr/src/instrumentation.test.ts b/experimental/instrumentation-xhr/src/instrumentation.test.ts new file mode 100644 index 000000000..470c9f566 --- /dev/null +++ b/experimental/instrumentation-xhr/src/instrumentation.test.ts @@ -0,0 +1,59 @@ +import { initializeFaro } from '@grafana/faro-core'; +import { mockConfig, MockTransport } from '@grafana/faro-core/src/testUtils'; + +import { XHRInstrumentation } from './instrumentation'; + +describe('XHRInstrumentation', () => { + it('initialize XHRInstrumentation with default options', () => { + const instrumentation = new XHRInstrumentation({}); + + expect(instrumentation.name).toBe('@grafana/faro-web-sdk:instrumentation-xhr'); + }); + + it('initialize XHRInstrumentation with provided options', () => { + const instrumentation = new XHRInstrumentation({ ignoredUrls: ['https://example.com'] }); + instrumentation.initialize(); + + expect(instrumentation.getIgnoredUrls()).toEqual(['https://example.com']); + }); + + it('initialize XHRInstrumentation and parses request URL', () => { + const instrumentation = new XHRInstrumentation({}); + instrumentation.initialize(); + + expect(instrumentation.getRequestUrl(new URL('https://example.com'))).toEqual('https://example.com/'); + expect(instrumentation.getRequestUrl('https://example.com')).toEqual('https://example.com'); + }); + + it('initialize XHRInstrumentation and send XHR request', () => { + const instrumentation = new XHRInstrumentation({}); + + initializeFaro(mockConfig({ instrumentations: [instrumentation] })); + + const fetchSpyOpen = jest.spyOn(instrumentation, 'originalOpen'); + const fetchSpySend = jest.spyOn(instrumentation, 'originalSend'); + + const xhr = new XMLHttpRequest(); + xhr.open('GET', 'https://grafana.com'); + expect(fetchSpyOpen).toHaveBeenCalledTimes(1); + + xhr.send(); + expect(fetchSpySend).toHaveBeenCalledTimes(1); + }); + + it('initialize XHRInstrumentation and send XHR request to ignoredUrl', () => { + const transport = new MockTransport(); + const instrumentation = new XHRInstrumentation({ ignoredUrls: ['https://example.com'] }); + + const fetchSpySend = jest.spyOn(instrumentation, 'originalSend'); + const faro = initializeFaro(mockConfig({ instrumentations: [instrumentation], transports: [transport] })); + faro.pause(); + + const xhr = new XMLHttpRequest(); + xhr.open('GET', 'https://example.com'); + xhr.send(); + + expect(transport.items).toHaveLength(0); + expect(fetchSpySend).toHaveBeenCalledTimes(1); + }); +}); diff --git a/experimental/instrumentation-xhr/src/instrumentation.ts b/experimental/instrumentation-xhr/src/instrumentation.ts new file mode 100644 index 000000000..9bdb2344e --- /dev/null +++ b/experimental/instrumentation-xhr/src/instrumentation.ts @@ -0,0 +1,150 @@ +import { BaseInstrumentation, faro, VERSION } from '@grafana/faro-core'; + +import { XHREventType, XHRInstrumentationOptions } from './types'; +import { parseXHREvent, parseXHRHeaders } from './utils'; + +export class XHRInstrumentation extends BaseInstrumentation { + readonly name = '@grafana/faro-web-sdk:instrumentation-xhr'; + readonly version = VERSION; + readonly originalOpen: XMLHttpRequest['open'] = XMLHttpRequest.prototype.open; + readonly originalSend: XMLHttpRequest['send'] = XMLHttpRequest.prototype.send; + private ignoredUrls: XHRInstrumentationOptions['ignoredUrls']; + + constructor(private options?: XHRInstrumentationOptions) { + super(); + } + + /** + * Initialize XMLHttpRequest instrumentation - XMLHttpRequest.prototype.open and XMLHttpRequest.prototype.send become instrumented + * and event listeners are added to the XMLHttpRequest instance + */ + initialize(): void { + this.internalLogger.info('Initializing XHR instrumentation'); + this.ignoredUrls = this.options?.ignoredUrls ?? this.getTransportIgnoreUrls(); + const instrumentation = this; + instrumentXMLHttpRequestOpen(); + instrumentXMLHttpRequestSend(); + + /** + * XMLHttpRequest.prototype.open becomes instrumented and the parameter defaults are properly passed to the original function + */ + function instrumentXMLHttpRequestOpen() { + XMLHttpRequest.prototype.open = function ( + method: string, + url: string | URL, + async?: boolean, + username?: string | null, + password?: string | null + ): void { + // @ts-expect-error - _url should be attached to "this" + this._url = url; + + return instrumentation.originalOpen.apply(this, [ + method, + url, + async === undefined ? true : !!async, + username === undefined ? null : username, + password === undefined ? null : password, + ]); + }; + } + + /** + * XMLHttpRequest.prototype.send becomes instrumented and the parameter defaults are properly passed to the original function + */ + function instrumentXMLHttpRequestSend() { + XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null | undefined): void { + // If the request url matches an ignored url, do not instrument the request + if ( + // @ts-expect-error - _url is attached to "this" in the open function above + instrumentation.ignoredUrls?.some((ignoredUrl) => instrumentation.getRequestUrl(this._url).match(ignoredUrl)) + ) { + return instrumentation.originalSend.apply(this, [body === undefined ? null : body]); + } + + this.addEventListener('load', loadEventListener.bind(this)); + this.addEventListener('abort', abortEventListener.bind(this)); + this.addEventListener('error', errorEventListener.bind(this)); + this.addEventListener('timeout', timeoutEventListener.bind(this)); + + return instrumentation.originalSend?.apply(this, [body === undefined ? null : body]); + }; + } + + /** + * Event listener function that is called when an XHR request is successfully completed. + * Pushes a log entry to the Faro API with information about the completed request. + * Removes the event listener to prevent duplicate log entries. + */ + function loadEventListener(this: XMLHttpRequest, event: ProgressEvent) { + faro.api.pushEvent(XHREventType.LOAD, { + ...parseXHREvent(this, event), + ...parseXHRHeaders(this), + }); + + this.removeEventListener('load', loadEventListener); + } + /** + * Event listener function that is called when an XHR request is aborted. + * Pushes a log entry to the Faro API with information about the aborted request. + * Removes the event listener to prevent duplicate log entries. + */ + function abortEventListener(this: XMLHttpRequest, event: ProgressEvent) { + faro.api.pushEvent(XHREventType.ABORT, { + ...parseXHREvent(this, event), + ...parseXHRHeaders(this), + }); + + this.removeEventListener('abort', abortEventListener); + } + + /** + * Event listener function that is called when an XHR request encounters an error. + * Pushes a log entry to the Faro API with information about the errored request. + * Removes the event listener to prevent duplicate log entries. + */ + function errorEventListener(this: XMLHttpRequest, event: ProgressEvent) { + faro.api.pushEvent(XHREventType.ERROR, { + ...parseXHREvent(this, event), + ...parseXHRHeaders(this), + }); + + this.removeEventListener('error', errorEventListener); + } + + /** + * Event listener function that is called when an XHR request times out. + * Pushes a log entry to the Faro API with information about the timed out request. + * Removes the event listener to prevent duplicate log entries. + */ + function timeoutEventListener(this: XMLHttpRequest, event: ProgressEvent) { + faro.api.pushEvent(XHREventType.TIMEOUT, { + ...parseXHREvent(this, event), + ...parseXHRHeaders(this), + }); + + this.removeEventListener('timeout', timeoutEventListener); + } + } + + /** + * Get the list of ignored urls from all transports + */ + private getTransportIgnoreUrls(): Array { + return faro?.transports?.transports?.flatMap((transport) => transport.getIgnoreUrls()); + } + + /** + * Get the list of ignored urls from all transports + */ + getIgnoredUrls(): Array { + return this.ignoredUrls ?? []; + } + + /** + * Parse the input object into a string URL + */ + getRequestUrl(input: string | URL): string { + return input instanceof URL ? input.href : input; + } +} diff --git a/experimental/instrumentation-xhr/src/types.ts b/experimental/instrumentation-xhr/src/types.ts new file mode 100644 index 000000000..45210e56c --- /dev/null +++ b/experimental/instrumentation-xhr/src/types.ts @@ -0,0 +1,11 @@ +export interface XHRInstrumentationOptions { + // For these URLs no events will be tracked + ignoredUrls?: Array; +} + +export enum XHREventType { + LOAD = 'faro.xhr.load', + ABORT = 'faro.xhr.abort', + ERROR = 'faro.xhr.error', + TIMEOUT = 'faro.xhr.timeout', +} diff --git a/experimental/instrumentation-xhr/src/utils.ts b/experimental/instrumentation-xhr/src/utils.ts new file mode 100644 index 000000000..68d1cfa6e --- /dev/null +++ b/experimental/instrumentation-xhr/src/utils.ts @@ -0,0 +1,28 @@ +// This code parses the headers from an XMLHttpRequest and returns them as an object. +export const parseXHRHeaders = (context: XMLHttpRequest): Record => { + const headers = context.getAllResponseHeaders().split('\r\n'); + const headerMap: Record = {}; + + for (const header of headers) { + const [key, value] = header.split(': '); + if (key && value) { + headerMap[`header_${key}`] = value; + } + } + + return headerMap; +}; + +// This code parses the xhr event and returns a record of the request url, response url, bytes loaded, status text, and status code. +export const parseXHREvent = (context: XMLHttpRequest, event: ProgressEvent): Record => { + // @ts-expect-error - _url is attached to the xhr object in XMLHttpRequest.prototype.open + const { _url, responseURL, statusText, status } = context; + + return { + request_url: _url?.toString() ?? '', + response_url: responseURL?.toString() ?? '', + bytes_loaded: event.loaded?.toString() ?? '', + status_text: statusText ?? '', + status: status?.toString() ?? '', + }; +}; diff --git a/experimental/instrumentation-xhr/tsconfig.cjs.json b/experimental/instrumentation-xhr/tsconfig.cjs.json new file mode 100644 index 000000000..87492cf60 --- /dev/null +++ b/experimental/instrumentation-xhr/tsconfig.cjs.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.cjs.json", + "compilerOptions": { + "declarationDir": "./dist/types", + "outDir": "./dist/cjs", + "rootDir": "./src", + "tsBuildInfoFile": "../../.cache/tsc/instrumentationXHR.cjs.tsbuildinfo" + }, + "include": ["./src"], + "exclude": ["**/*.test.ts"], + "references": [{ "path": "../../packages/core/tsconfig.spec.json" }] +} diff --git a/experimental/instrumentation-xhr/tsconfig.esm.json b/experimental/instrumentation-xhr/tsconfig.esm.json new file mode 100644 index 000000000..6b70771ac --- /dev/null +++ b/experimental/instrumentation-xhr/tsconfig.esm.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.esm.json", + "compilerOptions": { + "declarationDir": "./dist/types", + "outDir": "./dist/esm", + "rootDir": "./src", + "tsBuildInfoFile": "../../.cache/tsc/instrumentationXHR.esm.tsbuildinfo" + }, + "include": ["./src"], + "exclude": ["**/*.test.ts"], + "references": [{ "path": "../../packages/core/tsconfig.spec.json" }] +} diff --git a/experimental/instrumentation-xhr/tsconfig.json b/experimental/instrumentation-xhr/tsconfig.json new file mode 100644 index 000000000..db2455a0e --- /dev/null +++ b/experimental/instrumentation-xhr/tsconfig.json @@ -0,0 +1,7 @@ +{ + "references": [ + { "path": "./tsconfig.cjs.json" }, + { "path": "./tsconfig.esm.json" }, + { "path": "./tsconfig.spec.json" } + ] +} diff --git a/experimental/instrumentation-xhr/tsconfig.spec.json b/experimental/instrumentation-xhr/tsconfig.spec.json new file mode 100644 index 000000000..6f874eecd --- /dev/null +++ b/experimental/instrumentation-xhr/tsconfig.spec.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.spec.json", + "compilerOptions": { + "declarationDir": "./dist/types", + "outDir": "./dist/spec", + "rootDir": "../../", + "tsBuildInfoFile": "../../.cache/tsc/instrumentationXHR.spec.tsbuildinfo" + }, + "include": ["./src"], + "references": [{ "path": "../../packages/core/tsconfig.spec.json" }] +} diff --git a/rollup.config.base.js b/rollup.config.base.js index eb16d0f44..3704fce94 100644 --- a/rollup.config.base.js +++ b/rollup.config.base.js @@ -46,6 +46,12 @@ const modules = { globalName: 'GrafanaFaroInstrumentationFetch', externals: [], }, + instrumentationXHR: { + name: '@grafana/faro-instrumentation-xhr', + bundleName: 'faro-instrumentation-xhr', + globalName: 'GrafanaFaroInstrumentationXHR', + externals: [], + }, }; exports.getRollupConfigBase = (moduleName) => {