From f09c89e1ceb811e4af1b4b73ab317102e9a18b14 Mon Sep 17 00:00:00 2001 From: Tyler Dixon Date: Thu, 30 Jul 2026 13:09:23 -0700 Subject: [PATCH] ci: check the public type surface with api-extractor 4.2.4 shipped a breaking type change as a patch. ObservableStatus went from a flat interface to a discriminated union, so data became T | undefined and the documented destructure-and-use pattern stopped compiling for strict-TS consumers. Nothing in the release flow compared the published type surface, so it went out unnoticed. api-extractor generates etc/reactfire.api.md, a report of every public export. CI regenerates it and fails when it no longer matches, so a change to the type surface shows up as a reviewable diff in the pull request instead of being discovered by a consumer after release. The report is generated by npm run api:report and never hand-edited. Nothing here is maintained by hand. Verified against a faithful reproduction of 4.2.4, patched so only consumers break rather than internals: plain tsc still passes, and api:check exits 1 with a one-line diff, data: T becoming T | undefined. Two notes for review: api-extractor bundles TypeScript 5.9.3 and this repo is on 6.0.3, so it prints a version warning on every run. The emitted .d.ts contain no TypeScript 6 only syntax, and the report reproduces the emitted signatures exactly, so the older engine has nothing to misparse. Worth rechecking if the public surface ever adopts newer syntax. The job builds with vite, not tsc. tsconfig.json sets noEmit, so tsc is a type check only; @rollup/plugin-typescript inside the vite build is what emits the declarations api-extractor reads. Closes #749 --- .github/workflows/test.yaml | 30 ++- .gitignore | 3 + api-extractor.json | 35 +++ etc/reactfire.api.md | 432 ++++++++++++++++++++++++++++++++++++ package-lock.json | 380 +++++++++++++++++++++++++++++++ package.json | 5 +- 6 files changed, 883 insertions(+), 2 deletions(-) create mode 100644 api-extractor.json create mode 100644 etc/reactfire.api.md diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 1d3d55c3..394cccf7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -106,6 +106,34 @@ jobs: # builds its own dependency tree, which is why it is a separate job. - name: Entry-point load test run: node scripts/entry-load.mjs reactfire-${{ github.run_id }}/reactfire.tgz --react ${{ matrix.react }} + check-api: + runs-on: ubuntu-latest + name: Check API surface + steps: + - name: Checkout + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - name: Setup node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '24' + cache: 'npm' + - name: Install deps + run: npm ci + # vite build, not tsc: tsconfig.json sets noEmit, so tsc is a type check + # only. @rollup/plugin-typescript inside the vite build is what emits the + # declarations api-extractor reads. + - name: Build declarations + run: npx vite build + # Fails when the emitted public type surface no longer matches the + # committed report in etc/. The report is generated (npm run api:report) + # and never hand-edited, so a type change shows up as a reviewable diff in + # the pull request. Needs no network and no published release to compare + # against, which is what 4.2.4 lacked when it shipped a breaking type + # change as a patch (#749). + - name: API report + run: npm run api:check test: runs-on: ubuntu-latest needs: build @@ -176,7 +204,7 @@ jobs: publish: runs-on: ubuntu-latest name: Publish (NPM) - needs: [test, verify-package, verify-loads] + needs: [test, verify-package, verify-loads, check-api] if: ${{ github.ref == 'refs/heads/main' || github.event_name == 'release' }} steps: - name: Setup node diff --git a/.gitignore b/.gitignore index b2ebc657..8c302263 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ unpack.sh example/.parcel-cache/ .firebaserc stats.html + +# api-extractor scratch output +temp/ diff --git a/api-extractor.json b/api-extractor.json new file mode 100644 index 00000000..ad8e1870 --- /dev/null +++ b/api-extractor.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + // Runs against the emitted declarations, not src, so what is reported is what + // consumers actually resolve. + "mainEntryPointFilePath": "/dist/index.d.ts", + + "compiler": { + "tsconfigFilePath": "/tsconfig.json" + }, + + // The committed report is the whole point: a change to the public type surface + // shows up as a diff in review, and CI fails when the report is out of date. + // Generated, never hand-edited. + "apiReport": { + "enabled": true, + "reportFolder": "/etc/", + "reportFileName": "reactfire.api.md" + }, + + "docModel": { "enabled": false }, + "dtsRollup": { "enabled": false }, + "tsdocMetadata": { "enabled": false }, + + "messages": { + // Missing release tags are not interesting here; the repo does not use them. + "extractorMessageReporting": { + "ae-missing-release-tag": { "logLevel": "none" }, + "ae-forgotten-export": { "logLevel": "warning" } + }, + "tsdocMessageReporting": { + "default": { "logLevel": "none" } + } + } +} diff --git a/etc/reactfire.api.md b/etc/reactfire.api.md new file mode 100644 index 00000000..990cf252 --- /dev/null +++ b/etc/reactfire.api.md @@ -0,0 +1,432 @@ +## API Report File for "reactfire" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { AllParameters } from 'rxfire/remote-config'; +import type { Analytics } from 'firebase/analytics'; +import type { AppCheck } from 'firebase/app-check'; +import type { Auth } from 'firebase/auth'; +import type { Database } from 'firebase/database'; +import type { DatabaseReference } from 'firebase/database'; +import { DocumentData } from 'firebase/firestore'; +import { DocumentReference } from 'firebase/firestore'; +import { DocumentSnapshot } from 'firebase/firestore'; +import { FirebaseApp } from 'firebase/app'; +import type { FirebaseOptions } from 'firebase/app'; +import type { FirebasePerformance } from 'firebase/performance'; +import type { FirebaseStorage } from 'firebase/storage'; +import type { Firestore } from 'firebase/firestore'; +import type { Functions } from 'firebase/functions'; +import type { HttpsCallableOptions } from 'firebase/functions'; +import type { IdTokenResult } from 'firebase/auth'; +import { Observable } from 'rxjs'; +import type { Query } from 'firebase/database'; +import { Query as Query_2 } from 'firebase/firestore'; +import { QueryChange } from 'rxfire/database'; +import { QuerySnapshot } from 'firebase/firestore'; +import * as React_2 from 'react'; +import type { RemoteConfig } from 'firebase/remote-config'; +import type { StorageReference } from 'firebase/storage'; +import { Subject } from 'rxjs'; +import { Subscriber } from 'rxjs'; +import { Subscription } from 'rxjs'; +import type { UploadTask } from 'firebase/storage'; +import type { UploadTaskSnapshot } from 'firebase/storage'; +import type { User } from 'firebase/auth'; +import type { Value } from 'firebase/remote-config'; + +// @public (undocumented) +export const AnalyticsProvider: (props: React_2.PropsWithChildren<{ + sdk: Analytics; +}>) => React_2.ReactElement; + +// @public (undocumented) +export const AnalyticsSdkContext: React_2.Context; + +// @public (undocumented) +export const AppCheckProvider: (props: React_2.PropsWithChildren<{ + sdk: AppCheck; +}>) => React_2.ReactElement; + +// @public (undocumented) +export const AppCheckSdkContext: React_2.Context; + +// @public @deprecated (undocumented) +export function AuthCheck(input: AuthCheckProps): React_2.ReactElement; + +// @public (undocumented) +export interface AuthCheckProps { + // (undocumented) + children: React_2.ReactNode; + // (undocumented) + fallback: React_2.ReactNode; + // (undocumented) + requiredClaims?: Object; +} + +// @public (undocumented) +export const AuthProvider: (props: React_2.PropsWithChildren<{ + sdk: Auth; +}>) => React_2.ReactElement; + +// @public (undocumented) +export const AuthSdkContext: React_2.Context; + +// @public (undocumented) +export function checkIdField(options: ReactFireOptions): any; + +// @public (undocumented) +export function checkinitialData(options: ReactFireOptions): any; + +// @public (undocumented) +export function checkOptions(options: ReactFireOptions, field: string): any; + +// @public (undocumented) +export interface ClaimCheckErrors { + // (undocumented) + [key: string]: any[]; +} + +// @public @deprecated (undocumented) +export function ClaimsCheck(input: ClaimsCheckProps): React_2.JSX.Element; + +// @public (undocumented) +export interface ClaimsCheckProps { + // (undocumented) + children: React_2.ReactNode; + // (undocumented) + fallback: React_2.ReactNode; + // (undocumented) + requiredClaims: { + [key: string]: any; + }; + // (undocumented) + user: User; +} + +// @public (undocumented) +export interface ClaimsValidator { + // Warning: (ae-forgotten-export) The symbol "Claims" needs to be exported by the entry point index.d.ts + // + // (undocumented) + (claims: Claims): { + hasRequiredClaims: boolean; + errors: ClaimCheckErrors | {}; + }; +} + +// @public (undocumented) +export const DatabaseProvider: (props: React_2.PropsWithChildren<{ + sdk: Database; +}>) => React_2.ReactElement; + +// @public (undocumented) +export const DatabaseSdkContext: React_2.Context; + +// @public (undocumented) +export function FirebaseAppProvider(props: React_2.PropsWithChildren): React_2.JSX.Element; + +// @public (undocumented) +export interface FirebaseAppProviderProps { + // (undocumented) + appName?: string; + // (undocumented) + firebaseApp?: FirebaseApp; + // (undocumented) + firebaseConfig?: FirebaseOptions; + // (undocumented) + suspense?: boolean; +} + +// @public (undocumented) +export const FirestoreProvider: (props: React_2.PropsWithChildren<{ + sdk: Firestore; +}>) => React_2.ReactElement; + +// @public (undocumented) +export const FirestoreSdkContext: React_2.Context; + +// @public (undocumented) +export const FunctionsProvider: (props: React_2.PropsWithChildren<{ + sdk: Functions; +}>) => React_2.ReactElement; + +// @public (undocumented) +export const FunctionsSdkContext: React_2.Context; + +// @public (undocumented) +export interface ObservableStatus { + data: T; + error: Error | undefined; + firstValuePromise: Promise; + hasEmitted: boolean; + isComplete: boolean; + status: 'loading' | 'error' | 'success'; +} + +// @public (undocumented) +export const PerformanceProvider: (props: React_2.PropsWithChildren<{ + sdk: FirebasePerformance; +}>) => React_2.ReactElement; + +// @public (undocumented) +export const PerformanceSdkContext: React_2.Context; + +// Warning: (ae-forgotten-export) The symbol "SuspenseSubject" needs to be exported by the entry point index.d.ts +// +// @public +export function preloadFirestoreDoc(refProvider: () => Promise): Promise>>; + +// @public (undocumented) +export function preloadObservable(source: Observable, id: string, suspenseEnabled?: boolean): SuspenseSubject; + +// @public (undocumented) +export function preloadUser(authResolver: () => Promise): Promise; + +// @public (undocumented) +export class ReactFireError extends Error { + constructor(code: string, message: string, customData?: Record | undefined); + // (undocumented) + readonly code: string; + // (undocumented) + customData?: Record | undefined; + // (undocumented) + readonly name = "ReactFireError"; +} + +// @public (undocumented) +export type ReactFireGlobals = { + _reactFireDatabaseCachedQueries: Array; + _reactFireFirestoreQueryCache: Array; + _reactFirePreloadedObservables: Map>; +}; + +// @public (undocumented) +export interface ReactFireOptions { + // (undocumented) + idField?: string; + // (undocumented) + initialData?: T | any; + // @deprecated (undocumented) + startWithValue?: T | any; + // (undocumented) + suspense?: boolean; +} + +// @public (undocumented) +export const RemoteConfigProvider: (props: React_2.PropsWithChildren<{ + sdk: RemoteConfig; +}>) => React_2.ReactElement; + +// @public (undocumented) +export const RemoteConfigSdkContext: React_2.Context; + +// @public (undocumented) +export interface SignInCheckOptionsBasic extends ReactFireOptions { + // (undocumented) + forceRefresh?: boolean; +} + +// @public (undocumented) +export interface SignInCheckOptionsClaimsObject extends SignInCheckOptionsBasic { + // (undocumented) + requiredClaims: Claims; +} + +// @public (undocumented) +export interface SignInCheckOptionsClaimsValidator extends SignInCheckOptionsBasic { + // (undocumented) + validateCustomClaims: ClaimsValidator; +} + +// @public (undocumented) +export type SigninCheckResult = { + signedIn: false; + hasRequiredClaims: false; + errors: {}; + user: null; +} | { + signedIn: true; + hasRequiredClaims: boolean; + errors: ClaimCheckErrors; + user: User; +}; + +// @public (undocumented) +export function StorageImage(props: StorageImageProps & React_2.DetailedHTMLProps, HTMLImageElement>): React_2.JSX.Element; + +// @public (undocumented) +export type StorageImageProps = { + storagePath: string; + storage?: FirebaseStorage; + suspense?: boolean; + placeHolder?: React_2.ReactNode; +}; + +// @public (undocumented) +export const StorageProvider: (props: React_2.PropsWithChildren<{ + sdk: FirebaseStorage; +}>) => React_2.ReactElement; + +// @public (undocumented) +export const StorageSdkContext: React_2.Context; + +// @public (undocumented) +export interface SuspensePerfProps { + // (undocumented) + children: React_2.ReactNode; + // (undocumented) + fallback: React_2.ReactNode; + // (undocumented) + traceId: string; +} + +// @public (undocumented) +export function SuspenseWithPerf(input: SuspensePerfProps): React_2.ReactElement; + +// @public (undocumented) +export const useAnalytics: () => Analytics; + +// @public (undocumented) +export const useAppCheck: () => AppCheck; + +// @public (undocumented) +export const useAuth: () => Auth; + +// @public +export function useCallableFunctionResponse(functionName: string, options?: ReactFireOptions & { + httpsCallableOptions?: HttpsCallableOptions; + data?: RequestData; +}): ObservableStatus; + +// @public (undocumented) +export const useDatabase: () => Database; + +// @public +export function useDatabaseList(ref: DatabaseReference | Query, options?: ReactFireOptions): ObservableStatus; + +// @public (undocumented) +export function useDatabaseListData(ref: DatabaseReference | Query, options?: ReactFireOptions): ObservableStatus; + +// @public +export function useDatabaseObject(ref: DatabaseReference, options?: ReactFireOptions): ObservableStatus; + +// @public (undocumented) +export function useDatabaseObjectData(ref: DatabaseReference, options?: ReactFireOptions): ObservableStatus; + +// @public (undocumented) +export function useFirebaseApp(): FirebaseApp; + +// @public (undocumented) +export const useFirestore: () => Firestore; + +// @public +export function useFirestoreCollection(query: Query_2, options?: ReactFireOptions): ObservableStatus>; + +// @public +export function useFirestoreCollectionData(query: Query_2, options?: ReactFireOptions): ObservableStatus; + +// @public +export function useFirestoreDoc(ref: DocumentReference, options?: ReactFireOptions): ObservableStatus>; + +// @public +export function useFirestoreDocData(ref: DocumentReference, options?: ReactFireOptions): ObservableStatus; + +// @public +export function useFirestoreDocDataOnce(ref: DocumentReference, options?: ReactFireOptions): ObservableStatus; + +// @public +export function useFirestoreDocOnce(ref: DocumentReference, options?: ReactFireOptions): ObservableStatus>; + +// @public (undocumented) +export const useFunctions: () => Functions; + +// @public (undocumented) +export function useIdTokenResult(user: User, forceRefresh?: boolean, options?: ReactFireOptions): ObservableStatus; + +// Warning: (ae-forgotten-export) The symbol "InitSdkHook" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export const useInitAnalytics: InitSdkHook; + +// @public (undocumented) +export const useInitAppCheck: InitSdkHook; + +// @public (undocumented) +export const useInitAuth: InitSdkHook; + +// @public (undocumented) +export const useInitDatabase: InitSdkHook; + +// @public (undocumented) +export const useInitFirestore: InitSdkHook; + +// @public (undocumented) +export const useInitFunctions: InitSdkHook; + +// @public (undocumented) +export const useInitPerformance: InitSdkHook; + +// @public (undocumented) +export const useInitRemoteConfig: InitSdkHook; + +// @public (undocumented) +export const useInitStorage: InitSdkHook; + +// @public (undocumented) +export function useIsSuspenseEnabled(): boolean; + +// @public (undocumented) +export function useObservable(observableId: string, source: Observable, config?: ReactFireOptions): ObservableStatus; + +// @public (undocumented) +export const usePerformance: () => FirebasePerformance; + +// @public (undocumented) +export const useRemoteConfig: () => RemoteConfig; + +// @public +export function useRemoteConfigAll(key: string): ObservableStatus; + +// @public +export function useRemoteConfigBoolean(key: string): ObservableStatus; + +// @public +export function useRemoteConfigNumber(key: string): ObservableStatus; + +// @public +export function useRemoteConfigString(key: string): ObservableStatus; + +// @public +export function useRemoteConfigValue(key: string): ObservableStatus; + +// @public +export function useSigninCheck(options?: SignInCheckOptionsBasic | SignInCheckOptionsClaimsObject | SignInCheckOptionsClaimsValidator): ObservableStatus; + +// @public (undocumented) +export const useStorage: () => FirebaseStorage; + +// @public +export function useStorageDownloadURL(ref: StorageReference, options?: ReactFireOptions): ObservableStatus; + +// @public +export function useStorageTask(task: UploadTask, ref: StorageReference, options?: ReactFireOptions): ObservableStatus; + +// @public (undocumented) +export function useSuspenseEnabledFromConfigAndContext(suspenseFromConfig?: boolean): boolean; + +// @public +export function useUser(options?: ReactFireOptions): ObservableStatus; + +// @public (undocumented) +export const version: string; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/package-lock.json b/package-lock.json index dfa14da1..5eed61ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ }, "devDependencies": { "@arethetypeswrong/cli": "^0.18.5", + "@microsoft/api-extractor": "^7.58.12", "@rollup/plugin-typescript": "^12.3.0", "@size-limit/preset-small-lib": "^8.2.6", "@testing-library/jest-dom": "^5.16.5", @@ -2174,6 +2175,153 @@ "@braidai/lang": "^1.0.0" } }, + "node_modules/@microsoft/api-extractor": { + "version": "7.58.12", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.58.12.tgz", + "integrity": "sha512-VNpgC/1LaroLbn+UKmwDYspq+9r3EHyj4vJ3qUy/g8vB0rKt+1Wgs7WFj/JGpgxhG8SNyXs1XwYwe7nqkk8IDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor-model": "7.33.10", + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.1", + "@rushstack/node-core-library": "5.23.3", + "@rushstack/rig-package": "0.7.3", + "@rushstack/terminal": "0.24.2", + "@rushstack/ts-command-line": "5.3.12", + "diff": "~8.0.2", + "minimatch": "10.2.3", + "resolve": "~1.22.1", + "semver": "~7.7.4", + "source-map": "~0.6.1", + "typescript": "5.9.3" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.33.10", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.33.10.tgz", + "integrity": "sha512-uPUK17xGxeQ3av6TN7awp+dTTSTvx8fZC0XFL1gyt7hFapYuxND3XLXH8vkmI7UjfW92oogzFAFqHSifmJROaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.1", + "@rushstack/node-core-library": "5.23.3" + } + }, + "node_modules/@microsoft/api-extractor/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", + "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.1.tgz", + "integrity": "sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.16.0", + "ajv": "~8.18.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", @@ -3186,6 +3334,211 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@rushstack/node-core-library": { + "version": "5.23.3", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.23.3.tgz", + "integrity": "sha512-f6uuza7Um65bwsIJgf0MRs7IPA5IG+A+zs1AYGQvpZmLjtTGdfHowhQw4kwF0pPhJCrU4UHNhK8Qa6tLygYZCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "~8.20.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~11.3.0", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.7.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/fs-extra": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.6.tgz", + "integrity": "sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/node-core-library/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/problem-matcher": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@rushstack/problem-matcher/-/problem-matcher-0.2.1.tgz", + "integrity": "sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/rig-package": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.7.3.tgz", + "integrity": "sha512-aAA518n6wxxjCfnTAOjQnm7ngNE0FVHxHAw2pxKlIhxrMn0XQjGcXKF0oKWpjBgJOmsaJpVob/v+zr3zxgPWuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jju": "~1.4.0", + "resolve": "~1.22.1" + } + }, + "node_modules/@rushstack/terminal": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.24.2.tgz", + "integrity": "sha512-KB7PpvzDyKMw/RGU3TxOwxTs3OwZ4gq6+WHlTJN/JfQH4ezliNtWIqver78jTaAJyz/ZAAlJGH7a/M1WyFLFSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.23.3", + "@rushstack/problem-matcher": "0.2.1", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/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/@rushstack/ts-command-line": { + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.3.12.tgz", + "integrity": "sha512-Vg2n24arSf7JvUNga2DMHYTbxnVq9L5OtVCp4Gfr8YC/kmL/bmdR8FQhGcpMzMYNY9Vdw3+TaimG11Sf+z1Tpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/terminal": "0.24.2", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, + "node_modules/@rushstack/ts-command-line/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/@shikijs/engine-oniguruma": { "version": "3.23.0", "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", @@ -3847,6 +4200,13 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/aria-query": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", @@ -6553,6 +6913,16 @@ "node": ">=0.8.0" } }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.4.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", @@ -14630,6 +15000,16 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", diff --git a/package.json b/package.json index c7d8d183..a680d300 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,9 @@ "lint:package": "publint --strict", "lint:types": "attw", "docs": "typedoc --options typedoc.json && markdown-toc -i docs/use.md", - "docs:fork": "typedoc --options typedoc.json --gitRemote upstream && markdown-toc -i docs/use.md" + "docs:fork": "typedoc --options typedoc.json --gitRemote upstream && markdown-toc -i docs/use.md", + "api:report": "api-extractor run --local", + "api:check": "api-extractor run" }, "peerDependencies": { "firebase": "^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 || next", @@ -84,6 +86,7 @@ ], "devDependencies": { "@arethetypeswrong/cli": "^0.18.5", + "@microsoft/api-extractor": "^7.58.12", "@rollup/plugin-typescript": "^12.3.0", "@size-limit/preset-small-lib": "^8.2.6", "@testing-library/jest-dom": "^5.16.5",