diff --git a/common/api-review/telemetry-react.api.md b/common/api-review/telemetry-react.api.md new file mode 100644 index 00000000000..d7eeb9b53ea --- /dev/null +++ b/common/api-review/telemetry-react.api.md @@ -0,0 +1,17 @@ +## API Report File for "@firebase/telemetry-react" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { FirebaseOptions } from '@firebase/app'; + +// @public +export function FirebaseTelemetry({ firebaseOptions }: { + firebaseOptions?: FirebaseOptions; +}): null; + + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/api-review/telemetry.api.md b/common/api-review/telemetry.api.md index 066c7c9e4e2..623299b1ee8 100644 --- a/common/api-review/telemetry.api.md +++ b/common/api-review/telemetry.api.md @@ -6,6 +6,7 @@ import { AnyValueMap } from '@opentelemetry/api-logs'; import { FirebaseApp } from '@firebase/app'; +import { Instrumentation } from 'next'; import { LoggerProvider } from '@opentelemetry/sdk-logs'; // @public @@ -17,13 +18,7 @@ export function flush(telemetry: Telemetry): Promise; // @public export function getTelemetry(app?: FirebaseApp): Telemetry; -// @public (undocumented) -export namespace Instrumentation { - // Warning: (ae-forgotten-export) The symbol "InstrumentationOnRequestError" needs to be exported by the entry point index.d.ts - // - // (undocumented) - export type onRequestError = InstrumentationOnRequestError; -} +export { Instrumentation } // @public export const nextOnRequestError: Instrumentation.onRequestError; diff --git a/common/api-review/telemetry.react.api.md b/common/api-review/telemetry.react.api.md new file mode 100644 index 00000000000..560a395ca7f --- /dev/null +++ b/common/api-review/telemetry.react.api.md @@ -0,0 +1,17 @@ +## API Report File for "@firebase/telemetry/react" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { FirebaseOptions } from '@firebase/app'; + +// @public +export function FirebaseTelemetry({ firebaseOptions }: { + firebaseOptions?: FirebaseOptions; +}): null; + + +// (No @packageDocumentation comment for this package) + +``` diff --git a/config/webpack.test.js b/config/webpack.test.js index cc739313779..20a86740797 100644 --- a/config/webpack.test.js +++ b/config/webpack.test.js @@ -94,7 +94,7 @@ module.exports = { resolve: { modules: ['node_modules', path.resolve(__dirname, '../../node_modules')], mainFields: ['browser', 'module', 'main'], - extensions: ['.js', '.ts'], + extensions: ['.js', '.ts', '.tsx'], symlinks: true }, plugins: [ diff --git a/docs-devsite/_toc.yaml b/docs-devsite/_toc.yaml index 1b5243a1d11..b814910c792 100644 --- a/docs-devsite/_toc.yaml +++ b/docs-devsite/_toc.yaml @@ -687,7 +687,10 @@ toc: - title: UploadTaskSnapshot path: /docs/reference/js/storage.uploadtasksnapshot.md - title: telemetry - path: /docs/reference/js/telemetry.md + path: /docs/reference/js/telemetry_.md section: - title: Telemetry - path: /docs/reference/js/telemetry.telemetry.md + path: /docs/reference/js/telemetry_.telemetry.md +- title: telemetry/react + path: /docs/reference/js/telemetry_react.md + section: [] diff --git a/docs-devsite/telemetry.md b/docs-devsite/telemetry.md index fa62b62e7b8..c533e886f4f 100644 --- a/docs-devsite/telemetry.md +++ b/docs-devsite/telemetry.md @@ -11,128 +11,8 @@ https://github.com/firebase/firebase-js-sdk # telemetry package -## Functions - -| Function | Description | -| --- | --- | -| function(app, ...) | -| [getTelemetry(app)](./telemetry.md#gettelemetry_cf608e1) | Returns the default [Telemetry](./telemetry.telemetry.md#telemetry_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. | -| function(telemetry, ...) | -| [captureError(telemetry, error, attributes)](./telemetry.md#captureerror_862e6b3) | Enqueues an error to be uploaded to the Firebase Telemetry API. | -| [flush(telemetry)](./telemetry.md#flush_8975134) | Flushes all enqueued telemetry data immediately, instead of waiting for default batching. | - -## Interfaces - -| Interface | Description | -| --- | --- | -| [Telemetry](./telemetry.telemetry.md#telemetry_interface) | An instance of the Firebase Telemetry SDK.Do not create this instance directly. Instead, use [getTelemetry()](./telemetry.md#gettelemetry_cf608e1). | - -## Namespaces - -| Namespace | Description | +| Entry Point | Description | | --- | --- | -| [Instrumentation](./telemetry.instrumentation.md#instrumentation_namespace) | | - -## Variables - -| Variable | Description | -| --- | --- | -| [nextOnRequestError](./telemetry.md#nextonrequesterror) | Automatically report uncaught errors from server routes to Firebase Telemetry. | - -## function(app, ...) - -### getTelemetry(app) {:#gettelemetry_cf608e1} - -Returns the default [Telemetry](./telemetry.telemetry.md#telemetry_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. - -Signature: - -```typescript -export declare function getTelemetry(app?: FirebaseApp): Telemetry; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| app | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) to use. | - -Returns: - -[Telemetry](./telemetry.telemetry.md#telemetry_interface) - -The default [Telemetry](./telemetry.telemetry.md#telemetry_interface) instance for the given [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). - -### Example - - -```javascript -const telemetry = getTelemetry(app); - -``` - -## function(telemetry, ...) - -### captureError(telemetry, error, attributes) {:#captureerror_862e6b3} - -Enqueues an error to be uploaded to the Firebase Telemetry API. - -Signature: - -```typescript -export declare function captureError(telemetry: Telemetry, error: unknown, attributes?: AnyValueMap): void; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| telemetry | [Telemetry](./telemetry.telemetry.md#telemetry_interface) | The [Telemetry](./telemetry.telemetry.md#telemetry_interface) instance. | -| error | unknown | The caught exception, typically an | -| attributes | AnyValueMap | = Optional, arbitrary attributes to attach to the error log | - -Returns: - -void - -### flush(telemetry) {:#flush_8975134} - -Flushes all enqueued telemetry data immediately, instead of waiting for default batching. - -Signature: - -```typescript -export declare function flush(telemetry: Telemetry): Promise; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| telemetry | [Telemetry](./telemetry.telemetry.md#telemetry_interface) | The [Telemetry](./telemetry.telemetry.md#telemetry_interface) instance. | - -Returns: - -Promise<void> - -a promise which is resolved when all flushes are complete - -## nextOnRequestError - -Automatically report uncaught errors from server routes to Firebase Telemetry. - -Signature: - -```typescript -nextOnRequestError: Instrumentation.onRequestError -``` - -### Example - - -```javascript -// In instrumentation.ts (https://nextjs.org/docs/app/guides/instrumentation): -export { nextOnRequestError as onRequestError } from 'firebase/telemetry' - -``` +| [/](./telemetry_.md#@firebase/telemetry) | | +| [/react](./telemetry_react.md#@firebase/telemetry/react) | | diff --git a/docs-devsite/telemetry.instrumentation.md b/docs-devsite/telemetry_.instrumentation.md similarity index 87% rename from docs-devsite/telemetry.instrumentation.md rename to docs-devsite/telemetry_.instrumentation.md index 2e2be64711e..d1e2855f188 100644 --- a/docs-devsite/telemetry.instrumentation.md +++ b/docs-devsite/telemetry_.instrumentation.md @@ -20,7 +20,7 @@ export declare namespace Instrumentation | Type Alias | Description | | --- | --- | -| [onRequestError](./telemetry.instrumentation.md#instrumentationonrequesterror) | | +| [onRequestError](./telemetry_.instrumentation.md#instrumentationonrequesterror) | | ## Instrumentation.onRequestError diff --git a/docs-devsite/telemetry_.md b/docs-devsite/telemetry_.md new file mode 100644 index 00000000000..07e5c629012 --- /dev/null +++ b/docs-devsite/telemetry_.md @@ -0,0 +1,138 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# @firebase/telemetry + +## Functions + +| Function | Description | +| --- | --- | +| function(app, ...) | +| [getTelemetry(app)](./telemetry_.md#gettelemetry_cf608e1) | Returns the default [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. | +| function(telemetry, ...) | +| [captureError(telemetry, error, attributes)](./telemetry_.md#captureerror_862e6b3) | Enqueues an error to be uploaded to the Firebase Telemetry API. | +| [flush(telemetry)](./telemetry_.md#flush_8975134) | Flushes all enqueued telemetry data immediately, instead of waiting for default batching. | + +## Interfaces + +| Interface | Description | +| --- | --- | +| [Telemetry](./telemetry_.telemetry.md#telemetry_interface) | An instance of the Firebase Telemetry SDK.Do not create this instance directly. Instead, use [getTelemetry()](./telemetry_.md#gettelemetry_cf608e1). | + +## Namespaces + +| Namespace | Description | +| --- | --- | +| [Instrumentation](./telemetry_.instrumentation.md#instrumentation_namespace) | | + +## Variables + +| Variable | Description | +| --- | --- | +| [nextOnRequestError](./telemetry_.md#nextonrequesterror) | Automatically report uncaught errors from server routes to Firebase Telemetry. | + +## function(app, ...) + +### getTelemetry(app) {:#gettelemetry_cf608e1} + +Returns the default [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. + +Signature: + +```typescript +export declare function getTelemetry(app?: FirebaseApp): Telemetry; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| app | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) to use. | + +Returns: + +[Telemetry](./telemetry_.telemetry.md#telemetry_interface) + +The default [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance for the given [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). + +### Example + + +```javascript +const telemetry = getTelemetry(app); + +``` + +## function(telemetry, ...) + +### captureError(telemetry, error, attributes) {:#captureerror_862e6b3} + +Enqueues an error to be uploaded to the Firebase Telemetry API. + +Signature: + +```typescript +export declare function captureError(telemetry: Telemetry, error: unknown, attributes?: AnyValueMap): void; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| telemetry | [Telemetry](./telemetry_.telemetry.md#telemetry_interface) | The [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance. | +| error | unknown | The caught exception, typically an | +| attributes | AnyValueMap | = Optional, arbitrary attributes to attach to the error log | + +Returns: + +void + +### flush(telemetry) {:#flush_8975134} + +Flushes all enqueued telemetry data immediately, instead of waiting for default batching. + +Signature: + +```typescript +export declare function flush(telemetry: Telemetry): Promise; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| telemetry | [Telemetry](./telemetry_.telemetry.md#telemetry_interface) | The [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance. | + +Returns: + +Promise<void> + +a promise which is resolved when all flushes are complete + +## nextOnRequestError + +Automatically report uncaught errors from server routes to Firebase Telemetry. + +Signature: + +```typescript +nextOnRequestError: Instrumentation.onRequestError +``` + +### Example + + +```javascript +// In instrumentation.ts (https://nextjs.org/docs/app/guides/instrumentation): +export { nextOnRequestError as onRequestError } from 'firebase/telemetry' + +``` + diff --git a/docs-devsite/telemetry.telemetry.md b/docs-devsite/telemetry_.telemetry.md similarity index 55% rename from docs-devsite/telemetry.telemetry.md rename to docs-devsite/telemetry_.telemetry.md index 3e7396f79d6..6ac8337e178 100644 --- a/docs-devsite/telemetry.telemetry.md +++ b/docs-devsite/telemetry_.telemetry.md @@ -12,7 +12,7 @@ https://github.com/firebase/firebase-js-sdk # Telemetry interface An instance of the Firebase Telemetry SDK. -Do not create this instance directly. Instead, use [getTelemetry()](./telemetry.md#gettelemetry_cf608e1). +Do not create this instance directly. Instead, use [getTelemetry()](./telemetry_.md#gettelemetry_cf608e1). Signature: @@ -24,12 +24,12 @@ export interface Telemetry | Property | Type | Description | | --- | --- | --- | -| [app](./telemetry.telemetry.md#telemetryapp) | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [Telemetry](./telemetry.telemetry.md#telemetry_interface) instance is associated with. | -| [loggerProvider](./telemetry.telemetry.md#telemetryloggerprovider) | LoggerProvider | The this [Telemetry](./telemetry.telemetry.md#telemetry_interface) instance uses. | +| [app](./telemetry_.telemetry.md#telemetryapp) | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance is associated with. | +| [loggerProvider](./telemetry_.telemetry.md#telemetryloggerprovider) | LoggerProvider | The this [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance uses. | ## Telemetry.app -The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [Telemetry](./telemetry.telemetry.md#telemetry_interface) instance is associated with. +The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance is associated with. Signature: @@ -39,7 +39,7 @@ app: FirebaseApp; ## Telemetry.loggerProvider -The this [Telemetry](./telemetry.telemetry.md#telemetry_interface) instance uses. +The this [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance uses. Signature: diff --git a/docs-devsite/telemetry_react.md b/docs-devsite/telemetry_react.md new file mode 100644 index 00000000000..8857f508a6f --- /dev/null +++ b/docs-devsite/telemetry_react.md @@ -0,0 +1,58 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# @firebase/telemetry/react + +## Functions + +| Function | Description | +| --- | --- | +| [FirebaseTelemetry({ firebaseOptions })](./telemetry_react.md#firebasetelemetry_537af3f) | Registers event listeners for uncaught errors.This should be installed near the root of your application. Caught errors, including those implicitly caught by Error Boundaries, will not be captured by this component. | + +## function({ firebaseOptions }, ...) + +### FirebaseTelemetry({ firebaseOptions }) {:#firebasetelemetry_537af3f} + +Registers event listeners for uncaught errors. + +This should be installed near the root of your application. Caught errors, including those implicitly caught by Error Boundaries, will not be captured by this component. + +Signature: + +```typescript +export declare function FirebaseTelemetry({ firebaseOptions }: { + firebaseOptions?: FirebaseOptions; +}): null; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { firebaseOptions } | { firebaseOptions?: [FirebaseOptions](./app.firebaseoptions.md#firebaseoptions_interface); } | | + +Returns: + +null + +The default [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance for the given [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). + +### Example + + +```html + + + ... my app ... + + +``` + diff --git a/packages/telemetry/api-extractor.json b/packages/telemetry/api-extractor.json index 12e2ecdc56b..7bc55b00d75 100644 --- a/packages/telemetry/api-extractor.json +++ b/packages/telemetry/api-extractor.json @@ -1,11 +1,20 @@ { "extends": "../../config/api-extractor.json", - // Point it to your entry point d.ts file. "mainEntryPointFilePath": "/dist/index.d.ts", + "additionalEntryPoints": [ + { + "modulePath": "react", + "filePath": "/dist/react/index.d.ts" + } + ], "dtsRollup": { - "enabled": true, - "untrimmedFilePath": "/dist/.d.ts", - "betaTrimmedFilePath": "/dist/-public.d.ts" + // rollup is not supported when multiple entry points are present. + // npm script api-report:* is used to generate dts rollup. + "enabled": false + }, + "apiReport": { + // apiReport is handled by npm script api-report:* + "enabled": false }, "bundledPackages": ["next"] } diff --git a/packages/telemetry/karma.conf.js b/packages/telemetry/karma.conf.js index 02647ba4c35..1c1419f9e32 100644 --- a/packages/telemetry/karma.conf.js +++ b/packages/telemetry/karma.conf.js @@ -17,12 +17,15 @@ const karmaBase = require('../../config/karma.base'); -const files = [`src/**/*.test.ts`]; +const files = [`src/**/*.test.ts*`]; module.exports = function (config) { const karmaConfig = { ...karmaBase, files, + preprocessors: { + 'src/**/*.test.ts*': ['webpack', 'sourcemap'] + }, frameworks: ['mocha'] }; diff --git a/packages/telemetry/package.json b/packages/telemetry/package.json index 6cd9ee81f9f..d1c6a9c8ab2 100644 --- a/packages/telemetry/package.json +++ b/packages/telemetry/package.json @@ -8,7 +8,7 @@ "browser": "dist/index.esm.js", "exports": { ".": { - "types": "./dist/index.d.ts", + "types": "./dist/index-public.d.ts", "node": { "import": "./dist/node-esm/index.node.esm.js", "default": "./dist/index.node.cjs.js" @@ -19,6 +19,18 @@ }, "default": "./dist/index.esm.js" }, + "./react": { + "types": "./dist/react/index-public.d.ts", + "node": { + "import": "./dist/react/index.esm.js", + "default": "./dist/react/index.cjs.js" + }, + "browser": { + "require": "./dist/react/index.cjs.js", + "import": "./dist/react/index.esm.js" + }, + "default": "./dist/react/index.esm.js" + }, "./package.json": "./package.json" }, "files": [ @@ -34,14 +46,27 @@ "test:ci": "node ../../scripts/run_tests_in_ci.js -s test:all", "test:all": "run-p --npm-path npm test:browser test:node", "test:browser": "karma start", - "test:node": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha src/**/*.test.* --config ../../config/mocharc.node.js", + "test:node": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha **/*.test.ts src/**/*.test.ts --config ../../config/mocharc.node.js", "trusted-type-check": "tsec -p tsconfig.json --noEmit", - "api-report": "api-extractor run --local --verbose", - "typings:public": "node ../../scripts/build/use_typings.js ./dist/telemetry-public.d.ts" + "api-report": "yarn api-report:main && yarn api-report:react && yarn api-report:api-json", + "api-report:main": "ts-node-script ../../repo-scripts/prune-dts/extract-public-api.ts --package telemetry --packageRoot . --typescriptDts ./dist/index.d.ts --rollupDts ./dist/private.d.ts --untrimmedRollupDts ./dist/internal.d.ts --publicDts ./dist/index-public.d.ts", + "api-report:react": "ts-node-script ../../repo-scripts/prune-dts/extract-public-api.ts --package telemetry-react --packageRoot . --typescriptDts ./dist/react/index.d.ts --rollupDts ./dist/react/private.d.ts --untrimmedRollupDts ./dist/react/internal.d.ts --publicDts ./dist/react/index-public.d.ts", + "api-report:api-json": "api-extractor run --local --verbose", + "typings:public": "node ../../scripts/build/use_typings.js ./dist/index-public.d.ts" }, "peerDependencies": { "@firebase/app": "0.x", - "@firebase/app-types": "0.x" + "@firebase/app-types": "0.x", + "@types/react": "17.x || 18.x || 19.x", + "react": "17.x || 18.x || 19.x" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "@types/react": { + "optional": true + } }, "dependencies": { "@firebase/component": "0.7.0", @@ -60,8 +85,14 @@ "@firebase/app": "0.14.2", "@opentelemetry/sdk-trace-web": "2.1.0", "@rollup/plugin-json": "6.1.0", + "@testing-library/dom": "10.4.1", + "@testing-library/react": "16.3.0", + "@types/react": "19.1.13", "next": "15.5.2", + "react": "19.1.1", + "react-dom": "19.1.1", "rollup": "2.79.2", + "rollup-plugin-copy": "3.5.0", "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" @@ -74,7 +105,7 @@ "bugs": { "url": "https://github.com/firebase/firebase-js-sdk/issues" }, - "typings": "./dist/telemetry-public.d.ts", + "typings": "./dist/index.d.ts", "nyc": { "extension": [ ".ts" diff --git a/packages/telemetry/rollup.config.js b/packages/telemetry/rollup.config.js index 6ce188cb72d..fbeb28a5bd8 100644 --- a/packages/telemetry/rollup.config.js +++ b/packages/telemetry/rollup.config.js @@ -16,6 +16,7 @@ */ import json from '@rollup/plugin-json'; +import copy from 'rollup-plugin-copy'; import typescriptPlugin from 'rollup-plugin-typescript2'; import typescript from 'typescript'; import pkg from './package.json'; @@ -41,7 +42,7 @@ const browserBuilds = [ { input: 'index.ts', output: { - file: './dist/index.cjs.js', + file: pkg.exports['.'].browser.require, format: 'cjs', sourcemap: true }, @@ -73,4 +74,49 @@ const nodeBuilds = [ } ]; -export default [...browserBuilds, ...nodeBuilds]; +const reactBuilds = [ + { + input: 'src/react/index.ts', + output: { + file: pkg.exports['./react'].browser.import, + format: 'es', + sourcemap: true, + banner: `'use client';` + }, + plugins: [ + typescriptPlugin({ + typescript, + tsconfig: 'tsconfig.react.json' + }), + json(), + copy({ + targets: [ + { + src: 'dist/src/react/index.d.ts', + dest: 'dist/react' + } + ] + }) + ], + external: id => deps.some(dep => id === dep || id.startsWith(`${dep}/`)) + }, + { + input: 'src/react/index.ts', + output: { + file: pkg.exports['./react'].browser.require, + format: 'cjs', + sourcemap: true, + banner: `'use client';` + }, + plugins: [ + typescriptPlugin({ + typescript, + tsconfig: 'tsconfig.react.json' + }), + json() + ], + external: id => deps.some(dep => id === dep || id.startsWith(`${dep}/`)) + } +]; + +export default [...browserBuilds, ...nodeBuilds, ...reactBuilds]; diff --git a/packages/telemetry/src/react/index.test.tsx b/packages/telemetry/src/react/index.test.tsx new file mode 100644 index 00000000000..29643393133 --- /dev/null +++ b/packages/telemetry/src/react/index.test.tsx @@ -0,0 +1,104 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from 'chai'; +import sinonChai from 'sinon-chai'; +import chaiAsPromised from 'chai-as-promised'; +import { restore, stub } from 'sinon'; +import * as app from '@firebase/app'; +import * as telemetry from '../api'; +import { FirebaseApp } from '@firebase/app'; +import { Telemetry } from '../public-types'; +import { FirebaseTelemetry } from '.'; +import React from 'react'; +import { render } from '@testing-library/react'; + +use(sinonChai); +use(chaiAsPromised); + +describe.only('FirebaseTelemetry', () => { + let getTelemetryStub: sinon.SinonStub; + let captureErrorStub: sinon.SinonStub; + let initializeAppStub: sinon.SinonStub; + let getAppStub: sinon.SinonStub; + let fakeApp: FirebaseApp; + let fakeTelemetry: Telemetry; + + beforeEach(() => { + fakeApp = { name: 'fakeApp' } as FirebaseApp; + fakeTelemetry = {} as Telemetry; + + initializeAppStub = stub(app, 'initializeApp').returns(fakeApp); + getTelemetryStub = stub(telemetry, 'getTelemetry').returns(fakeTelemetry); + captureErrorStub = stub(telemetry, 'captureError'); + getAppStub = stub(app, 'getApp').returns(fakeApp); + }); + + afterEach(() => { + restore(); + }); + + it('gets telemetry with the default app if no firebaseOptions are provided', () => { + render(); + expect(initializeAppStub).not.to.have.been.called; + }); + + it('initializes a new app and gets telemetry if firebaseOptions are provided', () => { + const firebaseOptions = { apiKey: 'test' }; + render(); + expect(initializeAppStub).to.have.been.calledWith(firebaseOptions); + }); + + it('captures window errors', done => { + render(); + const error = new Error('test error'); + window.onerror = () => { + // Prevent error from bubbling up to test suite + }; + window.addEventListener('error', (event: ErrorEvent) => { + // Registers another listener (sequential) to confirm behaviour. + expect(getTelemetryStub).to.have.been.called; + expect(captureErrorStub).to.have.been.calledWith(fakeTelemetry, error); + done(); + }); + window.dispatchEvent(new ErrorEvent('error', { error })); + }); + + it('captures unhandled promise rejections', () => { + render(); + const reason = new Error('test rejection'); + const promise = Promise.reject(reason); + promise.catch(() => {}); + window.dispatchEvent( + new PromiseRejectionEvent('unhandledrejection', { reason, promise }) + ); + expect(getTelemetryStub).to.have.been.called; + expect(captureErrorStub).to.have.been.calledWith(fakeTelemetry, reason); + }); + + it('fails silently when getTelemetry fails', () => { + const error = new Error('getTelemetry failed'); + initializeAppStub.throws(error); + const consoleWarnStub = stub(console, 'warn'); + + expect(() => render()).not.to.throw(); + expect(consoleWarnStub).to.have.been.calledWith( + 'Firebase Telemetry was not initialized:\n', + error + ); + }); +}); diff --git a/packages/telemetry/src/react/index.ts b/packages/telemetry/src/react/index.ts new file mode 100644 index 00000000000..f1fde0d7525 --- /dev/null +++ b/packages/telemetry/src/react/index.ts @@ -0,0 +1,85 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FirebaseOptions, initializeApp } from '@firebase/app'; +import { registerTelemetry } from '../register'; +import { captureError, getTelemetry } from '../api'; +import { useEffect } from 'react'; + +registerTelemetry(); + +function errorListener(event: ErrorEvent): void { + captureError(getTelemetry(), event.error, {}); +} + +function unhandledRejectionListener(event: PromiseRejectionEvent): void { + captureError(getTelemetry(), event.reason, {}); +} + +/** + * Registers event listeners for uncaught errors. + * + * This should be installed near the root of your application. Caught errors, including those + * implicitly caught by Error Boundaries, will not be captured by this component. + * + * @example + * ```html + * + * + * ... my app ... + * + * ``` + * + * @param firebaseOptions - Options to run {@link @firebase/app#initializeApp}. If this is not provided, initializeApp needs to be called explicitly elsewhere in your application. + * @returns The default {@link Telemetry} instance for the given {@link @firebase/app#FirebaseApp}. + * + * @public + */ +export function FirebaseTelemetry({ + firebaseOptions +}: { + firebaseOptions?: FirebaseOptions; +}): null { + useEffect(() => { + if (typeof window === 'undefined') { + return; + } + + // TODO: This will be removed once there is a default endpoint + process.env.OTEL_ENDPOINT = window.location.origin; + + try { + if (firebaseOptions) { + initializeApp(firebaseOptions); + } + window.addEventListener('error', errorListener); + window.addEventListener('unhandledrejection', unhandledRejectionListener); + } catch (error) { + // Log the error here, but don't die. + console.warn(`Firebase Telemetry was not initialized:\n`, error); + } + return () => { + window.removeEventListener('error', errorListener); + window.removeEventListener( + 'unhandledrejection', + unhandledRejectionListener + ); + }; + }, []); + + return null; +} diff --git a/packages/telemetry/tsconfig.json b/packages/telemetry/tsconfig.json index 4e0ae05eebc..1840ca4857e 100644 --- a/packages/telemetry/tsconfig.json +++ b/packages/telemetry/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "../../config/tsconfig.base.json", "compilerOptions": { - "outDir": "dist" + "outDir": "dist", + "jsx": "react" }, "exclude": ["dist/**/*"] } diff --git a/packages/telemetry/tsconfig.react.json b/packages/telemetry/tsconfig.react.json new file mode 100644 index 00000000000..de2b1885562 --- /dev/null +++ b/packages/telemetry/tsconfig.react.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "dist/react", + "declarationDir": "dist/react", + "jsx": "react" + }, + "include": ["src/react/index.ts"] +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 8452a622544..e6e4692d51e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,6 +29,15 @@ js-tokens "^4.0.0" picocolors "^1.0.0" +"@babel/code-frame@^7.10.4": + version "7.27.1" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== + dependencies: + "@babel/helper-validator-identifier" "^7.27.1" + js-tokens "^4.0.0" + picocolors "^1.1.1" + "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.26.5": version "7.26.5" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz#df93ac37f4417854130e21d72c66ff3d4b897fc7" @@ -228,6 +237,11 @@ resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== + "@babel/helper-validator-option@^7.25.9": version "7.25.9" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" @@ -946,6 +960,11 @@ pirates "^4.0.6" source-map-support "^0.5.16" +"@babel/runtime@^7.12.5": + version "7.28.4" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" + integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== + "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4": version "7.26.7" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz#f4e7fe527cd710f8dc0618610b61b4b060c3c341" @@ -3187,6 +3206,27 @@ resolved "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.4.tgz#86e04e677cd6c05fa230dd15ac223fa72d1d7090" integrity sha512-kIhULpw9TrGYnHp/8VfdcneIcxKnLixmADtukQRtJUmsVlMg0niMkwV0xZmi8hqa57xqilIHjWFA0GKvEjVU5g== +"@testing-library/dom@10.4.1": + version "10.4.1" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz#d444f8a889e9a46e9a3b4f3b88e0fcb3efb6cf95" + integrity sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.3.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + picocolors "1.1.1" + pretty-format "^27.0.2" + +"@testing-library/react@16.3.0": + version "16.3.0" + resolved "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz#3a85bb9bdebf180cd76dba16454e242564d598a6" + integrity sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw== + dependencies: + "@babel/runtime" "^7.12.5" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -3227,6 +3267,11 @@ resolved "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA== +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + "@types/body-parser@*": version "1.19.5" resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" @@ -3549,6 +3594,13 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== +"@types/react@19.1.13": + version "19.1.13" + resolved "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz#fc650ffa680d739a25a530f5d7ebe00cdd771883" + integrity sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ== + dependencies: + csstype "^3.0.2" + "@types/request@2.48.12", "@types/request@^2.48.8": version "2.48.12" resolved "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz#0f590f615a10f87da18e9790ac94c29ec4c5ef30" @@ -4409,6 +4461,13 @@ argsarray@^0.0.1: resolved "https://registry.npmjs.org/argsarray/-/argsarray-0.0.1.tgz#6e7207b4ecdb39b0af88303fa5ae22bda8df61cb" integrity sha512-u96dg2GcAKtpTrBdDoFIM7PjcBA+6rSP0OR94MOReNRyUECL6MtQt5XXmRr4qrftYaef9+l5hcpO5te7sML1Cg== +aria-query@5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -6317,6 +6376,11 @@ css@^3.0.0: source-map "^0.6.1" source-map-resolve "^0.6.0" +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + csv-parse@^5.0.4: version "5.6.0" resolved "https://registry.npmjs.org/csv-parse/-/csv-parse-5.6.0.tgz#219beace2a3e9f28929999d2aa417d3fb3071c7f" @@ -6657,6 +6721,11 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + des.js@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" @@ -6768,6 +6837,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + dom-serialize@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" @@ -11335,6 +11409,11 @@ lunr@^2.3.8: resolved "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + magic-string@^0.25.2, magic-string@^0.25.7: version "0.25.9" resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" @@ -13349,16 +13428,16 @@ pgpass@1.x: dependencies: split2 "^4.1.0" +picocolors@1.1.1, picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picocolors@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== -picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -13540,6 +13619,15 @@ prettier@2.8.8, prettier@^2.7.1: resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -13919,11 +14007,28 @@ re2@^1.17.7: nan "^2.20.0" node-gyp "^10.2.0" +react-dom@19.1.1: + version "19.1.1" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz#2daa9ff7f3ae384aeb30e76d5ee38c046dc89893" + integrity sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw== + dependencies: + scheduler "^0.26.0" + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-is@^18.0.0: version "18.3.1" resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== +react@19.1.1: + version "19.1.1" + resolved "https://registry.npmjs.org/react/-/react-19.1.1.tgz#06d9149ec5e083a67f9a1e39ce97b06a03b644af" + integrity sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ== + read-cmd-shim@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" @@ -14687,6 +14792,11 @@ sax@>=0.6.0: resolved "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== +scheduler@^0.26.0: + version "0.26.0" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" + integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA== + schema-utils@^2.6.5: version "2.7.1" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"