diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d901385c60..68fd5e2cd1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,6 @@ jobs: run: npm run api-extractor - name: Run emulator-based integration tests run: | - npm install -g firebase-tools + npm install -g firebase-tools@11.30.0 firebase emulators:exec --project fake-project-id --only auth,database,firestore \ 'npx mocha \"test/integration/{auth,database,firestore}.spec.ts\" --slow 5000 --timeout 20000 --require ts-node/register' diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1e4997cbe7..5ebc0c9ff2 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -52,7 +52,7 @@ jobs: - name: Run emulator-based integration tests run: | - npm install -g firebase-tools + npm install -g firebase-tools@11.30.0 firebase emulators:exec --project fake-project-id --only auth,database,firestore \ 'npx mocha \"test/integration/{auth,database,firestore}.spec.ts\" --slow 5000 --timeout 20000 --require ts-node/register' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 813136e4ee..c01fba6dcf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -96,7 +96,7 @@ jobs: # 3. with the label 'release:publish', and # 4. the title prefix '[chore] Release '. if: github.event.pull_request.merged && - github.ref == 'master' && + github.ref == 'refs/heads/master' && contains(github.event.pull_request.labels.*.name, 'release:publish') && startsWith(github.event.pull_request.title, '[chore] Release ') diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4f80791cea..eefd161066 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -87,7 +87,7 @@ information on using pull requests. ### Prerequisites -1. Node.js 14 or higher. +1. Node.js 16 or higher. 2. `npm` 6 or higher. 3. Google Cloud SDK ([`gcloud`](https://cloud.google.com/sdk/downloads) utility). diff --git a/README.md b/README.md index 6a1d293bee..2d466926a7 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,8 @@ requests, code review feedback, and also pull requests. ## Supported Environments -We support Node.js 14 and higher. +We support Node.js 14 and higher. However, Node.js 14 support is deprecated. We strongly encourage +you to use Node.js 16 or higher as we will drop support for Node.js 14 in the next major version. Please also note that the Admin SDK should only be used in server-side/back-end environments controlled by the app developer. diff --git a/docgen/extras/firebase-admin.database.md b/docgen/extras/firebase-admin.database.md index 0f33392cf0..6e1c25770f 100644 --- a/docgen/extras/firebase-admin.database.md +++ b/docgen/extras/firebase-admin.database.md @@ -4,9 +4,9 @@ The following externally defined APIs are re-exported from this module entry poi | Symbol | Description | | --- | --- | -| [DataSnapshot](https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot) | `DataSnapshot` type from the `@firebase/database` package. | -| [EventType](https://firebase.google.com/docs/reference/js/firebase.database#eventtype) | `EventType` type from the `@firebase/database` package. | -| [OnDisconnect](https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect) | `OnDisconnect` type from the `@firebase/database` package. | -| [Query](https://firebase.google.com/docs/reference/js/firebase.database.Query) | `Query` type from the `@firebase/database` package. | -| [Reference](https://firebase.google.com/docs/reference/js/firebase.database.Reference) | `Reference` type from the `@firebase/database` package. | -| [ThenableReference](https://firebase.google.com/docs/reference/js/firebase.database.ThenableReference) | `ThenableReference` type from the `@firebase/database` package. | +| [DataSnapshot](https://firebase.google.com/docs/reference/js/database.datasnapshot) | `DataSnapshot` type from the `@firebase/database` package. | +| [EventType](https://firebase.google.com/docs/reference/js/database#eventtype) | `EventType` type from the `@firebase/database` package. | +| [OnDisconnect](https://firebase.google.com/docs/reference/js/database.ondisconnect) | `OnDisconnect` type from the `@firebase/database` package. | +| [Query](https://firebase.google.com/docs/reference/js/database.query) | `Query` type from the `@firebase/database` package. | +| [DatabaseReference](https://firebase.google.com/docs/reference/js/database.databasereference) | `DatabaseReference` type from the `@firebase/database` package. | +| [ThenableReference](https://firebase.google.com/docs/reference/js/database.thenablereference) | `ThenableReference` type from the `@firebase/database` package. | diff --git a/docgen/extras/firebase-admin.firestore.md b/docgen/extras/firebase-admin.firestore.md index 49617a6367..f00750727b 100644 --- a/docgen/extras/firebase-admin.firestore.md +++ b/docgen/extras/firebase-admin.firestore.md @@ -4,30 +4,32 @@ The following externally defined APIs are re-exported from this module entry poi | Symbol | Description | | --- | --- | -| [BulkWriter](https://googleapis.dev/nodejs/firestore/latest/BulkWriter.html) | `BulkWriter` type from the `@google-cloud/firestore` package. | -| [BulkWriterOptions](https://googleapis.dev/nodejs/firestore/latest/global.html#BulkWriterOptions) | `BulkWriterOptions` type from the `@google-cloud/firestore` package. | +| [BulkWriter](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/bulkwriter) | `BulkWriter` type from the `@google-cloud/firestore` package. | +| [AggregateField](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/aggregatefield) | `AggregateField` type from the `@google-cloud/firestore` package. | +| [BulkWriterOptions](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/bulkwriter) | `BulkWriterOptions` type from the `@google-cloud/firestore` package. | | [BundleBuilder](https://googleapis.dev/nodejs/firestore/latest/BundleBuilder.html) | `BundleBuilder` type from the `@google-cloud/firestore` package. | -| [CollectionGroup](https://googleapis.dev/nodejs/firestore/latest/CollectionGroup.html) | `CollectionGroup` type from the `@google-cloud/firestore` package. | -| [CollectionReference](https://googleapis.dev/nodejs/firestore/latest/CollectionReference.html) | `CollectionReference` type from the `@google-cloud/firestore` package. | -| [DocumentChange](https://googleapis.dev/nodejs/firestore/latest/DocumentChange.html) | `DocumentChange` type from the `@google-cloud/firestore` package. | +| [CollectionGroup](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/collectiongroup) | `CollectionGroup` type from the `@google-cloud/firestore` package. | +| [CollectionReference](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/collectionreference) | `CollectionReference` type from the `@google-cloud/firestore` package. | +| [DocumentChange](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/documentchange) | `DocumentChange` type from the `@google-cloud/firestore` package. | | [DocumentData](https://googleapis.dev/nodejs/firestore/latest/global.html#DocumentData) | `DocumentData` type from the `@google-cloud/firestore` package. | -| [DocumentReference](https://googleapis.dev/nodejs/firestore/latest/DocumentReference.html) | `DocumentReference` type from the `@google-cloud/firestore` package. | -| [DocumentSnapshot](https://googleapis.dev/nodejs/firestore/latest/DocumentSnapshot.html) | `DocumentSnapshot` type from the `@google-cloud/firestore` package. | -| [FieldPath](https://googleapis.dev/nodejs/firestore/latest/FieldPath.html) | `FieldPath` type from the `@google-cloud/firestore` package. | -| [FieldValue](https://googleapis.dev/nodejs/firestore/latest/FieldValue.html) | `FieldValue` type from the `@google-cloud/firestore` package. | -| [Firestore](https://googleapis.dev/nodejs/firestore/latest/Firestore.html) | `Firestore` type from the `@google-cloud/firestore` package. | +| [DocumentReference](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/documentreference) | `DocumentReference` type from the `@google-cloud/firestore` package. | +| [DocumentSnapshot](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/documentsnapshot) | `DocumentSnapshot` type from the `@google-cloud/firestore` package. | +| [FieldPath](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/fieldpath) | `FieldPath` type from the `@google-cloud/firestore` package. | +| [FieldValue](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/fieldvalue) | `FieldValue` type from the `@google-cloud/firestore` package. | +| [Filter](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/filter) | `Filter` type from the `@google-cloud/firestore` package. | +| [Firestore](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/firestore) | `Firestore` type from the `@google-cloud/firestore` package. | | [FirestoreDataConverter](https://googleapis.dev/nodejs/firestore/latest/global.html#FirestoreDataConverter) | `FirestoreDataConverter` type from the `@google-cloud/firestore` package. | -| [GeoPoint](https://googleapis.dev/nodejs/firestore/latest/GeoPoint.html) | `GeoPoint` type from the `@google-cloud/firestore` package. | +| [GeoPoint](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/geopoint) | `GeoPoint` type from the `@google-cloud/firestore` package. | | [GrpcStatus](https://googleapis.dev/nodejs/firestore/latest/global.html#GrpcStatus) | `GrpcStatus` type from the `@google-cloud/firestore` package. | | [Precondition](https://googleapis.dev/nodejs/firestore/latest/global.html#Precondition) | `Precondition` type from the `@google-cloud/firestore` package. | -| [Query](https://googleapis.dev/nodejs/firestore/latest/Query.html) | `Query` type from the `@google-cloud/firestore` package. | -| [QueryDocumentSnapshot](https://googleapis.dev/nodejs/firestore/latest/QueryDocumentSnapshot.html) | `QueryDocumentSnapshot` type from the `@google-cloud/firestore` package. | -| [QueryPartition](https://googleapis.dev/nodejs/firestore/latest/QueryPartition.html) | `QueryPartition` type from the `@google-cloud/firestore` package. | -| [QuerySnapshot](https://googleapis.dev/nodejs/firestore/latest/QuerySnapshot.html) | `QuerySnapshot` type from the `@google-cloud/firestore` package. | +| [Query](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/query) | `Query` type from the `@google-cloud/firestore` package. | +| [QueryDocumentSnapshot](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/querydocumentsnapshot) | `QueryDocumentSnapshot` type from the `@google-cloud/firestore` package. | +| [QueryPartition](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/querypartition) | `QueryPartition` type from the `@google-cloud/firestore` package. | +| [QuerySnapshot](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/querysnapshot) | `QuerySnapshot` type from the `@google-cloud/firestore` package. | | [ReadOptions](https://googleapis.dev/nodejs/firestore/latest/global.html#ReadOptions) | `ReadOptions` type from the `@google-cloud/firestore` package. | | [SetOptions](https://googleapis.dev/nodejs/firestore/latest/global.html#SetOptions) | `SetOptions` type from the `@google-cloud/firestore` package. | -| [Timestamp](https://googleapis.dev/nodejs/firestore/latest/Timestamp.html) | `Timestamp` type from the `@google-cloud/firestore` package. | -| [Transaction](https://googleapis.dev/nodejs/firestore/latest/Transaction.html) | `Transaction` type from the `@google-cloud/firestore` package. | -| [WriteBatch](https://googleapis.dev/nodejs/firestore/latest/WriteBatch.html) | `WriteBatch` type from the `@google-cloud/firestore` package. | -| [WriteResult](https://googleapis.dev/nodejs/firestore/latest/WriteResult.html) | `WriteResult` type from the `@google-cloud/firestore` package. | +| [Timestamp](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/timestamp) | `Timestamp` type from the `@google-cloud/firestore` package. | +| [Transaction](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/transaction) | `Transaction` type from the `@google-cloud/firestore` package. | +| [WriteBatch](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/writebatch) | `WriteBatch` type from the `@google-cloud/firestore` package. | +| [WriteResult](https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/writeresult) | `WriteResult` type from the `@google-cloud/firestore` package. | | [setLogFunction](https://googleapis.dev/nodejs/firestore/latest/global.html#setLogFunction) | `setLogFunction` function from the `@google-cloud/firestore` package. | diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index e8c27b99bd..ceb4ff1ab4 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -57,11 +57,11 @@ export namespace appCheck { // Warning: (ae-forgotten-export) The symbol "AppCheckToken" needs to be exported by the entry point default-namespace.d.ts export type AppCheckToken = AppCheckToken; // Warning: (ae-forgotten-export) The symbol "AppCheckTokenOptions" needs to be exported by the entry point default-namespace.d.ts - // - // (undocumented) export type AppCheckTokenOptions = AppCheckTokenOptions; // Warning: (ae-forgotten-export) The symbol "DecodedAppCheckToken" needs to be exported by the entry point default-namespace.d.ts export type DecodedAppCheckToken = DecodedAppCheckToken; + // Warning: (ae-forgotten-export) The symbol "VerifyAppCheckTokenOptions" needs to be exported by the entry point default-namespace.d.ts + export type VerifyAppCheckTokenOptions = VerifyAppCheckTokenOptions; // Warning: (ae-forgotten-export) The symbol "VerifyAppCheckTokenResponse" needs to be exported by the entry point default-namespace.d.ts export type VerifyAppCheckTokenResponse = VerifyAppCheckTokenResponse; } @@ -243,6 +243,13 @@ export function firestore(app?: App): _firestore.Firestore; export namespace firestore { import v1beta1 = _firestore.v1beta1; import v1 = _firestore.v1; + import AggregateField = _firestore.AggregateField; + import AggregateFieldType = _firestore.AggregateFieldType; + import AggregateQuery = _firestore.AggregateQuery; + import AggregateQuerySnapshot = _firestore.AggregateQuerySnapshot; + import AggregateSpecData = _firestore.AggregateSpecData; + import AggregateSpec = _firestore.AggregateSpec; + import AggregateType = _firestore.AggregateType; import BulkWriter = _firestore.BulkWriter; import BulkWriterOptions = _firestore.BulkWriterOptions; import BundleBuilder = _firestore.BundleBuilder; @@ -255,6 +262,7 @@ export namespace firestore { import DocumentSnapshot = _firestore.DocumentSnapshot; import FieldPath = _firestore.FieldPath; import FieldValue = _firestore.FieldValue; + import Filter = _firestore.Filter; import Firestore = _firestore.Firestore; import FirestoreDataConverter = _firestore.FirestoreDataConverter; import GeoPoint = _firestore.GeoPoint; @@ -319,10 +327,6 @@ export function machineLearning(app?: App): machineLearning.MachineLearning; // @public (undocumented) export namespace machineLearning { - // Warning: (ae-forgotten-export) The symbol "AutoMLTfliteModelOptions" needs to be exported by the entry point default-namespace.d.ts - // - // @deprecated - export type AutoMLTfliteModelOptions = AutoMLTfliteModelOptions; // Warning: (ae-forgotten-export) The symbol "GcsTfliteModelOptions" needs to be exported by the entry point default-namespace.d.ts export type GcsTfliteModelOptions = GcsTfliteModelOptions; // Warning: (ae-forgotten-export) The symbol "ListModelsOptions" needs to be exported by the entry point default-namespace.d.ts diff --git a/etc/firebase-admin.app-check.api.md b/etc/firebase-admin.app-check.api.md index fb4e10ff64..7c883d5f38 100644 --- a/etc/firebase-admin.app-check.api.md +++ b/etc/firebase-admin.app-check.api.md @@ -15,7 +15,7 @@ export class AppCheck { // (undocumented) readonly app: App; createToken(appId: string, options?: AppCheckTokenOptions): Promise; - verifyToken(appCheckToken: string): Promise; + verifyToken(appCheckToken: string, options?: VerifyAppCheckTokenOptions): Promise; } // @public @@ -44,8 +44,14 @@ export interface DecodedAppCheckToken { // @public export function getAppCheck(app?: App): AppCheck; +// @public +export interface VerifyAppCheckTokenOptions { + consume?: boolean; +} + // @public export interface VerifyAppCheckTokenResponse { + alreadyConsumed?: boolean; appId: string; token: DecodedAppCheckToken; } diff --git a/etc/firebase-admin.auth.api.md b/etc/firebase-admin.auth.api.md index c7090af304..3723abd051 100644 --- a/etc/firebase-admin.auth.api.md +++ b/etc/firebase-admin.auth.api.md @@ -138,6 +138,16 @@ export interface CreateRequest extends UpdateRequest { // @public export type CreateTenantRequest = UpdateTenantRequest; +// @public +export interface CustomStrengthOptionsConfig { + maxLength?: number; + minLength?: number; + requireLowercase?: boolean; + requireNonAlphanumeric?: boolean; + requireNumeric?: boolean; + requireUppercase?: boolean; +} + // @alpha (undocumented) export interface DecodedAuthBlockingToken { // (undocumented) @@ -228,6 +238,11 @@ export interface EmailIdentifier { email: string; } +// @public +export interface EmailPrivacyConfig { + enableImprovedEmailPrivacy?: boolean; +} + // @public export interface EmailSignInProviderConfig { enabled: boolean; @@ -267,6 +282,7 @@ export interface ListUsersResult { // @public export interface MultiFactorConfig { factorIds?: AuthFactorType[]; + providerConfigs?: MultiFactorProviderConfig[]; state: MultiFactorConfigState; } @@ -287,6 +303,12 @@ export abstract class MultiFactorInfo { readonly uid: string; } +// @public +export interface MultiFactorProviderConfig { + state: MultiFactorConfigState; + totpProviderConfig?: TotpMultiFactorProviderConfig; +} + // @public export class MultiFactorSettings { enrolledFactors: MultiFactorInfo[]; @@ -322,6 +344,16 @@ export interface OIDCUpdateAuthProviderRequest { responseType?: OAuthResponseType; } +// @public +export interface PasswordPolicyConfig { + constraints?: CustomStrengthOptionsConfig; + enforcementState?: PasswordPolicyEnforcementState; + forceUpgradeOnSignin?: boolean; +} + +// @public +export type PasswordPolicyEnforcementState = 'ENFORCE' | 'OFF'; + // @public export interface PhoneIdentifier { // (undocumented) @@ -336,6 +368,10 @@ export class PhoneMultiFactorInfo extends MultiFactorInfo { // @public export class ProjectConfig { + readonly emailPrivacyConfig?: EmailPrivacyConfig; + get multiFactorConfig(): MultiFactorConfig | undefined; + readonly passwordPolicyConfig?: PasswordPolicyConfig; + get recaptchaConfig(): RecaptchaConfig | undefined; readonly smsRegionConfig?: SmsRegionConfig; toJSON(): object; } @@ -354,6 +390,35 @@ export interface ProviderIdentifier { providerUid: string; } +// @public +export type RecaptchaAction = 'BLOCK'; + +// @public +export interface RecaptchaConfig { + emailPasswordEnforcementState?: RecaptchaProviderEnforcementState; + managedRules?: RecaptchaManagedRule[]; + recaptchaKeys?: RecaptchaKey[]; + useAccountDefender?: boolean; +} + +// @public +export interface RecaptchaKey { + key: string; + type?: RecaptchaKeyClientType; +} + +// @public +export type RecaptchaKeyClientType = 'WEB' | 'IOS' | 'ANDROID'; + +// @public +export interface RecaptchaManagedRule { + action?: RecaptchaAction; + endScore: number; +} + +// @public +export type RecaptchaProviderEnforcementState = 'OFF' | 'AUDIT' | 'ENFORCE'; + // @public export interface SAMLAuthProviderConfig extends BaseAuthProviderConfig { callbackURL?: string; @@ -387,8 +452,11 @@ export class Tenant { // (undocumented) readonly anonymousSignInEnabled: boolean; readonly displayName?: string; + readonly emailPrivacyConfig?: EmailPrivacyConfig; get emailSignInConfig(): EmailSignInProviderConfig | undefined; get multiFactorConfig(): MultiFactorConfig | undefined; + readonly passwordPolicyConfig?: PasswordPolicyConfig; + get recaptchaConfig(): RecaptchaConfig | undefined; readonly smsRegionConfig?: SmsRegionConfig; readonly tenantId: string; readonly testPhoneNumbers?: { @@ -415,6 +483,11 @@ export class TenantManager { updateTenant(tenantId: string, tenantOptions: UpdateTenantRequest): Promise; } +// @public +export interface TotpMultiFactorProviderConfig { + adjacentIntervals?: number; +} + // @public export interface UidIdentifier { // (undocumented) @@ -434,6 +507,10 @@ export interface UpdatePhoneMultiFactorInfoRequest extends BaseUpdateMultiFactor // @public export interface UpdateProjectConfigRequest { + emailPrivacyConfig?: EmailPrivacyConfig; + multiFactorConfig?: MultiFactorConfig; + passwordPolicyConfig?: PasswordPolicyConfig; + recaptchaConfig?: RecaptchaConfig; smsRegionConfig?: SmsRegionConfig; } @@ -455,8 +532,11 @@ export interface UpdateRequest { export interface UpdateTenantRequest { anonymousSignInEnabled?: boolean; displayName?: string; + emailPrivacyConfig?: EmailPrivacyConfig; emailSignInConfig?: EmailSignInProviderConfig; multiFactorConfig?: MultiFactorConfig; + passwordPolicyConfig?: PasswordPolicyConfig; + recaptchaConfig?: RecaptchaConfig; smsRegionConfig?: SmsRegionConfig; testPhoneNumbers?: { [phoneNumber: string]: string; diff --git a/etc/firebase-admin.firestore.api.md b/etc/firebase-admin.firestore.api.md index 6892560c86..80c0e5d9c3 100644 --- a/etc/firebase-admin.firestore.api.md +++ b/etc/firebase-admin.firestore.api.md @@ -8,6 +8,13 @@ import { AddPrefixToKeys } from '@google-cloud/firestore'; import { Agent } from 'http'; +import { AggregateField } from '@google-cloud/firestore'; +import { AggregateFieldType } from '@google-cloud/firestore'; +import { AggregateQuery } from '@google-cloud/firestore'; +import { AggregateQuerySnapshot } from '@google-cloud/firestore'; +import { AggregateSpec } from '@google-cloud/firestore'; +import { AggregateSpecData } from '@google-cloud/firestore'; +import { AggregateType } from '@google-cloud/firestore'; import { BulkWriter } from '@google-cloud/firestore'; import { BulkWriterOptions } from '@google-cloud/firestore'; import { BundleBuilder } from '@google-cloud/firestore'; @@ -21,6 +28,7 @@ import { DocumentReference } from '@google-cloud/firestore'; import { DocumentSnapshot } from '@google-cloud/firestore'; import { FieldPath } from '@google-cloud/firestore'; import { FieldValue } from '@google-cloud/firestore'; +import { Filter } from '@google-cloud/firestore'; import { Firestore } from '@google-cloud/firestore'; import { FirestoreDataConverter } from '@google-cloud/firestore'; import { GeoPoint } from '@google-cloud/firestore'; @@ -52,6 +60,20 @@ import { WriteResult } from '@google-cloud/firestore'; export { AddPrefixToKeys } +export { AggregateField } + +export { AggregateFieldType } + +export { AggregateQuery } + +export { AggregateQuerySnapshot } + +export { AggregateSpec } + +export { AggregateSpecData } + +export { AggregateType } + export { BulkWriter } export { BulkWriterOptions } @@ -78,6 +100,8 @@ export { FieldPath } export { FieldValue } +export { Filter } + export { Firestore } export { FirestoreDataConverter } @@ -97,11 +121,20 @@ export function getFirestore(): Firestore; // @public export function getFirestore(app: App): Firestore; +// @beta +export function getFirestore(databaseId: string): Firestore; + +// @beta +export function getFirestore(app: App, databaseId: string): Firestore; + export { GrpcStatus } // @public export function initializeFirestore(app: App, settings?: FirestoreSettings): Firestore; +// @beta +export function initializeFirestore(app: App, settings: FirestoreSettings, databaseId: string): Firestore; + export { NestedUpdateFields } export { OrderByDirection } diff --git a/etc/firebase-admin.functions.api.md b/etc/firebase-admin.functions.api.md index 2ed05d6f60..87f8656b4a 100644 --- a/etc/firebase-admin.functions.api.md +++ b/etc/firebase-admin.functions.api.md @@ -40,6 +40,8 @@ export function getFunctions(app?: App): Functions; // @public export type TaskOptions = DeliverySchedule & TaskOptionsExperimental & { dispatchDeadlineSeconds?: number; + id?: string; + headers?: Record; }; // @public @@ -50,6 +52,7 @@ export interface TaskOptionsExperimental { // @public export class TaskQueue> { + delete(id: string): Promise; enqueue(data: Args, opts?: TaskOptions): Promise; } diff --git a/etc/firebase-admin.machine-learning.api.md b/etc/firebase-admin.machine-learning.api.md index b99a41523b..b6c3569de9 100644 --- a/etc/firebase-admin.machine-learning.api.md +++ b/etc/firebase-admin.machine-learning.api.md @@ -8,14 +8,6 @@ import { Agent } from 'http'; -// @public @deprecated (undocumented) -export interface AutoMLTfliteModelOptions extends ModelOptionsBase { - // (undocumented) - tfliteModel: { - automlModel: string; - }; -} - // @public (undocumented) export interface GcsTfliteModelOptions extends ModelOptionsBase { // (undocumented) @@ -74,7 +66,7 @@ export class Model { } // @public (undocumented) -export type ModelOptions = ModelOptionsBase | GcsTfliteModelOptions | AutoMLTfliteModelOptions; +export type ModelOptions = ModelOptionsBase | GcsTfliteModelOptions; // @public export interface ModelOptionsBase { @@ -86,8 +78,6 @@ export interface ModelOptionsBase { // @public export interface TFLiteModel { - // @deprecated - readonly automlModel?: string; readonly gcsTfliteUri?: string; readonly sizeBytes: number; } diff --git a/etc/firebase-admin.messaging.api.md b/etc/firebase-admin.messaging.api.md index c37466734c..e9465a8e2d 100644 --- a/etc/firebase-admin.messaging.api.md +++ b/etc/firebase-admin.messaging.api.md @@ -184,10 +184,16 @@ export type Message = TokenMessage | TopicMessage | ConditionMessage; export class Messaging { get app(): App; send(message: Message, dryRun?: boolean): Promise; + // @deprecated sendAll(messages: Message[], dryRun?: boolean): Promise; + sendEach(messages: Message[], dryRun?: boolean): Promise; + sendEachForMulticast(message: MulticastMessage, dryRun?: boolean): Promise; + // @deprecated sendMulticast(message: MulticastMessage, dryRun?: boolean): Promise; sendToCondition(condition: string, payload: MessagingPayload, options?: MessagingOptions): Promise; + // @deprecated sendToDevice(registrationTokenOrTokens: string | string[], payload: MessagingPayload, options?: MessagingOptions): Promise; + // @deprecated sendToDeviceGroup(notificationKey: string, payload: MessagingPayload, options?: MessagingOptions): Promise; sendToTopic(topic: string, payload: MessagingPayload, options?: MessagingOptions): Promise; subscribeToTopic(registrationTokenOrTokens: string | string[], topic: string): Promise; @@ -199,14 +205,14 @@ export interface MessagingConditionResponse { messageId: number; } -// @public +// @public @deprecated export interface MessagingDeviceGroupResponse { failedRegistrationTokens: string[]; failureCount: number; successCount: number; } -// @public (undocumented) +// @public @deprecated export interface MessagingDeviceResult { canonicalRegistrationToken?: string; // Warning: (ae-forgotten-export) The symbol "FirebaseError" needs to be exported by the entry point index.d.ts @@ -214,7 +220,7 @@ export interface MessagingDeviceResult { messageId?: string; } -// @public +// @public @deprecated export interface MessagingDevicesResponse { // (undocumented) canonicalRegistrationTokenCount: number; diff --git a/etc/firebase-admin.storage.api.md b/etc/firebase-admin.storage.api.md index 204c033a6b..ab798e8fca 100644 --- a/etc/firebase-admin.storage.api.md +++ b/etc/firebase-admin.storage.api.md @@ -8,6 +8,10 @@ import { Agent } from 'http'; import { Bucket } from '@google-cloud/storage'; +import { File as File_2 } from '@google-cloud/storage'; + +// @public +export function getDownloadURL(file: File_2): Promise; // Warning: (ae-forgotten-export) The symbol "App" needs to be exported by the entry point index.d.ts // diff --git a/package-lock.json b/package-lock.json index aa2918dcd5..a120760e29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,214 +1,286 @@ { "name": "firebase-admin", - "version": "11.4.1", + "version": "11.11.1", "lockfileVersion": 1, "requires": true, "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "requires": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/compat-data": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.3.tgz", - "integrity": "sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true }, "@babel/core": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.3.tgz", - "integrity": "sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.3", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-module-transforms": "^7.19.0", - "@babel/helpers": "^7.19.0", - "@babel/parser": "^7.19.3", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.3", - "@babel/types": "^7.19.3", - "convert-source-map": "^1.7.0", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", + "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.5", + "@babel/parser": "^7.23.5", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.5", + "@babel/types": "^7.23.5", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } }, "@babel/generator": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.3.tgz", - "integrity": "sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", + "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", "dev": true, "requires": { - "@babel/types": "^7.19.3", + "@babel/types": "^7.23.5", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } } }, "@babel/helper-compilation-targets": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", - "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "requires": { - "@babel/compat-data": "^7.19.3", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.15" } }, "@babel/helper-module-transforms": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", - "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" } }, "@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true }, "@babel/helpers": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", - "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", + "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", "dev": true, "requires": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.5", + "@babel/types": "^7.23.5" } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { @@ -271,35 +343,36 @@ } }, "@babel/parser": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.3.tgz", - "integrity": "sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==" + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", + "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "dev": true }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.3.tgz", - "integrity": "sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.3", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.3", - "@babel/types": "^7.19.3", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", + "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.5", + "@babel/types": "^7.23.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -313,13 +386,13 @@ } }, "@babel/types": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.3.tgz", - "integrity": "sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", + "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -344,48 +417,48 @@ } } }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, "@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } } }, + "@eslint/js": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", + "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "dev": true + }, "@fastify/busboy": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.1.0.tgz", - "integrity": "sha512-Fv854f94v0CzIDllbY3i/0NJPNBRNLDawf3BTYVGCe9VrIIs3Wi7AFx24F9NzCxdf0wyx/x0Q9kEVnvDOPnlxA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.2.1.tgz", + "integrity": "sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==", "requires": { "text-decoding": "^1.0.0" } @@ -407,71 +480,34 @@ } }, "@firebase/app": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.8.2.tgz", - "integrity": "sha512-ByNDCe8h9O/szO3XVTrS484MtqBOKriVaNCQC7Y7KgZSaiA0OOWmIY5vwi63mBTYetqMNN5VGiG/6ZSmGIZyoQ==", + "version": "0.9.25", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.25.tgz", + "integrity": "sha512-fX22gL5USXhOK21Hlh3oTeOzQZ6th6S2JrjXNEpBARmwzuUkqmVGVdsOCIFYIsLpK0dQE3o8xZnLrRg5wnzZ/g==", "dev": true, "requires": { - "@firebase/component": "0.5.20", - "@firebase/logger": "0.3.3", - "@firebase/util": "1.7.2", - "idb": "7.0.1", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "idb": "7.1.1", "tslib": "^2.1.0" - }, - "dependencies": { - "@firebase/component": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.20.tgz", - "integrity": "sha512-wP51tQBlPFprfAWxWjzC/56hG4APhl43jFsgwuqCl3bhVbiKcr278QbrbGNmIXDeGKo4sGZLAnH9whl2apeCmA==", - "dev": true, - "requires": { - "@firebase/util": "1.7.2", - "tslib": "^2.1.0" - } - }, - "@firebase/util": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.7.2.tgz", - "integrity": "sha512-P3aTihYEMoz2QQlcn0T7av7HLEK9gsTc1ZiN9VA8wnUtEJscUNemCmTmP3RRysqEb3Z+tVVoycztY8f6R36rRw==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - } } }, + "@firebase/app-check-interop-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", + "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==" + }, "@firebase/app-compat": { - "version": "0.1.37", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.37.tgz", - "integrity": "sha512-doTKYGlVc8ZiQNOl66rpkU/YItRyOxCgMp4YWThXkPM4T/pTi4a9IMCe8K88gVNeYWd8sKW4vSnxjcOG5hQXEA==", + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.25.tgz", + "integrity": "sha512-B/JtCp1FsTuzlh1tIGQpYM2AXps21/zlzpFsk5LRsROOTRhBcR2N45AyaONPFD06C0yS0Tw19foxADzHyOSC3A==", "dev": true, "requires": { - "@firebase/app": "0.8.2", - "@firebase/component": "0.5.20", - "@firebase/logger": "0.3.3", - "@firebase/util": "1.7.2", + "@firebase/app": "0.9.25", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" - }, - "dependencies": { - "@firebase/component": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.20.tgz", - "integrity": "sha512-wP51tQBlPFprfAWxWjzC/56hG4APhl43jFsgwuqCl3bhVbiKcr278QbrbGNmIXDeGKo4sGZLAnH9whl2apeCmA==", - "dev": true, - "requires": { - "@firebase/util": "1.7.2", - "tslib": "^2.1.0" - } - }, - "@firebase/util": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.7.2.tgz", - "integrity": "sha512-P3aTihYEMoz2QQlcn0T7av7HLEK9gsTc1ZiN9VA8wnUtEJscUNemCmTmP3RRysqEb3Z+tVVoycztY8f6R36rRw==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - } } }, "@firebase/app-types": { @@ -480,221 +516,142 @@ "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==" }, "@firebase/auth": { - "version": "0.20.11", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.20.11.tgz", - "integrity": "sha512-cKy91l4URDG3yWfPK7tjUySh2wCLxtTilsR59jiqQJLReBrQsKP79eFDJ6jqWwbEh3+f1lmoH1nKswwbo9XdmA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.4.0.tgz", + "integrity": "sha512-SfFXZCHDbY+7oSR52NSwx0U7LjYiA+N8imloxphCf3/F+MFty/+mhdjSXGtrJYd0Gbud/qcyedfn2XnWJeIB/g==", "dev": true, "requires": { - "@firebase/component": "0.5.21", - "@firebase/logger": "0.3.4", - "@firebase/util": "1.7.3", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", "node-fetch": "2.6.7", - "selenium-webdriver": "4.5.0", "tslib": "^2.1.0" }, "dependencies": { - "@firebase/logger": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.4.tgz", - "integrity": "sha512-hlFglGRgZEwoyClZcGLx/Wd+zoLfGmbDkFx56mQt/jJ0XMbfPqwId1kiPl0zgdWZX+D8iH+gT6GuLPFsJWgiGw==", + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, "requires": { - "tslib": "^2.1.0" + "whatwg-url": "^5.0.0" } } } }, "@firebase/auth-compat": { - "version": "0.2.24", - "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.2.24.tgz", - "integrity": "sha512-IuZQScjtoOLkUHtmIUJ2F3E2OpDOyap6L/9HL/DX3nzEA1LrX7wlpeU6OF2jS9E0KLueWKIrSkIQOOsKoQj/sA==", + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.4.9.tgz", + "integrity": "sha512-Fw03i7vduIciEBG4imLtA1duJbljgkfbxiBo/EuekcB+BnPxHp+e8OGMUfemPYeO7Munj6kUC9gr5DelsQkiNA==", "dev": true, "requires": { - "@firebase/auth": "0.20.11", - "@firebase/auth-types": "0.11.1", - "@firebase/component": "0.5.21", - "@firebase/util": "1.7.3", + "@firebase/auth": "1.4.0", + "@firebase/auth-types": "0.12.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", "node-fetch": "2.6.7", - "selenium-webdriver": "4.5.0", "tslib": "^2.1.0" }, "dependencies": { - "@firebase/auth-types": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.11.1.tgz", - "integrity": "sha512-ud7T39VG9ptTrC2fOy/XlU+ubC+BVuBJPteuzsPZSa9l7gkntvWgVb3Z/3FxqqRPlkVUYiyvmsbRN3DE1He2ow==", - "dev": true + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } } } }, "@firebase/auth-interop-types": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.0.tgz", - "integrity": "sha512-7Mt2qzwvu5X3Qxz24gjj0qITrBsMmy1W4vGBP8TZRuQrjA4OTlGVCTG8ysvweZ3xpdl1XGhBsIjo2KjfOPg0xA==" + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz", + "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==" }, "@firebase/auth-types": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.11.1.tgz", - "integrity": "sha512-ud7T39VG9ptTrC2fOy/XlU+ubC+BVuBJPteuzsPZSa9l7gkntvWgVb3Z/3FxqqRPlkVUYiyvmsbRN3DE1He2ow==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.0.tgz", + "integrity": "sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==", "dev": true }, "@firebase/component": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.21.tgz", - "integrity": "sha512-12MMQ/ulfygKpEJpseYMR0HunJdlsLrwx2XcEs40M18jocy2+spyzHHEwegN3x/2/BLFBjR5247Etmz0G97Qpg==", - "dev": true, + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz", + "integrity": "sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==", "requires": { - "@firebase/util": "1.7.3", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" } }, "@firebase/database": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.14.0.tgz", - "integrity": "sha512-SM5eri3eGuPjQdXBRObqKTsgmkRwrSGsbgtD43EpGzU+lIeBVLqwRzfcFialYrWzFFI5V7hWXdS2oJxAkfnBFw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.2.tgz", + "integrity": "sha512-8X6NBJgUQzDz0xQVaCISoOLINKat594N2eBbMR3Mu/MH/ei4WM+aAMlsNzngF22eljXu1SILP5G3evkyvsG3Ng==", "requires": { - "@firebase/auth-interop-types": "0.2.0", - "@firebase/component": "0.6.0", + "@firebase/app-check-interop-types": "0.3.0", + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", - "@firebase/util": "1.8.0", + "@firebase/util": "1.9.3", "faye-websocket": "0.11.4", "tslib": "^2.1.0" - }, - "dependencies": { - "@firebase/component": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.0.tgz", - "integrity": "sha512-9hyNc4OmrXMtthDJq6zyJHll/UIYBWYmMG3rXty2eMeWxHWB0vlsq3AOI+k14PL15aSBAQolv0EZJWVJv/gCEg==", - "requires": { - "@firebase/util": "1.8.0", - "tslib": "^2.1.0" - } - }, - "@firebase/logger": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", - "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", - "requires": { - "tslib": "^2.1.0" - } - }, - "@firebase/util": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.8.0.tgz", - "integrity": "sha512-clK6pTTxIiLMYz4UrvDTVAs2rIaOiroAuFdX67C0JalvEwzi6Vv8li6xAGj38tkj7Qax06mosM1fQkxf2h4VTg==", - "requires": { - "tslib": "^2.1.0" - } - } } }, "@firebase/database-compat": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.3.0.tgz", - "integrity": "sha512-5kzhXdACd+RX/G8k/DKYAuiMYHDHIZ9WFV/ccVoPsC+bxIQEgPilDEtkljY5ZxiKbUj+PEOSYUfYdV/LQMJatQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.2.tgz", + "integrity": "sha512-09ryJnXDvuycsxn8aXBzLhBTuCos3HEnCOBWY6hosxfYlNCGnLvG8YMlbSAt5eNhf7/00B095AEfDsdrrLjxqA==", "requires": { - "@firebase/component": "0.6.0", - "@firebase/database": "0.14.0", - "@firebase/database-types": "0.10.0", + "@firebase/component": "0.6.4", + "@firebase/database": "1.0.2", + "@firebase/database-types": "1.0.0", "@firebase/logger": "0.4.0", - "@firebase/util": "1.8.0", + "@firebase/util": "1.9.3", "tslib": "^2.1.0" - }, - "dependencies": { - "@firebase/app-types": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", - "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==" - }, - "@firebase/component": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.0.tgz", - "integrity": "sha512-9hyNc4OmrXMtthDJq6zyJHll/UIYBWYmMG3rXty2eMeWxHWB0vlsq3AOI+k14PL15aSBAQolv0EZJWVJv/gCEg==", - "requires": { - "@firebase/util": "1.8.0", - "tslib": "^2.1.0" - } - }, - "@firebase/database-types": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.10.0.tgz", - "integrity": "sha512-jZHI1fY1tm+8heLR4sbgJHtSYI2kTlSp4QTXWALwdT+dfST5OlZYsZeb+hGWeqjHEElzUnkLbw8XuZSy9Uy6rA==", - "requires": { - "@firebase/app-types": "0.9.0", - "@firebase/util": "1.8.0" - } - }, - "@firebase/logger": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", - "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", - "requires": { - "tslib": "^2.1.0" - } - }, - "@firebase/util": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.8.0.tgz", - "integrity": "sha512-clK6pTTxIiLMYz4UrvDTVAs2rIaOiroAuFdX67C0JalvEwzi6Vv8li6xAGj38tkj7Qax06mosM1fQkxf2h4VTg==", - "requires": { - "tslib": "^2.1.0" - } - } } }, "@firebase/database-types": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.10.0.tgz", - "integrity": "sha512-jZHI1fY1tm+8heLR4sbgJHtSYI2kTlSp4QTXWALwdT+dfST5OlZYsZeb+hGWeqjHEElzUnkLbw8XuZSy9Uy6rA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.0.tgz", + "integrity": "sha512-SjnXStoE0Q56HcFgNQ+9SsmJc0c8TqGARdI/T44KXy+Ets3r6x/ivhQozT66bMnCEjJRywYoxNurRTMlZF8VNg==", "requires": { "@firebase/app-types": "0.9.0", - "@firebase/util": "1.8.0" - }, - "dependencies": { - "@firebase/util": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.8.0.tgz", - "integrity": "sha512-clK6pTTxIiLMYz4UrvDTVAs2rIaOiroAuFdX67C0JalvEwzi6Vv8li6xAGj38tkj7Qax06mosM1fQkxf2h4VTg==", - "requires": { - "tslib": "^2.1.0" - } - } + "@firebase/util": "1.9.3" } }, "@firebase/logger": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.3.tgz", - "integrity": "sha512-POTJl07jOKTOevLXrTvJD/VZ0M6PnJXflbAh5J9VGkmtXPXNG6MdZ9fmRgqYhXKTaDId6AQenQ262uwgpdtO0Q==", - "dev": true, + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", + "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", "requires": { "tslib": "^2.1.0" } }, "@firebase/util": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.7.3.tgz", - "integrity": "sha512-wxNqWbqokF551WrJ9BIFouU/V5SL1oYCGx1oudcirdhadnQRFH5v1sjgGL7cUV/UsekSycygphdrF2lxBxOYKg==", - "dev": true, + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", + "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", "requires": { "tslib": "^2.1.0" } }, "@google-cloud/firestore": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-6.4.0.tgz", - "integrity": "sha512-qhL5V8S6uIGlESQYC/TMKISlKHaM2qSACz0X15ID0s4F1NuVgSM3Z2FS10WYHdCGIwJ2C73xdLaS+ByFDsu7sg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.1.0.tgz", + "integrity": "sha512-kkTC0Sb9r2lONuFF8Tr2wFfBfk0DT1/EKcTKOhsuoXUVClv3jCqGYVPtHgQsHFjdOsubS+tx9G5D5WG+obB2DA==", "optional": true, "requires": { "fast-deep-equal": "^3.1.1", "functional-red-black-tree": "^1.0.1", - "google-gax": "^3.5.1", - "protobufjs": "^7.0.0" + "google-gax": "^4.0.4", + "protobufjs": "^7.2.5" } }, "@google-cloud/paginator": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz", - "integrity": "sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.0.tgz", + "integrity": "sha512-87aeg6QQcEPxGCOthnpUjvw4xAZ57G7pL8FS0C4e/81fr3FjkpUpibf1s2v5XGyGhUVGF4Jfg7yEcxqn2iUw1w==", "optional": true, "requires": { "arrify": "^2.0.0", @@ -702,39 +659,39 @@ } }, "@google-cloud/projectify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz", - "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", "optional": true }, "@google-cloud/promisify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz", - "integrity": "sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", "optional": true }, "@google-cloud/storage": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.8.0.tgz", - "integrity": "sha512-eRGsHrhVA7NORehYW9jLUWHRzYqFxbYiG3LQL50ZhjMekDwzhPKUQ7wbjAji9OFcO3Mk8jeNHeWdpAc/QZANCg==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.7.0.tgz", + "integrity": "sha512-EMCEY+6JiIkx7Dt8NXVGGjy1vRdSGdHkoqZoqjJw7cEBkT7ZkX0c7puedfn1MamnzW5SX4xoa2jVq5u7OWBmkQ==", "optional": true, "requires": { - "@google-cloud/paginator": "^3.0.7", - "@google-cloud/projectify": "^3.0.0", - "@google-cloud/promisify": "^3.0.0", + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", "abort-controller": "^3.0.0", "async-retry": "^1.3.3", "compressible": "^2.0.12", "duplexify": "^4.0.0", "ent": "^2.2.0", - "extend": "^3.0.2", - "gaxios": "^5.0.0", - "google-auth-library": "^8.0.1", + "fast-xml-parser": "^4.3.0", + "gaxios": "^6.0.2", + "google-auth-library": "^9.0.0", "mime": "^3.0.0", "mime-types": "^2.0.8", "p-limit": "^3.0.1", - "retry-request": "^5.0.0", - "teeny-request": "^8.0.0", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", "uuid": "^8.0.0" }, "dependencies": { @@ -747,75 +704,36 @@ } }, "@grpc/grpc-js": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.2.tgz", - "integrity": "sha512-MqqbVynbe3VUSnApFW/dpkDaa9T1ASqRnMWeSPGFO/Ro98R7XUDLacfeBa7RaSI1iFu9GYk5gBKARf0zipFe4w==", + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.12.tgz", + "integrity": "sha512-Um5MBuge32TS3lAKX02PGCnFM4xPT996yLgZNb5H03pn6NyJ4Iwn5YcPq6Jj9yxGRk7WOgaZFtVRH5iTdYBeUg==", "optional": true, "requires": { - "@grpc/proto-loader": "^0.7.0", + "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" } }, "@grpc/proto-loader": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.3.tgz", - "integrity": "sha512-5dAvoZwna2Py3Ef96Ux9jIkp3iZ62TUsV00p3wVBPNX5K178UbNi8Q7gQVqwXT1Yq9RejIGG9G2IPEo93T6RcA==", + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", + "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", "optional": true, "requires": { - "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^7.0.0", - "yargs": "^16.2.0" - }, - "dependencies": { - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "optional": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - } + "long": "^5.0.0", + "protobufjs": "^7.2.4", + "yargs": "^17.7.2" } }, "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } } }, "@humanwhocodes/module-importer": { @@ -825,9 +743,9 @@ "dev": true }, "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "@istanbuljs/load-nyc-config": { @@ -843,15 +761,6 @@ "resolve-from": "^5.0.0" }, "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -920,19 +829,20 @@ "dev": true }, "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true }, "@jridgewell/set-array": { @@ -942,25 +852,25 @@ "dev": true }, "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@mapbox/node-pre-gyp": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", - "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", "dev": true, "requires": { "detect-libc": "^2.0.0", @@ -972,26 +882,47 @@ "rimraf": "^3.0.2", "semver": "^7.3.5", "tar": "^6.1.11" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + } } }, "@microsoft/api-extractor": { - "version": "7.33.5", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.33.5.tgz", - "integrity": "sha512-ENoWpTWarKNuodpRFDQr3jyBigHuv98KuJ8H5qXc1LZ1aP5Mk77lCo88HbPisTmSnGevJJHTScfd/DPznOb4CQ==", + "version": "7.38.4", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.38.4.tgz", + "integrity": "sha512-0SW3Of6os4bAYlHdiD1hJx/ygXr7vRZi92E1pREufNERH87aZ0B9Vhku/4Mj2Oxp58gyV5d18t7uZold6HCSEw==", "dev": true, "requires": { - "@microsoft/api-extractor-model": "7.25.2", + "@microsoft/api-extractor-model": "7.28.2", "@microsoft/tsdoc": "0.14.2", "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.53.2", - "@rushstack/rig-package": "0.3.17", - "@rushstack/ts-command-line": "4.13.0", + "@rushstack/node-core-library": "3.61.0", + "@rushstack/rig-package": "0.5.1", + "@rushstack/ts-command-line": "4.17.1", "colors": "~1.2.1", "lodash": "~4.17.15", - "resolve": "~1.17.0", - "semver": "~7.3.0", + "resolve": "~1.22.1", + "semver": "~7.5.4", "source-map": "~0.6.1", - "typescript": "~4.8.4" + "typescript": "~5.0.4" }, "dependencies": { "@microsoft/tsdoc": { @@ -1001,25 +932,24 @@ "dev": true }, "@rushstack/node-core-library": { - "version": "3.53.2", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.53.2.tgz", - "integrity": "sha512-FggLe5DQs0X9MNFeJN3/EXwb+8hyZUTEp2i+V1e8r4Va4JgkjBNY0BuEaQI+3DW6S4apV3UtXU3im17MSY00DA==", + "version": "3.61.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.61.0.tgz", + "integrity": "sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==", "dev": true, "requires": { - "@types/node": "12.20.24", "colors": "~1.2.1", "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", - "resolve": "~1.17.0", - "semver": "~7.3.0", + "resolve": "~1.22.1", + "semver": "~7.5.4", "z-schema": "~5.0.2" } }, "@rushstack/ts-command-line": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.13.0.tgz", - "integrity": "sha512-crLT31kl+qilz0eBRjqqYO06CqwbElc0EvzS6jI69B9Ikt1SkkSzIZ2iDP7zt/rd1ZYipKIS9hf9CQR9swDIKg==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.17.1.tgz", + "integrity": "sha512-2jweO1O57BYP5qdBGl6apJLB+aRIn5ccIRTPDyULh0KMwVzFqWtw6IZWt1qtUoZD/pD2RNkIOosH6Cq45rIYeg==", "dev": true, "requires": { "@types/argparse": "1.0.38", @@ -1028,47 +958,29 @@ "string-argv": "~0.3.1" } }, - "@types/node": { - "version": "12.20.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", - "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, "colors": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", "dev": true }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } + "typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true } } }, "@microsoft/api-extractor-model": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.25.2.tgz", - "integrity": "sha512-+h1uCrLQXFAKMUdghhdDcnniDB+6UA/lS9ArlB4QZQ34UbLuXNy2oQ6fafFK8cKXU4mUPTF/yGRjv7JKD5L7eg==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.2.tgz", + "integrity": "sha512-vkojrM2fo3q4n4oPh4uUZdjJ2DxQ2+RnDQL/xhTWSRUNPF6P4QyrvY357HBxbnltKcYu+nNNolVqc6TIGQ73Ig==", "dev": true, "requires": { "@microsoft/tsdoc": "0.14.2", "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.53.2" + "@rushstack/node-core-library": "3.61.0" }, "dependencies": { "@microsoft/tsdoc": { @@ -1078,41 +990,25 @@ "dev": true }, "@rushstack/node-core-library": { - "version": "3.53.2", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.53.2.tgz", - "integrity": "sha512-FggLe5DQs0X9MNFeJN3/EXwb+8hyZUTEp2i+V1e8r4Va4JgkjBNY0BuEaQI+3DW6S4apV3UtXU3im17MSY00DA==", + "version": "3.61.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.61.0.tgz", + "integrity": "sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==", "dev": true, "requires": { - "@types/node": "12.20.24", "colors": "~1.2.1", "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", - "resolve": "~1.17.0", - "semver": "~7.3.0", + "resolve": "~1.22.1", + "semver": "~7.5.4", "z-schema": "~5.0.2" } }, - "@types/node": { - "version": "12.20.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", - "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", - "dev": true - }, "colors": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", "dev": true - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } } } }, @@ -1279,28 +1175,26 @@ "requires": { "path-parse": "^1.0.6" } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, "@rushstack/rig-package": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.17.tgz", - "integrity": "sha512-nxvAGeIMnHl1LlZSQmacgcRV4y1EYtgcDIrw6KkeVjudOMonlxO482PhDj3LVZEp6L7emSf6YSO2s5JkHlwfZA==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.1.tgz", + "integrity": "sha512-pXRYSe29TjRw7rqxD4WS3HN/sRSbfr+tJs4a9uuaSIBAITbUggygdhuG0VrO0EO+QqH91GhYMN4S6KRtOEmGVA==", "dev": true, "requires": { - "resolve": "~1.17.0", + "resolve": "~1.22.1", "strip-json-comments": "~3.1.1" - }, - "dependencies": { - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } } }, "@rushstack/ts-command-line": { @@ -1315,15 +1209,6 @@ "string-argv": "~0.3.1" }, "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, "colors": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", @@ -1333,32 +1218,43 @@ } }, "@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "requires": { "type-detect": "4.0.8" } }, "@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", "dev": true, "requires": { - "@sinonjs/commons": "^2.0.0" + "@sinonjs/commons": "^3.0.0" } }, "@sinonjs/samsam": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", - "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", "dev": true, "requires": { "@sinonjs/commons": "^2.0.0", "lodash.get": "^4.4.2", "type-detect": "^4.0.8" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + } } }, "@sinonjs/text-encoding": { @@ -1392,9 +1288,9 @@ "dev": true }, "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, "@types/argparse": { @@ -1404,89 +1300,94 @@ "dev": true }, "@types/bcrypt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", - "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", "dev": true, "requires": { "@types/node": "*" } }, "@types/bluebird": { - "version": "3.5.37", - "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.37.tgz", - "integrity": "sha512-g2qEd+zkfkTEudA2SrMAeAvY7CrFqtbsLILm2dT2VIeKTqMqVzcdfURlvu6FU3srRgbmXN1Srm94pg34EIehww==", + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.42.tgz", + "integrity": "sha512-Jhy+MWRlro6UjVi578V/4ZGNfeCOcNCp0YaFNIUGFKlImowqwb1O/22wDVk3FDGMLqxdpOV3qQHD5fPEH4hK6A==", "dev": true }, "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "requires": { "@types/connect": "*", "@types/node": "*" } }, "@types/caseless": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", - "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", - "dev": true + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==" }, "@types/chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", + "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", "dev": true }, "@types/chai-as-promised": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz", - "integrity": "sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==", + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", + "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", "dev": true, "requires": { "@types/chai": "*" } }, "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "requires": { "@types/node": "*" } }, "@types/express": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz", - "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "requires": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.31", + "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "@types/express-serve-static-core": { - "version": "4.17.32", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz", - "integrity": "sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==", + "version": "4.17.41", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", + "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", "requires": { "@types/node": "*", "@types/qs": "*", - "@types/range-parser": "*" + "@types/range-parser": "*", + "@types/send": "*" } }, "@types/firebase-token-generator": { - "version": "2.0.29", - "resolved": "https://registry.npmjs.org/@types/firebase-token-generator/-/firebase-token-generator-2.0.29.tgz", - "integrity": "sha512-IHFsMicKhaDYFvJmTokF8wLtIGUZVgh1Tie0jYOcgnwHFT1es+hoj2d+SlVx63q91fRHDP2veTUq1yIkqEOT/A==", + "version": "2.0.33", + "resolved": "https://registry.npmjs.org/@types/firebase-token-generator/-/firebase-token-generator-2.0.33.tgz", + "integrity": "sha512-FNZyTEHIE5Kq55QpVuyK7ZUJE/yoegRXalGVhbUh8+xC9pa1BUP7zspY+P5ci5SYKgoHjYQruC9gulFCn576Zw==", "dev": true }, + "@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/jsonwebtoken": { @@ -1498,16 +1399,10 @@ "@types/node": "*" } }, - "@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "optional": true - }, "@types/lodash": { - "version": "4.14.191", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", - "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", "dev": true }, "@types/long": { @@ -1516,26 +1411,10 @@ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", "optional": true }, - "@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "optional": true, - "requires": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "optional": true - }, "@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "@types/minimatch": { "version": "3.0.5", @@ -1544,15 +1423,15 @@ "dev": true }, "@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", "dev": true }, "@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", "dev": true }, "@types/nock": { @@ -1565,25 +1444,27 @@ } }, "@types/node": { - "version": "18.11.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.14.tgz", - "integrity": "sha512-0KXV57tENYmmJMl+FekeW9V3O/rlcqGQQJ/hNh9r8pKIj304pskWuEd8fCyNT86g/TpO0gcOTiLzsHLEURFMIQ==" + "version": "20.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz", + "integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==", + "requires": { + "undici-types": "~5.26.4" + } }, "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "version": "6.9.10", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", + "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==" }, "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "@types/request": { - "version": "2.48.8", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz", - "integrity": "sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==", - "dev": true, + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", "requires": { "@types/caseless": "*", "@types/node": "*", @@ -1592,9 +1473,9 @@ } }, "@types/request-promise": { - "version": "4.1.48", - "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.48.tgz", - "integrity": "sha512-sLsfxfwP5G3E3U64QXxKwA6ctsxZ7uKyl4I28pMj3JvV+ztWECRns73GL71KMOOJME5u1A5Vs5dkBqyiR1Zcnw==", + "version": "4.1.51", + "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.51.tgz", + "integrity": "sha512-qVcP9Fuzh9oaAh8oPxiSoWMFGnWKkJDknnij66vi09Yiy62bsSDqtd+fG5kIM9wLLgZsRP3Y6acqj9O/v2ZtRw==", "dev": true, "requires": { "@types/bluebird": "*", @@ -1602,33 +1483,43 @@ } }, "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "dev": true }, + "@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", "requires": { + "@types/http-errors": "*", "@types/mime": "*", "@types/node": "*" } }, "@types/sinon": { - "version": "10.0.13", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", - "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.2.tgz", + "integrity": "sha512-Zt6heIGsdqERkxctIpvN5Pv3edgBrhoeb3yHyxffd4InN0AX2SVNKSrhdDZKGQICVOxWP/q4DyhpfPNMSrpIiA==", "dev": true, "requires": { "@types/sinonjs__fake-timers": "*" } }, "@types/sinon-chai": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.9.tgz", - "integrity": "sha512-/19t63pFYU0ikrdbXKBWj9PCdnKyTd0Qkz0X91Ta081cYsq90OxYdcWwK/dwEoDa6dtXgj2HJfmzgq+QZTHdmQ==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.12.tgz", + "integrity": "sha512-9y0Gflk3b0+NhQZ/oxGtaAJDvRywCa5sIyaVnounqLvmf93yBF4EgIRspePtkMs3Tr844nCclYMlcCNmLCvjuQ==", "dev": true, "requires": { "@types/chai": "*", @@ -1636,88 +1527,88 @@ } }, "@types/sinonjs__fake-timers": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", - "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", "dev": true }, "@types/tough-cookie": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", - "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", - "dev": true + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" }, "@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", + "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.2.tgz", - "integrity": "sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.48.2", - "@typescript-eslint/type-utils": "5.48.2", - "@typescript-eslint/utils": "5.48.2", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" } }, "@typescript-eslint/parser": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.2.tgz", - "integrity": "sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.48.2", - "@typescript-eslint/types": "5.48.2", - "@typescript-eslint/typescript-estree": "5.48.2", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz", - "integrity": "sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "requires": { - "@typescript-eslint/types": "5.48.2", - "@typescript-eslint/visitor-keys": "5.48.2" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" } }, "@typescript-eslint/type-utils": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz", - "integrity": "sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.48.2", - "@typescript-eslint/utils": "5.48.2", + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz", - "integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz", - "integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.48.2", - "@typescript-eslint/visitor-keys": "5.48.2", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1726,31 +1617,37 @@ } }, "@typescript-eslint/utils": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz", - "integrity": "sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "requires": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.48.2", - "@typescript-eslint/types": "5.48.2", - "@typescript-eslint/typescript-estree": "5.48.2", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz", - "integrity": "sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.48.2", + "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" } }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1767,27 +1664,30 @@ } }, "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==" + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true }, "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", + "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", "dev": true }, "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "optional": true, "requires": { - "debug": "4" + "debug": "^4.3.4" } }, "aggregate-error": { @@ -2036,6 +1936,13 @@ "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", "dev": true }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "optional": true + }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", @@ -2045,6 +1952,15 @@ "path-parse": "^1.0.6" } }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, "validator": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz", @@ -2112,9 +2028,13 @@ "dev": true }, "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } }, "arr-diff": { "version": "4.0.0", @@ -2152,6 +2072,16 @@ "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", "dev": true }, + "array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + } + }, "array-differ": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", @@ -2236,6 +2166,21 @@ "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", "dev": true }, + "arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + } + }, "arrify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", @@ -2281,9 +2226,9 @@ } }, "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", + "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", "dev": true }, "async-retry": { @@ -2307,8 +2252,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "atob": { "version": "2.1.2", @@ -2316,6 +2260,12 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -2323,9 +2273,9 @@ "dev": true }, "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, "bach": { @@ -2348,7 +2298,8 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "base": { "version": "0.11.2", @@ -2374,33 +2325,14 @@ "is-descriptor": "^1.0.0" } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" } } } @@ -2411,12 +2343,12 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "bcrypt": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", - "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", "dev": true, "requires": { - "@mapbox/node-pre-gyp": "^1.0.10", + "@mapbox/node-pre-gyp": "^1.0.11", "node-addon-api": "^5.0.0" } }, @@ -2430,9 +2362,9 @@ } }, "bignumber.js": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz", - "integrity": "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", "optional": true }, "binary-extensions": { @@ -2454,15 +2386,17 @@ "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true }, "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "optional": true, + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "braces": { @@ -2481,15 +2415,15 @@ "dev": true }, "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" } }, "buffer": { @@ -2504,9 +2438,9 @@ } }, "buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha512-tcBWO2Dl4e7Asr9hTGcpVrCe+F7DubpmqWCTbj4FHLmjqO2hIaC383acQubWtRJhdceqs5uBHs6Es+Sk//RKiQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", "dev": true }, "buffer-equal-constant-time": { @@ -2550,13 +2484,14 @@ } }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" } }, "callsites": { @@ -2572,9 +2507,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001412", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001412.tgz", - "integrity": "sha512-+TeEIee1gS5bYOiuf+PS/kp2mrXic37Hl66VY6EAfxasIk5fELTktK2oOezYed12H8w7jt3s512PpulQidPjwA==", + "version": "1.0.30001566", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz", + "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==", "dev": true }, "caseless": { @@ -2583,28 +2518,19 @@ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, - "catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", - "optional": true, - "requires": { - "lodash": "^4.17.15" - } - }, "chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", "dev": true, "requires": { "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.5" + "type-detect": "^4.0.8" } }, "chai-as-promised": { @@ -2629,16 +2555,20 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.2" + } }, "child-process-promise": { "version": "2.2.1", @@ -2797,12 +2727,12 @@ "dev": true }, "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "requires": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, @@ -2836,9 +2766,9 @@ }, "dependencies": { "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -2923,15 +2853,14 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, "optional": true }, @@ -2942,9 +2871,9 @@ "dev": true }, "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true }, "compressible": { @@ -2959,7 +2888,8 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "concat-stream": { "version": "1.6.2", @@ -2974,9 +2904,9 @@ }, "dependencies": { "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3021,21 +2951,10 @@ "dev": true }, "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "copy-descriptor": { "version": "0.1.1", @@ -3141,9 +3060,9 @@ "dev": true }, "deep-eql": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.2.tgz", - "integrity": "sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "dev": true, "requires": { "type-detect": "^4.0.0" @@ -3152,7 +3071,8 @@ "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "default-compare": { "version": "1.0.0", @@ -3172,9 +3092,9 @@ } }, "default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", "dev": true, "requires": { "strip-bom": "^4.0.0" @@ -3194,12 +3114,24 @@ "integrity": "sha512-2xaP6GiwVwOEbXCGoJ4ufgC76m8cj805jrghScewJC2ZDsb9U0b4BIrba+xt/Uytyd0HvQ6+WymSRTfnYj59GQ==", "dev": true }, + "define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "requires": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } @@ -3214,33 +3146,14 @@ "isobject": "^3.0.1" }, "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" } } } @@ -3264,8 +3177,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "delegates": { "version": "1.0.0", @@ -3280,9 +3192,9 @@ "dev": true }, "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", "dev": true }, "diff": { @@ -3350,9 +3262,9 @@ } }, "electron-to-chromium": { - "version": "1.4.266", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.266.tgz", - "integrity": "sha512-saJTYECxUSv7eSpnXw0XIEvUkP9x4s/x2mm3TVX7k4rIFS6f5TjBih1B5h437WzIhHQjid+d8ouQzPQskMervQ==", + "version": "1.4.605", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.605.tgz", + "integrity": "sha512-V52j+P5z6cdRqTjPR/bYNxx7ETCHIkm5VIGuyCy3CMrfSnbEpIlLnk5oHmZo7gYvDfh2TfHeanB6rawyQ23ktg==", "dev": true }, "emoji-regex": { @@ -3374,12 +3286,6 @@ "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", "optional": true }, - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "optional": true - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -3390,35 +3296,61 @@ } }, "es-abstract": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", - "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "requires": { - "call-bind": "^1.0.2", + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", - "has": "^1.0.3", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.6", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + } + }, + "es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" } }, "es-to-primitive": { @@ -3488,89 +3420,57 @@ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "optional": true - }, - "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "optional": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "optional": true - } - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true }, "eslint": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz", - "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", + "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.55.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3582,22 +3482,22 @@ "which": "^2.0.1" } }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3607,54 +3507,6 @@ "is-glob": "^4.0.3" } }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3674,60 +3526,46 @@ "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" - }, - "dependencies": { - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - } - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } } }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true }, "espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, "requires": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" } }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } } }, "esrecurse": { @@ -3737,17 +3575,27 @@ "dev": true, "requires": { "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } } }, "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true }, "event-target-shim": { "version": "5.0.1", @@ -3891,33 +3739,14 @@ "is-extendable": "^0.1.0" } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" } } } @@ -3946,9 +3775,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -3967,18 +3796,22 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, - "fast-text-encoding": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", - "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==", - "optional": true + "fast-xml-parser": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz", + "integrity": "sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==", + "optional": true, + "requires": { + "strnum": "^1.0.5" + } }, "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -4193,19 +4026,20 @@ "dev": true }, "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "requires": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, "flush-write-stream": { @@ -4219,9 +4053,9 @@ }, "dependencies": { "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -4250,6 +4084,15 @@ } } }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -4307,7 +4150,6 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -4347,6 +4189,17 @@ "dev": true, "requires": { "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } } }, "fs-mkdirp-stream": { @@ -4362,7 +4215,8 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "fsevents": { "version": "1.2.13", @@ -4376,21 +4230,21 @@ } }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" } }, "functional-red-black-tree": { @@ -4423,24 +4277,24 @@ } }, "gaxios": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.0.2.tgz", - "integrity": "sha512-TjtV2AJOZoMQqRYoy5eM8cCQogYwazWNYLQ72QB0kwa6vHHruYkGmhhyrlzbmgNHK1dNnuP2WSH81urfzyN2Og==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz", + "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==", "optional": true, "requires": { "extend": "^3.0.2", - "https-proxy-agent": "^5.0.0", + "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", - "node-fetch": "^2.6.7" + "node-fetch": "^2.6.9" } }, "gcp-metadata": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.0.1.tgz", - "integrity": "sha512-jiRJ+Fk7e8FH68Z6TLaqwea307OktJpDjmYnU7/li6ziwvVvU2RlrCyQo5vkdeP94chm0kcSCOOszvmuaioq3g==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", "optional": true, "requires": { - "gaxios": "^5.0.0", + "gaxios": "^6.0.0", "json-bigint": "^1.0.0" } }, @@ -4456,20 +4310,21 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "get-package-type": { @@ -4510,16 +4365,17 @@ } }, "glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", - "optional": true, + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "glob-parent": { @@ -4549,30 +4405,6 @@ "unique-stream": "^2.0.2" }, "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -4592,19 +4424,10 @@ "is-extglob": "^2.1.0" } }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -4673,14 +4496,23 @@ } }, "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3" + } + }, "globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -4705,72 +4537,66 @@ } }, "google-auth-library": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.5.2.tgz", - "integrity": "sha512-FPfOSaI8n2TVXFHTP8/vAVFCXhyALj7w9/Rgefux3oeKZ/nQDNmfNTJ+lIKcoYT1cKkvMllp1Eood7Y5L+TP+A==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.4.1.tgz", + "integrity": "sha512-Chs7cuzDuav8W/BXOoRgSXw4u0zxYtuqAHETDR5Q6dG1RwNwz7NUKjsDDHAsBV3KkiiJBtJqjbzy1XU1L41w1g==", "optional": true, "requires": { - "arrify": "^2.0.0", "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", - "fast-text-encoding": "^1.0.0", - "gaxios": "^5.0.0", - "gcp-metadata": "^5.0.0", - "gtoken": "^6.1.0", - "jws": "^4.0.0", - "lru-cache": "^6.0.0" + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" } }, "google-gax": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-3.5.2.tgz", - "integrity": "sha512-AyP53w0gHcWlzxm+jSgqCR3Xu4Ld7EpSjhtNBnNhzwwWaIUyphH9kBGNIEH+i4UGkTUXOY29K/Re8EiAvkBRGw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.0.5.tgz", + "integrity": "sha512-yLoYtp4zE+8OQA74oBEbNkbzI6c95W01JSL7RqC8XERKpRvj3ytZp1dgnbA6G9aRsc8pZB25xWYBcCmrbYOEhA==", "optional": true, "requires": { - "@grpc/grpc-js": "~1.7.0", + "@grpc/grpc-js": "~1.9.6", "@grpc/proto-loader": "^0.7.0", "@types/long": "^4.0.0", "abort-controller": "^3.0.0", "duplexify": "^4.0.0", - "fast-text-encoding": "^1.0.3", - "google-auth-library": "^8.0.2", - "is-stream-ended": "^0.1.4", + "google-auth-library": "^9.0.0", "node-fetch": "^2.6.1", "object-hash": "^3.0.0", - "proto3-json-serializer": "^1.0.0", - "protobufjs": "7.1.2", - "protobufjs-cli": "1.0.2", - "retry-request": "^5.0.0" + "proto3-json-serializer": "^2.0.0", + "protobufjs": "7.2.5", + "retry-request": "^7.0.0" } }, - "google-p12-pem": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", - "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", - "optional": true, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, "requires": { - "node-forge": "^1.3.1" + "get-intrinsic": "^1.1.3" } }, "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "gtoken": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", - "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz", + "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==", "optional": true, "requires": { - "gaxios": "^5.0.1", - "google-p12-pem": "^4.0.0", + "gaxios": "^6.0.0", "jws": "^4.0.0" } }, @@ -5000,15 +4826,6 @@ "har-schema": "^2.0.0" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -5035,17 +4852,24 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, "requires": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" } }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -5137,6 +4961,15 @@ } } }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -5191,6 +5024,17 @@ "@tootallnate/once": "2", "agent-base": "6", "debug": "4" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "requires": { + "debug": "4" + } + } } }, "http-signature": { @@ -5205,18 +5049,19 @@ } }, "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "optional": true, "requires": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" } }, "idb": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", - "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", "dev": true }, "ieee754": { @@ -5226,15 +5071,9 @@ "dev": true }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true }, "import-fresh": { @@ -5269,6 +5108,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -5286,13 +5126,13 @@ "dev": true }, "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", "side-channel": "^1.0.4" } }, @@ -5319,23 +5159,23 @@ } }, "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", + "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "requires": { + "hasown": "^2.0.0" + } + }, + "is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" } }, "is-arrayish": { @@ -5385,32 +5225,21 @@ "dev": true }, "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "requires": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz", + "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", "dev": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "hasown": "^2.0.0" } }, "is-date-object": { @@ -5423,22 +5252,13 @@ } }, "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" } }, "is-extendable": { @@ -5554,12 +5374,6 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, - "is-stream-ended": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", - "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", - "optional": true - }, "is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -5578,6 +5392,15 @@ "has-symbols": "^1.0.2" } }, + "is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "requires": { + "which-typed-array": "^1.1.11" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -5651,9 +5474,9 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true }, "istanbul-lib-hook": { @@ -5678,9 +5501,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -5737,14 +5560,25 @@ } }, "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" + }, + "dependencies": { + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + } } }, "istanbul-lib-source-maps": { @@ -5759,9 +5593,9 @@ } }, "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -5775,15 +5609,9 @@ "dev": true }, "jose": { - "version": "4.11.2", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.2.tgz", - "integrity": "sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A==" - }, - "js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz", + "integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==" }, "js-tokens": { "version": "4.0.0", @@ -5798,15 +5626,14 @@ "dev": true, "requires": { "argparse": "^2.0.1" - } - }, - "js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", - "optional": true, - "requires": { - "xmlcreate": "^2.0.4" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + } } }, "jsbn": { @@ -5815,29 +5642,6 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, - "jsdoc": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", - "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", - "optional": true, - "requires": { - "@babel/parser": "^7.9.4", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", - "underscore": "~1.13.2" - } - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -5853,6 +5657,12 @@ "bignumber.js": "^9.0.0" } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -5899,14 +5709,20 @@ } }, "jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "requires": { "jws": "^3.2.2", - "lodash": "^4.17.21", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", "ms": "^2.1.1", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "dependencies": { "jwa": { @@ -5927,14 +5743,6 @@ "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "requires": { - "lru-cache": "^6.0.0" - } } } }, @@ -5950,50 +5758,6 @@ "verror": "1.10.0" } }, - "jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "dev": true, - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "just-debounce": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz", @@ -6018,22 +5782,22 @@ } }, "jwks-rsa": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.0.1.tgz", - "integrity": "sha512-UUOZ0CVReK1QVU3rbi9bC7N5/le8ziUj0A2ef1Q0M7OPD2KvjEYizptqIxGIo6fSLYDkqBrazILS18tYuRc8gw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", "requires": { - "@types/express": "^4.17.14", - "@types/jsonwebtoken": "^9.0.0", + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", "debug": "^4.3.4", - "jose": "^4.10.4", + "jose": "^4.14.6", "limiter": "^1.1.5", - "lru-memoizer": "^2.1.4" + "lru-memoizer": "^2.2.0" }, "dependencies": { "@types/jsonwebtoken": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", - "integrity": "sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", "requires": { "@types/node": "*" } @@ -6050,21 +5814,21 @@ "safe-buffer": "^5.0.1" } }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, - "klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "optional": true, - "requires": { - "graceful-fs": "^4.1.9" - } - }, "last-run": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", @@ -6085,9 +5849,9 @@ }, "dependencies": { "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -6135,22 +5899,13 @@ } }, "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "optional": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "requires": { - "immediate": "~3.0.5" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, "liftoff": { @@ -6174,15 +5929,6 @@ "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, - "linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "optional": true, - "requires": { - "uc.micro": "^1.0.1" - } - }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -6208,7 +5954,8 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "lodash._reinterpolate": { "version": "3.0.0", @@ -6239,18 +5986,53 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", "dev": true }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -6281,18 +6063,18 @@ } }, "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", "optional": true }, "loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "requires": { - "get-func-name": "^2.0.0" + "get-func-name": "^2.0.1" } }, "lru-cache": { @@ -6304,9 +6086,9 @@ } }, "lru-memoizer": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz", - "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.2.0.tgz", + "integrity": "sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==", "requires": { "lodash.clonedeep": "^4.5.0", "lru-cache": "~4.0.0" @@ -6338,9 +6120,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -6381,31 +6163,6 @@ "object-visit": "^1.0.0" } }, - "markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "optional": true, - "requires": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - } - }, - "markdown-it-anchor": { - "version": "8.6.5", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz", - "integrity": "sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==", - "optional": true - }, - "marked": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", - "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==", - "optional": true - }, "matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", @@ -6544,12 +6301,6 @@ } } }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "optional": true - }, "memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -6592,27 +6343,25 @@ } }, "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "optional": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "requires": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true }, "minipass": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", - "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true }, "minizlib": { "version": "2.1.2", @@ -6622,6 +6371,17 @@ "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } } }, "mixin-deep": { @@ -6648,7 +6408,8 @@ "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true }, "mocha": { "version": "10.2.0", @@ -6701,16 +6462,6 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -6727,16 +6478,21 @@ "readdirp": "~3.6.0" } }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "optional": true }, @@ -6857,27 +6613,6 @@ "array-union": "^2.1.0", "arrify": "^2.0.1", "minimatch": "^3.0.4" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } } }, "mute-stdout": { @@ -6898,9 +6633,9 @@ } }, "nan": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", - "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", "dev": true, "optional": true }, @@ -6954,9 +6689,9 @@ "dev": true }, "nise": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", - "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz", + "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==", "dev": true, "requires": { "@sinonjs/commons": "^2.0.0", @@ -6964,30 +6699,60 @@ "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", "path-to-regexp": "^1.7.0" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + } + } + } } }, "nock": { - "version": "13.2.9", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz", - "integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==", + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.4.0.tgz", + "integrity": "sha512-W8NVHjO/LCTNA64yxAPHV/K47LpGYcVzgKd3Q0n6owhwvD0Dgoterc25R4rnZbckJEb6Loxz1f5QMuJpJnbSyQ==", "dev": true, "requires": { "debug": "^4.1.0", "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.21", "propagate": "^2.0.0" } }, "node-addon-api": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", - "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", "dev": true }, "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "requires": { "whatwg-url": "^5.0.0" } @@ -7007,9 +6772,9 @@ } }, "node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "node-version": { @@ -7040,9 +6805,9 @@ }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true } } @@ -7088,16 +6853,6 @@ "color-convert": "^1.9.0" } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -7161,15 +6916,6 @@ "strip-bom": "^3.0.0" } }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -7213,9 +6959,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "shebang-command": { @@ -7303,16 +7049,6 @@ "yargs": "^15.0.2" }, "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -7332,26 +7068,12 @@ }, "find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, "locate-path": { @@ -7363,15 +7085,6 @@ "p-locate": "^4.1.0" } }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -7412,9 +7125,9 @@ "dev": true }, "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "dev": true }, "wrap-ansi": { @@ -7515,9 +7228,9 @@ "optional": true }, "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true }, "object-keys": { @@ -7536,13 +7249,13 @@ } }, "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } @@ -7597,17 +7310,17 @@ } }, "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "optional": true, + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" } }, "ordered-read-streams": { @@ -7620,9 +7333,9 @@ }, "dependencies": { "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -7704,12 +7417,6 @@ "release-zalgo": "^1.0.0" } }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7772,7 +7479,8 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true }, "path-key": { "version": "3.1.1", @@ -7942,10 +7650,10 @@ "dev": true }, "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "optional": true + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true }, "pretty-hrtime": { "version": "1.0.3", @@ -7981,18 +7689,18 @@ "dev": true }, "proto3-json-serializer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.0.tgz", - "integrity": "sha512-SjXwUWe/vANGs/mJJTbw5++7U67nwsymg7qsoPtw6GiXqw3kUy8ByojrlEdVE2efxAdKreX8WkDafxvYW95ZQg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.0.tgz", + "integrity": "sha512-FB/YaNrpiPkyQNSNPilpn8qn0KdEfkgmJ9JP93PQyF/U4bAiXY5BiUdDhiDO4S48uSQ6AesklgVlrKiqZPzegw==", "optional": true, "requires": { "protobufjs": "^7.0.0" } }, "protobufjs": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.2.tgz", - "integrity": "sha512-4ZPTPkXCdel3+L81yw3dG6+Kq3umdWKh7Dc7GW/CpNk4SX3hK58iPCWeCyhVTDrbkNeKrYNZ7EojM5WDaEWTLQ==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", "optional": true, "requires": { "@protobufjs/aspromise": "^1.1.2", @@ -8007,32 +7715,6 @@ "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" - }, - "dependencies": { - "long": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", - "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==", - "optional": true - } - } - }, - "protobufjs-cli": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.0.2.tgz", - "integrity": "sha512-cz9Pq9p/Zs7okc6avH20W7QuyjTclwJPgqXG11jNaulfS3nbVisID8rC+prfgq0gbZE0w9LBFd1OKFF03kgFzg==", - "optional": true, - "requires": { - "chalk": "^4.0.0", - "escodegen": "^1.13.0", - "espree": "^9.0.0", - "estraverse": "^5.1.0", - "glob": "^8.0.0", - "jsdoc": "^3.6.3", - "minimist": "^1.2.0", - "semver": "^7.1.2", - "tmp": "^0.2.1", - "uglify-js": "^3.7.7" } }, "pseudomap": { @@ -8080,9 +7762,9 @@ } }, "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -8112,9 +7794,9 @@ } }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, "qs": { @@ -8194,9 +7876,9 @@ } }, "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -8308,9 +7990,9 @@ } }, "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -8369,22 +8051,16 @@ } }, "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" } }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -8529,22 +8205,13 @@ "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", "dev": true }, - "requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", - "optional": true, - "requires": { - "lodash": "^4.17.14" - } - }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -8593,13 +8260,15 @@ "optional": true }, "retry-request": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz", - "integrity": "sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.1.tgz", + "integrity": "sha512-ZI6vJp9rfB71mrZpw+n9p/B6HCsd7QJlSEQftZ+xfJzr3cQ9EPGKw1FF0BnViJ0fYREX6FhymBD2CARpmsFciQ==", "optional": true, "requires": { + "@types/request": "^2.48.8", "debug": "^4.1.1", - "extend": "^3.0.2" + "extend": "^3.0.2", + "teeny-request": "^9.0.0" } }, "reusify": { @@ -8612,40 +8281,9 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, "requires": { "glob": "^7.1.3" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - } } }, "run-parallel": { @@ -8766,6 +8404,26 @@ } } }, + "safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -8797,21 +8455,10 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "selenium-webdriver": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.5.0.tgz", - "integrity": "sha512-9mSFii+lRwcnT2KUAB1kqvx6+mMiiQHH60Y0VUtr3kxxi3oZ3CV3B8e2nuJ7T4SPb+Q6VA0swswe7rYpez07Bg==", - "dev": true, - "requires": { - "jszip": "^3.10.0", - "tmp": "^0.2.1", - "ws": ">=8.7.0" - } - }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -8840,6 +8487,29 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "requires": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, + "set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "requires": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + } + }, "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -8863,12 +8533,6 @@ } } }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8885,9 +8549,9 @@ "dev": true }, "shell-quote": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", "dev": true }, "side-channel": { @@ -8908,17 +8572,25 @@ "dev": true }, "sinon": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz", - "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", "dev": true, "requires": { - "@sinonjs/commons": "^2.0.0", - "@sinonjs/fake-timers": "10.0.2", - "@sinonjs/samsam": "^7.0.1", - "diff": "^5.0.0", - "nise": "^5.1.2", + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", "supports-color": "^7.2.0" + }, + "dependencies": { + "diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true + } } }, "sinon-chai": { @@ -9010,33 +8682,14 @@ "is-descriptor": "^1.0.0" } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" } } } @@ -9064,7 +8717,8 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "source-map-resolve": { "version": "0.5.3", @@ -9117,9 +8771,9 @@ } }, "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -9143,9 +8797,9 @@ } }, "spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", "dev": true }, "split-string": { @@ -9164,9 +8818,9 @@ "dev": true }, "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -9249,9 +8903,9 @@ } }, "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "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 }, "string-width": { @@ -9265,36 +8919,47 @@ } }, "string.prototype.padend": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", - "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz", + "integrity": "sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" } }, "string_decoder": { @@ -9325,7 +8990,14 @@ "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true }, "stubs": { "version": "3.0.0", @@ -9337,6 +9009,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -9357,37 +9030,52 @@ "es6-symbol": "^3.1.1" } }, - "taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", - "optional": true - }, "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", "dev": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "teeny-request": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.2.tgz", - "integrity": "sha512-34pe0a4zASseXZCKdeTiIZqSKA8ETHb1EwItZr01PAR3CLPojeAKgSjzeNS4373gi59hNulyDrPKEbh2zO9sCg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", "optional": true, "requires": { "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.9", "stream-events": "^1.0.5", "uuid": "^9.0.0" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "requires": { + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "optional": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + } } }, "test-exclude": { @@ -9399,41 +9087,6 @@ "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } } }, "text-decoding": { @@ -9476,9 +9129,9 @@ }, "dependencies": { "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -9529,14 +9182,6 @@ "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", "dev": true }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "requires": { - "rimraf": "^3.0.0" - } - }, "to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", @@ -9648,9 +9293,9 @@ } }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "tsutils": { "version": "3.21.0", @@ -9691,12 +9336,12 @@ "dev": true }, "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "optional": true, + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "^1.2.1" } }, "type-detect": { @@ -9711,6 +9356,53 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -9727,23 +9419,11 @@ } }, "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "optional": true - }, - "uglify-js": { - "version": "3.17.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.3.tgz", - "integrity": "sha512-JmMFDME3iufZnBpyKL+uS78LRiC+mK55zWfM5f/pWBJfpOttXAqYfdDGRukYhJuyRinvPVAtUhvy7rlDybNtFg==", - "optional": true - }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -9762,12 +9442,6 @@ "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", "dev": true }, - "underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", - "optional": true - }, "undertaker": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", @@ -9800,6 +9474,11 @@ "integrity": "sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==", "dev": true }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -9875,9 +9554,9 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", - "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "requires": { "escalade": "^3.1.1", @@ -9911,9 +9590,9 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" }, "v8-compile-cache-lib": { "version": "3.0.1", @@ -9941,9 +9620,9 @@ } }, "validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", "dev": true }, "value-or-function": { @@ -10011,9 +9690,9 @@ }, "dependencies": { "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -10125,6 +9804,19 @@ "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==", "dev": true }, + "which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, "wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -10134,11 +9826,6 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" - }, "workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", @@ -10172,18 +9859,6 @@ "typedarray-to-buffer": "^3.1.5" } }, - "ws": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz", - "integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==", - "dev": true - }, - "xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "optional": true - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -10201,10 +9876,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", - "dev": true, + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "requires": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -10212,33 +9886,13 @@ "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "dependencies": { - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - } + "yargs-parser": "^21.1.1" } }, "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "optional": true + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" }, "yargs-unparser": { "version": "2.0.0", @@ -10278,12 +9932,12 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" }, "z-schema": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.4.tgz", - "integrity": "sha512-gm/lx3hDzJNcLwseIeQVm1UcwhWIKpSB4NqH89pTBtFns4k/HDHudsICtvG05Bvw/Mv3jMyk700y5dadueLHdA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", "dev": true, "requires": { - "commander": "^2.20.3", + "commander": "^9.4.1", "lodash.get": "^4.4.2", "lodash.isequal": "^4.5.0", "validator": "^13.7.0" diff --git a/package.json b/package.json index 175be54abf..c1f12f3283 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "11.5.0", + "version": "12.0.0", "description": "Firebase admin SDK for Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -196,24 +196,24 @@ } }, "dependencies": { - "@fastify/busboy": "^1.1.0", - "@firebase/database-compat": "^0.3.0", - "@firebase/database-types": "^0.10.0", - "@types/node": ">=12.12.47", + "@fastify/busboy": "^1.2.1", + "@firebase/database-compat": "^1.0.2", + "@firebase/database-types": "^1.0.0", + "@types/node": "^20.10.3", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", "node-forge": "^1.3.1", "uuid": "^9.0.0" }, "optionalDependencies": { - "@google-cloud/firestore": "^6.4.0", - "@google-cloud/storage": "^6.5.2" + "@google-cloud/firestore": "^7.1.0", + "@google-cloud/storage": "^7.7.0" }, "devDependencies": { "@firebase/api-documenter": "^0.3.0", - "@firebase/app-compat": "^0.1.2", - "@firebase/auth-compat": "^0.2.5", - "@firebase/auth-types": "^0.11.0", + "@firebase/app-compat": "^0.2.1", + "@firebase/auth-compat": "^0.4.1", + "@firebase/auth-types": "^0.12.0", "@microsoft/api-extractor": "^7.11.2", "@types/bcrypt": "^5.0.0", "@types/chai": "^4.0.0", @@ -226,11 +226,11 @@ "@types/nock": "^11.1.0", "@types/request": "^2.47.0", "@types/request-promise": "^4.1.41", - "@types/sinon": "^10.0.2", + "@types/sinon": "^17.0.2", "@types/sinon-chai": "^3.0.0", - "@types/uuid": "^8.3.4", - "@typescript-eslint/eslint-plugin": "^5.0.0", - "@typescript-eslint/parser": "^5.0.0", + "@types/uuid": "^9.0.1", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.62.0", "bcrypt": "^5.0.0", "chai": "^4.2.0", "chai-as-promised": "^7.0.0", @@ -255,10 +255,10 @@ "request": "^2.75.0", "request-promise": "^4.1.1", "run-sequence": "^2.2.1", - "sinon": "^15.0.1", + "sinon": "^17.0.1", "sinon-chai": "^3.0.0", "ts-node": "^10.2.0", - "typescript": "^4.6.4", + "typescript": "5.1.6", "yargs": "^17.0.1" } } diff --git a/src/app-check/app-check-api-client-internal.ts b/src/app-check/app-check-api-client-internal.ts index aae736cf63..6e99bc2c64 100644 --- a/src/app-check/app-check-api-client-internal.ts +++ b/src/app-check/app-check-api-client-internal.ts @@ -27,6 +27,7 @@ import { AppCheckToken } from './app-check-api' // App Check backend constants const FIREBASE_APP_CHECK_V1_API_URL_FORMAT = 'https://firebaseappcheck.googleapis.com/v1/projects/{projectId}/apps/{appId}:exchangeCustomToken'; +const ONE_TIME_USE_TOKEN_VERIFICATION_URL_FORMAT = 'https://firebaseappcheck.googleapis.com/v1beta/projects/{projectId}:verifyAppCheckToken'; const FIREBASE_APP_CHECK_CONFIG_HEADERS = { 'X-Firebase-Client': `fire-admin-node/${utils.getSdkVersion()}` @@ -86,6 +87,35 @@ export class AppCheckApiClient { }); } + public verifyReplayProtection(token: string): Promise { + if (!validator.isNonEmptyString(token)) { + throw new FirebaseAppCheckError( + 'invalid-argument', + '`token` must be a non-empty string.'); + } + return this.getVerifyTokenUrl() + .then((url) => { + const request: HttpRequestConfig = { + method: 'POST', + url, + headers: FIREBASE_APP_CHECK_CONFIG_HEADERS, + data: { app_check_token: token } + }; + return this.httpClient.send(request); + }) + .then((resp) => { + if (typeof resp.data.alreadyConsumed !== 'undefined' + && !validator.isBoolean(resp.data?.alreadyConsumed)) { + throw new FirebaseAppCheckError( + 'invalid-argument', '`alreadyConsumed` must be a boolean value.'); + } + return resp.data.alreadyConsumed || false; + }) + .catch((err) => { + throw this.toFirebaseError(err); + }); + } + private getUrl(appId: string): Promise { return this.getProjectId() .then((projectId) => { @@ -98,6 +128,17 @@ export class AppCheckApiClient { }); } + private getVerifyTokenUrl(): Promise { + return this.getProjectId() + .then((projectId) => { + const urlParams = { + projectId + }; + const baseUrl = utils.formatString(ONE_TIME_USE_TOKEN_VERIFICATION_URL_FORMAT, urlParams); + return utils.formatString(baseUrl); + }); + } + private getProjectId(): Promise { if (this.projectId) { return Promise.resolve(this.projectId); diff --git a/src/app-check/app-check-api.ts b/src/app-check/app-check-api.ts index ab959af04d..de44a5a854 100644 --- a/src/app-check/app-check-api.ts +++ b/src/app-check/app-check-api.ts @@ -41,6 +41,29 @@ export interface AppCheckTokenOptions { ttlMillis?: number; } +/** + * Interface representing options for the {@link AppCheck.verifyToken} method. + */ +export interface VerifyAppCheckTokenOptions { + /** + * To use the replay protection feature, set this to `true`. The {@link AppCheck.verifyToken} + * method will mark the token as consumed after verifying it. + * + * Tokens that are found to be already consumed will be marked as such in the response. + * + * Tokens are only considered to be consumed if it is sent to App Check backend by calling the + * {@link AppCheck.verifyToken} method with this field set to `true`; other uses of the token + * do not consume it. + * + * This replay protection feature requires an additional network call to the App Check backend + * and forces your clients to obtain a fresh attestation from your chosen attestation providers. + * This can therefore negatively impact performance and can potentially deplete your attestation + * providers' quotas faster. We recommend that you use this feature only for protecting + * low volume, security critical, or expensive operations. + */ + consume?: boolean; +} + /** * Interface representing a decoded Firebase App Check token, returned from the * {@link AppCheck.verifyToken} method. @@ -102,4 +125,17 @@ export interface VerifyAppCheckTokenResponse { * The decoded Firebase App Check token. */ token: DecodedAppCheckToken; + + /** + * Indicates weather this token was already consumed. + * If this is the first time {@link AppCheck.verifyToken} method has seen this token, + * this field will contain the value `false`. The given token will then be + * marked as `already_consumed` for all future invocations of this {@link AppCheck.verifyToken} + * method for this token. + * + * When this field is `true`, the caller is attempting to reuse a previously consumed token. + * You should take precautions against such a caller; for example, you can take actions such as + * rejecting the request or ask the caller to pass additional layers of security checks. + */ + alreadyConsumed?: boolean; } diff --git a/src/app-check/app-check-namespace.ts b/src/app-check/app-check-namespace.ts index 070fb7a751..13e5beb3a7 100644 --- a/src/app-check/app-check-namespace.ts +++ b/src/app-check/app-check-namespace.ts @@ -19,6 +19,7 @@ import { AppCheckToken as TAppCheckToken, AppCheckTokenOptions as TAppCheckTokenOptions, DecodedAppCheckToken as TDecodedAppCheckToken, + VerifyAppCheckTokenOptions as TVerifyAppCheckTokenOptions, VerifyAppCheckTokenResponse as TVerifyAppCheckTokenResponse, } from './app-check-api'; import { AppCheck as TAppCheck } from './app-check'; @@ -73,5 +74,13 @@ export namespace appCheck { */ export type VerifyAppCheckTokenResponse = TVerifyAppCheckTokenResponse; + /** + * Type alias to {@link firebase-admin.app-check#AppCheckTokenOptions}. + */ export type AppCheckTokenOptions = TAppCheckTokenOptions; + + /** + * Type alias to {@link firebase-admin.app-check#VerifyAppCheckTokenOptions}. + */ + export type VerifyAppCheckTokenOptions = TVerifyAppCheckTokenOptions; } diff --git a/src/app-check/app-check.ts b/src/app-check/app-check.ts index 0785fd6621..c81a04acf5 100644 --- a/src/app-check/app-check.ts +++ b/src/app-check/app-check.ts @@ -15,8 +15,10 @@ * limitations under the License. */ +import * as validator from '../utils/validator'; + import { App } from '../app'; -import { AppCheckApiClient } from './app-check-api-client-internal'; +import { AppCheckApiClient, FirebaseAppCheckError } from './app-check-api-client-internal'; import { appCheckErrorFromCryptoSignerError, AppCheckTokenGenerator, } from './token-generator'; @@ -26,6 +28,7 @@ import { cryptoSignerFromApp } from '../utils/crypto-signer'; import { AppCheckToken, AppCheckTokenOptions, + VerifyAppCheckTokenOptions, VerifyAppCheckTokenResponse, } from './app-check-api'; @@ -75,17 +78,41 @@ export class AppCheck { * rejected. * * @param appCheckToken - The App Check token to verify. + * @param options - Optional {@link VerifyAppCheckTokenOptions} object when verifying an App Check Token. * * @returns A promise fulfilled with the token's decoded claims * if the App Check token is valid; otherwise, a rejected promise. */ - public verifyToken(appCheckToken: string): Promise { + public verifyToken(appCheckToken: string, options?: VerifyAppCheckTokenOptions) + : Promise { + this.validateVerifyAppCheckTokenOptions(options); return this.appCheckTokenVerifier.verifyToken(appCheckToken) .then((decodedToken) => { + if (options?.consume) { + return this.client.verifyReplayProtection(appCheckToken) + .then((alreadyConsumed) => { + return { + alreadyConsumed, + appId: decodedToken.app_id, + token: decodedToken, + }; + }); + } return { appId: decodedToken.app_id, token: decodedToken, }; }); } + + private validateVerifyAppCheckTokenOptions(options?: VerifyAppCheckTokenOptions): void { + if (typeof options === 'undefined') { + return; + } + if (!validator.isNonNullObject(options)) { + throw new FirebaseAppCheckError( + 'invalid-argument', + 'VerifyAppCheckTokenOptions must be a non-null object.'); + } + } } diff --git a/src/app-check/index.ts b/src/app-check/index.ts index 72f4d54b00..54cd9291d8 100644 --- a/src/app-check/index.ts +++ b/src/app-check/index.ts @@ -29,6 +29,7 @@ export { AppCheckToken, AppCheckTokenOptions, DecodedAppCheckToken, + VerifyAppCheckTokenOptions, VerifyAppCheckTokenResponse, } from './app-check-api'; export { AppCheck } from './app-check'; diff --git a/src/auth/auth-api-request.ts b/src/auth/auth-api-request.ts index 2893d49a9d..9fd535777c 100644 --- a/src/auth/auth-api-request.ts +++ b/src/auth/auth-api-request.ts @@ -1603,7 +1603,7 @@ export abstract class AbstractAuthRequestHandler { * @param email - The email of the user the link is being sent to. * @param actionCodeSettings - The optional action code setings which defines whether * the link is to be handled by a mobile app and the additional state information to be passed in the - * deep link, etc. Required when requestType == 'EMAIL_SIGNIN' + * deep link, etc. Required when requestType === 'EMAIL_SIGNIN' * @param newEmail - The email address the account is being updated to. * Required only for VERIFY_AND_CHANGE_EMAIL requests. * @returns A promise that resolves with the email action link. @@ -2084,7 +2084,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler { * The FirebaseAuthRequestHandler constructor used to initialize an instance using a FirebaseApp. * * @param app - The app used to fetch access tokens to sign API requests. - * @constructor. + * @constructor */ constructor(app: App) { super(app); diff --git a/src/auth/auth-config.ts b/src/auth/auth-config.ts index 45ca3ef2d0..28ee595c46 100644 --- a/src/auth/auth-config.ts +++ b/src/auth/auth-config.ts @@ -478,6 +478,7 @@ const AUTH_FACTOR_SERVER_TO_CLIENT_TYPE: {[key: string]: AuthFactorType} = export interface MultiFactorAuthServerConfig { state?: MultiFactorConfigState; enabledProviders?: AuthFactorServerType[]; + providerConfigs?: MultiFactorProviderConfig[]; } /** @@ -506,16 +507,58 @@ export interface MultiFactorConfig { * Currently only ‘phone’ is supported. */ factorIds?: AuthFactorType[]; + + /** + * A list of multi-factor provider configurations. + * MFA providers (except phone) indicate whether they're enabled through this field. */ + providerConfigs?: MultiFactorProviderConfig[]; +} + +/** + * Interface representing a multi-factor auth provider configuration. + * This interface is used for second factor auth providers other than SMS. + * Currently, only TOTP is supported. + */export interface MultiFactorProviderConfig { + /** + * Indicates whether this multi-factor provider is enabled or disabled. */ + state: MultiFactorConfigState; + /** + * TOTP multi-factor provider config. */ + totpProviderConfig?: TotpMultiFactorProviderConfig; +} + +/** + * Interface representing configuration settings for TOTP second factor auth. + */ +export interface TotpMultiFactorProviderConfig { + /** + * The allowed number of adjacent intervals that will be used for verification + * to compensate for clock skew. */ + adjacentIntervals?: number; } /** * Defines the multi-factor config class used to convert client side MultiFactorConfig * to a format that is understood by the Auth server. + * + * @internal */ export class MultiFactorAuthConfig implements MultiFactorConfig { + /** + * The multi-factor config state. + */ public readonly state: MultiFactorConfigState; + /** + * The list of identifiers for enabled second factors. + * Currently only ‘phone’ is supported. + */ public readonly factorIds: AuthFactorType[]; + /** + * A list of multi-factor provider specific config. + * New MFA providers (except phone) will indicate enablement/disablement through this field. + */ + public readonly providerConfigs: MultiFactorProviderConfig[]; /** * Static method to convert a client side request to a MultiFactorAuthServerConfig. @@ -543,6 +586,9 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { request.enabledProviders = []; } } + if (Object.prototype.hasOwnProperty.call(options, 'providerConfigs')) { + request.providerConfigs = options.providerConfigs; + } return request; } @@ -551,10 +597,11 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { * * @param options - The options object to validate. */ - private static validate(options: MultiFactorConfig): void { + public static validate(options: MultiFactorConfig): void { const validKeys = { state: true, factorIds: true, + providerConfigs: true, }; if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( @@ -599,6 +646,71 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { } }); } + + if (typeof options.providerConfigs !== 'undefined') { + if (!validator.isArray(options.providerConfigs)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"MultiFactorConfig.providerConfigs" must be an array of valid "MultiFactorProviderConfig."', + ); + } + //Validate content of array. + options.providerConfigs.forEach((multiFactorProviderConfig) => { + if (typeof multiFactorProviderConfig === 'undefined' || !validator.isObject(multiFactorProviderConfig)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + `"${multiFactorProviderConfig}" is not a valid "MultiFactorProviderConfig" type.` + ) + } + const validProviderConfigKeys = { + state: true, + totpProviderConfig: true, + }; + for (const key in multiFactorProviderConfig) { + if (!(key in validProviderConfigKeys)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + `"${key}" is not a valid ProviderConfig parameter.`, + ); + } + } + if (typeof multiFactorProviderConfig.state === 'undefined' || + (multiFactorProviderConfig.state !== 'ENABLED' && + multiFactorProviderConfig.state !== 'DISABLED')) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"MultiFactorConfig.providerConfigs.state" must be either "ENABLED" or "DISABLED".', + ) + } + // Since TOTP is the only provider config available right now, not defining it will lead into an error + if (typeof multiFactorProviderConfig.totpProviderConfig === 'undefined') { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"MultiFactorConfig.providerConfigs.totpProviderConfig" must be defined.' + ) + } + const validTotpProviderConfigKeys = { + adjacentIntervals: true, + }; + for (const key in multiFactorProviderConfig.totpProviderConfig) { + if (!(key in validTotpProviderConfigKeys)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + `"${key}" is not a valid TotpProviderConfig parameter.`, + ); + } + } + const adjIntervals = multiFactorProviderConfig.totpProviderConfig.adjacentIntervals + if (typeof adjIntervals !== 'undefined' && + (!Number.isInteger(adjIntervals) || adjIntervals < 0 || adjIntervals > 10)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + '"MultiFactorConfig.providerConfigs.totpProviderConfig.adjacentIntervals" must' + + ' be a valid number between 0 and 10 (both inclusive).' + ) + } + }); + } } /** @@ -624,13 +736,29 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { this.factorIds.push(AUTH_FACTOR_SERVER_TO_CLIENT_TYPE[enabledProvider]); } }) + this.providerConfigs = []; + (response.providerConfigs || []).forEach((providerConfig) => { + if (typeof providerConfig !== 'undefined') { + if (typeof providerConfig.state === 'undefined' || + typeof providerConfig.totpProviderConfig === 'undefined' || + (typeof providerConfig.totpProviderConfig.adjacentIntervals !== 'undefined' && + typeof providerConfig.totpProviderConfig.adjacentIntervals !== 'number')) { + throw new FirebaseAuthError( + AuthClientErrorCode.INTERNAL_ERROR, + 'INTERNAL ASSERT FAILED: Invalid multi-factor configuration response'); + } + this.providerConfigs.push(providerConfig); + } + }) } - /** @returns The plain object representation of the multi-factor config instance. */ + /** Converts MultiFactorConfig to JSON object + * @returns The plain object representation of the multi-factor config instance. */ public toJSON(): object { return { state: this.state, factorIds: this.factorIds, + providerConfigs: this.providerConfigs, }; } } @@ -1389,7 +1517,7 @@ export class OIDCConfig implements OIDCAuthProviderConfig { const allKeys = Object.keys(options.responseType).length; const enabledCount = Object.values(options.responseType).filter(Boolean).length; // Only one of OAuth response types can be set to true. - if (allKeys > 1 && enabledCount != 1) { + if (allKeys > 1 && enabledCount !== 1) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_OAUTH_RESPONSETYPE, 'Only exactly one OAuth responseType should be set to true.', @@ -1594,3 +1722,607 @@ export class SmsRegionsAuthConfig { } } } +/** +* Enforcement state of reCAPTCHA protection. +* - 'OFF': Unenforced. +* - 'AUDIT': Create assessment but don't enforce the result. +* - 'ENFORCE': Create assessment and enforce the result. +*/ +export type RecaptchaProviderEnforcementState = 'OFF' | 'AUDIT' | 'ENFORCE'; + +/** +* The actions to take for reCAPTCHA-protected requests. +* - 'BLOCK': The reCAPTCHA-protected request will be blocked. +*/ +export type RecaptchaAction = 'BLOCK'; + +/** + * The config for a reCAPTCHA action rule. + */ +export interface RecaptchaManagedRule { + /** + * The action will be enforced if the reCAPTCHA score of a request is larger than endScore. + */ + endScore: number; + /** + * The action for reCAPTCHA-protected requests. + */ + action?: RecaptchaAction; +} + +/** + * The key's platform type. + */ +export type RecaptchaKeyClientType = 'WEB' | 'IOS' | 'ANDROID'; + +/** + * The reCAPTCHA key config. + */ +export interface RecaptchaKey { + /** + * The key's client platform type. + */ + type?: RecaptchaKeyClientType; + + /** + * The reCAPTCHA site key. + */ + key: string; +} + +/** + * The request interface for updating a reCAPTCHA Config. + * By enabling reCAPTCHA Enterprise Integration you are + * agreeing to reCAPTCHA Enterprise + * {@link https://cloud.google.com/terms/service-terms | Term of Service}. + */ +export interface RecaptchaConfig { + /** + * The enforcement state of the email password provider. + */ + emailPasswordEnforcementState?: RecaptchaProviderEnforcementState; + /** + * The reCAPTCHA managed rules. + */ + managedRules?: RecaptchaManagedRule[]; + + /** + * The reCAPTCHA keys. + */ + recaptchaKeys?: RecaptchaKey[]; + + /** + * Whether to use account defender for reCAPTCHA assessment. + * The default value is false. + */ + useAccountDefender?: boolean; +} + +export class RecaptchaAuthConfig implements RecaptchaConfig { + public readonly emailPasswordEnforcementState?: RecaptchaProviderEnforcementState; + public readonly managedRules?: RecaptchaManagedRule[]; + public readonly recaptchaKeys?: RecaptchaKey[]; + public readonly useAccountDefender?: boolean; + + constructor(recaptchaConfig: RecaptchaConfig) { + this.emailPasswordEnforcementState = recaptchaConfig.emailPasswordEnforcementState; + this.managedRules = recaptchaConfig.managedRules; + this.recaptchaKeys = recaptchaConfig.recaptchaKeys; + this.useAccountDefender = recaptchaConfig.useAccountDefender; + } + + /** + * Validates the RecaptchaConfig options object. Throws an error on failure. + * @param options - The options object to validate. + */ + public static validate(options: RecaptchaConfig): void { + const validKeys = { + emailPasswordEnforcementState: true, + managedRules: true, + recaptchaKeys: true, + useAccountDefender: true, + }; + + if (!validator.isNonNullObject(options)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"RecaptchaConfig" must be a non-null object.', + ); + } + + for (const key in options) { + if (!(key in validKeys)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + `"${key}" is not a valid RecaptchaConfig parameter.`, + ); + } + } + + // Validation + if (typeof options.emailPasswordEnforcementState !== undefined) { + if (!validator.isNonEmptyString(options.emailPasswordEnforcementState)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + '"RecaptchaConfig.emailPasswordEnforcementState" must be a valid non-empty string.', + ); + } + + if (options.emailPasswordEnforcementState !== 'OFF' && + options.emailPasswordEnforcementState !== 'AUDIT' && + options.emailPasswordEnforcementState !== 'ENFORCE') { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"RecaptchaConfig.emailPasswordEnforcementState" must be either "OFF", "AUDIT" or "ENFORCE".', + ); + } + } + + if (typeof options.managedRules !== 'undefined') { + // Validate array + if (!validator.isArray(options.managedRules)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"RecaptchaConfig.managedRules" must be an array of valid "RecaptchaManagedRule".', + ); + } + // Validate each rule of the array + options.managedRules.forEach((managedRule) => { + RecaptchaAuthConfig.validateManagedRule(managedRule); + }); + } + + if (typeof options.useAccountDefender !== 'undefined') { + if (!validator.isBoolean(options.useAccountDefender)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"RecaptchaConfig.useAccountDefender" must be a boolean value".', + ); + } + } + } + + /** + * Validate each element in ManagedRule array + * @param options - The options object to validate. + */ + private static validateManagedRule(options: RecaptchaManagedRule): void { + const validKeys = { + endScore: true, + action: true, + } + if (!validator.isNonNullObject(options)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"RecaptchaManagedRule" must be a non-null object.', + ); + } + // Check for unsupported top level attributes. + for (const key in options) { + if (!(key in validKeys)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + `"${key}" is not a valid RecaptchaManagedRule parameter.`, + ); + } + } + + // Validate content. + if (typeof options.action !== 'undefined' && + options.action !== 'BLOCK') { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"RecaptchaManagedRule.action" must be "BLOCK".', + ); + } + } + + /** + * Returns a JSON-serializable representation of this object. + * @returns The JSON-serializable object representation of the ReCaptcha config instance + */ + public toJSON(): object { + const json: any = { + emailPasswordEnforcementState: this.emailPasswordEnforcementState, + managedRules: deepCopy(this.managedRules), + recaptchaKeys: deepCopy(this.recaptchaKeys), + useAccountDefender: this.useAccountDefender, + } + + if (typeof json.emailPasswordEnforcementState === 'undefined') { + delete json.emailPasswordEnforcementState; + } + if (typeof json.managedRules === 'undefined') { + delete json.managedRules; + } + if (typeof json.recaptchaKeys === 'undefined') { + delete json.recaptchaKeys; + } + + if (typeof json.useAccountDefender === 'undefined') { + delete json.useAccountDefender; + } + + return json; + } +} + +/** + * A password policy configuration for a project or tenant +*/ +export interface PasswordPolicyConfig { + /** + * Enforcement state of the password policy + */ + enforcementState?: PasswordPolicyEnforcementState; + /** + * Require users to have a policy-compliant password to sign in + */ + forceUpgradeOnSignin?: boolean; + /** + * The constraints that make up the password strength policy + */ + constraints?: CustomStrengthOptionsConfig; +} + +/** + * A password policy's enforcement state. + */ +export type PasswordPolicyEnforcementState = 'ENFORCE' | 'OFF'; + +/** + * Constraints to be enforced on the password policy + */ +export interface CustomStrengthOptionsConfig { + /** + * The password must contain an upper case character + */ + requireUppercase?: boolean; + /** + * The password must contain a lower case character + */ + requireLowercase?: boolean; + /** + * The password must contain a non-alphanumeric character + */ + requireNonAlphanumeric?: boolean; + /** + * The password must contain a number + */ + requireNumeric?: boolean; + /** + * Minimum password length. Valid values are from 6 to 30 + */ + minLength?: number; + /** + * Maximum password length. No default max length + */ + maxLength?: number; +} + +/** + * Defines the password policy config class used to convert client side PasswordPolicyConfig + * to a format that is understood by the Auth server. + * + * @internal + */ +export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { + + /** + * Identifies a password policy configuration state. + */ + public readonly enforcementState: PasswordPolicyEnforcementState; + /** + * Users must have a password compliant with the password policy to sign-in + */ + public readonly forceUpgradeOnSignin: boolean; + /** + * Must be of length 1. Contains the strength attributes for the password policy + */ + public readonly constraints?: CustomStrengthOptionsConfig; + + /** + * Static method to convert a client side request to a PasswordPolicyAuthServerConfig. + * Throws an error if validation fails. + * + * @param options - The options object to convert to a server request. + * @returns The resulting server request. + * @internal + */ + public static buildServerRequest(options: PasswordPolicyConfig): PasswordPolicyAuthServerConfig { + const request: PasswordPolicyAuthServerConfig = {}; + PasswordPolicyAuthConfig.validate(options); + if (Object.prototype.hasOwnProperty.call(options, 'enforcementState')) { + request.passwordPolicyEnforcementState = options.enforcementState; + } + request.forceUpgradeOnSignin = false; + if (Object.prototype.hasOwnProperty.call(options, 'forceUpgradeOnSignin')) { + request.forceUpgradeOnSignin = options.forceUpgradeOnSignin; + } + const constraintsRequest: CustomStrengthOptionsAuthServerConfig = { + containsUppercaseCharacter: false, + containsLowercaseCharacter: false, + containsNonAlphanumericCharacter: false, + containsNumericCharacter: false, + minPasswordLength: 6, + maxPasswordLength: 4096, + }; + request.passwordPolicyVersions = []; + if (Object.prototype.hasOwnProperty.call(options, 'constraints')) { + if (options) { + if (options.constraints?.requireUppercase !== undefined) { + constraintsRequest.containsUppercaseCharacter = options.constraints.requireUppercase; + } + if (options.constraints?.requireLowercase !== undefined) { + constraintsRequest.containsLowercaseCharacter = options.constraints.requireLowercase; + } + if (options.constraints?.requireNonAlphanumeric !== undefined) { + constraintsRequest.containsNonAlphanumericCharacter = options.constraints.requireNonAlphanumeric; + } + if (options.constraints?.requireNumeric !== undefined) { + constraintsRequest.containsNumericCharacter = options.constraints.requireNumeric; + } + if (options.constraints?.minLength !== undefined) { + constraintsRequest.minPasswordLength = options.constraints.minLength; + } + if (options.constraints?.maxLength !== undefined) { + constraintsRequest.maxPasswordLength = options.constraints.maxLength; + } + } + } + request.passwordPolicyVersions.push({ customStrengthOptions: constraintsRequest }); + return request; + } + + /** + * Validates the PasswordPolicyConfig options object. Throws an error on failure. + * + * @param options - The options object to validate. + * @internal + */ + public static validate(options: PasswordPolicyConfig): void { + const validKeys = { + enforcementState: true, + forceUpgradeOnSignin: true, + constraints: true, + }; + if (!validator.isNonNullObject(options)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig" must be a non-null object.', + ); + } + // Check for unsupported top level attributes. + for (const key in options) { + if (!(key in validKeys)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + `"${key}" is not a valid PasswordPolicyConfig parameter.`, + ); + } + } + // Validate content. + if (typeof options.enforcementState === 'undefined' || + !(options.enforcementState === 'ENFORCE' || + options.enforcementState === 'OFF')) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.enforcementState" must be either "ENFORCE" or "OFF".', + ); + } + + if (typeof options.forceUpgradeOnSignin !== 'undefined') { + if (!validator.isBoolean(options.forceUpgradeOnSignin)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.forceUpgradeOnSignin" must be a boolean.', + ); + } + } + + if (typeof options.constraints !== 'undefined') { + if (options.enforcementState === 'ENFORCE' && !validator.isNonNullObject(options.constraints)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.constraints" must be a non-empty object.', + ); + } + + const validCharKeys = { + requireUppercase: true, + requireLowercase: true, + requireNumeric: true, + requireNonAlphanumeric: true, + minLength: true, + maxLength: true, + }; + + // Check for unsupported attributes. + for (const key in options.constraints) { + if (!(key in validCharKeys)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + `"${key}" is not a valid PasswordPolicyConfig.constraints parameter.`, + ); + } + } + if (typeof options.constraints.requireUppercase !== 'undefined' && + !validator.isBoolean(options.constraints.requireUppercase)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.constraints.requireUppercase" must be a boolean.', + ); + } + if (typeof options.constraints.requireLowercase !== 'undefined' && + !validator.isBoolean(options.constraints.requireLowercase)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.constraints.requireLowercase" must be a boolean.', + ); + } + if (typeof options.constraints.requireNonAlphanumeric !== 'undefined' && + !validator.isBoolean(options.constraints.requireNonAlphanumeric)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.constraints.requireNonAlphanumeric"' + + ' must be a boolean.', + ); + } + if (typeof options.constraints.requireNumeric !== 'undefined' && + !validator.isBoolean(options.constraints.requireNumeric)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.constraints.requireNumeric" must be a boolean.', + ); + } + if (typeof options.constraints.minLength === 'undefined') { + options.constraints.minLength = 6; + } else if (!validator.isNumber(options.constraints.minLength)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.constraints.minLength" must be a number.', + ); + } else { + if (!(options.constraints.minLength >= 6 + && options.constraints.minLength <= 30)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.constraints.minLength"' + + ' must be an integer between 6 and 30, inclusive.', + ); + } + } + if (typeof options.constraints.maxLength === 'undefined') { + options.constraints.maxLength = 4096; + } else if (!validator.isNumber(options.constraints.maxLength)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.constraints.maxLength" must be a number.', + ); + } else { + if (!(options.constraints.maxLength >= options.constraints.minLength && + options.constraints.maxLength <= 4096)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.constraints.maxLength"' + + ' must be greater than or equal to minLength and at max 4096.', + ); + } + } + } else { + if (options.enforcementState === 'ENFORCE') { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"PasswordPolicyConfig.constraints" must be defined.', + ); + } + } + } + + /** + * The PasswordPolicyAuthConfig constructor. + * + * @param response - The server side response used to initialize the + * PasswordPolicyAuthConfig object. + * @constructor + * @internal + */ + constructor(response: PasswordPolicyAuthServerConfig) { + if (typeof response.passwordPolicyEnforcementState === 'undefined') { + throw new FirebaseAuthError( + AuthClientErrorCode.INTERNAL_ERROR, + 'INTERNAL ASSERT FAILED: Invalid password policy configuration response'); + } + this.enforcementState = response.passwordPolicyEnforcementState; + let constraintsResponse: CustomStrengthOptionsConfig = {}; + if (typeof response.passwordPolicyVersions !== 'undefined') { + (response.passwordPolicyVersions || []).forEach((policyVersion) => { + constraintsResponse = { + requireLowercase: policyVersion.customStrengthOptions?.containsLowercaseCharacter, + requireUppercase: policyVersion.customStrengthOptions?.containsUppercaseCharacter, + requireNonAlphanumeric: policyVersion.customStrengthOptions?.containsNonAlphanumericCharacter, + requireNumeric: policyVersion.customStrengthOptions?.containsNumericCharacter, + minLength: policyVersion.customStrengthOptions?.minPasswordLength, + maxLength: policyVersion.customStrengthOptions?.maxPasswordLength, + }; + }); + } + this.constraints = constraintsResponse; + this.forceUpgradeOnSignin = response.forceUpgradeOnSignin?true:false; + } +} + +/** + * Server side password policy configuration. + */ +export interface PasswordPolicyAuthServerConfig { + passwordPolicyEnforcementState?: PasswordPolicyEnforcementState; + passwordPolicyVersions?: PasswordPolicyVersionsAuthServerConfig[]; + forceUpgradeOnSignin?: boolean; +} + +/** + * Server side password policy versions configuration. + */ +export interface PasswordPolicyVersionsAuthServerConfig { + customStrengthOptions?: CustomStrengthOptionsAuthServerConfig; +} + +/** + * Server side password policy constraints configuration. + */ +export interface CustomStrengthOptionsAuthServerConfig { + containsLowercaseCharacter?: boolean; + containsUppercaseCharacter?: boolean; + containsNumericCharacter?: boolean; + containsNonAlphanumericCharacter?: boolean; + minPasswordLength?: number; + maxPasswordLength?: number; +} + +/** + * The email privacy configuration of a project or tenant. + */ +export interface EmailPrivacyConfig { + /** + * Whether enhanced email privacy is enabled. + */ + enableImprovedEmailPrivacy?: boolean; +} + +/** + * Defines the EmailPrivacyAuthConfig class used for validation. + * + * @internal + */ +export class EmailPrivacyAuthConfig { + public static validate(options: EmailPrivacyConfig): void { + if (!validator.isNonNullObject(options)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"EmailPrivacyConfig" must be a non-null object.', + ); + } + + const validKeys = { + enableImprovedEmailPrivacy: true, + }; + + for (const key in options) { + if (!(key in validKeys)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + `"${key}" is not a valid "EmailPrivacyConfig" parameter.`, + ); + } + } + + if (typeof options.enableImprovedEmailPrivacy !== 'undefined' + && !validator.isBoolean(options.enableImprovedEmailPrivacy)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"EmailPrivacyConfig.enableImprovedEmailPrivacy" must be a valid boolean value.', + ); + } + } +} diff --git a/src/auth/index.ts b/src/auth/index.ts index 7dec658473..a559a706f8 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -80,9 +80,16 @@ export { MultiFactorConfigState, MultiFactorCreateSettings, MultiFactorUpdateSettings, + MultiFactorProviderConfig, OAuthResponseType, OIDCAuthProviderConfig, OIDCUpdateAuthProviderRequest, + RecaptchaAction, + RecaptchaConfig, + RecaptchaKey, + RecaptchaKeyClientType, + RecaptchaManagedRule, + RecaptchaProviderEnforcementState, SAMLAuthProviderConfig, SAMLUpdateAuthProviderRequest, SmsRegionConfig, @@ -91,6 +98,11 @@ export { UpdateMultiFactorInfoRequest, UpdatePhoneMultiFactorInfoRequest, UpdateRequest, + TotpMultiFactorProviderConfig, + PasswordPolicyConfig, + PasswordPolicyEnforcementState, + CustomStrengthOptionsConfig, + EmailPrivacyConfig, } from './auth-config'; export { diff --git a/src/auth/project-config-manager.ts b/src/auth/project-config-manager.ts index 030b64a779..847aa7d982 100644 --- a/src/auth/project-config-manager.ts +++ b/src/auth/project-config-manager.ts @@ -20,14 +20,10 @@ import { } from './auth-api-request'; /** - * Defines the project config manager used to help manage project config related operations. - * This includes: - *
    - *
  • The ability to update and get project config.
  • + * Manages (gets and updates) the current project config. */ export class ProjectConfigManager { private readonly authRequestHandler: AuthRequestHandler; - /** * Initializes a ProjectConfigManager instance for a specified FirebaseApp. * diff --git a/src/auth/project-config.ts b/src/auth/project-config.ts index 54dcfb3b9c..250d6549ac 100644 --- a/src/auth/project-config.ts +++ b/src/auth/project-config.ts @@ -18,6 +18,16 @@ import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; import { SmsRegionsAuthConfig, SmsRegionConfig, + MultiFactorConfig, + MultiFactorAuthConfig, + MultiFactorAuthServerConfig, + RecaptchaConfig, + RecaptchaAuthConfig, + PasswordPolicyAuthConfig, + PasswordPolicyAuthServerConfig, + PasswordPolicyConfig, + EmailPrivacyConfig, + EmailPrivacyAuthConfig, } from './auth-config'; import { deepCopy } from '../utils/deep-copy'; @@ -29,22 +39,48 @@ export interface UpdateProjectConfigRequest { * The SMS configuration to update on the project. */ smsRegionConfig?: SmsRegionConfig; + /** + * The multi-factor auth configuration to update on the project. + */ + multiFactorConfig?: MultiFactorConfig; + + /** + * The reCAPTCHA configuration to update on the project. + * By enabling reCAPTCHA Enterprise integration, you are + * agreeing to the reCAPTCHA Enterprise + * {@link https://cloud.google.com/terms/service-terms | Term of Service}. + */ + recaptchaConfig?: RecaptchaConfig; + /** + * The password policy configuration to update on the project + */ + passwordPolicyConfig?: PasswordPolicyConfig; + /** + * The email privacy configuration to update on the project + */ + emailPrivacyConfig?: EmailPrivacyConfig; } /** - * Response received from getting or updating a project config. - * This object currently exposes only the SMS Region config. + * Response received when getting or updating the project config. */ export interface ProjectConfigServerResponse { smsRegionConfig?: SmsRegionConfig; + mfa?: MultiFactorAuthServerConfig; + recaptchaConfig?: RecaptchaConfig; + passwordPolicyConfig?: PasswordPolicyAuthServerConfig; + emailPrivacyConfig?: EmailPrivacyConfig; } /** - * Request sent to update project config. - * This object currently exposes only the SMS Region config. + * Request to update the project config. */ export interface ProjectConfigClientRequest { smsRegionConfig?: SmsRegionConfig; + mfa?: MultiFactorAuthServerConfig; + recaptchaConfig?: RecaptchaConfig; + passwordPolicyConfig?: PasswordPolicyAuthServerConfig; + emailPrivacyConfig?: EmailPrivacyConfig; } /** @@ -58,12 +94,40 @@ export class ProjectConfig { */ public readonly smsRegionConfig?: SmsRegionConfig; + /** + * The project's multi-factor auth configuration. + * Supports only phone and TOTP. + */ + private readonly multiFactorConfig_?: MultiFactorConfig; + /** + * The multi-factor auth configuration. + */ + get multiFactorConfig(): MultiFactorConfig | undefined { + return this.multiFactorConfig_; + } + /** + * The reCAPTCHA configuration to update on the project. + * By enabling reCAPTCHA Enterprise integration, you are + * agreeing to the reCAPTCHA Enterprise + * {@link https://cloud.google.com/terms/service-terms | Term of Service}. + */ + private readonly recaptchaConfig_?: RecaptchaAuthConfig; + + /** + * The password policy configuration for the project + */ + public readonly passwordPolicyConfig?: PasswordPolicyConfig; + /** + * The email privacy configuration for the project + */ + public readonly emailPrivacyConfig?: EmailPrivacyConfig; + /** * Validates a project config options object. Throws an error on failure. * * @param request - The project config options object to validate. */ - private static validate(request: ProjectConfigClientRequest): void { + private static validate(request: UpdateProjectConfigRequest): void { if (!validator.isNonNullObject(request)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, @@ -72,6 +136,10 @@ export class ProjectConfig { } const validKeys = { smsRegionConfig: true, + multiFactorConfig: true, + recaptchaConfig: true, + passwordPolicyConfig: true, + emailPrivacyConfig: true, } // Check for unsupported top level attributes. for (const key in request) { @@ -86,6 +154,25 @@ export class ProjectConfig { if (typeof request.smsRegionConfig !== 'undefined') { SmsRegionsAuthConfig.validate(request.smsRegionConfig); } + + // Validate Multi Factor Config if provided + if (typeof request.multiFactorConfig !== 'undefined') { + MultiFactorAuthConfig.validate(request.multiFactorConfig); + } + // Validate reCAPTCHA config attribute. + if (typeof request.recaptchaConfig !== 'undefined') { + RecaptchaAuthConfig.validate(request.recaptchaConfig); + } + + // Validate Password policy Config if provided + if (typeof request.passwordPolicyConfig !== 'undefined') { + PasswordPolicyAuthConfig.validate(request.passwordPolicyConfig); + } + + // Validate Email Privacy Config if provided. + if (typeof request.emailPrivacyConfig !== 'undefined') { + EmailPrivacyAuthConfig.validate(request.emailPrivacyConfig); + } } /** @@ -97,9 +184,31 @@ export class ProjectConfig { */ public static buildServerRequest(configOptions: UpdateProjectConfigRequest): ProjectConfigClientRequest { ProjectConfig.validate(configOptions); - return configOptions as ProjectConfigClientRequest; + const request: ProjectConfigClientRequest = {}; + if (typeof configOptions.smsRegionConfig !== 'undefined') { + request.smsRegionConfig = configOptions.smsRegionConfig; + } + if (typeof configOptions.multiFactorConfig !== 'undefined') { + request.mfa = MultiFactorAuthConfig.buildServerRequest(configOptions.multiFactorConfig); + } + if (typeof configOptions.recaptchaConfig !== 'undefined') { + request.recaptchaConfig = configOptions.recaptchaConfig; + } + if (typeof configOptions.passwordPolicyConfig !== 'undefined') { + request.passwordPolicyConfig = PasswordPolicyAuthConfig.buildServerRequest(configOptions.passwordPolicyConfig); + } + if (typeof configOptions.emailPrivacyConfig !== 'undefined') { + request.emailPrivacyConfig = configOptions.emailPrivacyConfig; + } + return request; + } + + /** + * The reCAPTCHA configuration. + */ + get recaptchaConfig(): RecaptchaConfig | undefined { + return this.recaptchaConfig_; } - /** * The Project Config object constructor. * @@ -111,6 +220,20 @@ export class ProjectConfig { if (typeof response.smsRegionConfig !== 'undefined') { this.smsRegionConfig = response.smsRegionConfig; } + //Backend API returns "mfa" in case of project config and "mfaConfig" in case of tenant config. + //The SDK exposes it as multiFactorConfig always. + if (typeof response.mfa !== 'undefined') { + this.multiFactorConfig_ = new MultiFactorAuthConfig(response.mfa); + } + if (typeof response.recaptchaConfig !== 'undefined') { + this.recaptchaConfig_ = new RecaptchaAuthConfig(response.recaptchaConfig); + } + if (typeof response.passwordPolicyConfig !== 'undefined') { + this.passwordPolicyConfig = new PasswordPolicyAuthConfig(response.passwordPolicyConfig); + } + if (typeof response.emailPrivacyConfig !== 'undefined') { + this.emailPrivacyConfig = response.emailPrivacyConfig; + } } /** * Returns a JSON-serializable representation of this object. @@ -121,10 +244,26 @@ export class ProjectConfig { // JSON serialization const json = { smsRegionConfig: deepCopy(this.smsRegionConfig), + multiFactorConfig: deepCopy(this.multiFactorConfig), + recaptchaConfig: this.recaptchaConfig_?.toJSON(), + passwordPolicyConfig: deepCopy(this.passwordPolicyConfig), + emailPrivacyConfig: deepCopy(this.emailPrivacyConfig), }; if (typeof json.smsRegionConfig === 'undefined') { delete json.smsRegionConfig; } + if (typeof json.multiFactorConfig === 'undefined') { + delete json.multiFactorConfig; + } + if (typeof json.recaptchaConfig === 'undefined') { + delete json.recaptchaConfig; + } + if (typeof json.passwordPolicyConfig === 'undefined') { + delete json.passwordPolicyConfig; + } + if (typeof json.emailPrivacyConfig === 'undefined') { + delete json.emailPrivacyConfig; + } return json; } } diff --git a/src/auth/tenant.ts b/src/auth/tenant.ts index 56cf2abd8d..76d97f3259 100644 --- a/src/auth/tenant.ts +++ b/src/auth/tenant.ts @@ -21,7 +21,9 @@ import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; import { EmailSignInConfig, EmailSignInConfigServerRequest, MultiFactorAuthServerConfig, MultiFactorConfig, validateTestPhoneNumbers, EmailSignInProviderConfig, - MultiFactorAuthConfig, SmsRegionConfig, SmsRegionsAuthConfig + MultiFactorAuthConfig, SmsRegionConfig, SmsRegionsAuthConfig, RecaptchaAuthConfig, RecaptchaConfig, + PasswordPolicyConfig, + PasswordPolicyAuthConfig, PasswordPolicyAuthServerConfig, EmailPrivacyConfig, EmailPrivacyAuthConfig, } from './auth-config'; /** @@ -59,6 +61,22 @@ export interface UpdateTenantRequest { * The SMS configuration to update on the project. */ smsRegionConfig?: SmsRegionConfig; + + /** + * The reCAPTCHA configuration to update on the tenant. + * By enabling reCAPTCHA Enterprise integration, you are + * agreeing to the reCAPTCHA Enterprise + * {@link https://cloud.google.com/terms/service-terms | Term of Service}. + */ + recaptchaConfig?: RecaptchaConfig; + /** + * The password policy configuration for the tenant + */ + passwordPolicyConfig?: PasswordPolicyConfig; + /** + * The email privacy configuration for the tenant + */ + emailPrivacyConfig?: EmailPrivacyConfig; } /** @@ -74,6 +92,9 @@ export interface TenantOptionsServerRequest extends EmailSignInConfigServerReque mfaConfig?: MultiFactorAuthServerConfig; testPhoneNumbers?: {[key: string]: string}; smsRegionConfig?: SmsRegionConfig; + recaptchaConfig?: RecaptchaConfig; + passwordPolicyConfig?: PasswordPolicyAuthServerConfig; + emailPrivacyConfig?: EmailPrivacyConfig; } /** The tenant server response interface. */ @@ -86,6 +107,9 @@ export interface TenantServerResponse { mfaConfig?: MultiFactorAuthServerConfig; testPhoneNumbers?: {[key: string]: string}; smsRegionConfig?: SmsRegionConfig; + recaptchaConfig? : RecaptchaConfig; + passwordPolicyConfig?: PasswordPolicyAuthServerConfig; + emailPrivacyConfig?: EmailPrivacyConfig; } /** @@ -130,12 +154,27 @@ export class Tenant { private readonly emailSignInConfig_?: EmailSignInConfig; private readonly multiFactorConfig_?: MultiFactorAuthConfig; + /** + * The map conatining the reCAPTCHA config. + * By enabling reCAPTCHA Enterprise Integration you are + * agreeing to reCAPTCHA Enterprise + * {@link https://cloud.google.com/terms/service-terms | Term of Service}. + */ + private readonly recaptchaConfig_?: RecaptchaAuthConfig; /** * The SMS Regions Config to update a tenant. * Configures the regions where users are allowed to send verification SMS. * This is based on the calling code of the destination phone number. */ public readonly smsRegionConfig?: SmsRegionConfig; + /** + * The password policy configuration for the tenant + */ + public readonly passwordPolicyConfig?: PasswordPolicyConfig; + /** + * The email privacy configuration for the tenant + */ + public readonly emailPrivacyConfig?: EmailPrivacyConfig; /** * Builds the corresponding server request for a TenantOptions object. @@ -169,6 +208,15 @@ export class Tenant { if (typeof tenantOptions.smsRegionConfig !== 'undefined') { request.smsRegionConfig = tenantOptions.smsRegionConfig; } + if (typeof tenantOptions.recaptchaConfig !== 'undefined') { + request.recaptchaConfig = tenantOptions.recaptchaConfig; + } + if (typeof tenantOptions.passwordPolicyConfig !== 'undefined') { + request.passwordPolicyConfig = PasswordPolicyAuthConfig.buildServerRequest(tenantOptions.passwordPolicyConfig); + } + if (typeof tenantOptions.emailPrivacyConfig !== 'undefined') { + request.emailPrivacyConfig = tenantOptions.emailPrivacyConfig; + } return request; } @@ -203,6 +251,9 @@ export class Tenant { multiFactorConfig: true, testPhoneNumbers: true, smsRegionConfig: true, + recaptchaConfig: true, + passwordPolicyConfig: true, + emailPrivacyConfig: true, }; const label = createRequest ? 'CreateTenantRequest' : 'UpdateTenantRequest'; if (!validator.isNonNullObject(request)) { @@ -250,9 +301,22 @@ export class Tenant { MultiFactorAuthConfig.buildServerRequest(request.multiFactorConfig); } // Validate SMS Regions Config if provided. - if (typeof request.smsRegionConfig != 'undefined') { + if (typeof request.smsRegionConfig !== 'undefined') { SmsRegionsAuthConfig.validate(request.smsRegionConfig); } + // Validate reCAPTCHAConfig type if provided. + if (typeof request.recaptchaConfig !== 'undefined') { + RecaptchaAuthConfig.validate(request.recaptchaConfig); + } + // Validate passwordPolicyConfig type if provided. + if (typeof request.passwordPolicyConfig !== 'undefined') { + // This will throw an error if invalid. + PasswordPolicyAuthConfig.buildServerRequest(request.passwordPolicyConfig); + } + // Validate Email Privacy Config if provided. + if (typeof request.emailPrivacyConfig !== 'undefined') { + EmailPrivacyAuthConfig.validate(request.emailPrivacyConfig); + } } /** @@ -290,6 +354,15 @@ export class Tenant { if (typeof response.smsRegionConfig !== 'undefined') { this.smsRegionConfig = deepCopy(response.smsRegionConfig); } + if (typeof response.recaptchaConfig !== 'undefined') { + this.recaptchaConfig_ = new RecaptchaAuthConfig(response.recaptchaConfig); + } + if (typeof response.passwordPolicyConfig !== 'undefined') { + this.passwordPolicyConfig = new PasswordPolicyAuthConfig(response.passwordPolicyConfig); + } + if (typeof response.emailPrivacyConfig !== 'undefined') { + this.emailPrivacyConfig = deepCopy(response.emailPrivacyConfig); + } } /** @@ -306,6 +379,13 @@ export class Tenant { return this.multiFactorConfig_; } + /** + * The recaptcha config auth configuration of the current tenant. + */ + get recaptchaConfig(): RecaptchaConfig | undefined { + return this.recaptchaConfig_; + } + /** * Returns a JSON-serializable representation of this object. * @@ -320,6 +400,9 @@ export class Tenant { anonymousSignInEnabled: this.anonymousSignInEnabled, testPhoneNumbers: this.testPhoneNumbers, smsRegionConfig: deepCopy(this.smsRegionConfig), + recaptchaConfig: this.recaptchaConfig_?.toJSON(), + passwordPolicyConfig: deepCopy(this.passwordPolicyConfig), + emailPrivacyConfig: deepCopy(this.emailPrivacyConfig), }; if (typeof json.multiFactorConfig === 'undefined') { delete json.multiFactorConfig; @@ -330,6 +413,15 @@ export class Tenant { if (typeof json.smsRegionConfig === 'undefined') { delete json.smsRegionConfig; } + if (typeof json.recaptchaConfig === 'undefined') { + delete json.recaptchaConfig; + } + if (typeof json.passwordPolicyConfig === 'undefined') { + delete json.passwordPolicyConfig; + } + if (typeof json.emailPrivacyConfig === 'undefined') { + delete json.emailPrivacyConfig; + } return json; } } diff --git a/src/auth/token-verifier.ts b/src/auth/token-verifier.ts index e57040e4de..05566205b5 100644 --- a/src/auth/token-verifier.ts +++ b/src/auth/token-verifier.ts @@ -466,7 +466,7 @@ export class FirebaseTokenVerifier { private safeDecode(jwtToken: string): Promise { return decodeJwt(jwtToken) .catch((err: JwtError) => { - if (err.code == JwtErrorCode.INVALID_ARGUMENT) { + if (err.code === JwtErrorCode.INVALID_ARGUMENT) { const verifyJwtTokenDocsMessage = ` See ${this.tokenInfo.url} ` + `for details on how to retrieve ${this.shortNameArticle} ${this.tokenInfo.shortName}.`; const errorMessage = `Decoding ${this.tokenInfo.jwtName} failed. Make sure you passed ` + diff --git a/src/auth/user-record.ts b/src/auth/user-record.ts index 2bd2fdacbd..5b00151401 100644 --- a/src/auth/user-record.ts +++ b/src/auth/user-record.ts @@ -47,8 +47,13 @@ export interface MultiFactorInfoResponse { mfaEnrollmentId: string; displayName?: string; phoneInfo?: string; + totpInfo?: TotpInfoResponse; enrolledAt?: string; - [key: string]: any; + [key: string]: unknown; +} + +export interface TotpInfoResponse { + [key: string]: unknown; } export interface ProviderUserInfoResponse { @@ -84,6 +89,7 @@ export interface GetAccountInfoUserResponse { enum MultiFactorId { Phone = 'phone', + Totp = 'totp', } /** @@ -102,7 +108,9 @@ export abstract class MultiFactorInfo { public readonly displayName?: string; /** - * The type identifier of the second factor. For SMS second factors, this is `phone`. + * The type identifier of the second factor. + * For SMS second factors, this is `phone`. + * For TOTP second factors, this is `totp`. */ public readonly factorId: string; @@ -120,9 +128,15 @@ export abstract class MultiFactorInfo { */ public static initMultiFactorInfo(response: MultiFactorInfoResponse): MultiFactorInfo | null { let multiFactorInfo: MultiFactorInfo | null = null; - // Only PhoneMultiFactorInfo currently available. + // PhoneMultiFactorInfo, TotpMultiFactorInfo currently available. try { - multiFactorInfo = new PhoneMultiFactorInfo(response); + if (response.phoneInfo !== undefined) { + multiFactorInfo = new PhoneMultiFactorInfo(response); + } else if (response.totpInfo !== undefined) { + multiFactorInfo = new TotpMultiFactorInfo(response); + } else { + // Ignore the other SDK unsupported MFA factors to prevent blocking developers using the current SDK. + } } catch (e) { // Ignore error. } @@ -240,6 +254,60 @@ export class PhoneMultiFactorInfo extends MultiFactorInfo { } } +/** + * `TotpInfo` struct associated with a second factor + */ +export class TotpInfo { + +} + +/** + * Interface representing a TOTP specific user-enrolled second factor. + */ +export class TotpMultiFactorInfo extends MultiFactorInfo { + + /** + * `TotpInfo` struct associated with a second factor + */ + public readonly totpInfo: TotpInfo; + + /** + * Initializes the `TotpMultiFactorInfo` object using the server side response. + * + * @param response - The server side response. + * @constructor + * @internal + */ + constructor(response: MultiFactorInfoResponse) { + super(response); + utils.addReadonlyGetter(this, 'totpInfo', response.totpInfo); + } + + /** + * {@inheritdoc MultiFactorInfo.toJSON} + */ + public toJSON(): object { + return Object.assign( + super.toJSON(), + { + totpInfo: this.totpInfo, + }); + } + + /** + * Returns the factor ID based on the response provided. + * + * @param response - The server side response. + * @returns The multi-factor ID associated with the provided response. If the response is + * not associated with any known multi-factor ID, `null` is returned. + * + * @internal + */ + protected getFactorId(response: MultiFactorInfoResponse): string | null { + return (response && response.totpInfo) ? MultiFactorId.Totp : null; + } +} + /** * The multi-factor related user settings. */ @@ -247,12 +315,12 @@ export class MultiFactorSettings { /** * List of second factors enrolled with the current user. - * Currently only phone second factors are supported. + * Currently only phone and TOTP second factors are supported. */ public enrolledFactors: MultiFactorInfo[]; /** - * Initializes the MultiFactor object using the server side or JWT format response. + * Initializes the `MultiFactor` object using the server side or JWT format response. * * @param response - The server side response. * @constructor @@ -312,7 +380,7 @@ export class UserMetadata { public readonly lastRefreshTime?: string | null; /** - * @param response - The server side response returned from the getAccountInfo + * @param response - The server side response returned from the `getAccountInfo` * endpoint. * @constructor * @internal diff --git a/src/firestore/firestore-namespace.ts b/src/firestore/firestore-namespace.ts index a6ccc5c1d7..67733fb8f6 100644 --- a/src/firestore/firestore-namespace.ts +++ b/src/firestore/firestore-namespace.ts @@ -26,6 +26,13 @@ export namespace firestore { export import v1beta1 = _firestore.v1beta1; export import v1 = _firestore.v1; + export import AggregateField = _firestore.AggregateField; + export import AggregateFieldType = _firestore.AggregateFieldType; + export import AggregateQuery = _firestore.AggregateQuery; + export import AggregateQuerySnapshot = _firestore.AggregateQuerySnapshot; + export import AggregateSpecData = _firestore.AggregateSpecData; + export import AggregateSpec = _firestore.AggregateSpec; + export import AggregateType = _firestore.AggregateType; export import BulkWriter = _firestore.BulkWriter; export import BulkWriterOptions = _firestore.BulkWriterOptions; export import BundleBuilder = _firestore.BundleBuilder; @@ -38,6 +45,7 @@ export namespace firestore { export import DocumentSnapshot = _firestore.DocumentSnapshot; export import FieldPath = _firestore.FieldPath; export import FieldValue = _firestore.FieldValue; + export import Filter = _firestore.Filter; export import Firestore = _firestore.Firestore; export import FirestoreDataConverter = _firestore.FirestoreDataConverter; export import GeoPoint = _firestore.GeoPoint; diff --git a/src/firestore/index.ts b/src/firestore/index.ts index 661d9fe9bc..ff6954cefa 100644 --- a/src/firestore/index.ts +++ b/src/firestore/index.ts @@ -28,6 +28,13 @@ import { DEFAULT_DATABASE_ID } from '@google-cloud/firestore/build/src/path'; export { AddPrefixToKeys, + AggregateField, + AggregateFieldType, + AggregateQuery, + AggregateQuerySnapshot, + AggregateSpecData, + AggregateSpec, + AggregateType, BulkWriter, BulkWriterOptions, BundleBuilder, @@ -41,6 +48,7 @@ export { DocumentSnapshot, FieldPath, FieldValue, + Filter, Firestore, FirestoreDataConverter, GeoPoint, @@ -74,58 +82,72 @@ export { export { FirestoreSettings }; /** - * Gets the {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} + * Gets the default {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} * service for the default app. * - * `getFirestore()` can be called with no arguments to access the default - * app's `Firestore` service or as `getFirestore(app)` to access the - * `Firestore` service associated with a specific app. - * * @example * ```javascript - * // Get the Firestore service for the default app + * // Get the default Firestore service for the default app * const defaultFirestore = getFirestore(); * ``` * @returns The default {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} - * service if no app is provided or the `Firestore` service associated with the - * provided app. + * service for the default app. */ export function getFirestore(): Firestore; /** - * Gets the {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} + * Gets the default {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} * service for the given app. * - * `getFirestore()` can be called with no arguments to access the default - * app's `Firestore` service or as `getFirestore(app)` to access the - * `Firestore` service associated with a specific app. - * * @example * ```javascript - * // Get the Firestore service for a specific app + * // Get the default Firestore service for a specific app * const otherFirestore = getFirestore(app); * ``` * - * @param App - which `Firestore` service to - * return. If not provided, the default `Firestore` service will be returned. + * @param app - which `Firestore` service to return. * * @returns The default {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} - * service if no app is provided or the `Firestore` service associated with the - * provided app. + * service associated with the provided app. */ export function getFirestore(app: App): Firestore; /** - * @param databaseId - * @internal + * Gets the named {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} + * service for the default app. + * + * @example + * ```javascript + * // Get the Firestore service for a named database and default app + * const otherFirestore = getFirestore('otherDb'); + * ``` + * + * @param databaseId - name of database to return. + * + * @returns The named {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} + * service for the default app. + * @beta */ export function getFirestore(databaseId: string): Firestore; /** - * @param app - * @param databaseId - * @internal + * Gets the named {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} + * service for the given app. + * + * @example + * ```javascript + * // Get the Firestore service for a named database and specific app. + * const otherFirestore = getFirestore('otherDb'); + * ``` + * + * @param app - which `Firestore` service to return. + * + * @param databaseId - name of database to return. + * + * @returns The named {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} + * service associated with the provided app. + * @beta */ export function getFirestore(app: App, databaseId: string): Firestore; @@ -143,7 +165,7 @@ export function getFirestore( } /** - * Gets the {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} + * Gets the default {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} * service for the given app, passing extra parameters to its constructor. * * @example @@ -152,20 +174,32 @@ export function getFirestore( * const otherFirestore = initializeFirestore(app, {preferRest: true}); * ``` * - * @param App - which `Firestore` service to - * return. If not provided, the default `Firestore` service will be returned. - * + * @param app - which `Firestore` service to return. + * * @param settings - Settings object to be passed to the constructor. * - * @returns The `Firestore` service associated with the provided app and settings. + * @returns The default `Firestore` service associated with the provided app and settings. */ export function initializeFirestore(app: App, settings?: FirestoreSettings): Firestore; /** - * @param app - * @param settings - * @param databaseId - * @internal + * Gets the named {@link https://googleapis.dev/nodejs/firestore/latest/Firestore.html | Firestore} + * service for the given app, passing extra parameters to its constructor. + * + * @example + * ```javascript + * // Get the Firestore service for a specific app, require HTTP/1.1 REST transport + * const otherFirestore = initializeFirestore(app, {preferRest: true}, 'otherDb'); + * ``` + * + * @param app - which `Firestore` service to return. + * + * @param settings - Settings object to be passed to the constructor. + * + * @param databaseId - name of database to return. + * + * @returns The named `Firestore` service associated with the provided app and settings. + * @beta */ export function initializeFirestore( app: App, diff --git a/src/functions/functions-api-client-internal.ts b/src/functions/functions-api-client-internal.ts index 3dcbf4e3c6..85a447cf33 100644 --- a/src/functions/functions-api-client-internal.ts +++ b/src/functions/functions-api-client-internal.ts @@ -26,7 +26,8 @@ import * as validator from '../utils/validator'; import { TaskOptions } from './functions-api'; import { ComputeEngineCredential } from '../app/credential-internal'; -const CLOUD_TASKS_API_URL_FORMAT = 'https://cloudtasks.googleapis.com/v2/projects/{projectId}/locations/{locationId}/queues/{resourceId}/tasks'; +const CLOUD_TASKS_API_RESOURCE_PATH = 'projects/{projectId}/locations/{locationId}/queues/{resourceId}/tasks'; +const CLOUD_TASKS_API_URL_FORMAT = 'https://cloudtasks.googleapis.com/v2/' + CLOUD_TASKS_API_RESOURCE_PATH; const FIREBASE_FUNCTION_URL_FORMAT = 'https://{locationId}-{projectId}.cloudfunctions.net/{resourceId}'; const FIREBASE_FUNCTIONS_CONFIG_HEADERS = { @@ -54,6 +55,61 @@ export class FunctionsApiClient { } this.httpClient = new AuthorizedHttpClient(app as FirebaseApp); } + /** + * Deletes a task from a queue. + * + * @param id - The ID of the task to delete. + * @param functionName - The function name of the queue. + * @param extensionId - Optional canonical ID of the extension. + */ + public async delete(id: string, functionName: string, extensionId?: string): Promise { + if (!validator.isNonEmptyString(functionName)) { + throw new FirebaseFunctionsError( + 'invalid-argument', 'Function name must be a non empty string'); + } + if (!validator.isTaskId(id)) { + throw new FirebaseFunctionsError( + 'invalid-argument', 'id can contain only letters ([A-Za-z]), numbers ([0-9]), ' + + 'hyphens (-), or underscores (_). The maximum length is 500 characters.'); + } + + let resources: utils.ParsedResource; + try { + resources = utils.parseResourceName(functionName, 'functions'); + } catch (err) { + throw new FirebaseFunctionsError( + 'invalid-argument', 'Function name must be a single string or a qualified resource name'); + } + resources.projectId = resources.projectId || await this.getProjectId(); + resources.locationId = resources.locationId || DEFAULT_LOCATION; + if (!validator.isNonEmptyString(resources.resourceId)) { + throw new FirebaseFunctionsError( + 'invalid-argument', 'No valid function name specified to enqueue tasks for.'); + } + if (typeof extensionId !== 'undefined' && validator.isNonEmptyString(extensionId)) { + resources.resourceId = `ext-${extensionId}-${resources.resourceId}`; + } + + try { + const serviceUrl = await this.getUrl(resources, CLOUD_TASKS_API_URL_FORMAT.concat('/', id)); + const request: HttpRequestConfig = { + method: 'DELETE', + url: serviceUrl, + headers: FIREBASE_FUNCTIONS_CONFIG_HEADERS, + }; + await this.httpClient.send(request); + } catch (err: unknown) { + if (err instanceof HttpError) { + if (err.response.status === 404) { + // if no task with the provided ID exists, then ignore the delete. + return; + } + throw this.toFirebaseError(err); + } else { + throw err; + } + } + } /** * Creates a task and adds it to a queue. @@ -63,47 +119,53 @@ export class FunctionsApiClient { * @param extensionId - Optional canonical ID of the extension. * @param opts - Optional options when enqueuing a new task. */ - public enqueue(data: any, functionName: string, extensionId?: string, opts?: TaskOptions): Promise { + public async enqueue(data: any, functionName: string, extensionId?: string, opts?: TaskOptions): Promise { if (!validator.isNonEmptyString(functionName)) { throw new FirebaseFunctionsError( 'invalid-argument', 'Function name must be a non empty string'); } - const task = this.validateTaskOptions(data, opts); let resources: utils.ParsedResource; try { resources = utils.parseResourceName(functionName, 'functions'); - } - catch (err) { + } catch (err) { throw new FirebaseFunctionsError( 'invalid-argument', 'Function name must be a single string or a qualified resource name'); } - + resources.projectId = resources.projectId || await this.getProjectId(); + resources.locationId = resources.locationId || DEFAULT_LOCATION; + if (!validator.isNonEmptyString(resources.resourceId)) { + throw new FirebaseFunctionsError( + 'invalid-argument', 'No valid function name specified to enqueue tasks for.'); + } if (typeof extensionId !== 'undefined' && validator.isNonEmptyString(extensionId)) { resources.resourceId = `ext-${extensionId}-${resources.resourceId}`; } - - return this.getUrl(resources, CLOUD_TASKS_API_URL_FORMAT) - .then((serviceUrl) => { - return this.updateTaskPayload(task, resources, extensionId) - .then((task) => { - const request: HttpRequestConfig = { - method: 'POST', - url: serviceUrl, - headers: FIREBASE_FUNCTIONS_CONFIG_HEADERS, - data: { - task, - } - }; - return this.httpClient.send(request); - }) - }) - .then(() => { - return; - }) - .catch((err) => { - throw this.toFirebaseError(err); - }); + + const task = this.validateTaskOptions(data, resources, opts); + try { + const serviceUrl = await this.getUrl(resources, CLOUD_TASKS_API_URL_FORMAT); + const taskPayload = await this.updateTaskPayload(task, resources, extensionId); + const request: HttpRequestConfig = { + method: 'POST', + url: serviceUrl, + headers: FIREBASE_FUNCTIONS_CONFIG_HEADERS, + data: { + task: taskPayload, + } + }; + await this.httpClient.send(request); + } catch (err: unknown) { + if (err instanceof HttpError) { + if (err.response.status === 409) { + throw new FirebaseFunctionsError('task-already-exists', `A task with ID ${opts?.id} already exists`); + } else { + throw this.toFirebaseError(err); + } + } else { + throw err; + } + } } private getUrl(resourceName: utils.ParsedResource, urlFormat: string): Promise { @@ -167,7 +229,7 @@ export class FunctionsApiClient { }); } - private validateTaskOptions(data: any, opts?: TaskOptions): Task { + private validateTaskOptions(data: any, resources: utils.ParsedResource, opts?: TaskOptions): Task { const task: Task = { httpRequest: { url: '', @@ -175,7 +237,10 @@ export class FunctionsApiClient { serviceAccountEmail: '', }, body: Buffer.from(JSON.stringify({ data })).toString('base64'), - headers: { 'Content-Type': 'application/json' } + headers: { + 'Content-Type': 'application/json', + ...opts?.headers, + } } } @@ -214,6 +279,19 @@ export class FunctionsApiClient { } task.dispatchDeadline = `${opts.dispatchDeadlineSeconds}s`; } + if ('id' in opts && typeof opts.id !== 'undefined') { + if (!validator.isTaskId(opts.id)) { + throw new FirebaseFunctionsError( + 'invalid-argument', 'id can contain only letters ([A-Za-z]), numbers ([0-9]), ' + + 'hyphens (-), or underscores (_). The maximum length is 500 characters.'); + } + const resourcePath = utils.formatString(CLOUD_TASKS_API_RESOURCE_PATH, { + projectId: resources.projectId, + locationId: resources.locationId, + resourceId: resources.resourceId, + }); + task.name = resourcePath.concat('/', opts.id); + } if (typeof opts.uri !== 'undefined') { if (!validator.isURL(opts.uri)) { throw new FirebaseFunctionsError( @@ -280,6 +358,7 @@ interface Error { * containing the relevant fields for enqueueing tasks that tirgger Cloud Functions. */ export interface Task { + name?: string; // A timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional // digits. Examples: "2014-10-02T15:01:23Z" and "2014-10-02T15:01:23.045123456Z". scheduleTime?: string; @@ -317,7 +396,8 @@ export type FunctionsErrorCode = | 'permission-denied' | 'unauthenticated' | 'not-found' - | 'unknown-error'; + | 'unknown-error' + | 'task-already-exists'; /** * Firebase Functions error code structure. This extends PrefixedFirebaseError. diff --git a/src/functions/functions-api.ts b/src/functions/functions-api.ts index 60351211e9..a0473baee2 100644 --- a/src/functions/functions-api.ts +++ b/src/functions/functions-api.ts @@ -58,6 +58,40 @@ export type TaskOptions = DeliverySchedule & TaskOptionsExperimental & { * The default is 10 minutes. The deadline must be in the range of 15 seconds and 30 minutes. */ dispatchDeadlineSeconds?: number; + + /** + * The ID to use for the enqueued event. + * If not provided, one will be automatically generated. + * If provided, an explicitly specified task ID enables task de-duplication. If a task's ID is + * identical to that of an existing task or a task that was deleted or executed recently then + * the call will throw an error with code "functions/task-already-exists". Another task with + * the same ID can't be created for ~1hour after the original task was deleted or executed. + * + * Because there is an extra lookup cost to identify duplicate task IDs, setting ID + * significantly increases latency. Using hashed strings for the task ID or for the prefix of + * the task ID is recommended. Choosing task IDs that are sequential or have sequential + * prefixes, for example using a timestamp, causes an increase in latency and error rates in + * all task commands. The infrastructure relies on an approximately uniform distribution of + * task IDs to store and serve tasks efficiently. + * + * "Push IDs" from the Firebase Realtime Database make poor IDs because they are based on + * timestamps and will cause contention (slowdowns) in your task queue. Reversed push IDs + * however form a perfect distribution and are an ideal key. To reverse a string in + * javascript use `someString.split("").reverse().join("")` + */ + id?: string; + + /** + * HTTP request headers to include in the request to the task queue function. + * These headers represent a subset of the headers that will accompany the task's HTTP + * request. Some HTTP request headers will be ignored or replaced, e.g. Authorization, Host, Content-Length, + * User-Agent etc. cannot be overridden. + * + * By default, Content-Type is set to 'application/json'. + * + * The size of the headers must be less than 80KB. + */ + headers?: Record; } /** diff --git a/src/functions/functions.ts b/src/functions/functions.ts index 08a38ab7ac..f5a3ce6153 100644 --- a/src/functions/functions.ts +++ b/src/functions/functions.ts @@ -102,4 +102,13 @@ export class TaskQueue> { public enqueue(data: Args, opts?: TaskOptions): Promise { return this.client.enqueue(data, this.functionName, this.extensionId, opts); } + + /** + * Deletes an enqueued task if it has not yet completed. + * @param id - the ID of the task, relative to this queue. + * @returns A promise that resolves when the task has been deleted. + */ + public delete(id: string): Promise { + return this.client.delete(id, this.functionName, this.extensionId); + } } diff --git a/src/functions/index.ts b/src/functions/index.ts index a046c4dd93..11e05c6782 100644 --- a/src/functions/index.ts +++ b/src/functions/index.ts @@ -30,7 +30,7 @@ export { AbsoluteDelivery, DeliverySchedule, TaskOptions, - TaskOptionsExperimental + TaskOptionsExperimental, } from './functions-api'; export { Functions, diff --git a/src/machine-learning/index.ts b/src/machine-learning/index.ts index e541d82b35..433832c358 100644 --- a/src/machine-learning/index.ts +++ b/src/machine-learning/index.ts @@ -31,7 +31,6 @@ export { TFLiteModel, } from './machine-learning'; export { - AutoMLTfliteModelOptions, GcsTfliteModelOptions, ListModelsOptions, ModelOptions, diff --git a/src/machine-learning/machine-learning-api-client.ts b/src/machine-learning/machine-learning-api-client.ts index cb4c32963b..7ae1c3436a 100644 --- a/src/machine-learning/machine-learning-api-client.ts +++ b/src/machine-learning/machine-learning-api-client.ts @@ -38,16 +38,7 @@ export interface GcsTfliteModelOptions extends ModelOptionsBase { }; } -/** - * @deprecated AutoMLTfliteModelOptions will be removed in the next major version. - */ -export interface AutoMLTfliteModelOptions extends ModelOptionsBase { - tfliteModel: { - automlModel: string; - }; -} - -export type ModelOptions = ModelOptionsBase | GcsTfliteModelOptions | AutoMLTfliteModelOptions; +export type ModelOptions = ModelOptionsBase | GcsTfliteModelOptions; /** * Interface representing options for listing Models. @@ -108,7 +99,6 @@ export interface ModelContent { }; readonly tfliteModel?: { readonly gcsTfliteUri?: string; - readonly automlModel?: string; readonly sizeBytes: number; }; diff --git a/src/machine-learning/machine-learning-namespace.ts b/src/machine-learning/machine-learning-namespace.ts index e02601dd60..7c5786fdbb 100644 --- a/src/machine-learning/machine-learning-namespace.ts +++ b/src/machine-learning/machine-learning-namespace.ts @@ -22,7 +22,6 @@ import { TFLiteModel as TTFLiteModel, } from './machine-learning'; import { - AutoMLTfliteModelOptions as TAutoMLTfliteModelOptions, GcsTfliteModelOptions as TGcsTfliteModelOptions, ListModelsOptions as TListModelsOptions, ModelOptions as TModelOptions, @@ -80,13 +79,6 @@ export namespace machineLearning { */ export type TFLiteModel = TTFLiteModel; - /** - * Type alias to {@link firebase-admin.machine-learning#AutoMLTfliteModelOptions}. - * - * @deprecated AutoMLTfliteModelOptions will be removed in the next major version. - */ - export type AutoMLTfliteModelOptions = TAutoMLTfliteModelOptions; - /** * Type alias to {@link firebase-admin.machine-learning#GcsTfliteModelOptions}. */ diff --git a/src/machine-learning/machine-learning.ts b/src/machine-learning/machine-learning.ts index 1db987e421..98b956c85c 100644 --- a/src/machine-learning/machine-learning.ts +++ b/src/machine-learning/machine-learning.ts @@ -40,9 +40,6 @@ export interface ListModelsResult { /** * A TensorFlow Lite Model output object - * - * One of either the `gcsTfliteUri` or `automlModel` properties will be - * defined. */ export interface TFLiteModel { /** The size of the model. */ @@ -50,13 +47,6 @@ export interface TFLiteModel { /** The URI from which the model was originally provided to Firebase. */ readonly gcsTfliteUri?: string; - /** - * The AutoML model reference from which the model was originally provided - * to Firebase. - * - * @deprecated AutoML model support will be removed in the next major version. - */ - readonly automlModel?: string; } /** @@ -400,11 +390,9 @@ export class Model { } const tmpModel = deepCopy(model); - // If tflite Model is specified, it must have a source consisting of - // oneof {gcsTfliteUri, automlModel} + // If tflite Model is specified, it must have a source of {gcsTfliteUri} if (model.tfliteModel && - !validator.isNonEmptyString(model.tfliteModel.gcsTfliteUri) && - !validator.isNonEmptyString(model.tfliteModel.automlModel)) { + !validator.isNonEmptyString(model.tfliteModel.gcsTfliteUri)) { // If we have some other source, ignore the whole tfliteModel. delete (tmpModel as any).tfliteModel; } diff --git a/src/messaging/messaging-api-request-internal.ts b/src/messaging/messaging-api-request-internal.ts index 9097ef403c..90be03181f 100644 --- a/src/messaging/messaging-api-request-internal.ts +++ b/src/messaging/messaging-api-request-internal.ts @@ -96,6 +96,34 @@ export class FirebaseMessagingRequestHandler { }); } + /** + * Invokes the request handler with the provided request data. + * + * @param host - The host to which to send the request. + * @param path - The path to which to send the request. + * @param requestData - The request data. + * @returns A promise that resolves with the {@link SendResponse}. + */ + public invokeRequestHandlerForSendResponse(host: string, path: string, requestData: object): Promise { + const request: HttpRequestConfig = { + method: FIREBASE_MESSAGING_HTTP_METHOD, + url: `https://${host}${path}`, + data: requestData, + headers: LEGACY_FIREBASE_MESSAGING_HEADERS, + timeout: FIREBASE_MESSAGING_TIMEOUT, + }; + return this.httpClient.send(request).then((response) => { + return this.buildSendResponse(response); + }) + .catch((err) => { + if (err instanceof HttpError) { + return this.buildSendResponseFromError(err); + } + // Re-throw the error if it already has the proper format. + throw err; + }); + } + /** * Sends the given array of sub requests as a single batch to FCM, and parses the result into * a BatchResponse object. @@ -136,4 +164,11 @@ export class FirebaseMessagingRequestHandler { } return result; } + + private buildSendResponseFromError(err: HttpError): SendResponse { + return { + success: false, + error: createFirebaseError(err) + }; + } } diff --git a/src/messaging/messaging-api.ts b/src/messaging/messaging-api.ts index d41e6a61c2..29f7f1fdc9 100644 --- a/src/messaging/messaging-api.ts +++ b/src/messaging/messaging-api.ts @@ -940,7 +940,11 @@ export interface MessagingOptions { [key: string]: any | undefined; } -/* Individual status response payload from single devices */ +/** + * Individual status response payload from single devices + * + * @deprecated Returned by {@link Messaging#sendToDevice}, which is also deprecated. + */ export interface MessagingDeviceResult { /** * The error that occurred when processing the message for the recipient. @@ -967,6 +971,8 @@ export interface MessagingDeviceResult { * See * {@link https://firebase.google.com/docs/cloud-messaging/admin/send-messages#send_to_individual_devices | * Send to individual devices} for code samples and detailed documentation. + * + * @deprecated Returned by {@link Messaging.sendToDevice}, which is also deprecated. */ export interface MessagingDevicesResponse { canonicalRegistrationTokenCount: number; @@ -983,6 +989,8 @@ export interface MessagingDevicesResponse { * See * {@link https://firebase.google.com/docs/cloud-messaging/send-message?authuser=0#send_messages_to_device_groups | * Send messages to device groups} for code samples and detailed documentation. + * + * @deprecated Returned by {@link Messaging.sendToDeviceGroup}, which is also deprecated. */ export interface MessagingDeviceGroupResponse { diff --git a/src/messaging/messaging.ts b/src/messaging/messaging.ts index c208a34a79..932b8d8e52 100644 --- a/src/messaging/messaging.ts +++ b/src/messaging/messaging.ts @@ -39,6 +39,7 @@ import { MessagingConditionResponse, DataMessagePayload, NotificationMessagePayload, + SendResponse, } from './messaging-api'; // FCM endpoints @@ -250,6 +251,124 @@ export class Messaging { }); } + /** + * Sends each message in the given array via Firebase Cloud Messaging. + * + * Unlike {@link Messaging.sendAll}, this method makes a single RPC call for each message + * in the given array. + * + * The responses list obtained from the return value corresponds to the order of `messages`. + * An error from this method or a `BatchResponse` with all failures indicates a total failure, + * meaning that none of the messages in the list could be sent. Partial failures or no + * failures are only indicated by a `BatchResponse` return value. + * + * @param messages - A non-empty array + * containing up to 500 messages. + * @param dryRun - Whether to send the messages in the dry-run + * (validation only) mode. + * @returns A Promise fulfilled with an object representing the result of the + * send operation. + */ + public sendEach(messages: Message[], dryRun?: boolean): Promise { + if (validator.isArray(messages) && messages.constructor !== Array) { + // In more recent JS specs, an array-like object might have a constructor that is not of + // Array type. Our deepCopy() method doesn't handle them properly. Convert such objects to + // a regular array here before calling deepCopy(). See issue #566 for details. + messages = Array.from(messages); + } + + const copy: Message[] = deepCopy(messages); + if (!validator.isNonEmptyArray(copy)) { + throw new FirebaseMessagingError( + MessagingClientErrorCode.INVALID_ARGUMENT, 'messages must be a non-empty array'); + } + if (copy.length > FCM_MAX_BATCH_SIZE) { + throw new FirebaseMessagingError( + MessagingClientErrorCode.INVALID_ARGUMENT, + `messages list must not contain more than ${FCM_MAX_BATCH_SIZE} items`); + } + if (typeof dryRun !== 'undefined' && !validator.isBoolean(dryRun)) { + throw new FirebaseMessagingError( + MessagingClientErrorCode.INVALID_ARGUMENT, 'dryRun must be a boolean'); + } + + return this.getUrlPath() + .then((urlPath) => { + const requests: Promise[] = copy.map((message) => { + validateMessage(message); + const request: { message: Message; validate_only?: boolean } = { message }; + if (dryRun) { + request.validate_only = true; + } + return this.messagingRequestHandler.invokeRequestHandlerForSendResponse(FCM_SEND_HOST, urlPath, request); + }); + return Promise.allSettled(requests); + }).then((results) => { + const responses: SendResponse[] = []; + results.forEach(result => { + if (result.status === 'fulfilled') { + responses.push(result.value); + } else { // rejected + responses.push({ success: false, error: result.reason }) + } + }) + const successCount: number = responses.filter((resp) => resp.success).length; + return { + responses, + successCount, + failureCount: responses.length - successCount, + }; + }); + } + + /** + * Sends the given multicast message to all the FCM registration tokens + * specified in it. + * + * This method uses the {@link Messaging.sendEach} API under the hood to send the given + * message to all the target recipients. The responses list obtained from the + * return value corresponds to the order of tokens in the `MulticastMessage`. + * An error from this method or a `BatchResponse` with all failures indicates a total + * failure, meaning that the messages in the list could be sent. Partial failures or + * failures are only indicated by a `BatchResponse` return value. + * + * @param message - A multicast message + * containing up to 500 tokens. + * @param dryRun - Whether to send the message in the dry-run + * (validation only) mode. + * @returns A Promise fulfilled with an object representing the result of the + * send operation. + */ + public sendEachForMulticast(message: MulticastMessage, dryRun?: boolean): Promise { + const copy: MulticastMessage = deepCopy(message); + if (!validator.isNonNullObject(copy)) { + throw new FirebaseMessagingError( + MessagingClientErrorCode.INVALID_ARGUMENT, 'MulticastMessage must be a non-null object'); + } + if (!validator.isNonEmptyArray(copy.tokens)) { + throw new FirebaseMessagingError( + MessagingClientErrorCode.INVALID_ARGUMENT, 'tokens must be a non-empty array'); + } + if (copy.tokens.length > FCM_MAX_BATCH_SIZE) { + throw new FirebaseMessagingError( + MessagingClientErrorCode.INVALID_ARGUMENT, + `tokens list must not contain more than ${FCM_MAX_BATCH_SIZE} items`); + } + + const messages: Message[] = copy.tokens.map((token) => { + return { + token, + android: copy.android, + apns: copy.apns, + data: copy.data, + notification: copy.notification, + webpush: copy.webpush, + fcmOptions: copy.fcmOptions, + }; + }); + return this.sendEach(messages, dryRun); + } + /** * Sends all the messages in the given array via Firebase Cloud Messaging. * Employs batching to send the entire list as a single RPC call. Compared @@ -258,8 +377,8 @@ export class Messaging { * * The responses list obtained from the return value * corresponds to the order of tokens in the `MulticastMessage`. An error - * from this method indicates a total failure -- i.e. none of the messages in - * the list could be sent. Partial failures are indicated by a `BatchResponse` + * from this method indicates a total failure, meaning that none of the messages + * in the list could be sent. Partial failures are indicated by a `BatchResponse` * return value. * * @param messages - A non-empty array @@ -268,6 +387,8 @@ export class Messaging { * (validation only) mode. * @returns A Promise fulfilled with an object representing the result of the * send operation. + * + * @deprecated Use {@link Messaging.sendEach} instead. */ public sendAll(messages: Message[], dryRun?: boolean): Promise { if (validator.isArray(messages) && messages.constructor !== Array) { @@ -316,9 +437,9 @@ export class Messaging { * This method uses the `sendAll()` API under the hood to send the given * message to all the target recipients. The responses list obtained from the * return value corresponds to the order of tokens in the `MulticastMessage`. - * An error from this method indicates a total failure -- i.e. the message was - * not sent to any of the tokens in the list. Partial failures are indicated by - * a `BatchResponse` return value. + * An error from this method indicates a total failure, meaning that the message + * was not sent to any of the tokens in the list. Partial failures are indicated + * by a `BatchResponse` return value. * * @param message - A multicast message * containing up to 500 tokens. @@ -326,6 +447,8 @@ export class Messaging { * (validation only) mode. * @returns A Promise fulfilled with an object representing the result of the * send operation. + * + * @deprecated Use {@link Messaging.sendEachForMulticast} instead. */ public sendMulticast(message: MulticastMessage, dryRun?: boolean): Promise { const copy: MulticastMessage = deepCopy(message); @@ -376,6 +499,8 @@ export class Messaging { * * @returns A promise fulfilled with the server's response after the message * has been sent. + * + * @deprecated Use {@link Messaging.send} instead. */ public sendToDevice( registrationTokenOrTokens: string | string[], @@ -445,6 +570,8 @@ export class Messaging { * * @returns A promise fulfilled with the server's response after the message * has been sent. + * + * @deprecated Use {@link Messaging.send} instead. */ public sendToDeviceGroup( notificationKey: string, diff --git a/src/remote-config/remote-config-api-client-internal.ts b/src/remote-config/remote-config-api-client-internal.ts index 9ea77bb532..b8cfe22fc4 100644 --- a/src/remote-config/remote-config-api-client-internal.ts +++ b/src/remote-config/remote-config-api-client-internal.ts @@ -110,7 +110,7 @@ export class RemoteConfigApiClient { public publishTemplate(template: RemoteConfigTemplate, options?: { force: boolean }): Promise { template = this.validateInputRemoteConfigTemplate(template); let ifMatch: string = template.etag; - if (options && options.force == true) { + if (options && options.force === true) { // setting `If-Match: *` forces the Remote Config template to be updated // and circumvent the ETag, and the protection from that it provides. ifMatch = '*'; @@ -244,7 +244,7 @@ export class RemoteConfigApiClient { * @param {string} customEtag A custom etag to replace the etag fom the API response (Optional). */ private toRemoteConfigTemplate(resp: HttpResponse, customEtag?: string): RemoteConfigTemplate { - const etag = (typeof customEtag == 'undefined') ? resp.headers['etag'] : customEtag; + const etag = (typeof customEtag === 'undefined') ? resp.headers['etag'] : customEtag; this.validateEtag(etag); return { conditions: resp.data.conditions, diff --git a/src/storage/index.ts b/src/storage/index.ts index bbab751584..7b963593e8 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -20,12 +20,16 @@ * @packageDocumentation */ +import { File } from '@google-cloud/storage'; import { App, getApp } from '../app'; import { FirebaseApp } from '../app/firebase-app'; import { Storage } from './storage'; +import { FirebaseError } from '../utils/error'; +import { getFirebaseMetadata } from './utils'; export { Storage } from './storage'; + /** * Gets the {@link Storage} service for the default app or a given app. * @@ -53,3 +57,34 @@ export function getStorage(app?: App): Storage { const firebaseApp: FirebaseApp = app as FirebaseApp; return firebaseApp.getOrInitService('storage', (app) => new Storage(app)); } + + + +/** + * Gets the download URL for the given {@link https://cloud.google.com/nodejs/docs/reference/storage/latest/storage/file | File}. + * + * @example + * ```javascript + * // Get the downloadUrl for a given file ref + * const storage = getStorage(); + * const myRef = ref(storage, 'images/mountains.jpg'); + * const downloadUrl = await getDownloadURL(myRef); + * ``` + */ +export async function getDownloadURL(file: File): Promise { + const endpoint = + (process.env.STORAGE_EMULATOR_HOST || + 'https://firebasestorage.googleapis.com') + '/v0'; + const { downloadTokens } = await getFirebaseMetadata(endpoint, file); + if (!downloadTokens) { + throw new FirebaseError({ + code: 'storage/no-download-token', + message: + 'No download token available. Please create one in the Firebase Console.', + }); + } + const [token] = downloadTokens.split(','); + return `${endpoint}/b/${file.bucket.name}/o/${encodeURIComponent( + file.name + )}?alt=media&token=${token}`; +} diff --git a/src/storage/storage.ts b/src/storage/storage.ts index bf1fdfb790..6e155b379e 100644 --- a/src/storage/storage.ts +++ b/src/storage/storage.ts @@ -117,7 +117,6 @@ export class Storage { 'explicitly when calling the getBucket() method.', }); } - /** * Optional app whose `Storage` service to * return. If not provided, the default `Storage` service will be returned. diff --git a/src/storage/utils.ts b/src/storage/utils.ts new file mode 100644 index 0000000000..bb6711521b --- /dev/null +++ b/src/storage/utils.ts @@ -0,0 +1,43 @@ +import { File } from '@google-cloud/storage'; +export interface FirebaseMetadata { + name: string; + bucket: string; + generation: string; + metageneration: string; + contentType: string; + timeCreated: string; + updated: string; + storageClass: string; + size: string; + md5Hash: string; + contentEncoding: string; + contentDisposition: string; + crc32c: string; + etag: string; + downloadTokens?: string; +} + +export function getFirebaseMetadata( + endpoint: string, + file: File +): Promise { + const uri = `${endpoint}/b/${file.bucket.name}/o/${encodeURIComponent( + file.name + )}`; + + return new Promise((resolve, reject) => { + file.storage.makeAuthenticatedRequest( + { + method: 'GET', + uri, + }, + (err, body) => { + if (err) { + reject(err); + } else { + resolve(body); + } + } + ); + }); +} diff --git a/src/utils/api-request.ts b/src/utils/api-request.ts index 64fc49d87c..2408e92344 100644 --- a/src/utils/api-request.ts +++ b/src/utils/api-request.ts @@ -504,14 +504,6 @@ class AsyncHttpCall { if (timeout) { // Listen to timeouts and throw an error. req.setTimeout(timeout, timeoutCallback); - req.on('socket', (socket) => { - socket.setMaxListeners(socket.getMaxListeners() + 1); - socket.setTimeout(timeout, timeoutCallback); - socket.on('end', () => { - socket.setTimeout(0); - socket.setMaxListeners(Math.max(socket.getMaxListeners() - 1, 0)); - }); - }); } // Send the request @@ -926,8 +918,8 @@ export class ExponentialBackoffPoller extends EventEmitter { private numTries = 0; private completed = false; - private masterTimer: NodeJS.Timer; - private repollTimer: NodeJS.Timer; + private masterTimer: NodeJS.Timeout; + private repollTimer: NodeJS.Timeout; private pollCallback?: () => Promise; private resolve: (result: T) => void; diff --git a/src/utils/error.ts b/src/utils/error.ts index 6c74748ed1..cdb7faef05 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -737,6 +737,18 @@ export class AuthClientErrorCode { code: 'user-not-disabled', message: 'The user must be disabled in order to bulk delete it (or you must pass force=true).', }; + public static INVALID_RECAPTCHA_ACTION = { + code: 'invalid-recaptcha-action', + message: 'reCAPTCHA action must be "BLOCK".' + } + public static INVALID_RECAPTCHA_ENFORCEMENT_STATE = { + code: 'invalid-recaptcha-enforcement-state', + message: 'reCAPTCHA enforcement state must be either "OFF", "AUDIT" or "ENFORCE".' + } + public static RECAPTCHA_NOT_ENABLED = { + code: 'racaptcha-not-enabled', + message: 'reCAPTCHA enterprise is not enabled.' + } } /** @@ -996,6 +1008,12 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = { USER_DISABLED: 'USER_DISABLED', // Password provided is too weak. WEAK_PASSWORD: 'INVALID_PASSWORD', + // Unrecognized reCAPTCHA action. + INVALID_RECAPTCHA_ACTION: 'INVALID_RECAPTCHA_ACTION', + // Unrecognized reCAPTCHA enforcement state. + INVALID_RECAPTCHA_ENFORCEMENT_STATE: 'INVALID_RECAPTCHA_ENFORCEMENT_STATE', + // reCAPTCHA is not enabled for account defender. + RECAPTCHA_NOT_ENABLED: 'RECAPTCHA_NOT_ENABLED' }; /** @const {ServerToClientCode} Messaging server to client enum error codes. */ diff --git a/src/utils/validator.ts b/src/utils/validator.ts index 642437c2de..d63d14772c 100644 --- a/src/utils/validator.ts +++ b/src/utils/validator.ts @@ -278,3 +278,19 @@ export function isTopic(topic: any): boolean { const VALID_TOPIC_REGEX = /^(\/topics\/)?(private\/)?[a-zA-Z0-9-_.~%]+$/; return VALID_TOPIC_REGEX.test(topic); } + +/** + * Validates that the provided string can be used as a task ID + * for Cloud Tasks. + * + * @param taskId - the task ID to validate. + * @returns Whether the provided task ID is valid. + */ +export function isTaskId(taskId: any): boolean { + if (typeof taskId !== 'string') { + return false; + } + + const VALID_TASK_ID_REGEX = /^[A-Za-z0-9_-]+$/; + return VALID_TASK_ID_REGEX.test(taskId); +} diff --git a/test/integration/app-check.spec.ts b/test/integration/app-check.spec.ts index 82ad153498..83a71a6f53 100644 --- a/test/integration/app-check.spec.ts +++ b/test/integration/app-check.spec.ts @@ -103,7 +103,7 @@ describe('admin.appCheck', () => { return admin.appCheck().verifyToken(validToken.token) .then((verifedToken) => { expect(verifedToken).to.have.keys(['token', 'appId']); - expect(verifedToken.token).to.have.keys(['iss', 'sub', 'aud', 'exp', 'iat', 'app_id']); + expect(verifedToken.token).to.include.keys(['iss', 'sub', 'aud', 'exp', 'iat', 'app_id']); expect(verifedToken.token.app_id).to.be.a('string').and.equals(appId); }); }); diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index 68e66aaf06..7b113b3156 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -31,7 +31,8 @@ import { deepExtend, deepCopy } from '../../src/utils/deep-copy'; import { AuthProviderConfig, CreateTenantRequest, DeleteUsersResult, PhoneMultiFactorInfo, TenantAwareAuth, UpdatePhoneMultiFactorInfoRequest, UpdateTenantRequest, UserImportOptions, - UserImportRecord, UserRecord, getAuth, UpdateProjectConfigRequest, UserMetadata, + UserImportRecord, UserRecord, getAuth, UpdateProjectConfigRequest, UserMetadata, MultiFactorConfig, + PasswordPolicyConfig, SmsRegionConfig, } from '../../lib/auth/index'; import * as sinon from 'sinon'; import * as sinonChai from 'sinon-chai'; @@ -1206,43 +1207,163 @@ describe('admin.auth', () => { this.skip(); // getConfig is not supported in Auth Emulator } }); - const projectConfigOption1: UpdateProjectConfigRequest = { - smsRegionConfig: { - allowByDefault: { - disallowedRegions: ['AC', 'AD'], + + after(() => { + getAuth().projectConfigManager().updateProjectConfig({ + passwordPolicyConfig: { + enforcementState: 'OFF', + forceUpgradeOnSignin: false, + constraints: { + requireLowercase: false, + requireNonAlphanumeric: false, + requireNumeric: false, + requireUppercase: false, + maxLength: 4096, + minLength: 6, + } } + }) + }); + + const mfaSmsEnabledTotpEnabledConfig: MultiFactorConfig = { + state: 'ENABLED', + factorIds: ['phone'], + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + adjacentIntervals: 5, + }, + }, + ], + }; + const mfaSmsEnabledTotpDisabledConfig: MultiFactorConfig = { + state: 'ENABLED', + factorIds: ['phone'], + providerConfigs: [ + { + state: 'DISABLED', + totpProviderConfig: {}, + } + ], + }; + const passwordConfig: PasswordPolicyConfig = { + enforcementState: 'ENFORCE', + forceUpgradeOnSignin: true, + constraints: { + requireUppercase: true, + requireLowercase: true, + requireNonAlphanumeric: true, + requireNumeric: true, + minLength: 8, + maxLength: 30, + }, + }; + const smsRegionAllowByDefaultConfig: SmsRegionConfig = { + allowByDefault: { + disallowedRegions: ['AC', 'AD'], + } + }; + const smsRegionAllowlistOnlyConfig: SmsRegionConfig = { + allowlistOnly: { + allowedRegions: ['AC', 'AD'], + } + }; + const projectConfigOption1: UpdateProjectConfigRequest = { + smsRegionConfig: smsRegionAllowByDefaultConfig, + multiFactorConfig: mfaSmsEnabledTotpEnabledConfig, + passwordPolicyConfig: passwordConfig, + recaptchaConfig: { + emailPasswordEnforcementState: 'AUDIT', + managedRules: [ + { + endScore: 0.1, + action: 'BLOCK', + }, + ], + useAccountDefender: true, }, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: true, + } }; const projectConfigOption2: UpdateProjectConfigRequest = { - smsRegionConfig: { - allowlistOnly: { - allowedRegions: ['AC', 'AD'], - } + smsRegionConfig: smsRegionAllowlistOnlyConfig, + recaptchaConfig: { + emailPasswordEnforcementState: 'OFF', + useAccountDefender: false, }, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: false, + } + }; + const projectConfigOptionSmsEnabledTotpDisabled: UpdateProjectConfigRequest = { + smsRegionConfig: smsRegionAllowlistOnlyConfig, + multiFactorConfig: mfaSmsEnabledTotpDisabledConfig, }; const expectedProjectConfig1: any = { - smsRegionConfig: { - allowByDefault: { - disallowedRegions: ['AC', 'AD'], - } + smsRegionConfig: smsRegionAllowByDefaultConfig, + multiFactorConfig: mfaSmsEnabledTotpEnabledConfig, + passwordPolicyConfig: passwordConfig, + recaptchaConfig: { + emailPasswordEnforcementState: 'AUDIT', + managedRules: [ + { + endScore: 0.1, + action: 'BLOCK', + }, + ], + useAccountDefender: true, + }, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: true, }, }; const expectedProjectConfig2: any = { - smsRegionConfig: { - allowlistOnly: { - allowedRegions: ['AC', 'AD'], - } + smsRegionConfig: smsRegionAllowlistOnlyConfig, + multiFactorConfig: mfaSmsEnabledTotpEnabledConfig, + passwordPolicyConfig: passwordConfig, + recaptchaConfig: { + emailPasswordEnforcementState: 'OFF', + managedRules: [ + { + endScore: 0.1, + action: 'BLOCK', + }, + ], }, + emailPrivacyConfig: {}, + }; + const expectedProjectConfigSmsEnabledTotpDisabled: any = { + smsRegionConfig: smsRegionAllowlistOnlyConfig, + multiFactorConfig: mfaSmsEnabledTotpDisabledConfig, + passwordPolicyConfig: passwordConfig, + recaptchaConfig: { + emailPasswordEnforcementState: 'OFF', + managedRules: [ + { + endScore: 0.1, + action: 'BLOCK', + }, + ], + }, + emailPrivacyConfig: {}, }; it('updateProjectConfig() should resolve with the updated project config', () => { return getAuth().projectConfigManager().updateProjectConfig(projectConfigOption1) .then((actualProjectConfig) => { + // ReCAPTCHA keys are generated differently each time. + delete actualProjectConfig.recaptchaConfig?.recaptchaKeys; expect(actualProjectConfig.toJSON()).to.deep.equal(expectedProjectConfig1); return getAuth().projectConfigManager().updateProjectConfig(projectConfigOption2); }) .then((actualProjectConfig) => { expect(actualProjectConfig.toJSON()).to.deep.equal(expectedProjectConfig2); + return getAuth().projectConfigManager().updateProjectConfig(projectConfigOptionSmsEnabledTotpDisabled); + }) + .then((actualProjectConfig) => { + expect(actualProjectConfig.toJSON()).to.deep.equal(expectedProjectConfigSmsEnabledTotpDisabled); }); }); @@ -1250,7 +1371,7 @@ describe('admin.auth', () => { return getAuth().projectConfigManager().getProjectConfig() .then((actualConfig) => { const actualConfigObj = actualConfig.toJSON(); - expect(actualConfigObj).to.deep.equal(expectedProjectConfig2); + expect(actualConfigObj).to.deep.equal(expectedProjectConfigSmsEnabledTotpDisabled); }); }); }); @@ -1258,21 +1379,58 @@ describe('admin.auth', () => { describe('Tenant management operations', () => { let createdTenantId: string; const createdTenants: string[] = []; + const mfaSmsEnabledTotpEnabledConfig: MultiFactorConfig = { + state: 'ENABLED', + factorIds: ['phone'], + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + adjacentIntervals: 5, + }, + }, + ], + }; + const mfaSmsEnabledTotpDisabledConfig: MultiFactorConfig = { + state: 'ENABLED', + factorIds: ['phone'], + providerConfigs: [ + { + state: 'DISABLED', + totpProviderConfig: {}, + } + ], + } + const mfaSmsDisabledTotpEnabledConfig: MultiFactorConfig = { + state: 'DISABLED', + factorIds: [], + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: {}, + } + ], + } + const smsRegionAllowByDefaultConfig: SmsRegionConfig = { + allowByDefault: { + disallowedRegions: ['AC', 'AD'], + } + } const tenantOptions: CreateTenantRequest = { displayName: 'testTenant1', emailSignInConfig: { enabled: true, passwordRequired: true, }, - multiFactorConfig: { - state: 'ENABLED', - factorIds: ['phone'], - }, + multiFactorConfig: mfaSmsEnabledTotpEnabledConfig, // Add random phone number / code pairs. testPhoneNumbers: { '+16505551234': '019287', '+16505550676': '985235', }, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: true, + }, }; const expectedCreatedTenant: any = { displayName: 'testTenant1', @@ -1281,10 +1439,7 @@ describe('admin.auth', () => { passwordRequired: true, }, anonymousSignInEnabled: false, - multiFactorConfig: { - state: 'ENABLED', - factorIds: ['phone'], - }, + multiFactorConfig: mfaSmsEnabledTotpEnabledConfig, // These test phone numbers will not be checked when running integration // tests against the emulator suite and are ignored in auth emulator // altogether. For more information, please refer to this section of the @@ -1293,6 +1448,9 @@ describe('admin.auth', () => { '+16505551234': '019287', '+16505550676': '985235', }, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: true, + }, }; const expectedUpdatedTenant: any = { displayName: 'testTenantUpdated', @@ -1301,16 +1459,24 @@ describe('admin.auth', () => { passwordRequired: true, }, anonymousSignInEnabled: false, - multiFactorConfig: { - state: 'DISABLED', - factorIds: [], - }, + multiFactorConfig: mfaSmsDisabledTotpEnabledConfig, // Test phone numbers will not be checked when running integration tests // against emulator suite. For more information, please refer to: // go/firebase-auth-emulator-dd#heading=h.odk06so2ydjd testPhoneNumbers: { '+16505551234': '123456', }, + recaptchaConfig: { + emailPasswordEnforcementState: 'AUDIT', + managedRules: [ + { + endScore: 0.3, + action: 'BLOCK', + }, + ], + useAccountDefender: true, + }, + emailPrivacyConfig: {}, }; const expectedUpdatedTenant2: any = { displayName: 'testTenantUpdated', @@ -1319,15 +1485,40 @@ describe('admin.auth', () => { passwordRequired: false, }, anonymousSignInEnabled: false, - multiFactorConfig: { - state: 'ENABLED', - factorIds: ['phone'], + multiFactorConfig: mfaSmsEnabledTotpEnabledConfig, + smsRegionConfig: smsRegionAllowByDefaultConfig, + recaptchaConfig: { + emailPasswordEnforcementState: 'OFF', + managedRules: [ + { + endScore: 0.3, + action: 'BLOCK', + }, + ], + useAccountDefender: false, }, - smsRegionConfig: { - allowByDefault: { - disallowedRegions: ['AC', 'AD'], - } + emailPrivacyConfig: {}, + }; + const expectedUpdatedTenantSmsEnabledTotpDisabled: any = { + displayName: 'testTenantUpdated', + emailSignInConfig: { + enabled: true, + passwordRequired: false, }, + anonymousSignInEnabled: false, + multiFactorConfig: mfaSmsEnabledTotpDisabledConfig, + smsRegionConfig: smsRegionAllowByDefaultConfig, + recaptchaConfig: { + emailPasswordEnforcementState: 'OFF', + managedRules: [ + { + endScore: 0.3, + action: 'BLOCK', + }, + ], + useAccountDefender: false, + }, + emailPrivacyConfig: {}, }; // https://mochajs.org/ @@ -1740,6 +1931,8 @@ describe('admin.auth', () => { }, multiFactorConfig: deepCopy(expectedUpdatedTenant.multiFactorConfig), testPhoneNumbers: deepCopy(expectedUpdatedTenant.testPhoneNumbers), + recaptchaConfig: deepCopy(expectedUpdatedTenant.recaptchaConfig), + emailPrivacyConfig: { enableImprovedEmailPrivacy: false }, }; const updatedOptions2: UpdateTenantRequest = { emailSignInConfig: { @@ -1750,6 +1943,10 @@ describe('admin.auth', () => { // Test clearing of phone numbers. testPhoneNumbers: null, smsRegionConfig: deepCopy(expectedUpdatedTenant2.smsRegionConfig), + recaptchaConfig: deepCopy(expectedUpdatedTenant2.recaptchaConfig), + emailPrivacyConfig: { + enableImprovedEmailPrivacy: false, + }, }; if (authEmulatorHost) { return getAuth().tenantManager().updateTenant(createdTenantId, updatedOptions) @@ -1775,7 +1972,10 @@ describe('admin.auth', () => { return getAuth().tenantManager().updateTenant(createdTenantId, updatedOptions2); }) .then((actualTenant) => { - expect(actualTenant.toJSON()).to.deep.equal(expectedUpdatedTenant2); + // response from backend ignores account defender status is recaptcha status is OFF. + const expectedUpdatedTenantCopy = deepCopy(expectedUpdatedTenant2); + delete expectedUpdatedTenantCopy.recaptchaConfig.useAccountDefender; + expect(actualTenant.toJSON()).to.deep.equal(expectedUpdatedTenantCopy); }); }); @@ -1797,7 +1997,87 @@ describe('admin.auth', () => { } return getAuth().tenantManager().updateTenant(createdTenantId, updatedOptions2) .then((actualTenant) => { - expect(actualTenant.toJSON()).to.deep.equal(expectedUpdatedTenant2); + // response from backend ignores account defender status is recaptcha status is OFF. + const expectedUpdatedTenantCopy = deepCopy(expectedUpdatedTenant2); + delete expectedUpdatedTenantCopy.recaptchaConfig.useAccountDefender; + expect(actualTenant.toJSON()).to.deep.equal(expectedUpdatedTenantCopy); + }); + }); + + it('updateTenant() should not update MFA-related config of tenant when MultiFactorConfig is undefined', () => { + expectedUpdatedTenant.tenantId = createdTenantId; + const updateRequestNoMfaConfig: UpdateTenantRequest = { + displayName: expectedUpdatedTenant2.displayName, + multiFactorConfig: undefined, + }; + if (authEmulatorHost) { + return getAuth().tenantManager().updateTenant(createdTenantId, updateRequestNoMfaConfig) + .then((actualTenant) => { + const actualTenantObj = actualTenant.toJSON(); + // Configuring test phone numbers are not supported in Auth Emulator + delete (actualTenantObj as { testPhoneNumbers?: Record }).testPhoneNumbers; + delete expectedUpdatedTenant2.testPhoneNumbers; + expect(actualTenantObj).to.deep.equal(expectedUpdatedTenant2); + }); + } + return getAuth().tenantManager().updateTenant(createdTenantId, updateRequestNoMfaConfig) + }); + + it('updateTenant() should not update tenant reCAPTCHA config is undefined', () => { + expectedUpdatedTenant.tenantId = createdTenantId; + const updatedOptions2: UpdateTenantRequest = { + displayName: expectedUpdatedTenant2.displayName, + recaptchaConfig: undefined, + }; + if (authEmulatorHost) { + return getAuth().tenantManager().updateTenant(createdTenantId, updatedOptions2) + .then((actualTenant) => { + const actualTenantObj = actualTenant.toJSON(); + // Not supported in Auth Emulator + delete (actualTenantObj as { testPhoneNumbers?: Record }).testPhoneNumbers; + delete expectedUpdatedTenant2.testPhoneNumbers; + expect(actualTenantObj).to.deep.equal(expectedUpdatedTenant2); + }); + } + return getAuth().tenantManager().updateTenant(createdTenantId, updatedOptions2) + .then((actualTenant) => { + // response from backend ignores account defender status is recaptcha status is OFF. + const expectedUpdatedTenantCopy = deepCopy(expectedUpdatedTenant2); + delete expectedUpdatedTenantCopy.recaptchaConfig.useAccountDefender; + expect(actualTenant.toJSON()).to.deep.equal(expectedUpdatedTenantCopy); + }); + }); + it('updateTenant() should not disable SMS MFA when TOTP is disabled', () => { + expectedUpdatedTenantSmsEnabledTotpDisabled.tenantId = createdTenantId; + const updateRequestSMSEnabledTOTPDisabled: UpdateTenantRequest = { + displayName: expectedUpdatedTenant2.displayName, + multiFactorConfig: { + state: 'ENABLED', + factorIds: ['phone'], + providerConfigs: [ + { + state: 'DISABLED', + totpProviderConfig: {} + }, + ], + }, + }; + if (authEmulatorHost) { + return getAuth().tenantManager().updateTenant(createdTenantId, updateRequestSMSEnabledTOTPDisabled) + .then((actualTenant) => { + const actualTenantObj = actualTenant.toJSON(); + // Configuring test phone numbers are not supported in Auth Emulator + delete (actualTenantObj as { testPhoneNumbers?: Record }).testPhoneNumbers; + delete expectedUpdatedTenantSmsEnabledTotpDisabled.testPhoneNumbers; + expect(actualTenantObj).to.deep.equal(expectedUpdatedTenantSmsEnabledTotpDisabled); + }); + } + return getAuth().tenantManager().updateTenant(createdTenantId, updateRequestSMSEnabledTOTPDisabled) + .then((actualTenant) => { + // response from backend ignores account defender status is recaptcha status is OFF. + const expectedUpdatedTenantCopy = deepCopy(expectedUpdatedTenantSmsEnabledTotpDisabled); + delete expectedUpdatedTenantCopy.recaptchaConfig.useAccountDefender; + expect(actualTenant.toJSON()).to.deep.equal(expectedUpdatedTenantCopy); }); }); @@ -1820,6 +2100,47 @@ describe('admin.auth', () => { expect(tenant.anonymousSignInEnabled).to.be.false; }); + it('updateTenant() should enforce password policies on tenant', () => { + const passwordConfig: PasswordPolicyConfig = { + enforcementState: 'ENFORCE', + forceUpgradeOnSignin: true, + constraints: { + requireLowercase: true, + requireNonAlphanumeric: true, + requireNumeric: true, + requireUppercase: true, + minLength: 6, + maxLength: 30, + }, + }; + return getAuth().tenantManager().updateTenant(createdTenantId, { passwordPolicyConfig: passwordConfig }) + .then((actualTenant) => { + expect(deepCopy(actualTenant.passwordPolicyConfig)).to.deep.equal(passwordConfig as any); + }); + }); + + it('updateTenant() should disable password policies on tenant', () => { + const passwordConfig: PasswordPolicyConfig = { + enforcementState: 'OFF', + }; + const expectedPasswordConfig: any = { + enforcementState: 'OFF', + forceUpgradeOnSignin: false, + constraints: { + requireLowercase: false, + requireNonAlphanumeric: false, + requireNumeric: false, + requireUppercase: false, + minLength: 6, + maxLength: 4096, + }, + }; + return getAuth().tenantManager().updateTenant(createdTenantId, { passwordPolicyConfig: passwordConfig }) + .then((actualTenant) => { + expect(deepCopy(actualTenant.passwordPolicyConfig)).to.deep.equal(expectedPasswordConfig); + }); + }); + it('listTenants() should resolve with expected number of tenants', () => { const allTenantIds: string[] = []; const tenantOptions2 = deepCopy(tenantOptions); diff --git a/test/integration/firestore.spec.ts b/test/integration/firestore.spec.ts index 493a71eaa5..9127ef2e17 100644 --- a/test/integration/firestore.spec.ts +++ b/test/integration/firestore.spec.ts @@ -110,6 +110,10 @@ describe('admin.firestore', () => { expect(typeof admin.firestore.FieldValue).to.be.not.undefined; }); + it('admin.firestore.Filter type is defined', () => { + expect(typeof admin.firestore.Filter).to.be.not.undefined; + }); + it('admin.firestore.GeoPoint type is defined', () => { expect(typeof admin.firestore.GeoPoint).to.be.not.undefined; }); diff --git a/test/integration/machine-learning.spec.ts b/test/integration/machine-learning.spec.ts index 900bbaa699..b44ebdc1e2 100644 --- a/test/integration/machine-learning.spec.ts +++ b/test/integration/machine-learning.spec.ts @@ -17,11 +17,10 @@ import path = require('path'); import * as chai from 'chai'; -import { projectId } from './setup'; import { Bucket } from '@google-cloud/storage'; import { getStorage } from '../../lib/storage/index'; import { - AutoMLTfliteModelOptions, GcsTfliteModelOptions, Model, ModelOptions, getMachineLearning, + GcsTfliteModelOptions, Model, ModelOptions, getMachineLearning, } from '../../lib/machine-learning/index'; const expect = chai.expect; @@ -104,31 +103,6 @@ describe('admin.machineLearning', () => { }); }); - it('creates a new Model with valid AutoML TFLite ModelFormat', function () { - // AutoML models require verification. This takes between 20 and 60 seconds - this.timeout(60000); // Allow up to 60 seconds for this test. - return getAutoMLModelReference() - .then((automlRef: string) => { - if (!automlRef) { - this.skip(); - return; - } - const modelOptions: ModelOptions = { - displayName: 'node-integ-test-create-automl', - tags: ['tagAutoml'], - tfliteModel: { automlModel: automlRef } - }; - return getMachineLearning().createModel(modelOptions) - .then((model) => { - return model.waitForUnlocked(55000) - .then(() => { - scheduleForDelete(model); - verifyModel(model, modelOptions); - }); - }); - }); - }); - it('creates a new Model with invalid ModelFormat', () => { // Upload a file to default gcs bucket const modelOptions: ModelOptions = { @@ -233,33 +207,6 @@ describe('admin.machineLearning', () => { }); }); - it('updates the automl model', function () { - // AutoML models require verification. This takes between 20 and 60 seconds - this.timeout(60000); // Allow up to 60 seconds for this test. - return createTemporaryModel({ - displayName: 'node-integ-test-update-automl' - }).then((model) => { - - return getAutoMLModelReference() - .then((automlRef: string) => { - if (!automlRef) { - this.skip(); - return; - } - const modelOptions: ModelOptions = { - tfliteModel: { automlModel: automlRef }, - }; - return getMachineLearning().updateModel(model.modelId, modelOptions) - .then((updatedModel) => { - return updatedModel.waitForUnlocked(55000) - .then(() => { - verifyModel(updatedModel, modelOptions); - }); - }); - }); - }); - }); - it('can update more than 1 field', () => { const DISPLAY_NAME = 'node-integ-test-update-3b'; const TAGS = ['node-integ-tag-1', 'node-integ-tag-2']; @@ -540,8 +487,6 @@ function verifyModel(model: Model, expectedOptions: ModelOptions): void { } if ((expectedOptions as GcsTfliteModelOptions).tfliteModel?.gcsTfliteUri !== undefined) { verifyGcsTfliteModel(model, (expectedOptions as GcsTfliteModelOptions)); - } else if ((expectedOptions as AutoMLTfliteModelOptions).tfliteModel?.automlModel !== undefined) { - verifyAutomlTfliteModel(model, (expectedOptions as AutoMLTfliteModelOptions)); } else { expect(model.validationError).to.equal('No model file has been uploaded.'); } @@ -558,35 +503,3 @@ function verifyGcsTfliteModel(model: Model, expectedOptions: GcsTfliteModelOptio expect(model.validationError).to.be.undefined; } } - -function verifyAutomlTfliteModel(model: Model, expectedOptions: AutoMLTfliteModelOptions): void { - const expectedAutomlReference = expectedOptions.tfliteModel.automlModel; - expect(model.tfliteModel!.automlModel).to.equal(expectedAutomlReference); - expect(model.validationError).to.be.undefined; - expect(model.tfliteModel!.sizeBytes).to.not.be.undefined; - expect(model.modelHash).to.not.be.undefined; -} - -function getAutoMLModelReference(): Promise { - let automl; - try { - const { AutoMlClient } = require('@google-cloud/automl').v1; - automl = new AutoMlClient(); - } - catch (error) { - // Returning an empty string will result in skipping the test. - return Promise.resolve(''); - } - - const parent = automl.locationPath(projectId, 'us-central1'); - return automl.listModels({ parent, filter:'displayName=admin_sdk_integ_test1' }) - .then(([models]: [any]) => { - let modelRef = ''; - for (const model of models) { - modelRef = model.name; - } - return modelRef; - }) - // Skip the test if anything goes wrong with listing the models. - .catch(() => ''); -} diff --git a/test/integration/messaging.spec.ts b/test/integration/messaging.spec.ts index bc3533893e..0a17a2d750 100644 --- a/test/integration/messaging.spec.ts +++ b/test/integration/messaging.spec.ts @@ -108,6 +108,37 @@ describe('admin.messaging', () => { }); }); + it('sendEach()', () => { + const messages: Message[] = [message, message, message]; + return getMessaging().sendEach(messages, true) + .then((response) => { + expect(response.responses.length).to.equal(messages.length); + expect(response.successCount).to.equal(messages.length); + expect(response.failureCount).to.equal(0); + response.responses.forEach((resp) => { + expect(resp.success).to.be.true; + expect(resp.messageId).matches(/^projects\/.*\/messages\/.*$/); + }); + }); + }); + + it('sendEach(500)', () => { + const messages: Message[] = []; + for (let i = 0; i < 500; i++) { + messages.push({ topic: `foo-bar-${i % 10}` }); + } + return getMessaging().sendEach(messages, true) + .then((response) => { + expect(response.responses.length).to.equal(messages.length); + expect(response.successCount).to.equal(messages.length); + expect(response.failureCount).to.equal(0); + response.responses.forEach((resp) => { + expect(resp.success).to.be.true; + expect(resp.messageId).matches(/^projects\/.*\/messages\/.*$/); + }); + }); + }); + it('sendAll()', () => { const messages: Message[] = [message, message, message]; return getMessaging().sendAll(messages, true) @@ -139,6 +170,25 @@ describe('admin.messaging', () => { }); }); + it('sendEachForMulticast()', () => { + const multicastMessage: MulticastMessage = { + data: message.data, + android: message.android, + tokens: ['not-a-token', 'also-not-a-token'], + }; + return getMessaging().sendEachForMulticast(multicastMessage, true) + .then((response) => { + expect(response.responses.length).to.equal(2); + expect(response.successCount).to.equal(0); + expect(response.failureCount).to.equal(2); + response.responses.forEach((resp) => { + expect(resp.success).to.be.false; + expect(resp.messageId).to.be.undefined; + expect(resp.error).to.have.property('code', 'messaging/invalid-argument'); + }); + }); + }); + it('sendMulticast()', () => { const multicastMessage: MulticastMessage = { data: message.data, diff --git a/test/integration/storage.spec.ts b/test/integration/storage.spec.ts index f7467ac9fc..78d1c731b5 100644 --- a/test/integration/storage.spec.ts +++ b/test/integration/storage.spec.ts @@ -19,7 +19,9 @@ import * as chaiAsPromised from 'chai-as-promised'; import { Bucket, File } from '@google-cloud/storage'; import { projectId } from './setup'; -import { getStorage } from '../../lib/storage/index'; +import { getDownloadURL, getStorage } from '../../lib/storage/index'; +import { getFirebaseMetadata } from '../../src/storage/utils'; +import { FirebaseError } from '../../src/utils/error'; chai.should(); chai.use(chaiAsPromised); @@ -27,6 +29,13 @@ chai.use(chaiAsPromised); const expect = chai.expect; describe('admin.storage', () => { + let currentRef: File | null = null; + afterEach(async () => { + if (currentRef) { + await currentRef.delete(); + } + currentRef = null; + }); it('bucket() returns a handle to the default bucket', () => { const bucket: Bucket = getStorage().bucket(); return verifyBucket(bucket, 'storage().bucket()') @@ -39,6 +48,35 @@ describe('admin.storage', () => { .should.eventually.be.fulfilled; }); + it('getDownloadUrl returns a download URL', async () => { + const bucket = getStorage().bucket(projectId + '.appspot.com'); + currentRef = await verifyBucketDownloadUrl(bucket, 'testName'); + // Note: For now, this generates a download token when needed, but in the future it may not. + const metadata = await getFirebaseMetadata( + 'https://firebasestorage.googleapis.com/v0', + currentRef + ); + if (!metadata.downloadTokens) { + expect(getDownloadURL(currentRef)).to.eventually.throw( + new FirebaseError({ + code: 'storage/invalid-argument', + message: + 'Bucket name not specified or invalid. Specify a valid bucket name via the ' + + 'storageBucket option when initializing the app, or specify the bucket name ' + + 'explicitly when calling the getBucket() method.', + }) + ); + return; + } + const downloadUrl = await getDownloadURL(currentRef); + + const [token] = metadata.downloadTokens.split(','); + const storageEndpoint = `https://firebasestorage.googleapis.com/v0/b/${ + bucket.name + }/o/${encodeURIComponent(currentRef.name)}?alt=media&token=${token}`; + expect(downloadUrl).to.equal(storageEndpoint); + }); + it('bucket(non-existing) returns a handle which can be queried for existence', () => { const bucket: Bucket = getStorage().bucket('non.existing'); return bucket.exists() @@ -46,6 +84,7 @@ describe('admin.storage', () => { expect(data[0]).to.be.false; }); }); + }); function verifyBucket(bucket: Bucket, testName: string): Promise { @@ -66,3 +105,10 @@ function verifyBucket(bucket: Bucket, testName: string): Promise { expect(data[0], 'File not deleted').to.be.false; }); } + +async function verifyBucketDownloadUrl(bucket: Bucket, testName: string): Promise { + const expected: string = 'Hello World: ' + testName; + const file: File = bucket.file('data_' + Date.now() + '.txt'); + await file.save(expected) + return file; +} diff --git a/test/resources/mocks.ts b/test/resources/mocks.ts index 4239f6eebf..a528dd5497 100644 --- a/test/resources/mocks.ts +++ b/test/resources/mocks.ts @@ -104,8 +104,8 @@ export class MockComputeEngineCredential extends ComputeEngineCredential { } } -export function app(): FirebaseApp { - return new FirebaseApp(appOptions, appName); +export function app(altName?: string): FirebaseApp { + return new FirebaseApp(appOptions, altName || appName); } export function mockCredentialApp(): FirebaseApp { diff --git a/test/unit/app-check/app-check-api-client-internal.spec.ts b/test/unit/app-check/app-check-api-client-internal.spec.ts index 65bc64d69d..bd048bd9e4 100644 --- a/test/unit/app-check/app-check-api-client-internal.spec.ts +++ b/test/unit/app-check/app-check-api-client-internal.spec.ts @@ -235,4 +235,133 @@ describe('AppCheckApiClient', () => { }); }); }); + + describe('verifyReplayProtection', () => { + it('should reject when project id is not available', () => { + return clientWithoutProjectId.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE) + .should.eventually.be.rejectedWith(noProjectId); + }); + + it('should throw given no token', () => { + expect(() => { + (apiClient as any).verifyReplayProtection(undefined); + }).to.throw('`token` must be a non-empty string.'); + }); + + [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop].forEach((invalidToken) => { + it('should throw given a non-string token: ' + JSON.stringify(invalidToken), () => { + expect(() => { + apiClient.verifyReplayProtection(invalidToken as any); + }).to.throw('`token` must be a non-empty string.'); + }); + }); + + it('should throw given an empty string token', () => { + expect(() => { + apiClient.verifyReplayProtection(''); + }).to.throw('`token` must be a non-empty string.'); + }); + + it('should reject when a full platform error response is received', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom(ERROR_RESPONSE, 404)); + stubs.push(stub); + const expected = new FirebaseAppCheckError('not-found', 'Requested entity not found'); + return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject with unknown-error when error code is not present', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, 404)); + stubs.push(stub); + const expected = new FirebaseAppCheckError('unknown-error', 'Unknown server error: {}'); + return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject with unknown-error for non-json response', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom('not json', 404)); + stubs.push(stub); + const expected = new FirebaseAppCheckError( + 'unknown-error', 'Unexpected response with status: 404 and body: not json'); + return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject when rejected with a FirebaseAppError', () => { + const expected = new FirebaseAppError('network-error', 'socket hang up'); + const stub = sinon + .stub(HttpClient.prototype, 'send') + .rejects(expected); + stubs.push(stub); + return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + ['', 'abc', '3s2', 'sssa', '3.000000001', '3.2', null, NaN, [], {}, 100, 1.2, -200, -2.4] + .forEach((invalidAlreadyConsumed) => { + it(`should throw if the returned alreadyConsumed value is: ${invalidAlreadyConsumed}`, () => { + const response = { alreadyConsumed: invalidAlreadyConsumed }; + const stub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(response, 200)); + stubs.push(stub); + const expected = new FirebaseAppCheckError( + 'invalid-argument', '`alreadyConsumed` must be a boolean value.'); + return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE) + .should.eventually.be.rejected.and.deep.include(expected); + }); + }); + + it('should resolve with the alreadyConsumed status on success', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({ alreadyConsumed: true }, 200)); + stubs.push(stub); + return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE) + .then((alreadyConsumed) => { + expect(alreadyConsumed).to.equal(true); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://firebaseappcheck.googleapis.com/v1beta/projects/test-project:verifyAppCheckToken', + headers: EXPECTED_HEADERS, + data: { app_check_token: TEST_TOKEN_TO_EXCHANGE } + }); + }); + }); + + [true, false].forEach((expectedAlreadyConsumed) => { + it(`should resolve with alreadyConsumed as ${expectedAlreadyConsumed} when alreadyConsumed + from server is: ${expectedAlreadyConsumed}`, () => { + const response = { alreadyConsumed: expectedAlreadyConsumed }; + const stub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(response, 200)); + stubs.push(stub); + return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE) + .then((alreadyConsumed) => { + expect(alreadyConsumed).to.equal(expectedAlreadyConsumed); + }); + }); + }); + + it(`should resolve with alreadyConsumed as false when alreadyConsumed + from server is: undefined`, () => { + const response = { }; + const stub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(response, 200)); + stubs.push(stub); + return apiClient.verifyReplayProtection(TEST_TOKEN_TO_EXCHANGE) + .then((alreadyConsumed) => { + expect(alreadyConsumed).to.equal(false); + }); + }); + }); + }); diff --git a/test/unit/app-check/app-check.spec.ts b/test/unit/app-check/app-check.spec.ts index 61b7d81bf8..62b8eeac64 100644 --- a/test/unit/app-check/app-check.spec.ts +++ b/test/unit/app-check/app-check.spec.ts @@ -197,6 +197,143 @@ describe('AppCheck', () => { .then((tokenResponse) => { expect(tokenResponse.appId).equals('app-id'); expect(tokenResponse.token).equals(response); + expect(tokenResponse.alreadyConsumed).equals(undefined); + }); + }); + + it('should throw given an invalid options', () => { + [null, 100, -100, 'abc', [], true].forEach((invalidOptions) => { + expect(() => { + return appCheck.verifyToken('token', invalidOptions as any) + }).to.throw( + 'VerifyAppCheckTokenOptions must be a non-null object.'); + }); + }); + + it('should call verifyReplayProtection when consume is set to true', () => { + const response = { + sub: 'app-id', + iss: 'https://firebaseappcheck.googleapis.com/123456', + app_id: 'app-id', + aud: ['123456', 'project-id'], + exp: 1617741496, + iat: 1516239022, + }; + const verifierStub = sinon + .stub(AppCheckTokenVerifier.prototype, 'verifyToken') + .resolves(response); + const replayStub = sinon + .stub(AppCheckApiClient.prototype, 'verifyReplayProtection') + .resolves(true); + stubs.push(verifierStub); + stubs.push(replayStub); + return appCheck.verifyToken('token', { consume: true }) + .then((tokenResponse) => { + expect(tokenResponse.appId).equals('app-id'); + expect(tokenResponse.token).equals(response); + expect(tokenResponse.alreadyConsumed).equals(true); + + expect(verifierStub).to.have.been.calledOnce.and.calledWith('token'); + expect(replayStub).to.have.been.calledOnce.and.calledWith('token'); + }); + }); + + it('should not call verifyReplayProtection when consume is set to false', () => { + const response = { + sub: 'app-id', + iss: 'https://firebaseappcheck.googleapis.com/123456', + app_id: 'app-id', + aud: ['123456', 'project-id'], + exp: 1617741496, + iat: 1516239022, + }; + const verifierStub = sinon + .stub(AppCheckTokenVerifier.prototype, 'verifyToken') + .resolves(response); + const replayStub = sinon + .stub(AppCheckApiClient.prototype, 'verifyReplayProtection') + .resolves(true); + stubs.push(verifierStub); + stubs.push(replayStub); + return appCheck.verifyToken('token', { consume: false }) + .then((tokenResponse) => { + expect(tokenResponse.appId).equals('app-id'); + expect(tokenResponse.token).equals(response); + expect(tokenResponse.alreadyConsumed).equals(undefined); + + expect(verifierStub).to.have.been.calledOnce.and.calledWith('token'); + expect(replayStub).to.not.have.been.called; + }); + }); + + it('should not call verifyReplayProtection when consume is set to undefined', () => { + const response = { + sub: 'app-id', + iss: 'https://firebaseappcheck.googleapis.com/123456', + app_id: 'app-id', + aud: ['123456', 'project-id'], + exp: 1617741496, + iat: 1516239022, + }; + const verifierStub = sinon + .stub(AppCheckTokenVerifier.prototype, 'verifyToken') + .resolves(response); + const replayStub = sinon + .stub(AppCheckApiClient.prototype, 'verifyReplayProtection') + .resolves(true); + stubs.push(verifierStub); + stubs.push(replayStub); + return appCheck.verifyToken('token', { consume: undefined }) + .then((tokenResponse) => { + expect(tokenResponse.appId).equals('app-id'); + expect(tokenResponse.token).equals(response); + expect(tokenResponse.alreadyConsumed).equals(undefined); + + expect(verifierStub).to.have.been.calledOnce.and.calledWith('token'); + expect(replayStub).to.not.have.been.called; + }); + }); + + it('should not call verifyReplayProtection for an invalid token when consume is set to true', () => { + const verifierStub = sinon + .stub(AppCheckTokenVerifier.prototype, 'verifyToken') + .rejects(INTERNAL_ERROR); + const replayStub = sinon + .stub(AppCheckApiClient.prototype, 'verifyReplayProtection') + .resolves(true); + stubs.push(verifierStub); + stubs.push(replayStub); + appCheck.verifyToken('token', { consume: true }) + .should.eventually.be.rejected.and.deep.equal(INTERNAL_ERROR); + expect(verifierStub).to.have.been.calledOnce.and.calledWith('token'); + return expect(replayStub).to.not.have.been.called; + }); + + it('should resolve with VerifyAppCheckTokenResponse on success with alreadyConsumed set', () => { + const response = { + sub: 'app-id', + iss: 'https://firebaseappcheck.googleapis.com/123456', + app_id: 'app-id', + aud: ['123456', 'project-id'], + exp: 1617741496, + iat: 1516239022, + }; + const verifierStub = sinon + .stub(AppCheckTokenVerifier.prototype, 'verifyToken') + .resolves(response); + const replayStub = sinon + .stub(AppCheckApiClient.prototype, 'verifyReplayProtection') + .resolves(false); + stubs.push(verifierStub); + stubs.push(replayStub); + return appCheck.verifyToken('token', { consume: true }) + .then((tokenResponse) => { + expect(tokenResponse.appId).equals('app-id'); + expect(tokenResponse.token).equals(response); + expect(tokenResponse.alreadyConsumed).equals(false); + + expect(verifierStub).to.have.been.calledOnce.and.calledWith('token'); + expect(replayStub).to.have.been.calledOnce.and.calledWith('token'); }); }); }); diff --git a/test/unit/auth/auth-config.spec.ts b/test/unit/auth/auth-config.spec.ts index 62e7d4e3f6..8894bdc5ff 100644 --- a/test/unit/auth/auth-config.spec.ts +++ b/test/unit/auth/auth-config.spec.ts @@ -26,6 +26,8 @@ import { OIDCConfigServerResponse, EmailSignInConfig, MultiFactorAuthConfig, validateTestPhoneNumbers, MAXIMUM_TEST_PHONE_NUMBERS, + PasswordPolicyAuthConfig, + CustomStrengthOptionsConfig, } from '../../../src/auth/auth-config'; import { SAMLUpdateAuthProviderRequest, OIDCUpdateAuthProviderRequest, @@ -206,10 +208,26 @@ describe('MultiFactorAuthConfig', () => { const config = new MultiFactorAuthConfig({ state: 'ENABLED', enabledProviders: ['PHONE_SMS'], + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + adjacentIntervals: 5, + }, + }, + ], }); expect(config.toJSON()).to.deep.equal({ state: 'ENABLED', factorIds: ['phone'], + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + adjacentIntervals: 5, + }, + }, + ], }); }); }); @@ -296,6 +314,142 @@ describe('MultiFactorAuthConfig', () => { }).to.throw(`"${factorId}" is not a valid "AuthFactorType".`); }); }); + + const totpBaseConfig = { + state: 'DISABLED', + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: {}, + }, + ], + } as any; + const expectedTotpConfig = deepCopy(totpBaseConfig); + it('should build server request with TOTP enabled', () => { + expect(MultiFactorAuthConfig.buildServerRequest(totpBaseConfig)).to.deep.equal(expectedTotpConfig); + }); + + it('should build server request with TOTP disabled', () => { + totpBaseConfig.providerConfigs[0].state = 'DISABLED'; + const expectedTotpConfig = deepCopy(totpBaseConfig); + expect(MultiFactorAuthConfig.buildServerRequest(totpBaseConfig)).to.deep.equal(expectedTotpConfig); + }); + + it('should return expected server request on valid TOTP provider config', () => { + totpBaseConfig.providerConfigs[0].totpProviderConfig.adjacentIntervals = 5; + const expectedTotpConfig = deepCopy(totpBaseConfig); + expect(MultiFactorAuthConfig.buildServerRequest(totpBaseConfig)).to.deep.equal(expectedTotpConfig); + }); + + it('should return empty enabledProviders when an empty "options.factorIds" is provided', () => { + expect(MultiFactorAuthConfig.buildServerRequest({ + state: 'DISABLED', + factorIds: [], + })).to.deep.equal({ + state: 'DISABLED', + enabledProviders: [], + }); + }); + + const invalidProviderConfigs = [NaN, 0, 1, '', 'a', {}, { a: 1 }, _.noop, true, false,] + invalidProviderConfigs.forEach((config) => { + it('should throw an error for invalid providerConfigs type:' + JSON.stringify(config), () => { + expect(() => { + MultiFactorAuthConfig.buildServerRequest({ + state: 'DISABLED', + providerConfigs: config, + } as any); + }).to.throw('"MultiFactorConfig.providerConfigs" must be an array of valid "MultiFactorProviderConfig."') + }); + }); + it('should throw on providerConfig with unsupported attribute', () => { + expect(() => { + MultiFactorAuthConfig.buildServerRequest({ + state: 'DISABLED', + providerConfigs: [ + { + unsupported: true, + state: 'ENABLED', + totpProviderConfig: {}, + } + ], + } as any); + }).to.throw('"unsupported" is not a valid ProviderConfig parameter.'); + }); + const invalidProviderConfigObjects = [undefined, NaN, 0, 1, 'a', [], true, false,] + invalidProviderConfigObjects.forEach((object) => { + it('should throw an error for invalid providerConfig type:' + JSON.stringify(object), () => { + expect(() => { + MultiFactorAuthConfig.buildServerRequest({ + state: 'DISABLED', + providerConfigs: [ + object + ], + } as any); + }).to.throw(`"${object}" is not a valid "MultiFactorProviderConfig" type.`) + }); + }); + invalidState.forEach((state) => { + it('should throw an error for invalid providerConfig.state type', () => { + expect(() => { + MultiFactorAuthConfig.buildServerRequest({ + state: 'DISABLED', + providerConfigs: [ + { + state, + totpProviderConfig: {}, + }, + ], + } as any); + }).to.throw('"MultiFactorConfig.providerConfigs.state" must be either "ENABLED" or "DISABLED".') + }); + }); + it('should throw on totpProviderConfig with unsupported attribute', () => { + expect(() => { + MultiFactorAuthConfig.buildServerRequest({ + state: 'DISABLED', + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + unsupported: true, + }, + } + ], + } as any); + }).to.throw('"unsupported" is not a valid TotpProviderConfig parameter.'); + }); + it('should throw an error for undefined totpProviderConfig', () => { + expect(() => { + MultiFactorAuthConfig.buildServerRequest({ + state: 'DISABLED', + providerConfigs: [ + { + state: 'ENABLED', + }, + ], + } as any); + }).to.throw('"MultiFactorConfig.providerConfigs.totpProviderConfig" must be defined.') + }); + const invalidAdjacentIntervals = [null, NaN, '', 'a', [], [1, 'a'], {}, { a: 1 }, _.noop, true, false, -1, 11, 1.1] + invalidAdjacentIntervals.forEach((interval) => { + it('should throw an error for invalid adjacentIntervals type: ' + JSON.stringify(interval), () => { + expect(() => { + MultiFactorAuthConfig.buildServerRequest({ + state: 'DISABLED', + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + adjacentIntervals: interval, + }, + }, + ], + } as any); + }).to.throw('"MultiFactorConfig.providerConfigs.totpProviderConfig.adjacentIntervals" must' + + ' be a valid number between 0 and 10 (both inclusive).') + }); + }); }); }); @@ -1086,3 +1240,85 @@ describe('OIDCConfig', () => { }); }); }); + +describe('PasswordPolicyAuthConfig',() => { + describe('constructor',() => { + const validConfig = new PasswordPolicyAuthConfig({ + passwordPolicyEnforcementState: 'ENFORCE', + passwordPolicyVersions: [ + { + customStrengthOptions: { + containsNumericCharacter: true, + containsLowercaseCharacter: true, + containsNonAlphanumericCharacter: true, + containsUppercaseCharacter: true, + minPasswordLength: 8, + maxPasswordLength: 30, + }, + }, + ], + forceUpgradeOnSignin: true, + }); + + it('should throw an error on missing state',() => { + expect(() => new PasswordPolicyAuthConfig({ + passwordPolicyVersions: [ + { + customStrengthOptions: {}, + } + ], + } as any)).to.throw('INTERNAL ASSERT FAILED: Invalid password policy configuration response'); + }); + + it('should set readonly property "enforcementState" to ENFORCE on state enforced',() => { + expect(validConfig.enforcementState).to.equal('ENFORCE'); + }); + + it('should set readonly property "enforcementState" to OFF on state disabling',() => { + const offStateConfig=new PasswordPolicyAuthConfig({ + passwordPolicyEnforcementState: 'OFF', + }); + expect(offStateConfig.enforcementState).to.equal('OFF'); + }); + + it('should set readonly property "constraints"',() => { + const expectedConstraints: CustomStrengthOptionsConfig = { + requireUppercase: true, + requireLowercase: true, + requireNonAlphanumeric: true, + requireNumeric: true, + minLength: 8, + maxLength: 30, + } + expect(validConfig.constraints).to.deep.equal(expectedConstraints); + }); + + it('should set readonly property "forceUpgradeOnSignin"',() => { + expect(validConfig.forceUpgradeOnSignin).to.deep.equal(true); + }); + }); + + describe('buildServerRequest()', () => { + it('should return server request with default constraints', () => { + expect(PasswordPolicyAuthConfig.buildServerRequest({ + enforcementState: 'ENFORCE', + constraints: {}, + })).to.deep.equal({ + passwordPolicyEnforcementState: 'ENFORCE', + forceUpgradeOnSignin: false, + passwordPolicyVersions: [ + { + customStrengthOptions: { + containsLowercaseCharacter: false, + containsUppercaseCharacter: false, + containsNumericCharacter: false, + containsNonAlphanumericCharacter: false, + minPasswordLength: 6, + maxPasswordLength: 4096, + } + } + ] + }); + }); + }); +}); diff --git a/test/unit/auth/project-config-manager.spec.ts b/test/unit/auth/project-config-manager.spec.ts index d06b24fa80..3fc0770b36 100644 --- a/test/unit/auth/project-config-manager.spec.ts +++ b/test/unit/auth/project-config-manager.spec.ts @@ -51,6 +51,17 @@ describe('ProjectConfigManager', () => { allowedRegions: [ 'AC', 'AD' ], }, }, + recaptchaConfig: { + emailPasswordEnforcementState: 'AUDIT', + managedRules: [ { + endScore: 0.2, + action: 'BLOCK' + } ], + recaptchaKeys: [ { + type: 'WEB', + key: 'test-key-1' } + ], + } }; before(() => { @@ -131,6 +142,13 @@ describe('ProjectConfigManager', () => { disallowedRegions: [ 'AC', 'AD' ], }, }, + recaptchaConfig: { + emailPasswordEnforcementState: 'AUDIT', + managedRules: [ { + endScore: 0.2, + action: 'BLOCK' + } ], + } }; const expectedProjectConfig = new ProjectConfig(GET_CONFIG_RESPONSE); const expectedError = new FirebaseAuthError( @@ -193,4 +211,4 @@ describe('ProjectConfigManager', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/test/unit/auth/project-config.spec.ts b/test/unit/auth/project-config.spec.ts index 19cc8f420d..5934dd15fd 100644 --- a/test/unit/auth/project-config.spec.ts +++ b/test/unit/auth/project-config.spec.ts @@ -20,6 +20,7 @@ import * as sinonChai from 'sinon-chai'; import * as chaiAsPromised from 'chai-as-promised'; import { deepCopy } from '../../../src/utils/deep-copy'; +import { RecaptchaAuthConfig } from '../../../src/auth/auth-config'; import { ProjectConfig, ProjectConfigServerResponse, @@ -39,6 +40,36 @@ describe('ProjectConfig', () => { disallowedRegions: [ 'AC', 'AD' ], }, }, + mfa: { + state: 'DISABLED', + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + adjacentIntervals: 5, + }, + }, + ], + }, + passwordPolicyConfig: { + passwordPolicyEnforcementState: 'ENFORCE', + forceUpgradeOnSignin: true, + passwordPolicyVersions: [ + { + customStrengthOptions: { + containsLowercaseCharacter: true, + containsNonAlphanumericCharacter: true, + containsNumericCharacter: true, + containsUppercaseCharacter: true, + minPasswordLength: 8, + maxPasswordLength: 30, + }, + }, + ], + }, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: true, + }, }; const updateProjectConfigRequest1: UpdateProjectConfigRequest = { @@ -47,6 +78,21 @@ describe('ProjectConfig', () => { disallowedRegions: [ 'AC', 'AD' ], }, }, + passwordPolicyConfig: { + enforcementState: 'ENFORCE', + forceUpgradeOnSignin: true, + constraints: { + requireLowercase: true, + requireNonAlphanumeric: true, + requireNumeric: true, + requireUppercase: true, + minLength: 8, + maxLength: 30, + }, + }, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: false, + }, }; const updateProjectConfigRequest2: UpdateProjectConfigRequest = { @@ -66,6 +112,29 @@ describe('ProjectConfig', () => { disallowedRegions: ['AC', 'AD'], }, }, + recaptchaConfig: { + emailPasswordEnforcementState: 'AUDIT', + managedRules: [ { + endScore: 0.2, + action: 'BLOCK' + } ], + recaptchaKeys: [ { + type: 'WEB', + key: 'test-key-1' } + ], + useAccountDefender: true, + } + }; + + const updateProjectConfigRequest: UpdateProjectConfigRequest = { + recaptchaConfig: { + emailPasswordEnforcementState: 'AUDIT', + managedRules: [ { + endScore: 0.2, + action: 'BLOCK' + } ], + useAccountDefender: true, + } }; describe('buildServerRequest()', () => { @@ -136,6 +205,251 @@ describe('ProjectConfig', () => { ProjectConfig.buildServerRequest(configOptionsClientRequest2); }).not.to.throw; }); + it('should throw on null RecaptchaConfig attribute', () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest) as any; + configOptionsClientRequest.recaptchaConfig = null; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"RecaptchaConfig" must be a non-null object.'); + }); + + it('should throw on invalid RecaptchaConfig attribute', () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest) as any; + configOptionsClientRequest.recaptchaConfig.invalidParameter = 'invalid'; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"invalidParameter" is not a valid RecaptchaConfig parameter.'); + }); + + it('should throw on null emailPasswordEnforcementState attribute', () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest) as any; + configOptionsClientRequest.recaptchaConfig.emailPasswordEnforcementState = null; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"RecaptchaConfig.emailPasswordEnforcementState" must be a valid non-empty string.'); + }); + + it('should throw on invalid emailPasswordEnforcementState attribute', () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest) as any; + configOptionsClientRequest.recaptchaConfig + .emailPasswordEnforcementState = 'INVALID'; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"RecaptchaConfig.emailPasswordEnforcementState" must be either "OFF", "AUDIT" or "ENFORCE".'); + }); + + const invalidUseAccountDefender = [null, NaN, 0, 1, '', 'a', [], [1, 'a'], {}, { a: 1 }, _.noop]; + invalidUseAccountDefender.forEach((useAccountDefender) => { + it(`should throw given invalid useAccountDefender parameter: ${JSON.stringify(useAccountDefender)}`, () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest) as any; + configOptionsClientRequest.recaptchaConfig.useAccountDefender = useAccountDefender; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"RecaptchaConfig.useAccountDefender" must be a boolean value".'); + }); + }); + + it('should throw on non-array managedRules attribute', () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest) as any; + configOptionsClientRequest.recaptchaConfig.managedRules = 'non-array'; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"RecaptchaConfig.managedRules" must be an array of valid "RecaptchaManagedRule".'); + }); + + it('should throw on invalid managedRules attribute', () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest) as any; + configOptionsClientRequest.recaptchaConfig.managedRules = + [{ 'score': 0.1, 'action': 'BLOCK' }]; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"score" is not a valid RecaptchaManagedRule parameter.'); + }); + + it('should throw on invalid RecaptchaManagedRule.action attribute', () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest) as any; + configOptionsClientRequest.recaptchaConfig.managedRules = + [{ 'endScore': 0.1, 'action': 'ALLOW' }]; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"RecaptchaManagedRule.action" must be "BLOCK".'); + }); + + it('should throw on null PasswordPolicyConfig attribute', () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + configOptionsClientRequest.passwordPolicyConfig = null; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig" must be a non-null object.'); + }); + + it('should throw on invalid PasswordPolicyConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.invalidParameter = 'invalid', + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"invalidParameter" is not a valid PasswordPolicyConfig parameter.'); + }); + + it('should throw on missing enforcementState', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + delete tenantOptionsClientRequest.passwordPolicyConfig.enforcementState; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.enforcementState" must be either "ENFORCE" or "OFF".'); + }); + + it('should throw on invalid enforcementState', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.enforcementState = 'INVALID_STATE'; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.enforcementState" must be either "ENFORCE" or "OFF".'); + }); + + it('should throw on invalid forceUpgradeOnSignin', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.forceUpgradeOnSignin = 'INVALID'; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.forceUpgradeOnSignin" must be a boolean.'); + }); + + it('should throw on undefined constraints when state is enforced', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + delete tenantOptionsClientRequest.passwordPolicyConfig.constraints; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints" must be defined.'); + }); + + it('should throw on invalid constraints attribute', ()=> { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.invalidParameter = 'invalid'; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"invalidParameter" is not a valid PasswordPolicyConfig.constraints parameter.'); + }); + + it('should throw on null constraints object', ()=> { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints = null; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints" must be a non-empty object.'); + }); + + it('should throw on invalid constraints object', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints = 'invalid'; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints" must be a non-empty object.'); + }); + + it('should throw on invalid uppercase type', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireUppercase = 'invalid'; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireUppercase"' + + ' must be a boolean.'); + }); + + it('should throw on invalid lowercase type', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireLowercase = 'invalid'; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireLowercase"' + + ' must be a boolean.'); + }); + + it('should throw on invalid numeric type', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireNumeric = 'invalid'; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireNumeric"' + + ' must be a boolean.'); + }); + + it('should throw on invalid non-alphanumeric type', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireNonAlphanumeric = 'invalid'; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireNonAlphanumeric"' + + ' must be a boolean.'); + }); + + it('should throw on invalid minLength type', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.minLength = 'invalid'; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints.minLength" must be a number.'); + }); + + it('should throw on invalid maxLength type', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.maxLength = 'invalid'; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints.maxLength" must be a number.'); + }); + + it('should throw on invalid minLength range', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.minLength = 45; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints.minLength"' + + ' must be an integer between 6 and 30, inclusive.'); + }); + + it('should throw on invalid maxLength range', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.maxLength = 5000; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints.maxLength"' + + ' must be greater than or equal to minLength and at max 4096.'); + }); + + it('should throw if minLength is greater than maxLength', () => { + const tenantOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.minLength = 20; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.maxLength = 7; + expect(() => { + ProjectConfig.buildServerRequest(tenantOptionsClientRequest); + }).to.throw('"PasswordPolicyConfig.constraints.maxLength"' + + ' must be greater than or equal to minLength and at max 4096.'); + }); + + it('should throw on null EmailPrivacyConfig attribute', () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + configOptionsClientRequest.emailPrivacyConfig = null; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"EmailPrivacyConfig" must be a non-null object.'); + }); + + it('should throw on invalid EmailPrivacyConfig attribute', () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + configOptionsClientRequest.emailPrivacyConfig.invalidParameter = 'invalid'; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"invalidParameter" is not a valid "EmailPrivacyConfig" parameter.'); + }); + + it('should throw on invalid enableImprovedEmailPrivacy attribute', () => { + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + configOptionsClientRequest.emailPrivacyConfig.enableImprovedEmailPrivacy = []; + expect(() => { + ProjectConfig.buildServerRequest(configOptionsClientRequest); + }).to.throw('"EmailPrivacyConfig.enableImprovedEmailPrivacy" must be a valid boolean value.'); + }); const nonObjects = [null, NaN, 0, 1, true, false, '', 'a', [], [1, 'a'], _.noop]; nonObjects.forEach((request) => { @@ -147,7 +461,7 @@ describe('ProjectConfig', () => { }); it('should throw on unsupported attribute for update request', () => { - const configOptionsClientRequest = deepCopy(updateProjectConfigRequest1) as any; + const configOptionsClientRequest = deepCopy(updateProjectConfigRequest) as any; configOptionsClientRequest.unsupported = 'value'; expect(() => { ProjectConfig.buildServerRequest(configOptionsClientRequest); @@ -172,21 +486,91 @@ describe('ProjectConfig', () => { }; expect(projectConfig.smsRegionConfig).to.deep.equal(expectedSmsRegionConfig); }); + + it('should set readonly property multiFactorConfig', () => { + const expectedMultiFactorConfig = { + state: 'DISABLED', + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + adjacentIntervals: 5, + }, + }, + ], + }; + expect(projectConfig.multiFactorConfig).to.deep.equal(expectedMultiFactorConfig); + }); + + it('should set readonly property recaptchaConfig', () => { + const expectedRecaptchaConfig = new RecaptchaAuthConfig( + { + emailPasswordEnforcementState: 'AUDIT', + managedRules: [ { + endScore: 0.2, + action: 'BLOCK' + } ], + recaptchaKeys: [ { + type: 'WEB', + key: 'test-key-1' } + ], + useAccountDefender: true, + } + ); + expect(projectConfig.recaptchaConfig).to.deep.equal(expectedRecaptchaConfig); + }); + + it('should set readonly property passwordPolicyConfig', () => { + const expectedPasswordPolicyConfig = { + enforcementState: 'ENFORCE', + forceUpgradeOnSignin: true, + constraints: { + requireLowercase: true, + requireNonAlphanumeric: true, + requireNumeric: true, + requireUppercase: true, + minLength: 8, + maxLength: 30, + }, + }; + expect(projectConfig.passwordPolicyConfig).to.deep.equal(expectedPasswordPolicyConfig); + }); + + it('should set readonly property emailPrivacyConfig', () => { + const expectedEmailPrivacyConfig = { + enableImprovedEmailPrivacy: true, + }; + expect(projectConfig.emailPrivacyConfig).to.deep.equal(expectedEmailPrivacyConfig); + }); }); describe('toJSON()', () => { const serverResponseCopy: ProjectConfigServerResponse = deepCopy(serverResponse); it('should return the expected object representation of project config', () => { expect(new ProjectConfig(serverResponseCopy).toJSON()).to.deep.equal({ - smsRegionConfig: deepCopy(serverResponse.smsRegionConfig) + smsRegionConfig: deepCopy(serverResponse.smsRegionConfig), + multiFactorConfig: deepCopy(serverResponse.mfa), + recaptchaConfig: deepCopy(serverResponse.recaptchaConfig), + passwordPolicyConfig: deepCopy(serverResponse.passwordPolicyConfig), + emailPrivacyConfig: deepCopy(serverResponse.emailPrivacyConfig), }); }); it('should not populate optional fields if not available', () => { const serverResponseOptionalCopy: ProjectConfigServerResponse = deepCopy(serverResponse); delete serverResponseOptionalCopy.smsRegionConfig; - - expect(new ProjectConfig(serverResponseOptionalCopy).toJSON()).to.deep.equal({}); + delete serverResponseOptionalCopy.mfa; + delete serverResponseOptionalCopy.recaptchaConfig?.emailPasswordEnforcementState; + delete serverResponseOptionalCopy.recaptchaConfig?.managedRules; + delete serverResponseOptionalCopy.recaptchaConfig?.useAccountDefender; + delete serverResponseOptionalCopy.passwordPolicyConfig; + delete serverResponseOptionalCopy.passwordPolicyConfig; + delete serverResponseOptionalCopy.emailPrivacyConfig; + expect(new ProjectConfig(serverResponseOptionalCopy).toJSON()).to.deep.equal({ + recaptchaConfig: { + recaptchaKeys: deepCopy(serverResponse.recaptchaConfig?.recaptchaKeys), + } + }); }); }); -}); \ No newline at end of file +}); diff --git a/test/unit/auth/tenant.spec.ts b/test/unit/auth/tenant.spec.ts index 44885ecafa..e1006e47b7 100644 --- a/test/unit/auth/tenant.spec.ts +++ b/test/unit/auth/tenant.spec.ts @@ -20,7 +20,9 @@ import * as sinonChai from 'sinon-chai'; import * as chaiAsPromised from 'chai-as-promised'; import { deepCopy } from '../../../src/utils/deep-copy'; -import { EmailSignInConfig, MultiFactorAuthConfig } from '../../../src/auth/auth-config'; +import { EmailSignInConfig, MultiFactorAuthConfig, RecaptchaAuthConfig, + PasswordPolicyAuthServerConfig, PasswordPolicyConfig, +} from '../../../src/auth/auth-config'; import { TenantServerResponse } from '../../../src/auth/tenant'; import { CreateTenantRequest, UpdateTenantRequest, EmailSignInProviderConfig, Tenant, @@ -45,6 +47,36 @@ describe('Tenant', () => { }, }; + const passwordPolicyClientConfig: PasswordPolicyConfig = { + enforcementState: 'ENFORCE', + forceUpgradeOnSignin: true, + constraints: { + requireLowercase: true, + requireUppercase: true, + requireNonAlphanumeric: true, + requireNumeric: true, + minLength: 8, + maxLength: 30, + } + }; + + const passwordPolicyServerConfig: PasswordPolicyAuthServerConfig = { + passwordPolicyEnforcementState: 'ENFORCE', + forceUpgradeOnSignin: true, + passwordPolicyVersions: [ + { + customStrengthOptions: { + containsLowercaseCharacter: true, + containsNonAlphanumericCharacter: true, + containsNumericCharacter: true, + containsUppercaseCharacter: true, + minPasswordLength: 8, + maxPasswordLength: 30, + }, + }, + ], + }; + const serverRequest: TenantServerResponse = { name: 'projects/project1/tenants/TENANT-ID', displayName: 'TENANT-DISPLAY-NAME', @@ -53,12 +85,24 @@ describe('Tenant', () => { mfaConfig: { state: 'ENABLED', enabledProviders: ['PHONE_SMS'], + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + adjacentIntervals: 5, + }, + }, + ], }, testPhoneNumbers: { '+16505551234': '019287', '+16505550676': '985235', }, smsRegionConfig: smsAllowByDefault, + passwordPolicyConfig: passwordPolicyServerConfig, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: true, + }, }; const clientRequest: UpdateTenantRequest = { @@ -70,12 +114,24 @@ describe('Tenant', () => { multiFactorConfig: { state: 'ENABLED', factorIds: ['phone'], + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + adjacentIntervals: 5, + }, + }, + ], }, testPhoneNumbers: { '+16505551234': '019287', '+16505550676': '985235', }, smsRegionConfig: smsAllowByDefault, + passwordPolicyConfig: passwordPolicyClientConfig, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: true, + }, }; const serverRequestWithoutMfa: TenantServerResponse = { @@ -83,6 +139,10 @@ describe('Tenant', () => { displayName: 'TENANT-DISPLAY-NAME', allowPasswordSignup: true, enableEmailLinkSignin: true, + passwordPolicyConfig: passwordPolicyServerConfig, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: true, + }, }; const clientRequestWithoutMfa: UpdateTenantRequest = { @@ -91,6 +151,74 @@ describe('Tenant', () => { enabled: true, passwordRequired: false, }, + passwordPolicyConfig: passwordPolicyClientConfig, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: true, + }, + }; + + const clientRequestWithRecaptcha: UpdateTenantRequest = { + displayName: 'TENANT-DISPLAY-NAME', + emailSignInConfig: { + enabled: true, + passwordRequired: false, + }, + multiFactorConfig: { + state: 'ENABLED', + factorIds: ['phone'], + }, + testPhoneNumbers: { + '+16505551234': '019287', + '+16505550676': '985235', + }, + recaptchaConfig: { + managedRules: [{ + endScore: 0.2, + action: 'BLOCK' + }], + emailPasswordEnforcementState: 'AUDIT', + useAccountDefender: true, + }, + }; + + const serverResponseWithRecaptcha: TenantServerResponse = { + name: 'projects/project1/tenants/TENANT-ID', + displayName: 'TENANT-DISPLAY-NAME', + allowPasswordSignup: true, + enableEmailLinkSignin: true, + mfaConfig: { + state: 'ENABLED', + enabledProviders: ['PHONE_SMS'], + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + adjacentIntervals: 5, + }, + }, + ], + }, + testPhoneNumbers: { + '+16505551234': '019287', + '+16505550676': '985235', + }, + recaptchaConfig: { + emailPasswordEnforcementState: 'AUDIT', + managedRules: [ { + endScore: 0.2, + action: 'BLOCK' + } ], + recaptchaKeys: [ { + type: 'WEB', + key: 'test-key-1' } + ], + useAccountDefender: true, + }, + smsRegionConfig: smsAllowByDefault, + passwordPolicyConfig: passwordPolicyServerConfig, + emailPrivacyConfig: { + enableImprovedEmailPrivacy: true, + }, }; describe('buildServerRequest()', () => { @@ -105,7 +233,7 @@ describe('Tenant', () => { .to.deep.equal(tenantOptionsServerRequest); }); - it('should return the expected server request with multi-factor and phone config', () => { + it('should return the expected server request with multi-factor (SMS, TOTP) and testPhoneNumber config', () => { const tenantOptionsClientRequest = deepCopy(clientRequest); const tenantOptionsServerRequest = deepCopy(serverRequest); delete (tenantOptionsServerRequest as any).name; @@ -136,6 +264,73 @@ describe('Tenant', () => { }).to.throw('"MultiFactorConfig.state" must be either "ENABLED" or "DISABLED".'); }); + it('should throw on null RecaptchaConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig = null; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"RecaptchaConfig" must be a non-null object.'); + }); + + it('should throw on invalid RecaptchaConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.invalidParameter = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"invalidParameter" is not a valid RecaptchaConfig parameter.'); + }); + + it('should throw on null emailPasswordEnforcementState attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.emailPasswordEnforcementState = null; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"RecaptchaConfig.emailPasswordEnforcementState" must be a valid non-empty string.'); + }); + + it('should throw on invalid emailPasswordEnforcementState attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig + .emailPasswordEnforcementState = 'INVALID'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"RecaptchaConfig.emailPasswordEnforcementState" must be either "OFF", "AUDIT" or "ENFORCE".'); + }); + + it('should throw on non-array managedRules attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.managedRules = 'non-array'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"RecaptchaConfig.managedRules" must be an array of valid "RecaptchaManagedRule".'); + }); + + it('should throw on non-boolean useAccountDefender attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.useAccountDefender = 'yes'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"RecaptchaConfig.useAccountDefender" must be a boolean value".'); + }); + + it('should throw on invalid managedRules attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.managedRules = + [{ 'score': 0.1, 'action': 'BLOCK' }]; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"score" is not a valid RecaptchaManagedRule parameter.'); + }); + + it('should throw on invalid RecaptchaManagedRule.action attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.managedRules = + [{ 'endScore': 0.1, 'action': 'ALLOW' }]; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"RecaptchaManagedRule.action" must be "BLOCK".'); + }); + it('should throw on invalid testPhoneNumbers attribute', () => { const tenantOptionsClientRequest = deepCopy(clientRequest) as any; tenantOptionsClientRequest.testPhoneNumbers = 'invalid'; @@ -213,8 +408,184 @@ describe('Tenant', () => { }).to.throw('SmsRegionConfig cannot have both "allowByDefault" and "allowlistOnly" parameters.'); }); + it('should throw on null PasswordPolicyConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig = null; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig" must be a non-null object.'); + }); + + it('should throw on invalid PasswordPolicyConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.invalidParameter = 'invalid', + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"invalidParameter" is not a valid PasswordPolicyConfig parameter.'); + }); + + it('should throw on missing enforcementState', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + delete tenantOptionsClientRequest.passwordPolicyConfig.enforcementState; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.enforcementState" must be either "ENFORCE" or "OFF".'); + }); + + it('should throw on invalid enforcementState', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.enforcementState = 'INVALID_STATE'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.enforcementState" must be either "ENFORCE" or "OFF".'); + }); + + it('should throw on invalid forceUpgradeOnSignin', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.forceUpgradeOnSignin = 'INVALID'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.forceUpgradeOnSignin" must be a boolean.'); + }); + + it('should throw on undefined constraints when state is enforced', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + delete tenantOptionsClientRequest.passwordPolicyConfig.constraints; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints" must be defined.'); + }); + + it('should throw on invalid constraints attribute', ()=> { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.invalidParameter = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"invalidParameter" is not a valid PasswordPolicyConfig.constraints parameter.'); + }); + + it('should throw on null constraints object', ()=> { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints = null; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints" must be a non-empty object.'); + }); + + it('should throw on invalid constraints object', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints" must be a non-empty object.'); + }); + + it('should throw on invalid uppercase type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireUppercase = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireUppercase"' + + ' must be a boolean.'); + }); + + it('should throw on invalid lowercase type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireLowercase = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireLowercase"' + + ' must be a boolean.'); + }); + + it('should throw on invalid numeric type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireNumeric = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireNumeric"' + + ' must be a boolean.'); + }); + + it('should throw on invalid non-alphanumeric type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireNonAlphanumeric = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireNonAlphanumeric"' + + ' must be a boolean.'); + }); + + it('should throw on invalid minLength type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.minLength = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.minLength" must be a number.'); + }); + + it('should throw on invalid maxLength type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.maxLength = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.maxLength" must be a number.'); + }); + + it('should throw on invalid minLength range', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.minLength = 45; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.minLength"' + + ' must be an integer between 6 and 30, inclusive.'); + }); + + it('should throw on invalid maxLength range', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.maxLength = 5000; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.maxLength"' + + ' must be greater than or equal to minLength and at max 4096.'); + }); + + it('should throw if minLength is greater than maxLength', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.minLength = 20; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.maxLength = 7; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.maxLength"' + + ' must be greater than or equal to minLength and at max 4096.'); + }); + + it('should throw on null EmailPrivacyConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.emailPrivacyConfig = null; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"EmailPrivacyConfig" must be a non-null object.'); + }); + + it('should throw on invalid EmailPrivacyConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.emailPrivacyConfig.invalidParameter = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"invalidParameter" is not a valid "EmailPrivacyConfig" parameter.'); + }); + + it('should throw on invalid enableImprovedEmailPrivacy attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.emailPrivacyConfig.enableImprovedEmailPrivacy = []; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); + }).to.throw('"EmailPrivacyConfig.enableImprovedEmailPrivacy" must be a valid boolean value.'); + }); + it('should not throw on valid client request object', () => { - const tenantOptionsClientRequest = deepCopy(clientRequest); + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha); expect(() => { Tenant.buildServerRequest(tenantOptionsClientRequest, !createRequest); }).not.to.throw; @@ -276,7 +647,7 @@ describe('Tenant', () => { .to.throw('"EmailSignInConfig" must be a non-null object.'); }); - it('should throw on invalid MultiFactorConfig attribute', () => { + it('should throw on invalid MultiFactorConfig.factorIds attribute', () => { const tenantOptionsClientRequest = deepCopy(clientRequest) as any; tenantOptionsClientRequest.multiFactorConfig.factorIds = ['invalid']; expect(() => { @@ -284,6 +655,76 @@ describe('Tenant', () => { }).to.throw('"invalid" is not a valid "AuthFactorType".',); }); + it('should throw on null RecaptchaConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig = null; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"RecaptchaConfig" must be a non-null object.'); + }); + + it('should throw on invalid RecaptchaConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.invalidParameter = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"invalidParameter" is not a valid RecaptchaConfig parameter.'); + }); + + it('should throw on null emailPasswordEnforcementState attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.emailPasswordEnforcementState = null; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"RecaptchaConfig.emailPasswordEnforcementState" must be a valid non-empty string.'); + }); + + it('should throw on invalid emailPasswordEnforcementState attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig + .emailPasswordEnforcementState = 'INVALID'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"RecaptchaConfig.emailPasswordEnforcementState" must be either "OFF", "AUDIT" or "ENFORCE".'); + }); + + it('should throw on non-array managedRules attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.managedRules = 'non-array'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"RecaptchaConfig.managedRules" must be an array of valid "RecaptchaManagedRule".'); + }); + + const invalidUseAccountDefender = [null, NaN, 0, 1, '', 'a', [], [1, 'a'], {}, { a: 1 }, _.noop]; + invalidUseAccountDefender.forEach((useAccountDefender) => { + it('should throw on non-boolean useAccountDefender attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.useAccountDefender = useAccountDefender; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"RecaptchaConfig.useAccountDefender" must be a boolean value".'); + }); + }); + + it('should throw on invalid managedRules attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.managedRules = + [{ 'score': 0.1, 'action': 'BLOCK' }]; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"score" is not a valid RecaptchaManagedRule parameter.'); + }); + + it('should throw on invalid RecaptchaManagedRule.action attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequestWithRecaptcha) as any; + tenantOptionsClientRequest.recaptchaConfig.managedRules = + [{ 'endScore': 0.1, 'action': 'ALLOW' }]; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"RecaptchaManagedRule.action" must be "BLOCK".'); + }); + it('should throw on invalid testPhoneNumbers attribute', () => { const tenantOptionsClientRequest = deepCopy(clientRequest) as any; tenantOptionsClientRequest.testPhoneNumbers = { 'invalid': '123456' }; @@ -362,6 +803,182 @@ describe('Tenant', () => { }).to.throw('SmsRegionConfig cannot have both "allowByDefault" and "allowlistOnly" parameters.'); }); + it('should throw on null PasswordPolicyConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig = null; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig" must be a non-null object.'); + }); + + it('should throw on invalid PasswordPolicyConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.invalidParameter = 'invalid', + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"invalidParameter" is not a valid PasswordPolicyConfig parameter.'); + }); + + it('should throw on missing enforcementState', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + delete tenantOptionsClientRequest.passwordPolicyConfig.enforcementState; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.enforcementState" must be either "ENFORCE" or "OFF".'); + }); + + it('should throw on invalid enforcementState', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.enforcementState = 'INVALID_STATE'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.enforcementState" must be either "ENFORCE" or "OFF".'); + }); + + it('should throw on invalid forceUpgradeOnSignin', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.forceUpgradeOnSignin = 'INVALID'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.forceUpgradeOnSignin" must be a boolean.'); + }); + + it('should throw on undefined constraints when state is enforced', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + delete tenantOptionsClientRequest.passwordPolicyConfig.constraints; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints" must be defined.'); + }); + + it('should throw on invalid constraints attribute', ()=> { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.invalidParameter = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"invalidParameter" is not a valid PasswordPolicyConfig.constraints parameter.'); + }); + + it('should throw on null constraints object', ()=> { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints = null; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints" must be a non-empty object.'); + }); + + it('should throw on invalid constraints object', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints" must be a non-empty object.'); + }); + + it('should throw on invalid uppercase type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireUppercase = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireUppercase"' + + ' must be a boolean.'); + }); + + it('should throw on invalid lowercase type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireLowercase = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireLowercase"' + + ' must be a boolean.'); + }); + + it('should throw on invalid numeric type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireNumeric = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireNumeric"' + + ' must be a boolean.'); + }); + + it('should throw on invalid non-alphanumeric type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.requireNonAlphanumeric = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.requireNonAlphanumeric"' + + ' must be a boolean.'); + }); + + it('should throw on invalid minLength type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.minLength = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.minLength" must be a number.'); + }); + + it('should throw on invalid maxLength type', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.maxLength = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.maxLength" must be a number.'); + }); + + it('should throw on invalid minLength range', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.minLength = 45; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.minLength"' + + ' must be an integer between 6 and 30, inclusive.'); + }); + + it('should throw on invalid maxLength range', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.maxLength = 5000; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.maxLength"' + + ' must be greater than or equal to minLength and at max 4096.'); + }); + + it('should throw if minLength is greater than maxLength', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.minLength = 20; + tenantOptionsClientRequest.passwordPolicyConfig.constraints.maxLength = 7; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"PasswordPolicyConfig.constraints.maxLength"' + + ' must be greater than or equal to minLength and at max 4096.'); + }); + + it('should throw on null EmailPrivacyConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.emailPrivacyConfig = null; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"EmailPrivacyConfig" must be a non-null object.'); + }); + + it('should throw on invalid EmailPrivacyConfig attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.emailPrivacyConfig.invalidParameter = 'invalid'; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"invalidParameter" is not a valid "EmailPrivacyConfig" parameter.'); + }); + + it('should throw on invalid enableImprovedEmailPrivacy attribute', () => { + const tenantOptionsClientRequest = deepCopy(clientRequest) as any; + tenantOptionsClientRequest.emailPrivacyConfig.enableImprovedEmailPrivacy = []; + expect(() => { + Tenant.buildServerRequest(tenantOptionsClientRequest, createRequest); + }).to.throw('"EmailPrivacyConfig.enableImprovedEmailPrivacy" must be a valid boolean value.'); + }); + const nonObjects = [null, NaN, 0, 1, true, false, '', 'a', [], [1, 'a'], _.noop]; nonObjects.forEach((request) => { it('should throw on invalid CreateTenantRequest:' + JSON.stringify(request), () => { @@ -435,10 +1052,37 @@ describe('Tenant', () => { const expectedMultiFactorConfig = new MultiFactorAuthConfig({ state: 'ENABLED', enabledProviders: ['PHONE_SMS'], + providerConfigs: [ + { + state: 'ENABLED', + totpProviderConfig: { + adjacentIntervals: 5, + }, + }, + ], }); expect(tenant.multiFactorConfig).to.deep.equal(expectedMultiFactorConfig); }); + it('should set readonly property recaptchaConfig', () => { + const serverRequestWithRecaptchaCopy: TenantServerResponse = + deepCopy(serverResponseWithRecaptcha); + const tenantWithRecaptcha = new Tenant(serverRequestWithRecaptchaCopy); + const expectedRecaptchaConfig = new RecaptchaAuthConfig({ + emailPasswordEnforcementState: 'AUDIT', + managedRules: [{ + endScore: 0.2, + action: 'BLOCK' + }], + recaptchaKeys: [ { + type: 'WEB', + key: 'test-key-1' } + ], + useAccountDefender: true, + }); + expect(tenantWithRecaptcha.recaptchaConfig).to.deep.equal(expectedRecaptchaConfig); + }); + it('should set readonly property testPhoneNumbers', () => { expect(tenant.testPhoneNumbers).to.deep.equal( deepCopy(clientRequest.testPhoneNumbers)); @@ -449,6 +1093,18 @@ describe('Tenant', () => { deepCopy(clientRequest.smsRegionConfig)); }); + it('should set readonly property passwordPolicyConfig', () => { + expect(tenant.passwordPolicyConfig).to.deep.equal( + deepCopy(clientRequest.passwordPolicyConfig)); + }); + + it('should set readonly property emailPrivacyConfig', () => { + const expectedEmailPrivacyConfig = { + enableImprovedEmailPrivacy: true, + }; + expect(clientRequest.emailPrivacyConfig).to.deep.equal(expectedEmailPrivacyConfig); + }); + it('should throw when no tenant ID is provided', () => { const invalidOptions = deepCopy(serverRequest); // Use resource name that does not include a tenant ID. @@ -475,7 +1131,7 @@ describe('Tenant', () => { }); describe('toJSON()', () => { - const serverRequestCopy: TenantServerResponse = deepCopy(serverRequest); + const serverRequestCopy: TenantServerResponse = deepCopy(serverResponseWithRecaptcha); it('should return the expected object representation of a tenant', () => { expect(new Tenant(serverRequestCopy).toJSON()).to.deep.equal({ tenantId: 'TENANT-ID', @@ -488,15 +1144,20 @@ describe('Tenant', () => { multiFactorConfig: deepCopy(clientRequest.multiFactorConfig), testPhoneNumbers: deepCopy(clientRequest.testPhoneNumbers), smsRegionConfig: deepCopy(clientRequest.smsRegionConfig), + recaptchaConfig: deepCopy(serverResponseWithRecaptcha.recaptchaConfig), + passwordPolicyConfig: deepCopy(clientRequest.passwordPolicyConfig), + emailPrivacyConfig: deepCopy(clientRequest.emailPrivacyConfig), }); }); it('should not populate optional fields if not available', () => { - const serverRequestCopyWithoutMfa: TenantServerResponse = deepCopy(serverRequest); + const serverRequestCopyWithoutMfa: TenantServerResponse = deepCopy(serverResponseWithRecaptcha); delete serverRequestCopyWithoutMfa.mfaConfig; delete serverRequestCopyWithoutMfa.testPhoneNumbers; delete serverRequestCopyWithoutMfa.smsRegionConfig; - + delete serverRequestCopyWithoutMfa.recaptchaConfig; + delete serverRequestCopyWithoutMfa.passwordPolicyConfig; + delete serverRequestCopyWithoutMfa.emailPrivacyConfig; expect(new Tenant(serverRequestCopyWithoutMfa).toJSON()).to.deep.equal({ tenantId: 'TENANT-ID', displayName: 'TENANT-DISPLAY-NAME', diff --git a/test/unit/auth/user-record.spec.ts b/test/unit/auth/user-record.spec.ts index 4ec43af305..dc332c13b9 100644 --- a/test/unit/auth/user-record.spec.ts +++ b/test/unit/auth/user-record.spec.ts @@ -21,7 +21,7 @@ import * as chaiAsPromised from 'chai-as-promised'; import { deepCopy } from '../../../src/utils/deep-copy'; import { - GetAccountInfoUserResponse, ProviderUserInfoResponse, MultiFactorInfoResponse, + GetAccountInfoUserResponse, ProviderUserInfoResponse, MultiFactorInfoResponse, TotpMultiFactorInfo, } from '../../../src/auth/user-record'; import { UserInfo, UserMetadata, UserRecord, MultiFactorSettings, MultiFactorInfo, PhoneMultiFactorInfo, @@ -379,18 +379,157 @@ describe('PhoneMultiFactorInfo', () => { }); }); -describe('MultiFactorInfo', () => { +describe('TotpMultiFactorInfo', () => { const serverResponse: MultiFactorInfoResponse = { + mfaEnrollmentId: 'enrollmentId1', + displayName: 'displayName1', + enrolledAt: now.toISOString(), + totpInfo: {}, + }; + const totpMultiFactorInfo = new TotpMultiFactorInfo(serverResponse); + const totpMultiFactorInfoMissingFields = new TotpMultiFactorInfo({ + mfaEnrollmentId: serverResponse.mfaEnrollmentId, + totpInfo: serverResponse.totpInfo, + }); + + describe('constructor', () => { + it('should throw when an empty object is provided', () => { + expect(() => { + return new TotpMultiFactorInfo({} as any); + }).to.throw('INTERNAL ASSERT FAILED: Invalid multi-factor info response'); + }); + + it('should throw when an undefined response is provided', () => { + expect(() => { + return new TotpMultiFactorInfo(undefined as any); + }).to.throw('INTERNAL ASSERT FAILED: Invalid multi-factor info response'); + }); + + it('should succeed when mfaEnrollmentId and totpInfo are both provided', () => { + expect(() => { + return new TotpMultiFactorInfo({ + mfaEnrollmentId: 'enrollmentId1', + totpInfo: {}, + }); + }).not.to.throw(Error); + }); + + it('should throw when only mfaEnrollmentId is provided', () => { + expect(() => { + return new TotpMultiFactorInfo({ + mfaEnrollmentId: 'enrollmentId1', + } as any); + }).to.throw('INTERNAL ASSERT FAILED: Invalid multi-factor info response'); + }); + + it('should throw when only totpInfo is provided', () => { + expect(() => { + return new TotpMultiFactorInfo({ + totpInfo: {}, + } as any); + }).to.throw('INTERNAL ASSERT FAILED: Invalid multi-factor info response'); + }); + }); + + describe('getters', () => { + it('should set missing optional fields to null', () => { + expect(totpMultiFactorInfoMissingFields.uid).to.equal(serverResponse.mfaEnrollmentId); + expect(totpMultiFactorInfoMissingFields.displayName).to.be.undefined; + expect(totpMultiFactorInfoMissingFields.totpInfo).to.equal(serverResponse.totpInfo); + expect(totpMultiFactorInfoMissingFields.enrollmentTime).to.be.null; + expect(totpMultiFactorInfoMissingFields.factorId).to.equal('totp'); + }); + + it('should return expected factorId', () => { + expect(totpMultiFactorInfo.factorId).to.equal('totp'); + }); + + it('should throw when modifying readonly factorId property', () => { + expect(() => { + (totpMultiFactorInfo as any).factorId = 'other'; + }).to.throw(Error); + }); + + it('should return expected displayName', () => { + expect(totpMultiFactorInfo.displayName).to.equal(serverResponse.displayName); + }); + + it('should throw when modifying readonly displayName property', () => { + expect(() => { + (totpMultiFactorInfo as any).displayName = 'Modified'; + }).to.throw(Error); + }); + + it('should return expected totpInfo object', () => { + expect(totpMultiFactorInfo.totpInfo).to.equal(serverResponse.totpInfo); + }); + + it('should return expected uid', () => { + expect(totpMultiFactorInfo.uid).to.equal(serverResponse.mfaEnrollmentId); + }); + + it('should throw when modifying readonly uid property', () => { + expect(() => { + (totpMultiFactorInfo as any).uid = 'modifiedEnrollmentId'; + }).to.throw(Error); + }); + + it('should return expected enrollmentTime', () => { + expect(totpMultiFactorInfo.enrollmentTime).to.equal(now.toUTCString()); + }); + + it('should throw when modifying readonly uid property', () => { + expect(() => { + (totpMultiFactorInfo as any).enrollmentTime = new Date().toISOString(); + }).to.throw(Error); + }); + }); + + describe('toJSON', () => { + it('should return expected JSON object', () => { + expect(totpMultiFactorInfo.toJSON()).to.deep.equal({ + uid: 'enrollmentId1', + displayName: 'displayName1', + enrollmentTime: now.toUTCString(), + totpInfo: {}, + factorId: 'totp', + }); + }); + + it('should return expected JSON object with missing fields set to null', () => { + expect(totpMultiFactorInfoMissingFields.toJSON()).to.deep.equal({ + uid: 'enrollmentId1', + displayName: undefined, + enrollmentTime: null, + totpInfo: {}, + factorId: 'totp', + }); + }); + }); +}); + +describe('MultiFactorInfo', () => { + const phoneServerResponse: MultiFactorInfoResponse = { mfaEnrollmentId: 'enrollmentId1', displayName: 'displayName1', enrolledAt: now.toISOString(), phoneInfo: '+16505551234', }; - const phoneMultiFactorInfo = new PhoneMultiFactorInfo(serverResponse); + const phoneMultiFactorInfo = new PhoneMultiFactorInfo(phoneServerResponse); + const totpServerResponse: MultiFactorInfoResponse = { + mfaEnrollmentId: 'enrollmentId1', + displayName: 'displayName1', + enrolledAt: now.toISOString(), + totpInfo: {}, + }; + const totpMultiFactorInfo = new TotpMultiFactorInfo(totpServerResponse); describe('initMultiFactorInfo', () => { it('should return expected PhoneMultiFactorInfo', () => { - expect(MultiFactorInfo.initMultiFactorInfo(serverResponse)).to.deep.equal(phoneMultiFactorInfo); + expect(MultiFactorInfo.initMultiFactorInfo(phoneServerResponse)).to.deep.equal(phoneMultiFactorInfo); + }); + it('should return expected TotpMultiFactorInfo', () => { + expect(MultiFactorInfo.initMultiFactorInfo(totpServerResponse)).to.deep.equal(totpMultiFactorInfo); }); it('should return null for invalid MultiFactorInfo', () => { @@ -425,6 +564,12 @@ describe('MultiFactorSettings', () => { enrolledAt: now.toISOString(), secretKey: 'SECRET_KEY', }, + { + mfaEnrollmentId: 'enrollmentId5', + displayName: 'displayName1', + enrolledAt: now.toISOString(), + totpInfo: {}, + }, ], }; const expectedMultiFactorInfo = [ @@ -439,6 +584,12 @@ describe('MultiFactorSettings', () => { enrolledAt: now.toISOString(), phoneInfo: '+16505556789', }), + new TotpMultiFactorInfo({ + mfaEnrollmentId: 'enrollmentId5', + displayName: 'displayName1', + enrolledAt: now.toISOString(), + totpInfo: {}, + }) ]; describe('constructor', () => { @@ -457,9 +608,10 @@ describe('MultiFactorSettings', () => { it('should populate expected enrolledFactors', () => { const multiFactor = new MultiFactorSettings(serverResponse); - expect(multiFactor.enrolledFactors.length).to.equal(2); + expect(multiFactor.enrolledFactors.length).to.equal(3); expect(multiFactor.enrolledFactors[0]).to.deep.equal(expectedMultiFactorInfo[0]); expect(multiFactor.enrolledFactors[1]).to.deep.equal(expectedMultiFactorInfo[1]); + expect(multiFactor.enrolledFactors[2]).to.deep.equal(expectedMultiFactorInfo[2]); }); }); @@ -504,6 +656,7 @@ describe('MultiFactorSettings', () => { enrolledFactors: [ expectedMultiFactorInfo[0].toJSON(), expectedMultiFactorInfo[1].toJSON(), + expectedMultiFactorInfo[2].toJSON(), ], }); }); diff --git a/test/unit/functions/functions-api-client-internal.spec.ts b/test/unit/functions/functions-api-client-internal.spec.ts index 02e76765b2..138280f287 100644 --- a/test/unit/functions/functions-api-client-internal.spec.ts +++ b/test/unit/functions/functions-api-client-internal.spec.ts @@ -137,63 +137,72 @@ describe('FunctionsApiClient', () => { for (const invalidName of [null, NaN, 0, 1, true, false, '', [], [1, 'a'], {}, { a: 1 }, _.noop, undefined]) { it(`should throw if functionName is ${invalidName}`, () => { - expect(() => apiClient.enqueue({}, invalidName as any)) - .to.throw('Function name must be a non empty string'); + expect(apiClient.enqueue({}, invalidName as any)) + .to.eventually.throw('Function name must be a non empty string'); }); } for (const invalidName of ['project/abc/locations/east/fname', 'location/west/', '//']) { it(`should throw if functionName is ${invalidName}`, () => { - expect(() => apiClient.enqueue({}, invalidName as any)) - .to.throw('Function name must be a single string or a qualified resource name'); + expect(apiClient.enqueue({}, invalidName as any)) + .to.eventually.throw('Function name must be a single string or a qualified resource name'); }); } for (const invalidOption of [null, 'abc', '', [], true, 102, 1.2]) { it(`should throw if options is ${invalidOption}`, () => { - expect(() => apiClient.enqueue({}, FUNCTION_NAME, '', invalidOption as any)) - .to.throw('TaskOptions must be a non-null object'); + expect(apiClient.enqueue({}, FUNCTION_NAME, '', invalidOption as any)) + .to.eventually.throw('TaskOptions must be a non-null object'); }); } for (const invalidScheduleTime of [null, '', 'abc', 102, 1.2, [], {}, true, NaN]) { it(`should throw if scheduleTime is ${invalidScheduleTime}`, () => { - expect(() => apiClient.enqueue({}, FUNCTION_NAME, '', { scheduleTime: invalidScheduleTime } as any)) - .to.throw('scheduleTime must be a valid Date object.'); + expect(apiClient.enqueue({}, FUNCTION_NAME, '', { scheduleTime: invalidScheduleTime } as any)) + .to.eventually.throw('scheduleTime must be a valid Date object.'); }); } for (const invalidScheduleDelaySeconds of [null, 'abc', '', [], {}, true, NaN, -1]) { it(`should throw if scheduleDelaySeconds is ${invalidScheduleDelaySeconds}`, () => { - expect(() => apiClient.enqueue({}, FUNCTION_NAME, '', + expect(apiClient.enqueue({}, FUNCTION_NAME, '', { scheduleDelaySeconds: invalidScheduleDelaySeconds } as any)) - .to.throw('scheduleDelaySeconds must be a non-negative duration in seconds.'); + .to.eventually.throw('scheduleDelaySeconds must be a non-negative duration in seconds.'); }); } for (const invalidDispatchDeadlineSeconds of [null, 'abc', '', [], {}, true, NaN, -1, 14, 1801]) { it(`should throw if dispatchDeadlineSeconds is ${invalidDispatchDeadlineSeconds}`, () => { - expect(() => apiClient.enqueue({}, FUNCTION_NAME, '', + expect(apiClient.enqueue({}, FUNCTION_NAME, '', { dispatchDeadlineSeconds: invalidDispatchDeadlineSeconds } as any)) - .to.throw('dispatchDeadlineSeconds must be a non-negative duration in seconds ' + .to.eventually.throw('dispatchDeadlineSeconds must be a non-negative duration in seconds ' + 'and must be in the range of 15s to 30 mins.'); }); } for (const invalidUri of [null, '', 'a', 'foo', 'image.jpg', [], {}, true, NaN]) { it(`should throw given an invalid uri: ${invalidUri}`, () => { - expect(() => apiClient.enqueue({}, FUNCTION_NAME, '', + expect(apiClient.enqueue({}, FUNCTION_NAME, '', { uri: invalidUri } as any)) - .to.throw('uri must be a valid URL string.'); + .to.eventually.throw('uri must be a valid URL string.'); + }); + } + + for (const invalidTaskId of [1234, 'task!', 'id:0', '[1234]', '(1234)']) { + it(`should throw given an invalid task ID: ${invalidTaskId}`, () => { + expect(apiClient.enqueue({}, FUNCTION_NAME, '', + { id: invalidTaskId } as any )) + .to.eventually.throw('id can contain only letters ([A-Za-z]), numbers ([0-9]), ' + + 'hyphens (-), or underscores (_). The maximum length is 500 characters.') }); } it('should throw when both scheduleTime and scheduleDelaySeconds are provided', () => { - expect(() => apiClient.enqueue({}, FUNCTION_NAME, '', { + expect(apiClient.enqueue({}, FUNCTION_NAME, '', { scheduleTime: new Date(), scheduleDelaySeconds: 1000 } as any)) - .to.throw('Both scheduleTime and scheduleDelaySeconds are provided. Only one value should be set.'); + .to.eventually.throw('Both scheduleTime and scheduleDelaySeconds are provided. Only one value should be set.'); }); it('should reject when a full platform error response is received', () => { @@ -237,6 +246,19 @@ describe('FunctionsApiClient', () => { .should.eventually.be.rejected.and.deep.include(expected); }); + it('should reject when a task with the same ID exists', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, 409)); + stubs.push(stub); + expect(apiClient.enqueue({}, FUNCTION_NAME, undefined, { id: 'mock-task' })).to.eventually.throw( + new FirebaseFunctionsError( + 'task-already-exists', + 'A task with ID mock-task already exists' + ) + ) + }); + it('should resolve on success', () => { const stub = sinon .stub(HttpClient.prototype, 'send') @@ -455,4 +477,48 @@ describe('FunctionsApiClient', () => { }); }); }); + + describe('delete', () => { + for (const invalidTaskId of [1234, 'task!', 'id:0', '[1234]', '(1234)']) { + it(`should throw given an invalid task ID: ${invalidTaskId}`, () => { + expect(apiClient.delete(invalidTaskId as any, FUNCTION_NAME)) + .to.eventually.throw('id can contain only letters ([A-Za-z]), numbers ([0-9]), ' + + 'hyphens (-), or underscores (_). The maximum length is 500 characters.') + }); + } + + it('should reject when no valid function name is specified', () => { + expect(apiClient.delete('mock-task', '/projects/abc/locations/def')) + .to.eventually.throw('No valid function name specified to enqueue tasks for.'); + }); + + it('should resolve on success', async () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({}, 200)); + stubs.push(stub); + await apiClient.delete('mock-task', FUNCTION_NAME); + expect(stub).to.have.been.calledWith({ + method: 'DELETE', + url: CLOUD_TASKS_URL.concat('/', 'mock-task'), + headers: EXPECTED_HEADERS, + }); + }); + + it('should ignore deletes if no task with task ID exists', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, 404)); + stubs.push(stub); + expect(apiClient.delete('nonexistent-task', FUNCTION_NAME)).to.eventually.not.throw(utils.errorFrom({}, 404)); + }); + + it('should throw on non-404 HTTP errors', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, 500)); + stubs.push(stub); + expect(apiClient.delete('mock-task', FUNCTION_NAME)).to.eventually.throw(utils.errorFrom({}, 500)); + }); + }) }); diff --git a/test/unit/index.spec.ts b/test/unit/index.spec.ts index ffa46e7ef4..c2ce02ff3f 100644 --- a/test/unit/index.spec.ts +++ b/test/unit/index.spec.ts @@ -102,7 +102,7 @@ import './remote-config/remote-config-api-client.spec'; import './app-check/app-check.spec'; import './app-check/app-check-api-client-internal.spec'; import './app-check/token-generator.spec'; -import './app-check/token-verifier.spec.ts'; +import './app-check/token-verifier.spec'; // Eventarc import './eventarc/eventarc.spec'; diff --git a/test/unit/machine-learning/machine-learning-api-client.spec.ts b/test/unit/machine-learning/machine-learning-api-client.spec.ts index 01f6936869..9fbb94066e 100644 --- a/test/unit/machine-learning/machine-learning-api-client.spec.ts +++ b/test/unit/machine-learning/machine-learning-api-client.spec.ts @@ -64,20 +64,6 @@ describe('MachineLearningApiClient', () => { sizeBytes: 2220022, }, }; - const MODEL_RESPONSE_AUTOML = { - name: 'projects/test-project/models/3456789', - createTime: '2020-07-15T18:12:25.123987Z', - updateTime: '2020-07-15T19:15:32.965435Z', - etag: 'etag345', - modelHash: 'modelHash345', - displayName: 'model_automl', - tags: ['tag_automl'], - state: { published: true }, - tfliteModel: { - automlModel: 'projects/65432/models/ICN123', - sizeBytes: 3330033, - }, - }; const PROJECT_ID = 'test-project'; const PROJECT_NUMBER = '1234567'; @@ -105,10 +91,6 @@ describe('MachineLearningApiClient', () => { }, done: false, }; - const OPERATION_AUTOML_RESPONSE = { - done: true, - response: MODEL_RESPONSE_AUTOML, - }; const LOCKED_MODEL_RESPONSE = { name: 'projects/test-project/models/1234567', createTime: '2020-02-07T23:45:23.288047Z', @@ -179,12 +161,6 @@ describe('MachineLearningApiClient', () => { gcsTfliteUri: 'gcsUri1', }, }; - const AUTOML_OPTIONS: ModelOptions = { - displayName: 'name3', - tfliteModel: { - automlModel: 'automlModel', - }, - }; const invalidContent: any[] = [null, undefined, {}, { tags: [] }]; invalidContent.forEach((content) => { @@ -236,19 +212,6 @@ describe('MachineLearningApiClient', () => { }); }); - it('should accept AutoML options', () => { - const stub = sinon - .stub(HttpClient.prototype, 'send') - .resolves(utils.responseFrom(OPERATION_AUTOML_RESPONSE)); - stubs.push(stub); - return apiClient.createModel(AUTOML_OPTIONS) - .then((resp) => { - expect(resp.done).to.be.true; - expect(resp.name).to.be.undefined; - expect(resp.response).to.deep.equal(MODEL_RESPONSE_AUTOML); - }); - }); - it('should resolve with error when the operation fails', () => { const stub = sinon .stub(HttpClient.prototype, 'send') @@ -302,20 +265,12 @@ describe('MachineLearningApiClient', () => { gcsTfliteUri: 'gcsUri1', }, }; - const AUTOML_OPTIONS: ModelOptions = { - displayName: 'name3', - tfliteModel: { - automlModel: 'automlModel', - }, - }; const NAME_ONLY_MASK_LIST = ['displayName']; const GCS_MASK_LIST = ['displayName', 'tfliteModel.gcsTfliteUri']; - const AUTOML_MASK_LIST = ['displayName', 'tfliteModel.automlModel']; const NAME_ONLY_UPDATE_MASK_STRING = 'updateMask=displayName'; const GCS_UPDATE_MASK_STRING = 'updateMask=displayName,tfliteModel.gcsTfliteUri'; - const AUTOML_UPDATE_MASK_STRING = 'updateMask=displayName,tfliteModel.automlModel'; const invalidOptions: any[] = [null, undefined]; invalidOptions.forEach((option) => { @@ -385,25 +340,6 @@ describe('MachineLearningApiClient', () => { }); }); - it('should resolve with the updated AutoML resource on success', () => { - const stub = sinon - .stub(HttpClient.prototype, 'send') - .resolves(utils.responseFrom(OPERATION_SUCCESS_RESPONSE)); - stubs.push(stub); - return apiClient.updateModel(MODEL_ID, AUTOML_OPTIONS, AUTOML_MASK_LIST) - .then((resp) => { - expect(resp.done).to.be.true; - expect(resp.name).to.be.undefined; - expect(resp.response).to.deep.equal(MODEL_RESPONSE); - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'PATCH', - headers: EXPECTED_HEADERS, - url: `${BASE_URL}/projects/test-project/models/${MODEL_ID}?${AUTOML_UPDATE_MASK_STRING}`, - data: AUTOML_OPTIONS, - }); - }); - }); - it('should resolve with error when the operation fails', () => { const stub = sinon .stub(HttpClient.prototype, 'send') diff --git a/test/unit/messaging/messaging.spec.ts b/test/unit/messaging/messaging.spec.ts index df459c6ec3..dc978c7866 100644 --- a/test/unit/messaging/messaging.spec.ts +++ b/test/unit/messaging/messaging.spec.ts @@ -72,11 +72,11 @@ const STATUS_CODE_TO_ERROR_MAP = { 503: 'messaging/server-unavailable', }; -function mockSendRequest(): nock.Scope { +function mockSendRequest(messageId = 'projects/projec_id/messages/message_id'): nock.Scope { return nock(`https://${FCM_SEND_HOST}:443`) .post('/v1/projects/project_id/messages:send') .reply(200, { - name: 'projects/projec_id/messages/message_id', + name: `${messageId}`, }); } @@ -159,7 +159,7 @@ function mockErrorResponse( } function mockSendToDeviceStringRequest(mockFailure = false): nock.Scope { - let deviceResult: object = { message_id: `0:${ mocks.messaging.messageId }` }; + let deviceResult: object = { message_id: `0:${mocks.messaging.messageId}` }; if (mockFailure) { deviceResult = { error: 'InvalidRegistration' }; } @@ -171,7 +171,7 @@ function mockSendToDeviceStringRequest(mockFailure = false): nock.Scope { success: mockFailure ? 0 : 1, failure: mockFailure ? 1 : 0, canonical_ids: 0, - results: [ deviceResult ], + results: [deviceResult], }); } @@ -185,7 +185,7 @@ function mockSendToDeviceArrayRequest(): nock.Scope { canonical_ids: 1, results: [ { - message_id: `0:${ mocks.messaging.messageId }`, + message_id: `0:${mocks.messaging.messageId}`, registration_id: mocks.messaging.registrationToken + '3', }, { error: 'some-error' }, @@ -313,8 +313,8 @@ describe('Messaging', () => { let getTokenStub: sinon.SinonStub; let nullAccessTokenMessaging: Messaging; - let messagingService: {[key: string]: any}; - let nullAccessTokenMessagingService: {[key: string]: any}; + let messagingService: { [key: string]: any }; + let nullAccessTokenMessagingService: { [key: string]: any }; const mockAccessToken: string = utils.generateRandomAccessToken(); const expectedHeaders = { @@ -351,7 +351,7 @@ describe('Messaging', () => { describe('Constructor', () => { const invalidApps = [null, NaN, 0, 1, true, false, '', 'a', [], [1, 'a'], {}, { a: 1 }, _.noop]; invalidApps.forEach((invalidApp) => { - it(`should throw given invalid app: ${ JSON.stringify(invalidApp) }`, () => { + it(`should throw given invalid app: ${JSON.stringify(invalidApp)}`, () => { expect(() => { const messagingAny: any = Messaging; return new messagingAny(invalidApp); @@ -410,7 +410,7 @@ describe('Messaging', () => { {}, { token: null }, { token: '' }, { topic: null }, { topic: '' }, { condition: null }, { condition: '' }, ]; noTarget.forEach((message) => { - it(`should throw given message without target: ${ JSON.stringify(message) }`, () => { + it(`should throw given message without target: ${JSON.stringify(message)}`, () => { expect(() => { messaging.send(message as any); }).to.throw('Exactly one of topic, token or condition is required'); @@ -424,7 +424,7 @@ describe('Messaging', () => { { token: 'a', topic: 'b', condition: 'c' }, ]; multipleTargets.forEach((message) => { - it(`should throw given message without target: ${ JSON.stringify(message)}`, () => { + it(`should throw given message without target: ${JSON.stringify(message)}`, () => { expect(() => { messaging.send(message as any); }).to.throw('Exactly one of topic, token or condition is required'); @@ -558,6 +558,526 @@ describe('Messaging', () => { }); }); + describe('sendEach()', () => { + const validMessage: Message = { token: 'a' }; + + function checkSendResponseSuccess(response: SendResponse, messageId: string): void { + expect(response.success).to.be.true; + expect(response.messageId).to.equal(messageId); + expect(response.error).to.be.undefined; + } + + function checkSendResponseFailure(response: SendResponse, code: string, msg?: string): void { + expect(response.success).to.be.false; + expect(response.messageId).to.be.undefined; + expect(response.error).to.have.property('code', code); + if (msg) { + expect(response.error!.toString()).to.contain(msg); + } + } + + it('should throw given no messages', () => { + expect(() => { + messaging.sendEach(undefined as any); + }).to.throw('messages must be a non-empty array'); + expect(() => { + messaging.sendEach(null as any); + }).to.throw('messages must be a non-empty array'); + expect(() => { + messaging.sendEach([]); + }).to.throw('messages must be a non-empty array'); + }); + + it('should throw when called with more than 500 messages', () => { + const messages: Message[] = []; + for (let i = 0; i < 501; i++) { + messages.push(validMessage); + } + expect(() => { + messaging.sendEach(messages); + }).to.throw('messages list must not contain more than 500 items'); + }); + + it('should reject when a message is invalid', () => { + const invalidMessage: Message = {} as any; + messaging.sendEach([validMessage, invalidMessage]) + .should.eventually.be.rejectedWith('Exactly one of topic, token or condition is required'); + }); + + const invalidDryRun = [null, NaN, 0, 1, '', 'a', [], [1, 'a'], {}, { a: 1 }, _.noop]; + invalidDryRun.forEach((dryRun) => { + it(`should throw given invalid dryRun parameter: ${JSON.stringify(dryRun)}`, () => { + expect(() => { + messaging.sendEach([{ token: 'a' }], dryRun as any); + }).to.throw('dryRun must be a boolean'); + }); + }); + + it('should be fulfilled with a BatchResponse given valid messages', () => { + const messageIds = [ + 'projects/projec_id/messages/1', + 'projects/projec_id/messages/2', + 'projects/projec_id/messages/3', + ]; + messageIds.forEach(id => mockedRequests.push(mockSendRequest(id))) + return messaging.sendEach([validMessage, validMessage, validMessage]) + .then((response: BatchResponse) => { + expect(response.successCount).to.equal(3); + expect(response.failureCount).to.equal(0); + response.responses.forEach((resp, idx) => { + expect(resp.success).to.be.true; + expect(resp.messageId).to.equal(messageIds[idx]); + expect(resp.error).to.be.undefined; + }); + }); + }); + + it('should be fulfilled with a BatchResponse given array-like (issue #566)', () => { + const messageIds = [ + 'projects/projec_id/messages/1', + 'projects/projec_id/messages/2', + 'projects/projec_id/messages/3', + ]; + messageIds.forEach(id => mockedRequests.push(mockSendRequest(id))) + const message = { + token: 'a', + android: { + ttl: 3600, + }, + }; + const arrayLike = new CustomArray(); + arrayLike.push(message); + arrayLike.push(message); + arrayLike.push(message); + // Explicitly patch the constructor so that down compiling to ES5 doesn't affect the test. + // See https://github.com/firebase/firebase-admin-node/issues/566#issuecomment-501974238 + // for more context. + arrayLike.constructor = CustomArray; + + return messaging.sendEach(arrayLike) + .then((response: BatchResponse) => { + expect(response.successCount).to.equal(3); + expect(response.failureCount).to.equal(0); + response.responses.forEach((resp, idx) => { + expect(resp.success).to.be.true; + expect(resp.messageId).to.equal(messageIds[idx]); + expect(resp.error).to.be.undefined; + }); + }); + }); + + it('should be fulfilled with a BatchResponse given valid messages in dryRun mode', () => { + const messageIds = [ + 'projects/projec_id/messages/1', + 'projects/projec_id/messages/2', + 'projects/projec_id/messages/3', + ]; + messageIds.forEach(id => mockedRequests.push(mockSendRequest(id))) + return messaging.sendEach([validMessage, validMessage, validMessage], true) + .then((response: BatchResponse) => { + expect(response.successCount).to.equal(3); + expect(response.failureCount).to.equal(0); + expect(response.responses.length).to.equal(3); + response.responses.forEach((resp, idx) => { + checkSendResponseSuccess(resp, messageIds[idx]); + }); + }); + }); + + it('should be fulfilled with a BatchResponse for partial failures', () => { + const messageIds = [ + 'projects/projec_id/messages/1', + 'projects/projec_id/messages/2', + ]; + const errors = [ + { + error: { + status: 'INVALID_ARGUMENT', + message: 'test error message', + }, + }, + ]; + messageIds.forEach(id => mockedRequests.push(mockSendRequest(id))) + errors.forEach(error => mockedRequests.push(mockSendError(400, 'json', error))) + return messaging.sendEach([validMessage, validMessage, validMessage], true) + .then((response: BatchResponse) => { + expect(response.successCount).to.equal(2); + expect(response.failureCount).to.equal(1); + expect(response.responses.length).to.equal(3); + + const responses = response.responses; + checkSendResponseSuccess(responses[0], messageIds[0]); + checkSendResponseSuccess(responses[1], messageIds[1]); + checkSendResponseFailure( + responses[2], 'messaging/invalid-argument', 'test error message'); + }); + }); + + it('should be fulfilled with a BatchResponse for all failures given an app which' + + 'returns null access tokens', () => { + return nullAccessTokenMessaging.sendEach( + [validMessage, validMessage], + ).then((response: BatchResponse) => { + expect(response.failureCount).to.equal(2); + response.responses.forEach(resp => checkSendResponseFailure( + resp, 'app/invalid-credential')); + }); + }); + + it('should expose the FCM error code in a detailed error via BatchResponse', () => { + const messageIds = [ + 'projects/projec_id/messages/1', + ]; + const errors = [ + { + error: { + status: 'INVALID_ARGUMENT', + message: 'test error message', + details: [ + { + '@type': 'type.googleapis.com/google.firebase.fcm.v1.FcmError', + 'errorCode': 'UNREGISTERED', + }, + ], + }, + }, + ]; + messageIds.forEach(id => mockedRequests.push(mockSendRequest(id))) + errors.forEach(error => mockedRequests.push(mockSendError(404, 'json', error))) + return messaging.sendEach([validMessage, validMessage], true) + .then((response: BatchResponse) => { + expect(response.successCount).to.equal(1); + expect(response.failureCount).to.equal(1); + expect(response.responses.length).to.equal(2); + + const responses = response.responses; + checkSendResponseSuccess(responses[0], messageIds[0]); + checkSendResponseFailure( + responses[1], 'messaging/registration-token-not-registered'); + }); + }); + + it('should map server error code to client-side error', () => { + const error = { + error: { + status: 'NOT_FOUND', + message: 'test error message', + } + }; + mockedRequests.push(mockSendError(404, 'json', error)); + mockedRequests.push(mockSendError(400, 'json', { error: 'test error message' })); + mockedRequests.push(mockSendError(400, 'text', 'foo bar')); + return messaging.sendEach( + [validMessage, validMessage, validMessage], + ).then((response: BatchResponse) => { + expect(response.failureCount).to.equal(3); + const responses = response.responses; + checkSendResponseFailure(responses[0], 'messaging/registration-token-not-registered'); + checkSendResponseFailure(responses[1], 'messaging/unknown-error'); + checkSendResponseFailure(responses[2], 'messaging/invalid-argument'); + }); + }); + + // This test was added to also verify https://github.com/firebase/firebase-admin-node/issues/1146 + it('should be fulfilled when called with different message types', () => { + const messageIds = [ + 'projects/projec_id/messages/1', + 'projects/projec_id/messages/2', + 'projects/projec_id/messages/3', + ]; + const tokenMessage: TokenMessage = { token: 'test' }; + const topicMessage: TopicMessage = { topic: 'test' }; + const conditionMessage: ConditionMessage = { condition: 'test' }; + const messages: Message[] = [tokenMessage, topicMessage, conditionMessage]; + + messageIds.forEach(id => mockedRequests.push(mockSendRequest(id))) + + return messaging.sendEach(messages) + .then((response: BatchResponse) => { + expect(response.successCount).to.equal(3); + expect(response.failureCount).to.equal(0); + response.responses.forEach((resp, idx) => { + expect(resp.success).to.be.true; + expect(resp.messageId).to.equal(messageIds[idx]); + expect(resp.error).to.be.undefined; + }); + }); + }); + }); + + describe('sendEachForMulticast()', () => { + const mockResponse: BatchResponse = { + successCount: 3, + failureCount: 0, + responses: [ + { success: true, messageId: 'projects/projec_id/messages/1' }, + { success: true, messageId: 'projects/projec_id/messages/2' }, + { success: true, messageId: 'projects/projec_id/messages/3' }, + ], + }; + + let stub: sinon.SinonStub | null; + + afterEach(() => { + if (stub) { + stub.restore(); + } + stub = null; + }); + + it('should throw given no messages', () => { + expect(() => { + messaging.sendEachForMulticast(undefined as any); + }).to.throw('MulticastMessage must be a non-null object'); + expect(() => { + messaging.sendEachForMulticast({} as any); + }).to.throw('tokens must be a non-empty array'); + expect(() => { + messaging.sendEachForMulticast({ tokens: [] }); + }).to.throw('tokens must be a non-empty array'); + }); + + it('should throw when called with more than 500 messages', () => { + const tokens: string[] = []; + for (let i = 0; i < 501; i++) { + tokens.push(`token${i}`); + } + expect(() => { + messaging.sendEachForMulticast({ tokens }); + }).to.throw('tokens list must not contain more than 500 items'); + }); + + const invalidDryRun = [null, NaN, 0, 1, '', 'a', [], [1, 'a'], {}, { a: 1 }, _.noop]; + invalidDryRun.forEach((dryRun) => { + it(`should throw given invalid dryRun parameter: ${JSON.stringify(dryRun)}`, () => { + expect(() => { + messaging.sendEachForMulticast({ tokens: ['a'] }, dryRun as any); + }).to.throw('dryRun must be a boolean'); + }); + }); + + it('should create multiple messages using the empty multicast payload', () => { + stub = sinon.stub(messaging, 'sendEach').resolves(mockResponse); + const tokens = ['a', 'b', 'c']; + return messaging.sendEachForMulticast({ tokens }) + .then((response: BatchResponse) => { + expect(response).to.deep.equal(mockResponse); + expect(stub).to.have.been.calledOnce; + const messages: Message[] = stub!.args[0][0]; + expect(messages.length).to.equal(3); + expect(stub!.args[0][1]).to.be.undefined; + messages.forEach((message, idx) => { + expect((message as TokenMessage).token).to.equal(tokens[idx]); + expect(message.android).to.be.undefined; + expect(message.apns).to.be.undefined; + expect(message.data).to.be.undefined; + expect(message.notification).to.be.undefined; + expect(message.webpush).to.be.undefined; + }); + }); + }); + + it('should create multiple messages using the multicast payload', () => { + stub = sinon.stub(messaging, 'sendEach').resolves(mockResponse); + const tokens = ['a', 'b', 'c']; + const multicast: MulticastMessage = { + tokens, + android: { ttl: 100 }, + apns: { payload: { aps: { badge: 42 } } }, + data: { key: 'value' }, + notification: { title: 'test title' }, + webpush: { data: { webKey: 'webValue' } }, + fcmOptions: { analyticsLabel: 'label' }, + }; + return messaging.sendEachForMulticast(multicast) + .then((response: BatchResponse) => { + expect(response).to.deep.equal(mockResponse); + expect(stub).to.have.been.calledOnce; + const messages: Message[] = stub!.args[0][0]; + expect(messages.length).to.equal(3); + expect(stub!.args[0][1]).to.be.undefined; + messages.forEach((message, idx) => { + expect((message as TokenMessage).token).to.equal(tokens[idx]); + expect(message.android).to.deep.equal(multicast.android); + expect(message.apns).to.be.deep.equal(multicast.apns); + expect(message.data).to.be.deep.equal(multicast.data); + expect(message.notification).to.deep.equal(multicast.notification); + expect(message.webpush).to.deep.equal(multicast.webpush); + expect(message.fcmOptions).to.deep.equal(multicast.fcmOptions); + }); + }); + }); + + it('should pass dryRun argument through', () => { + stub = sinon.stub(messaging, 'sendEach').resolves(mockResponse); + const tokens = ['a', 'b', 'c']; + return messaging.sendEachForMulticast({ tokens }, true) + .then((response: BatchResponse) => { + expect(response).to.deep.equal(mockResponse); + expect(stub).to.have.been.calledOnce; + expect(stub!.args[0][1]).to.be.true; + }); + }); + + it('should be fulfilled with a BatchResponse given valid message', () => { + const messageIds = [ + 'projects/projec_id/messages/1', + 'projects/projec_id/messages/2', + 'projects/projec_id/messages/3', + ]; + messageIds.forEach(id => mockedRequests.push(mockSendRequest(id))) + return messaging.sendEachForMulticast({ + tokens: ['a', 'b', 'c'], + android: { ttl: 100 }, + apns: { payload: { aps: { badge: 42 } } }, + data: { key: 'value' }, + notification: { title: 'test title' }, + webpush: { data: { webKey: 'webValue' } }, + }).then((response: BatchResponse) => { + expect(response.successCount).to.equal(3); + expect(response.failureCount).to.equal(0); + response.responses.forEach((resp, idx) => { + expect(resp.success).to.be.true; + expect(resp.messageId).to.equal(messageIds[idx]); + expect(resp.error).to.be.undefined; + }); + }); + }); + + it('should be fulfilled with a BatchResponse given valid message in dryRun mode', () => { + const messageIds = [ + 'projects/projec_id/messages/1', + 'projects/projec_id/messages/2', + 'projects/projec_id/messages/3', + ]; + messageIds.forEach(id => mockedRequests.push(mockSendRequest(id))) + return messaging.sendEachForMulticast({ + tokens: ['a', 'b', 'c'], + android: { ttl: 100 }, + apns: { payload: { aps: { badge: 42 } } }, + data: { key: 'value' }, + notification: { title: 'test title' }, + webpush: { data: { webKey: 'webValue' } }, + }, true).then((response: BatchResponse) => { + expect(response.successCount).to.equal(3); + expect(response.failureCount).to.equal(0); + expect(response.responses.length).to.equal(3); + response.responses.forEach((resp, idx) => { + checkSendResponseSuccess(resp, messageIds[idx]); + }); + }); + }); + + it('should be fulfilled with a BatchResponse for partial failures', () => { + const messageIds = [ + 'projects/projec_id/messages/1', + 'projects/projec_id/messages/2', + ]; + const errors = [ + { + error: { + status: 'INVALID_ARGUMENT', + message: 'test error message', + }, + }, + ]; + messageIds.forEach(id => mockedRequests.push(mockSendRequest(id))) + errors.forEach(err => mockedRequests.push(mockSendError(400, 'json', err))) + return messaging.sendEachForMulticast({ tokens: ['a', 'b', 'c'] }) + .then((response: BatchResponse) => { + expect(response.successCount).to.equal(2); + expect(response.failureCount).to.equal(1); + expect(response.responses.length).to.equal(3); + + const responses = response.responses; + checkSendResponseSuccess(responses[0], messageIds[0]); + checkSendResponseSuccess(responses[1], messageIds[1]); + checkSendResponseFailure( + responses[2], 'messaging/invalid-argument', 'test error message'); + }); + }); + + it('should be fulfilled with a BatchResponse for all failures given an app which ' + + 'returns null access tokens', () => { + return nullAccessTokenMessaging.sendEachForMulticast( + { tokens: ['a', 'a'] }, + ).then((response: BatchResponse) => { + expect(response.failureCount).to.equal(2); + response.responses.forEach(resp => checkSendResponseFailure( + resp, 'app/invalid-credential')); + }); + }); + + it('should expose the FCM error code in a detailed error via BatchResponse', () => { + const messageIds = [ + 'projects/projec_id/messages/1', + ]; + const errors = [ + { + error: { + status: 'INVALID_ARGUMENT', + message: 'test error message', + details: [ + { + '@type': 'type.googleapis.com/google.firebase.fcm.v1.FcmError', + 'errorCode': 'UNREGISTERED', + }, + ], + }, + }, + ]; + messageIds.forEach(id => mockedRequests.push(mockSendRequest(id))) + errors.forEach(err => mockedRequests.push(mockSendError(400, 'json', err))) + return messaging.sendEachForMulticast({ tokens: ['a', 'b'] }) + .then((response: BatchResponse) => { + expect(response.successCount).to.equal(1); + expect(response.failureCount).to.equal(1); + expect(response.responses.length).to.equal(2); + + const responses = response.responses; + checkSendResponseSuccess(responses[0], messageIds[0]); + checkSendResponseFailure( + responses[1], 'messaging/registration-token-not-registered'); + }); + }); + + it('should map server error code to client-side error', () => { + const error = { + error: { + status: 'NOT_FOUND', + message: 'test error message', + } + }; + mockedRequests.push(mockSendError(404, 'json', error)); + mockedRequests.push(mockSendError(400, 'json', { error: 'test error message' })); + mockedRequests.push(mockSendError(400, 'text', 'foo bar')); + return messaging.sendEachForMulticast( + { tokens: ['a', 'a', 'a'] }, + ).then((response: BatchResponse) => { + expect(response.failureCount).to.equal(3); + const responses = response.responses; + checkSendResponseFailure(responses[0], 'messaging/registration-token-not-registered'); + checkSendResponseFailure(responses[1], 'messaging/unknown-error'); + checkSendResponseFailure(responses[2], 'messaging/invalid-argument'); + }); + }); + + function checkSendResponseSuccess(response: SendResponse, messageId: string): void { + expect(response.success).to.be.true; + expect(response.messageId).to.equal(messageId); + expect(response.error).to.be.undefined; + } + + function checkSendResponseFailure(response: SendResponse, code: string, msg?: string): void { + expect(response.success).to.be.false; + expect(response.messageId).to.be.undefined; + expect(response.error).to.have.property('code', code); + if (msg) { + expect(response.error!.toString()).to.contain(msg); + } + } + }); + describe('sendAll()', () => { const validMessage: Message = { token: 'a' }; diff --git a/test/unit/storage/index.spec.ts b/test/unit/storage/index.spec.ts index 8251207677..7924f8a61c 100644 --- a/test/unit/storage/index.spec.ts +++ b/test/unit/storage/index.spec.ts @@ -19,11 +19,13 @@ import * as chai from 'chai'; import * as sinonChai from 'sinon-chai'; +import { createSandbox, SinonSandbox } from 'sinon'; import * as chaiAsPromised from 'chai-as-promised'; import * as mocks from '../../resources/mocks'; import { App } from '../../../src/app/index'; -import { getStorage, Storage } from '../../../src/storage/index'; +import * as StorageUtils from '../../../src/storage/utils'; +import { getStorage, Storage, getDownloadURL } from '../../../src/storage/index'; chai.should(); chai.use(sinonChai); @@ -35,13 +37,19 @@ describe('Storage', () => { let mockApp: App; let mockCredentialApp: App; - const noProjectIdError = 'Failed to initialize Google Cloud Storage client with the ' - + 'available credential. Must initialize the SDK with a certificate credential or ' - + 'application default credentials to use Cloud Storage API.'; + const noProjectIdError = + 'Failed to initialize Google Cloud Storage client with the ' + + 'available credential. Must initialize the SDK with a certificate credential or ' + + 'application default credentials to use Cloud Storage API.'; + let sandbox: SinonSandbox; beforeEach(() => { mockApp = mocks.app(); mockCredentialApp = mocks.mockCredentialApp(); + sandbox = createSandbox(); + }); + afterEach(() => { + sandbox.restore(); }); describe('getStorage()', () => { @@ -69,5 +77,72 @@ describe('Storage', () => { const storage2: Storage = getStorage(mockApp); expect(storage1).to.equal(storage2); }); + + it('should return an error when no metadata is available', async () => { + sandbox + .stub(StorageUtils, 'getFirebaseMetadata') + .returns(Promise.resolve({} as StorageUtils.FirebaseMetadata)); + const storage1 = getStorage(mockApp); + const fileRef = storage1.bucket('gs://mock').file('abc'); + await expect(getDownloadURL(fileRef)).to.be.rejectedWith( + 'No download token available. Please create one in the Firebase Console.' + ); + }); + + it('should return an error when unable to fetch metadata', async () => { + const error = new Error('Could not get metadata'); + sandbox + .stub(StorageUtils, 'getFirebaseMetadata') + .returns(Promise.reject(error)); + const storage1 = getStorage(mockApp); + const fileRef = storage1.bucket('gs://mock').file('abc'); + await expect(getDownloadURL(fileRef)).to.be.rejectedWith( + error + ); + }); + it('should return the proper download url when metadata is available', async () => { + const downloadTokens = ['abc', 'def']; + sandbox + .stub(StorageUtils, 'getFirebaseMetadata') + .returns( + Promise.resolve({ + downloadTokens: downloadTokens.join(','), + } as StorageUtils.FirebaseMetadata) + ); + const storage1 = getStorage(mockApp); + const fileRef = storage1.bucket('gs://mock').file('abc'); + await expect(getDownloadURL(fileRef)).to.eventually.eq( + `https://firebasestorage.googleapis.com/v0/b/${fileRef.bucket.name}/o/${encodeURIComponent(fileRef.name)}?alt=media&token=${downloadTokens[0]}` + ); + }); + it('should use the emulator host name when either envs are set', async () => { + const HOST = 'localhost:9091'; + const envsToCheck = [ + { envName: 'FIREBASE_STORAGE_EMULATOR_HOST', value: HOST }, + { envName: 'STORAGE_EMULATOR_HOST', value: `http://${HOST}` }, + ]; + const downloadTokens = ['abc', 'def']; + sandbox.stub(StorageUtils, 'getFirebaseMetadata').returns( + Promise.resolve({ + downloadTokens: downloadTokens.join(','), + } as StorageUtils.FirebaseMetadata) + ); + for (const { envName, value } of envsToCheck) { + + delete process.env.STORAGE_EMULATOR_HOST; + delete process.env[envName]; + process.env[envName] = value; + + // Need to create a new mock app to force `getStorage`'s checking of env vars. + const storage1 = getStorage(mocks.app(envName)); + const fileRef = storage1.bucket('gs://mock').file('abc'); + await expect(getDownloadURL(fileRef)).to.eventually.eq( + `http://${HOST}/v0/b/${fileRef.bucket.name}/o/${encodeURIComponent( + fileRef.name + )}?alt=media&token=${downloadTokens[0]}` + ); + delete process.env[envName]; + } + }); }); });