Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make UpdateData type Generic to fix lack of type safety when using withConverter() #1448

Closed
JamieCurnow opened this issue Mar 12, 2021 · 15 comments
Assignees
Labels
api: firestore Issues related to the googleapis/nodejs-firestore API. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.

Comments

@JamieCurnow
Copy link

Is your feature request related to a problem? Please describe.
This package works great with Typescript when using the .withConverter method and enforces correct types when performing a get() or set() or onSnapshot(), but fails to enforce types on the update() method.

With TS v4.1 we can now safely enforce that dot-notation strings as object keys are correct, so we can do better than:

export type UpdateData = {[fieldPath: string]: any};

Check it out...

import { firestore } from "firebase-admin"

interface User {
  name: string
  email: string
  address: {
    line1: string
    line2: string
    postcode: string
    verified: boolean
    timeAtAddress: {
      days: string
      months: string
      hours: string
    }
  }
}

// This helper function pipes my types through the converter
const converter = <T>() => ({
  toFirestore: (data: Partial<T>) => data,
  fromFirestore: (snap: FirebaseFirestore.QueryDocumentSnapshot) => snap.data() as T
})

const getUser = async () => {
  const userDoc = await firestore().collection('user').withConverter(converter<User>()).doc('1234').get()
  if (!userDoc.exists) return null
  const userData = userDoc.data()
  console.log(userData.email) // nice!
  console.log(userData.unknownKey)
  // TS Error: Property 'unknownKey' does not exist on type 'User'.
  // Great!
}

const setUser = async () => {
  await firestore().collection('user').withConverter(converter<User>()).doc('1234').set({
    email: '',
    name: ''
  })
  // TS Error: Property 'address' is missing in type '{ email: string; name: string; }' but required in type 'User'
  // Great!
}

const setUserMerged = async () => {
  await firestore().collection('user').withConverter(converter<User>()).doc('1234').set({
    email: '',
    name: '',
    unknownKey: ''
  }, { merge: true })
  // TS Error: Object literal may only specify known properties, and 'unknownKey' does not exist in type 'Partial<User>'.
  // Great!
}

const updateUserUnsafe = async () => {
  await firestore().collection('user').withConverter(converter<User>()).doc('1234').update({
    unknownKey: '', // no error here
    'anything.I.like': true // no type error
  })
}

// Here's how we could do an update with type safety:

type PathImpl<T, K extends keyof T> =
  K extends string
  ? T[K] extends Record<string, any>
  ? T[K] extends ArrayLike<any>
  ? K | `${K}.${PathImpl<T[K], Exclude<keyof T[K], keyof any[]>>}`
  : K | `${K}.${PathImpl<T[K], keyof T[K]>}`
  : K
  : never

type Path<T> = PathImpl<T, keyof T> | keyof T

type PathValue<T, P extends Path<T>> =
  P extends `${infer K}.${infer Rest}`
  ? K extends keyof T
  ? Rest extends Path<T[K]>
  ? PathValue<T[K], Rest>
  : never
  : never
  : P extends keyof T
  ? T[P]
  : never

type CustomUpdateData<T extends object> = Partial<{
  [TKey in Path<T>]: PathValue<T, TKey>
}>

const updateUserSafe = async () => {
  // Here I make the object first which provides some safety
  const updatesObject: CustomUpdateData<User> = {
    name: '',
    'address.timeAtAddress.days': '',
    'some.unknown.path': false,
    // Object literal may only specify known properties, and ''some.unknown.path'' does not exist in type 'Partial<CustomUpdateData<User>>'
    'address.verified': 'Oops! Wrong value type!'
    // Type 'string' is not assignable to type 'boolean'
  }

  await firestore().collection('user').withConverter(converter<User>()).doc('1234').update(updatesObject)
}

Describe the solution you'd like
I propose that the helper types for creating a type that is able to handle object paths should be added to FirebaseFirestore.UpdateData. I think that a solution could look like this:

// @google-cloud/firestore/types/firestore.d.ts

type PathImpl<T, K extends keyof T> =
  K extends string
  ? T[K] extends Record<string, any>
  ? T[K] extends ArrayLike<any>
  ? K | `${K}.${PathImpl<T[K], Exclude<keyof T[K], keyof any[]>>}`
  : K | `${K}.${PathImpl<T[K], keyof T[K]>}`
  : K
  : never

type Path<T> = PathImpl<T, keyof T> | keyof T

type PathValue<T, P extends Path<T>> =
  P extends `${infer K}.${infer Rest}`
  ? K extends keyof T
  ? Rest extends Path<T[K]>
  ? PathValue<T[K], Rest>
  : never
  : never
  : P extends keyof T
  ? T[P]
  : never
  
declare namespace FirebaseFirestore {
  export type DocumentData = { [field: string]: any }

  export type UpdateData<T = DocumentData> = Partial<{
    [TKey in Path<T>]: PathValue<T, TKey>
  }>

  // ...
  
  export class DocumentReference<T = DocumentData> {
    // ...
    update(data: UpdateData<T>, precondition?: Precondition): Promise<WriteResult>
  }
}

Which would allow type safety like this:

const updateSafe = async () => {
  await firestore().collection('user').withConverter(converter<User>()).doc('1234').update({
    name: '',
    'address.line1': '',
    'some.unknown.path': false,
    // Object literal may only specify known properties, and ''some.unknown.path'' does not exist in type 'Partial<CustomUpdateData<User>>'
    'address.verified': 'Oops! Wrong value type!'
    // Type 'string' is not assignable to type 'boolean'
  })
}

Additional context
Proposed solution in action:
Screenshot 2021-03-12 at 12 42 17

@product-auto-label product-auto-label bot added the api: firestore Issues related to the googleapis/nodejs-firestore API. label Mar 12, 2021
@JamieCurnow
Copy link
Author

This would require typescript to be updated from 3.8.3 to at least 4.1.0 which will probably be a block.

@schmidt-sebastian schmidt-sebastian added the type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. label Mar 12, 2021
@schmidt-sebastian
Copy link
Contributor

Thanks for suggesting this. We are considering this for our next breaking change release (based on a similar suggestion here: firebase/firebase-js-sdk#4277)

@rgant
Copy link

rgant commented Dec 20, 2021

I think maybe this is causing a problem conflict between firebase-admin@10.0.1 and @google-cloud/firestore@5.0.1.

node_modules/@google-cloud/firestore/types/firestore.d.ts:23:1 - error TS6200: Definitions of the following identifiers conflict with those in another file: DocumentData, UpdateData, Firestore, GeoPoint, Transaction, BulkWriter, BulkWriterError, WriteBatch, SetOptions, WriteResult, DocumentReference, DocumentSnapshot, QueryDocumentSnapshot, OrderByDirection, WhereFilterOp, Query, QuerySnapshot, DocumentChangeType, CollectionReference, CollectionGroup, QueryPartition, FieldValue, FieldPath, Timestamp, BundleBuilder, v1beta1, v1, OK, CANCELLED, UNKNOWN, INVALID_ARGUMENT, DEADLINE_EXCEEDED, NOT_FOUND, ALREADY_EXISTS, PERMISSION_DENIED, RESOURCE_EXHAUSTED, FAILED_PRECONDITION, ABORTED, OUT_OF_RANGE, UNIMPLEMENTED, INTERNAL, UNAVAILABLE, DATA_LOSS, UNAUTHENTICATED, FirebaseFirestore

23 declare namespace FirebaseFirestore {
   ~~~~~~~

  node_modules/firebase-admin/node_modules/@google-cloud/firestore/types/firestore.d.ts:23:1
    23 declare namespace FirebaseFirestore {
       ~~~~~~~
    Conflicts are in this file.

node_modules/@google-cloud/firestore/types/firestore.d.ts:71:25 - error TS2315: Type 'UpdateData' is not generic.

71     ? {[K in keyof T]?: UpdateData<T[K]> | FieldValue} & NestedUpdateFields<T>
                           ~~~~~~~~~~~~~~~~

node_modules/@google-cloud/firestore/types/firestore.d.ts:104:28 - error TS2315: Type 'UpdateData' is not generic.

104         AddPrefixToKeys<K, UpdateData<V>>
                               ~~~~~~~~~~~~~

node_modules/@google-cloud/firestore/types/firestore.d.ts:282:5 - error TS2374: Duplicate index signature for type 'string'.

282     [key: string]: any; // Accept other properties, such as GRPC settings.
        ~~~~~~~~~~~~~~~~~~~

node_modules/@google-cloud/firestore/types/firestore.d.ts:644:13 - error TS2315: Type 'UpdateData' is not generic.

644       data: UpdateData<T>,
                ~~~~~~~~~~~~~

node_modules/@google-cloud/firestore/types/firestore.d.ts:789:13 - error TS2315: Type 'UpdateData' is not generic.

789       data: UpdateData<T>,
                ~~~~~~~~~~~~~

node_modules/@google-cloud/firestore/types/firestore.d.ts:999:13 - error TS2315: Type 'UpdateData' is not generic.

999       data: UpdateData<T>,
                ~~~~~~~~~~~~~

node_modules/@google-cloud/firestore/types/firestore.d.ts:1209:13 - error TS2315: Type 'UpdateData' is not generic.

1209       data: UpdateData<T>,
                 ~~~~~~~~~~~~~

node_modules/firebase-admin/node_modules/@google-cloud/firestore/types/firestore.d.ts:23:1 - error TS6200: Definitions of the following identifiers conflict with those in another file: DocumentData, UpdateData, Firestore, GeoPoint, Transaction, BulkWriter, BulkWriterError, WriteBatch, SetOptions, WriteResult, DocumentReference, DocumentSnapshot, QueryDocumentSnapshot, OrderByDirection, WhereFilterOp, Query, QuerySnapshot, DocumentChangeType, CollectionReference, CollectionGroup, QueryPartition, FieldValue, FieldPath, Timestamp, BundleBuilder, v1beta1, v1, OK, CANCELLED, UNKNOWN, INVALID_ARGUMENT, DEADLINE_EXCEEDED, NOT_FOUND, ALREADY_EXISTS, PERMISSION_DENIED, RESOURCE_EXHAUSTED, FAILED_PRECONDITION, ABORTED, OUT_OF_RANGE, UNIMPLEMENTED, INTERNAL, UNAVAILABLE, DATA_LOSS, UNAUTHENTICATED, FirebaseFirestore

23 declare namespace FirebaseFirestore {
   ~~~~~~~

  node_modules/@google-cloud/firestore/types/firestore.d.ts:23:1
    23 declare namespace FirebaseFirestore {
       ~~~~~~~
    Conflicts are in this file.

node_modules/firebase-admin/node_modules/@google-cloud/firestore/types/firestore.d.ts:174:5 - error TS2374: Duplicate index signature for type 'string'.

174     [key: string]: any; // Accept other properties, such as GRPC settings.
        ~~~~~~~~~~~~~~~~~~~


Found 10 errors.

Seems like the TypeScript version for this package is already updated to support this.

It always surprises me that all the different node Firebase packages have different implementations of the types for Firebase...

@schmidt-sebastian
Copy link
Contributor

@rgant Your error suggests that you are using two different versions of Firestore. Can you confirm whether your build is using more than one version?

Unrelated to this, since we published 5.x, this issue can now be closed. We have updated our converter code to enforce type safety for update() calls.

@rgant
Copy link

rgant commented Dec 21, 2021

I believe these are the latest versions of both tools:

firebase-admin@10.0.1 - For Firebase Functions to access Firestore. (https://firebase.google.com/docs/functions/get-started)

@google-cloud/firestore@5.0.1 - To setup a scheduled backup of Firestore. (https://firebase.google.com/docs/firestore/solutions/schedule-export)

Both as per the documentation best I can tell. Seems odd these two tools aren't in sync with the types.

Any advice where I should post this issue? To my eye, @google-cloud/firestore@5 types are less useful compared to firebase-admin@10.

@schmidt-sebastian
Copy link
Contributor

Can you post the output of "npm list"?

@rgant
Copy link

rgant commented Dec 23, 2021

Click to expand:

`npm list`
learnlux-cloud-firestore@1.0.5 /Users/rgant/Programming/LearnLux/firestore-data
├─┬ @firebase/rules-unit-testing@2.0.1
│ └── node-fetch@2.6.2
├─┬ @google-cloud/firestore@5.0.1
│ ├── fast-deep-equal@3.1.3
│ ├── functional-red-black-tree@1.0.1
│ ├─┬ google-gax@2.28.1
│ │ ├─┬ @grpc/grpc-js@1.4.5
│ │ │ ├── @grpc/proto-loader@0.6.7 deduped
│ │ │ └── @types/node@14.18.2 deduped
│ │ ├─┬ @grpc/proto-loader@0.6.7
│ │ │ ├── @types/long@4.0.1 deduped
│ │ │ ├── lodash.camelcase@4.3.0
│ │ │ ├── long@4.0.0 deduped
│ │ │ ├── protobufjs@6.11.2 deduped
│ │ │ └─┬ yargs@16.2.0
│ │ │   ├─┬ cliui@7.0.4
│ │ │   │ ├── string-width@4.2.3 deduped
│ │ │   │ ├── strip-ansi@6.0.1 deduped
│ │ │   │ └─┬ wrap-ansi@7.0.0
│ │ │   │   ├── ansi-styles@4.3.0 deduped
│ │ │   │   ├── string-width@4.2.3 deduped
│ │ │   │   └── strip-ansi@6.0.1 deduped
│ │ │   ├── escalade@3.1.1
│ │ │   ├── get-caller-file@2.0.5
│ │ │   ├── require-directory@2.1.1
│ │ │   ├─┬ string-width@4.2.3
│ │ │   │ ├── emoji-regex@8.0.0
│ │ │   │ ├── is-fullwidth-code-point@3.0.0
│ │ │   │ └── strip-ansi@6.0.1 deduped
│ │ │   ├── y18n@5.0.8
│ │ │   └── yargs-parser@20.2.9
│ │ ├── @types/long@4.0.1
│ │ ├─┬ abort-controller@3.0.0
│ │ │ └── event-target-shim@5.0.1
│ │ ├─┬ duplexify@4.1.2
│ │ │ ├─┬ end-of-stream@1.4.4
│ │ │ │ └── once@1.4.0 deduped
│ │ │ ├── inherits@2.0.4 deduped
│ │ │ ├─┬ readable-stream@3.6.0
│ │ │ │ ├── inherits@2.0.4 deduped
│ │ │ │ ├─┬ string_decoder@1.3.0
│ │ │ │ │ └── safe-buffer@5.2.1 deduped
│ │ │ │ └── util-deprecate@1.0.2
│ │ │ └── stream-shift@1.0.1
│ │ ├── fast-text-encoding@1.0.3
│ │ ├─┬ google-auth-library@7.11.0
│ │ │ ├── arrify@2.0.1 deduped
│ │ │ ├── base64-js@1.5.1
│ │ │ ├─┬ ecdsa-sig-formatter@1.0.11
│ │ │ │ └── safe-buffer@5.2.1 deduped
│ │ │ ├── fast-text-encoding@1.0.3 deduped
│ │ │ ├─┬ gaxios@4.3.2
│ │ │ │ ├── abort-controller@3.0.0 deduped
│ │ │ │ ├── extend@3.0.2 deduped
│ │ │ │ ├─┬ https-proxy-agent@5.0.0
│ │ │ │ │ ├─┬ agent-base@6.0.2
│ │ │ │ │ │ └── debug@4.3.3 deduped
│ │ │ │ │ └── debug@4.3.3 deduped
│ │ │ │ ├── is-stream@2.0.1
│ │ │ │ └── node-fetch@2.6.6 deduped
│ │ │ ├─┬ gcp-metadata@4.3.1
│ │ │ │ ├── gaxios@4.3.2 deduped
│ │ │ │ └─┬ json-bigint@1.0.0
│ │ │ │   └── bignumber.js@9.0.2
│ │ │ ├─┬ gtoken@5.3.1
│ │ │ │ ├── gaxios@4.3.2 deduped
│ │ │ │ ├─┬ google-p12-pem@3.1.2
│ │ │ │ │ └── node-forge@0.10.0 deduped
│ │ │ │ └── jws@4.0.0 deduped
│ │ │ ├─┬ jws@4.0.0
│ │ │ │ ├─┬ jwa@2.0.0
│ │ │ │ │ ├── buffer-equal-constant-time@1.0.1 deduped
│ │ │ │ │ ├── ecdsa-sig-formatter@1.0.11 deduped
│ │ │ │ │ └── safe-buffer@5.2.1 deduped
│ │ │ │ └── safe-buffer@5.2.1 deduped
│ │ │ └── lru-cache@6.0.0 deduped
│ │ ├── is-stream-ended@0.1.4
│ │ ├── node-fetch@2.6.6 deduped
│ │ ├── object-hash@2.2.0
│ │ ├─┬ proto3-json-serializer@0.1.6
│ │ │ └── protobufjs@6.11.2 deduped
│ │ ├── protobufjs@6.11.2 deduped
│ │ └─┬ retry-request@4.2.2
│ │   ├── debug@4.3.3 deduped
│ │   └── extend@3.0.2 deduped
│ └─┬ protobufjs@6.11.2
│   ├── @protobufjs/aspromise@1.1.2
│   ├── @protobufjs/base64@1.1.2
│   ├── @protobufjs/codegen@2.0.4
│   ├── @protobufjs/eventemitter@1.1.0
│   ├─┬ @protobufjs/fetch@1.1.0
│   │ ├── @protobufjs/aspromise@1.1.2 deduped
│   │ └── @protobufjs/inquire@1.1.0 deduped
│   ├── @protobufjs/float@1.0.2
│   ├── @protobufjs/inquire@1.1.0
│   ├── @protobufjs/path@1.1.2
│   ├── @protobufjs/pool@1.1.0
│   ├── @protobufjs/utf8@1.1.0
│   ├── @types/long@4.0.1 deduped
│   ├── @types/node@14.18.2 deduped
│   └── long@4.0.0
├── @types/jasmine@3.10.2
├── @types/node@14.18.2
├─┬ @types/node-fetch@2.5.12
│ ├── @types/node@14.18.2 deduped
│ └─┬ form-data@3.0.1
│   ├── asynckit@0.4.0
│   ├─┬ combined-stream@1.0.8
│   │ └── delayed-stream@1.0.0
│   └─┬ mime-types@2.1.34
│     └── mime-db@1.51.0
├─┬ @typescript-eslint/eslint-plugin@5.8.0
│ ├─┬ @typescript-eslint/experimental-utils@5.8.0
│ │ ├── @types/json-schema@7.0.9
│ │ ├── @typescript-eslint/scope-manager@5.8.0 deduped
│ │ ├── @typescript-eslint/types@5.8.0 deduped
│ │ ├── @typescript-eslint/typescript-estree@5.8.0 deduped
│ │ ├─┬ eslint-scope@5.1.1
│ │ │ ├── esrecurse@4.3.0 deduped
│ │ │ └── estraverse@4.3.0
│ │ └── eslint-utils@3.0.0 deduped
│ ├─┬ @typescript-eslint/scope-manager@5.8.0
│ │ ├── @typescript-eslint/types@5.8.0 deduped
│ │ └─┬ @typescript-eslint/visitor-keys@5.8.0
│ │   ├── @typescript-eslint/types@5.8.0 deduped
│ │   └── eslint-visitor-keys@3.1.0 deduped
│ ├─┬ debug@4.3.3
│ │ └── ms@2.1.2
│ ├── functional-red-black-tree@1.0.1 deduped
│ ├── ignore@5.2.0
│ ├── regexpp@3.2.0
│ ├─┬ semver@7.3.5
│ │ └─┬ lru-cache@6.0.0
│ │   └── yallist@4.0.0
│ └─┬ tsutils@3.21.0
│   └── tslib@1.14.1
├─┬ @typescript-eslint/eslint-plugin-tslint@5.8.0
│ ├── @typescript-eslint/experimental-utils@5.8.0 deduped
│ └── lodash@4.17.21
├─┬ @typescript-eslint/parser@5.8.0
│ ├── @typescript-eslint/scope-manager@5.8.0 deduped
│ ├── @typescript-eslint/types@5.8.0
│ ├─┬ @typescript-eslint/typescript-estree@5.8.0
│ │ ├── @typescript-eslint/types@5.8.0 deduped
│ │ ├── @typescript-eslint/visitor-keys@5.8.0 deduped
│ │ ├── debug@4.3.3 deduped
│ │ ├─┬ globby@11.0.4
│ │ │ ├── array-union@2.1.0
│ │ │ ├─┬ dir-glob@3.0.1
│ │ │ │ └── path-type@4.0.0
│ │ │ ├─┬ fast-glob@3.2.7
│ │ │ │ ├── @nodelib/fs.stat@2.0.5
│ │ │ │ ├─┬ @nodelib/fs.walk@1.2.8
│ │ │ │ │ ├─┬ @nodelib/fs.scandir@2.1.5
│ │ │ │ │ │ ├── @nodelib/fs.stat@2.0.5 deduped
│ │ │ │ │ │ └─┬ run-parallel@1.2.0
│ │ │ │ │ │   └── queue-microtask@1.2.3
│ │ │ │ │ └─┬ fastq@1.13.0
│ │ │ │ │   └── reusify@1.0.4
│ │ │ │ ├─┬ glob-parent@5.1.2
│ │ │ │ │ └── is-glob@4.0.3 deduped
│ │ │ │ ├── merge2@1.4.1 deduped
│ │ │ │ └─┬ micromatch@4.0.4
│ │ │ │   ├─┬ braces@3.0.2
│ │ │ │   │ └─┬ fill-range@7.0.1
│ │ │ │   │   └─┬ to-regex-range@5.0.1
│ │ │ │   │     └── is-number@7.0.0
│ │ │ │   └── picomatch@2.3.0
│ │ │ ├── ignore@5.2.0 deduped
│ │ │ ├── merge2@1.4.1
│ │ │ └── slash@3.0.0
│ │ ├── is-glob@4.0.3 deduped
│ │ ├─┬ semver@7.3.5
│ │ │ └── lru-cache@6.0.0 deduped
│ │ └── tsutils@3.21.0 deduped
│ └── debug@4.3.3 deduped
├─┬ eslint@8.5.0
│ ├─┬ @eslint/eslintrc@1.0.5
│ │ ├── ajv@6.12.6 deduped
│ │ ├── debug@4.3.3 deduped
│ │ ├── espree@9.2.0 deduped
│ │ ├── globals@13.12.0 deduped
│ │ ├── ignore@4.0.6
│ │ ├── import-fresh@3.3.0 deduped
│ │ ├── js-yaml@4.1.0 deduped
│ │ ├── minimatch@3.0.4 deduped
│ │ └── strip-json-comments@3.1.1 deduped
│ ├─┬ @humanwhocodes/config-array@0.9.2
│ │ ├── @humanwhocodes/object-schema@1.2.1
│ │ ├── debug@4.3.3 deduped
│ │ └── minimatch@3.0.4 deduped
│ ├─┬ ajv@6.12.6
│ │ ├── fast-deep-equal@3.1.3 deduped
│ │ ├── fast-json-stable-stringify@2.1.0
│ │ ├── json-schema-traverse@0.4.1
│ │ └─┬ uri-js@4.4.1
│ │   └── punycode@2.1.1
│ ├─┬ chalk@4.1.2
│ │ ├─┬ ansi-styles@4.3.0
│ │ │ └─┬ color-convert@2.0.1
│ │ │   └── color-name@1.1.4
│ │ └─┬ supports-color@7.2.0
│ │   └── has-flag@4.0.0
│ ├─┬ cross-spawn@7.0.3
│ │ ├── path-key@3.1.1
│ │ ├─┬ shebang-command@2.0.0
│ │ │ └── shebang-regex@3.0.0
│ │ └─┬ which@2.0.2
│ │   └── isexe@2.0.0
│ ├── debug@4.3.3 deduped
│ ├─┬ doctrine@3.0.0
│ │ └── esutils@2.0.3 deduped
│ ├─┬ enquirer@2.3.6
│ │ └── ansi-colors@4.1.1
│ ├── escape-string-regexp@4.0.0
│ ├─┬ eslint-scope@7.1.0
│ │ ├─┬ esrecurse@4.3.0
│ │ │ └── estraverse@5.3.0
│ │ └── estraverse@5.3.0
│ ├─┬ eslint-utils@3.0.0
│ │ └── eslint-visitor-keys@2.1.0
│ ├── eslint-visitor-keys@3.1.0
│ ├─┬ espree@9.2.0
│ │ ├── acorn@8.6.0 deduped
│ │ ├── acorn-jsx@5.3.2
│ │ └── eslint-visitor-keys@3.1.0 deduped
│ ├─┬ esquery@1.4.0
│ │ └── estraverse@5.3.0
│ ├── esutils@2.0.3
│ ├── fast-deep-equal@3.1.3 deduped
│ ├─┬ file-entry-cache@6.0.1
│ │ └─┬ flat-cache@3.0.4
│ │   ├── flatted@3.2.4
│ │   └─┬ rimraf@3.0.2
│ │     └── glob@7.2.0 deduped
│ ├── functional-red-black-tree@1.0.1 deduped
│ ├─┬ glob-parent@6.0.2
│ │ └── is-glob@4.0.3 deduped
│ ├─┬ globals@13.12.0
│ │ └── type-fest@0.20.2
│ ├── ignore@4.0.6
│ ├─┬ import-fresh@3.3.0
│ │ ├─┬ parent-module@1.0.1
│ │ │ └── callsites@3.1.0
│ │ └── resolve-from@4.0.0
│ ├── imurmurhash@0.1.4
│ ├─┬ is-glob@4.0.3
│ │ └── is-extglob@2.1.1
│ ├─┬ js-yaml@4.1.0
│ │ └── argparse@2.0.1
│ ├── json-stable-stringify-without-jsonify@1.0.1
│ ├─┬ levn@0.4.1
│ │ ├── prelude-ls@1.2.1
│ │ └─┬ type-check@0.4.0
│ │   └── prelude-ls@1.2.1 deduped
│ ├── lodash.merge@4.6.2
│ ├─┬ minimatch@3.0.4
│ │ └─┬ brace-expansion@1.1.11
│ │   ├── balanced-match@1.0.2
│ │   └── concat-map@0.0.1
│ ├── natural-compare@1.4.0
│ ├─┬ optionator@0.9.1
│ │ ├── deep-is@0.1.4
│ │ ├── fast-levenshtein@2.0.6
│ │ ├── levn@0.4.1 deduped
│ │ ├── prelude-ls@1.2.1 deduped
│ │ ├── type-check@0.4.0 deduped
│ │ └── word-wrap@1.2.3
│ ├── progress@2.0.3
│ ├── regexpp@3.2.0 deduped
│ ├─┬ semver@7.3.5
│ │ └── lru-cache@6.0.0 deduped
│ ├─┬ strip-ansi@6.0.1
│ │ └── ansi-regex@5.0.1
│ ├── strip-json-comments@3.1.1
│ ├── text-table@0.2.0
│ └── v8-compile-cache@2.3.0
├─┬ eslint-import-resolver-typescript@2.5.0
│ ├── debug@4.3.3 deduped
│ ├─┬ glob@7.2.0
│ │ ├── fs.realpath@1.0.0
│ │ ├─┬ inflight@1.0.6
│ │ │ ├── once@1.4.0 deduped
│ │ │ └── wrappy@1.0.2
│ │ ├── inherits@2.0.4
│ │ ├── minimatch@3.0.4 deduped
│ │ ├─┬ once@1.4.0
│ │ │ └── wrappy@1.0.2 deduped
│ │ └── path-is-absolute@1.0.1
│ ├── is-glob@4.0.3 deduped
│ ├─┬ resolve@1.20.0
│ │ ├── is-core-module@2.8.0 deduped
│ │ └── path-parse@1.0.7
│ └── tsconfig-paths@3.12.0 deduped
├─┬ eslint-plugin-import@2.25.3
│ ├─┬ array-includes@3.1.4
│ │ ├─┬ call-bind@1.0.2
│ │ │ ├── function-bind@1.1.1 deduped
│ │ │ └── get-intrinsic@1.1.1 deduped
│ │ ├─┬ define-properties@1.1.3
│ │ │ └── object-keys@1.1.1
│ │ ├─┬ es-abstract@1.19.1
│ │ │ ├── call-bind@1.0.2 deduped
│ │ │ ├─┬ es-to-primitive@1.2.1
│ │ │ │ ├── is-callable@1.2.4 deduped
│ │ │ │ ├─┬ is-date-object@1.0.5
│ │ │ │ │ └── has-tostringtag@1.0.0 deduped
│ │ │ │ └─┬ is-symbol@1.0.4
│ │ │ │   └── has-symbols@1.0.2 deduped
│ │ │ ├── function-bind@1.1.1 deduped
│ │ │ ├── get-intrinsic@1.1.1 deduped
│ │ │ ├─┬ get-symbol-description@1.0.0
│ │ │ │ ├── call-bind@1.0.2 deduped
│ │ │ │ └── get-intrinsic@1.1.1 deduped
│ │ │ ├── has@1.0.3 deduped
│ │ │ ├── has-symbols@1.0.2
│ │ │ ├─┬ internal-slot@1.0.3
│ │ │ │ ├── get-intrinsic@1.1.1 deduped
│ │ │ │ ├── has@1.0.3 deduped
│ │ │ │ └─┬ side-channel@1.0.4
│ │ │ │   ├── call-bind@1.0.2 deduped
│ │ │ │   ├── get-intrinsic@1.1.1 deduped
│ │ │ │   └── object-inspect@1.12.0 deduped
│ │ │ ├── is-callable@1.2.4
│ │ │ ├── is-negative-zero@2.0.2
│ │ │ ├─┬ is-regex@1.1.4
│ │ │ │ ├── call-bind@1.0.2 deduped
│ │ │ │ └── has-tostringtag@1.0.0 deduped
│ │ │ ├── is-shared-array-buffer@1.0.1
│ │ │ ├── is-string@1.0.7 deduped
│ │ │ ├─┬ is-weakref@1.0.2
│ │ │ │ └── call-bind@1.0.2 deduped
│ │ │ ├── object-inspect@1.12.0
│ │ │ ├── object-keys@1.1.1 deduped
│ │ │ ├─┬ object.assign@4.1.2
│ │ │ │ ├── call-bind@1.0.2 deduped
│ │ │ │ ├── define-properties@1.1.3 deduped
│ │ │ │ ├── has-symbols@1.0.2 deduped
│ │ │ │ └── object-keys@1.1.1 deduped
│ │ │ ├─┬ string.prototype.trimend@1.0.4
│ │ │ │ ├── call-bind@1.0.2 deduped
│ │ │ │ └── define-properties@1.1.3 deduped
│ │ │ ├─┬ string.prototype.trimstart@1.0.4
│ │ │ │ ├── call-bind@1.0.2 deduped
│ │ │ │ └── define-properties@1.1.3 deduped
│ │ │ └─┬ unbox-primitive@1.0.1
│ │ │   ├── function-bind@1.1.1 deduped
│ │ │   ├── has-bigints@1.0.1
│ │ │   ├── has-symbols@1.0.2 deduped
│ │ │   └─┬ which-boxed-primitive@1.0.2
│ │ │     ├─┬ is-bigint@1.0.4
│ │ │     │ └── has-bigints@1.0.1 deduped
│ │ │     ├─┬ is-boolean-object@1.1.2
│ │ │     │ ├── call-bind@1.0.2 deduped
│ │ │     │ └── has-tostringtag@1.0.0 deduped
│ │ │     ├─┬ is-number-object@1.0.6
│ │ │     │ └── has-tostringtag@1.0.0 deduped
│ │ │     ├── is-string@1.0.7 deduped
│ │ │     └── is-symbol@1.0.4 deduped
│ │ ├─┬ get-intrinsic@1.1.1
│ │ │ ├── function-bind@1.1.1 deduped
│ │ │ ├── has@1.0.3 deduped
│ │ │ └── has-symbols@1.0.2 deduped
│ │ └─┬ is-string@1.0.7
│ │   └─┬ has-tostringtag@1.0.0
│ │     └── has-symbols@1.0.2 deduped
│ ├─┬ array.prototype.flat@1.2.5
│ │ ├── call-bind@1.0.2 deduped
│ │ ├── define-properties@1.1.3 deduped
│ │ └── es-abstract@1.19.1 deduped
│ ├─┬ debug@2.6.9
│ │ └── ms@2.0.0
│ ├─┬ doctrine@2.1.0
│ │ └── esutils@2.0.3 deduped
│ ├─┬ eslint-import-resolver-node@0.3.6
│ │ ├─┬ debug@3.2.7
│ │ │ └── ms@2.1.2 deduped
│ │ └── resolve@1.20.0 deduped
│ ├─┬ eslint-module-utils@2.7.1
│ │ ├─┬ debug@3.2.7
│ │ │ └── ms@2.1.2 deduped
│ │ ├─┬ find-up@2.1.0
│ │ │ └─┬ locate-path@2.0.0
│ │ │   ├─┬ p-locate@2.0.0
│ │ │   │ └─┬ p-limit@1.3.0
│ │ │   │   └── p-try@1.0.0
│ │ │   └── path-exists@3.0.0
│ │ └─┬ pkg-dir@2.0.0
│ │   └── find-up@2.1.0 deduped
│ ├─┬ has@1.0.3
│ │ └── function-bind@1.1.1
│ ├─┬ is-core-module@2.8.0
│ │ └── has@1.0.3 deduped
│ ├── is-glob@4.0.3 deduped
│ ├── minimatch@3.0.4 deduped
│ ├─┬ object.values@1.1.5
│ │ ├── call-bind@1.0.2 deduped
│ │ ├── define-properties@1.1.3 deduped
│ │ └── es-abstract@1.19.1 deduped
│ ├── resolve@1.20.0 deduped
│ └── tsconfig-paths@3.12.0 deduped
├─┬ eslint-plugin-jsdoc@37.4.0
│ ├─┬ @es-joy/jsdoccomment@0.13.0
│ │ ├── comment-parser@1.3.0 deduped
│ │ ├── esquery@1.4.0 deduped
│ │ └── jsdoc-type-pratt-parser@2.0.0 deduped
│ ├── comment-parser@1.3.0
│ ├── debug@4.3.3 deduped
│ ├── escape-string-regexp@4.0.0 deduped
│ ├── esquery@1.4.0 deduped
│ ├── jsdoc-type-pratt-parser@2.0.0
│ ├── regextras@0.8.0
│ ├─┬ semver@7.3.5
│ │ └── lru-cache@6.0.0 deduped
│ └─┬ spdx-expression-parse@3.0.1
│   ├── spdx-exceptions@2.3.0
│   └── spdx-license-ids@3.0.11
├── eslint-plugin-prefer-arrow@1.2.3
├── eslint-plugin-promise@6.0.0
├─┬ eslint-plugin-typescript-sort-keys@2.1.0
│ ├── @typescript-eslint/experimental-utils@5.8.0 deduped
│ ├── json-schema@0.4.0
│ └── natural-compare-lite@1.4.0
├─┬ eslint-plugin-unicorn@39.0.0
│ ├── @babel/helper-validator-identifier@7.15.7
│ ├── ci-info@3.3.0
│ ├─┬ clean-regexp@1.0.0
│ │ └── escape-string-regexp@1.0.5
│ ├─┬ eslint-template-visitor@2.3.2
│ │ ├─┬ @babel/core@7.16.5
│ │ │ ├── @babel/code-frame@7.16.0 deduped
│ │ │ ├─┬ @babel/generator@7.16.5
│ │ │ │ ├── @babel/types@7.16.0 deduped
│ │ │ │ ├── jsesc@2.5.2
│ │ │ │ └── source-map@0.5.7 deduped
│ │ │ ├─┬ @babel/helper-compilation-targets@7.16.3
│ │ │ │ ├── @babel/compat-data@7.16.4
│ │ │ │ ├── @babel/helper-validator-option@7.14.5
│ │ │ │ ├─┬ browserslist@4.19.1
│ │ │ │ │ ├── caniuse-lite@1.0.30001292
│ │ │ │ │ ├── electron-to-chromium@1.4.27
│ │ │ │ │ ├── escalade@3.1.1 deduped
│ │ │ │ │ ├── node-releases@2.0.1
│ │ │ │ │ └── picocolors@1.0.0
│ │ │ │ └── semver@6.3.0 deduped
│ │ │ ├─┬ @babel/helper-module-transforms@7.16.5
│ │ │ │ ├─┬ @babel/helper-environment-visitor@7.16.5
│ │ │ │ │ └── @babel/types@7.16.0 deduped
│ │ │ │ ├─┬ @babel/helper-module-imports@7.16.0
│ │ │ │ │ └── @babel/types@7.16.0 deduped
│ │ │ │ ├─┬ @babel/helper-simple-access@7.16.0
│ │ │ │ │ └── @babel/types@7.16.0 deduped
│ │ │ │ ├─┬ @babel/helper-split-export-declaration@7.16.0
│ │ │ │ │ └── @babel/types@7.16.0 deduped
│ │ │ │ ├── @babel/helper-validator-identifier@7.15.7 deduped
│ │ │ │ ├── @babel/template@7.16.0 deduped
│ │ │ │ ├── @babel/traverse@7.16.5 deduped
│ │ │ │ └── @babel/types@7.16.0 deduped
│ │ │ ├─┬ @babel/helpers@7.16.5
│ │ │ │ ├── @babel/template@7.16.0 deduped
│ │ │ │ ├── @babel/traverse@7.16.5 deduped
│ │ │ │ └── @babel/types@7.16.0 deduped
│ │ │ ├── @babel/parser@7.16.6
│ │ │ ├─┬ @babel/template@7.16.0
│ │ │ │ ├── @babel/code-frame@7.16.0 deduped
│ │ │ │ ├── @babel/parser@7.16.6 deduped
│ │ │ │ └── @babel/types@7.16.0 deduped
│ │ │ ├─┬ @babel/traverse@7.16.5
│ │ │ │ ├── @babel/code-frame@7.16.0 deduped
│ │ │ │ ├── @babel/generator@7.16.5 deduped
│ │ │ │ ├── @babel/helper-environment-visitor@7.16.5 deduped
│ │ │ │ ├─┬ @babel/helper-function-name@7.16.0
│ │ │ │ │ ├─┬ @babel/helper-get-function-arity@7.16.0
│ │ │ │ │ │ └── @babel/types@7.16.0 deduped
│ │ │ │ │ ├── @babel/template@7.16.0 deduped
│ │ │ │ │ └── @babel/types@7.16.0 deduped
│ │ │ │ ├─┬ @babel/helper-hoist-variables@7.16.0
│ │ │ │ │ └── @babel/types@7.16.0 deduped
│ │ │ │ ├── @babel/helper-split-export-declaration@7.16.0 deduped
│ │ │ │ ├── @babel/parser@7.16.6 deduped
│ │ │ │ ├── @babel/types@7.16.0 deduped
│ │ │ │ ├── debug@4.3.3 deduped
│ │ │ │ └── globals@11.12.0
│ │ │ ├─┬ @babel/types@7.16.0
│ │ │ │ ├── @babel/helper-validator-identifier@7.15.7 deduped
│ │ │ │ └── to-fast-properties@2.0.0
│ │ │ ├─┬ convert-source-map@1.8.0
│ │ │ │ └── safe-buffer@5.1.2
│ │ │ ├── debug@4.3.3 deduped
│ │ │ ├── gensync@1.0.0-beta.2
│ │ │ ├─┬ json5@2.2.0
│ │ │ │ └── minimist@1.2.5 deduped
│ │ │ ├── semver@6.3.0
│ │ │ └── source-map@0.5.7
│ │ ├─┬ @babel/eslint-parser@7.16.5
│ │ │ ├── eslint-scope@5.1.1 deduped
│ │ │ ├── eslint-visitor-keys@2.1.0
│ │ │ └── semver@6.3.0 deduped
│ │ ├── eslint-visitor-keys@2.1.0
│ │ ├── esquery@1.4.0 deduped
│ │ └── multimap@1.1.0
│ ├── eslint-utils@3.0.0 deduped
│ ├── esquery@1.4.0 deduped
│ ├── indent-string@4.0.0
│ ├─┬ is-builtin-module@3.1.0
│ │ └── builtin-modules@3.2.0
│ ├── lodash@4.17.21 deduped
│ ├── pluralize@8.0.0
│ ├─┬ read-pkg-up@7.0.1
│ │ ├─┬ find-up@4.1.0
│ │ │ ├─┬ locate-path@5.0.0
│ │ │ │ └─┬ p-locate@4.1.0
│ │ │ │   └─┬ p-limit@2.3.0
│ │ │ │     └── p-try@2.2.0
│ │ │ └── path-exists@4.0.0
│ │ ├─┬ read-pkg@5.2.0
│ │ │ ├── @types/normalize-package-data@2.4.1
│ │ │ ├─┬ normalize-package-data@2.5.0
│ │ │ │ ├── hosted-git-info@2.8.9
│ │ │ │ ├── resolve@1.20.0 deduped
│ │ │ │ ├── semver@5.7.1
│ │ │ │ └─┬ validate-npm-package-license@3.0.4
│ │ │ │   ├─┬ spdx-correct@3.1.1
│ │ │ │   │ ├── spdx-expression-parse@3.0.1 deduped
│ │ │ │   │ └── spdx-license-ids@3.0.11 deduped
│ │ │ │   └── spdx-expression-parse@3.0.1 deduped
│ │ │ ├─┬ parse-json@5.2.0
│ │ │ │ ├── @babel/code-frame@7.16.0 deduped
│ │ │ │ ├─┬ error-ex@1.3.2
│ │ │ │ │ └── is-arrayish@0.2.1
│ │ │ │ ├── json-parse-even-better-errors@2.3.1
│ │ │ │ └── lines-and-columns@1.2.4
│ │ │ └── type-fest@0.6.0
│ │ └── type-fest@0.8.1
│ ├── regexp-tree@0.1.24
│ ├─┬ safe-regex@2.1.1
│ │ └── regexp-tree@0.1.24 deduped
│ ├─┬ semver@7.3.5
│ │ └── lru-cache@6.0.0 deduped
│ └─┬ strip-indent@3.0.0
│   └── min-indent@1.0.1
├─┬ firebase@9.6.1
│ ├─┬ @firebase/analytics@0.7.4
│ │ ├─┬ @firebase/component@0.5.9
│ │ │ ├── @firebase/util@1.4.2 deduped
│ │ │ └── tslib@2.3.1 deduped
│ │ ├── @firebase/installations@0.5.4 deduped
│ │ ├─┬ @firebase/logger@0.3.2
│ │ │ └── tslib@2.3.1 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1
│ ├─┬ @firebase/analytics-compat@0.1.5
│ │ ├── @firebase/analytics@0.7.4 deduped
│ │ ├── @firebase/analytics-types@0.7.0
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/app@0.7.11
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/app-check@0.5.2
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/app-check-compat@0.2.2
│ │ ├── @firebase/app-check@0.5.2 deduped
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/app-compat@0.1.12
│ │ ├── @firebase/app@0.7.11 deduped
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├── @firebase/app-types@0.7.0
│ ├─┬ @firebase/auth@0.19.4
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ ├─┬ node-fetch@2.6.5
│ │ │ └── whatwg-url@5.0.0 deduped
│ │ ├─┬ selenium-webdriver@4.0.0-rc-1
│ │ │ ├─┬ jszip@3.7.1
│ │ │ │ ├─┬ lie@3.3.0
│ │ │ │ │ └── immediate@3.0.6
│ │ │ │ ├── pako@1.0.11
│ │ │ │ ├─┬ readable-stream@2.3.7
│ │ │ │ │ ├── core-util-is@1.0.3
│ │ │ │ │ ├── inherits@2.0.4 deduped
│ │ │ │ │ ├── isarray@1.0.0
│ │ │ │ │ ├── process-nextick-args@2.0.1
│ │ │ │ │ ├── safe-buffer@5.1.2
│ │ │ │ │ ├─┬ string_decoder@1.1.1
│ │ │ │ │ │ └── safe-buffer@5.1.2 deduped
│ │ │ │ │ └── util-deprecate@1.0.2 deduped
│ │ │ │ └── set-immediate-shim@1.0.1
│ │ │ ├── rimraf@3.0.2 deduped
│ │ │ ├─┬ tmp@0.2.1
│ │ │ │ └── rimraf@3.0.2 deduped
│ │ │ └── ws@8.4.0
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/auth-compat@0.2.4
│ │ ├── @firebase/auth@0.19.4 deduped
│ │ ├── @firebase/auth-types@0.11.0
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ ├─┬ node-fetch@2.6.5
│ │ │ └── whatwg-url@5.0.0 deduped
│ │ ├── selenium-webdriver@4.0.0-rc-1 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/database@0.12.4
│ │ ├── @firebase/auth-interop-types@0.1.6
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ ├─┬ faye-websocket@0.11.4
│ │ │ └─┬ websocket-driver@0.7.4
│ │ │   ├── http-parser-js@0.5.5
│ │ │   ├── safe-buffer@5.2.1 deduped
│ │ │   └── websocket-extensions@0.1.4
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/database-compat@0.1.4
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/database@0.12.4 deduped
│ │ ├─┬ @firebase/database-types@0.9.3
│ │ │ ├── @firebase/app-types@0.7.0 deduped
│ │ │ └── @firebase/util@1.4.2 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/firestore@3.4.1
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ ├── @firebase/webchannel-wrapper@0.6.1
│ │ ├── @grpc/grpc-js@1.4.5 deduped
│ │ ├── @grpc/proto-loader@0.6.7 deduped
│ │ ├─┬ node-fetch@2.6.5
│ │ │ └── whatwg-url@5.0.0 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/firestore-compat@0.1.10
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/firestore@3.4.1 deduped
│ │ ├── @firebase/firestore-types@2.5.0
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/functions@0.7.6
│ │ ├── @firebase/app-check-interop-types@0.1.0
│ │ ├── @firebase/auth-interop-types@0.1.6 deduped
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/messaging-interop-types@0.1.0
│ │ ├── @firebase/util@1.4.2 deduped
│ │ ├─┬ node-fetch@2.6.5
│ │ │ └── whatwg-url@5.0.0 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/functions-compat@0.1.7
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/functions@0.7.6 deduped
│ │ ├── @firebase/functions-types@0.5.0
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/installations@0.5.4
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ ├── idb@3.0.2
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/messaging@0.9.4
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/installations@0.5.4 deduped
│ │ ├── @firebase/messaging-interop-types@0.1.0 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ ├── idb@3.0.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/messaging-compat@0.1.4
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/messaging@0.9.4 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/performance@0.5.4
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/installations@0.5.4 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/performance-compat@0.1.4
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/performance@0.5.4 deduped
│ │ ├── @firebase/performance-types@0.1.0
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/polyfill@0.3.36
│ │ ├── core-js@3.6.5
│ │ ├── promise-polyfill@8.1.3
│ │ └── whatwg-fetch@2.0.4
│ ├─┬ @firebase/remote-config@0.3.3
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/installations@0.5.4 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/remote-config-compat@0.1.4
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/logger@0.3.2 deduped
│ │ ├── @firebase/remote-config@0.3.3 deduped
│ │ ├── @firebase/remote-config-types@0.2.0
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/storage@0.9.0
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/util@1.4.2 deduped
│ │ ├─┬ node-fetch@2.6.5
│ │ │ └── whatwg-url@5.0.0 deduped
│ │ └── tslib@2.3.1 deduped
│ ├─┬ @firebase/storage-compat@0.1.8
│ │ ├── @firebase/component@0.5.9 deduped
│ │ ├── @firebase/storage@0.9.0 deduped
│ │ ├── @firebase/storage-types@0.6.0
│ │ ├── @firebase/util@1.4.2 deduped
│ │ └── tslib@2.3.1 deduped
│ └─┬ @firebase/util@1.4.2
│   └── tslib@2.3.1 deduped
├─┬ firebase-admin@10.0.1
│ ├── @firebase/database-compat@0.1.4 deduped
│ ├─┬ @firebase/database-types@0.7.3
│ │ └── @firebase/app-types@0.6.3
│ ├─┬ @google-cloud/firestore@4.15.1
│ │ ├── fast-deep-equal@3.1.3 deduped
│ │ ├── functional-red-black-tree@1.0.1 deduped
│ │ ├── google-gax@2.28.1 deduped
│ │ └── protobufjs@6.11.2 deduped
│ ├─┬ @google-cloud/storage@5.16.1
│ │ ├─┬ @google-cloud/common@3.8.1
│ │ │ ├── @google-cloud/projectify@2.1.1
│ │ │ ├── @google-cloud/promisify@2.0.4 deduped
│ │ │ ├── arrify@2.0.1 deduped
│ │ │ ├── duplexify@4.1.2 deduped
│ │ │ ├── ent@2.2.0
│ │ │ ├── extend@3.0.2 deduped
│ │ │ ├── google-auth-library@7.11.0 deduped
│ │ │ ├── retry-request@4.2.2 deduped
│ │ │ └─┬ teeny-request@7.1.3
│ │ │   ├─┬ http-proxy-agent@5.0.0
│ │ │   │ ├── @tootallnate/once@2.0.0
│ │ │   │ ├── agent-base@6.0.2 deduped
│ │ │   │ └── debug@4.3.3 deduped
│ │ │   ├── https-proxy-agent@5.0.0 deduped
│ │ │   ├── node-fetch@2.6.6 deduped
│ │ │   ├── stream-events@1.0.5 deduped
│ │ │   └── uuid@8.3.2
│ │ ├─┬ @google-cloud/paginator@3.0.6
│ │ │ ├── arrify@2.0.1 deduped
│ │ │ └── extend@3.0.2 deduped
│ │ ├── @google-cloud/promisify@2.0.4
│ │ ├── arrify@2.0.1
│ │ ├─┬ async-retry@1.3.3
│ │ │ └── retry@0.13.1
│ │ ├─┬ compressible@2.0.18
│ │ │ └── mime-db@1.51.0 deduped
│ │ ├── date-and-time@2.0.1
│ │ ├── duplexify@4.1.2 deduped
│ │ ├── extend@3.0.2
│ │ ├─┬ gcs-resumable-upload@3.6.0
│ │ │ ├── abort-controller@3.0.0 deduped
│ │ │ ├── async-retry@1.3.3 deduped
│ │ │ ├─┬ configstore@5.0.1
│ │ │ │ ├─┬ dot-prop@5.3.0
│ │ │ │ │ └── is-obj@2.0.0
│ │ │ │ ├── graceful-fs@4.2.8
│ │ │ │ ├─┬ make-dir@3.1.0
│ │ │ │ │ └── semver@6.3.0 deduped
│ │ │ │ ├─┬ unique-string@2.0.0
│ │ │ │ │ └── crypto-random-string@2.0.0
│ │ │ │ ├─┬ write-file-atomic@3.0.3
│ │ │ │ │ ├── imurmurhash@0.1.4 deduped
│ │ │ │ │ ├── is-typedarray@1.0.0
│ │ │ │ │ ├── signal-exit@3.0.6
│ │ │ │ │ └─┬ typedarray-to-buffer@3.1.5
│ │ │ │ │   └── is-typedarray@1.0.0 deduped
│ │ │ │ └── xdg-basedir@4.0.0 deduped
│ │ │ ├── extend@3.0.2 deduped
│ │ │ ├── gaxios@4.3.2 deduped
│ │ │ ├── google-auth-library@7.11.0 deduped
│ │ │ ├── pumpify@2.0.1 deduped
│ │ │ └── stream-events@1.0.5 deduped
│ │ ├── get-stream@6.0.1
│ │ ├── hash-stream-validation@0.2.4
│ │ ├── mime@3.0.0
│ │ ├── mime-types@2.1.34 deduped
│ │ ├─┬ p-limit@3.1.0
│ │ │ └── yocto-queue@0.1.0
│ │ ├─┬ pumpify@2.0.1
│ │ │ ├── duplexify@4.1.2 deduped
│ │ │ ├── inherits@2.0.4 deduped
│ │ │ └─┬ pump@3.0.0
│ │ │   ├── end-of-stream@1.4.4 deduped
│ │ │   └── once@1.4.0 deduped
│ │ ├── snakeize@0.1.0
│ │ ├─┬ stream-events@1.0.5
│ │ │ └── stubs@3.0.0
│ │ └── xdg-basedir@4.0.0
│ ├── @types/node@14.18.2 deduped
│ ├─┬ dicer@0.3.1
│ │ └── streamsearch@1.1.0
│ ├─┬ jsonwebtoken@8.5.1
│ │ ├─┬ jws@3.2.2
│ │ │ ├─┬ jwa@1.4.1
│ │ │ │ ├── buffer-equal-constant-time@1.0.1
│ │ │ │ ├── ecdsa-sig-formatter@1.0.11 deduped
│ │ │ │ └── safe-buffer@5.2.1 deduped
│ │ │ └── safe-buffer@5.2.1 deduped
│ │ ├── 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.1.1
│ │ ├── ms@2.1.2 deduped
│ │ └── semver@5.7.1
│ ├─┬ jwks-rsa@2.0.5
│ │ ├─┬ @types/express-jwt@0.0.42
│ │ │ ├─┬ @types/express@4.17.13
│ │ │ │ ├── @types/body-parser@1.19.2 deduped
│ │ │ │ ├── @types/express-serve-static-core@4.17.26 deduped
│ │ │ │ ├── @types/qs@6.9.7 deduped
│ │ │ │ └── @types/serve-static@1.13.10 deduped
│ │ │ └─┬ @types/express-unless@0.5.2
│ │ │   └── @types/express@4.17.13 deduped
│ │ ├── debug@4.3.3 deduped
│ │ ├─┬ jose@2.0.5
│ │ │ └── @panva/asn1.js@1.0.0
│ │ ├── limiter@1.1.5
│ │ └─┬ lru-memoizer@2.1.4
│ │   ├── lodash.clonedeep@4.5.0
│ │   └─┬ lru-cache@4.0.2
│ │     ├── pseudomap@1.0.2
│ │     └── yallist@2.1.2
│ └── node-forge@0.10.0
├─┬ firebase-functions@3.16.0
│ ├── @types/cors@2.8.12
│ ├─┬ @types/express@4.17.3
│ │ ├─┬ @types/body-parser@1.19.2
│ │ │ ├─┬ @types/connect@3.4.35
│ │ │ │ └── @types/node@14.18.2 deduped
│ │ │ └── @types/node@14.18.2 deduped
│ │ ├─┬ @types/express-serve-static-core@4.17.26
│ │ │ ├── @types/node@14.18.2 deduped
│ │ │ ├── @types/qs@6.9.7
│ │ │ └── @types/range-parser@1.2.4
│ │ └─┬ @types/serve-static@1.13.10
│ │   ├── @types/mime@1.3.2
│ │   └── @types/node@14.18.2 deduped
│ ├─┬ cors@2.8.5
│ │ ├── object-assign@4.1.1
│ │ └── vary@1.1.2
│ ├─┬ express@4.17.2
│ │ ├─┬ accepts@1.3.7
│ │ │ ├── mime-types@2.1.34 deduped
│ │ │ └── negotiator@0.6.2
│ │ ├── array-flatten@1.1.1
│ │ ├─┬ body-parser@1.19.1
│ │ │ ├── bytes@3.1.1
│ │ │ ├── content-type@1.0.4 deduped
│ │ │ ├─┬ debug@2.6.9
│ │ │ │ └── ms@2.0.0
│ │ │ ├── depd@1.1.2 deduped
│ │ │ ├─┬ http-errors@1.8.1
│ │ │ │ ├── depd@1.1.2 deduped
│ │ │ │ ├── inherits@2.0.4 deduped
│ │ │ │ ├── setprototypeof@1.2.0 deduped
│ │ │ │ ├── statuses@1.5.0 deduped
│ │ │ │ └── toidentifier@1.0.1
│ │ │ ├─┬ iconv-lite@0.4.24
│ │ │ │ └── safer-buffer@2.1.2
│ │ │ ├── on-finished@2.3.0 deduped
│ │ │ ├── qs@6.9.6 deduped
│ │ │ ├─┬ raw-body@2.4.2
│ │ │ │ ├── bytes@3.1.1 deduped
│ │ │ │ ├── http-errors@1.8.1 deduped
│ │ │ │ ├── iconv-lite@0.4.24 deduped
│ │ │ │ └── unpipe@1.0.0 deduped
│ │ │ └── type-is@1.6.18 deduped
│ │ ├─┬ content-disposition@0.5.4
│ │ │ └── safe-buffer@5.2.1 deduped
│ │ ├── content-type@1.0.4
│ │ ├── cookie@0.4.1
│ │ ├── cookie-signature@1.0.6
│ │ ├─┬ debug@2.6.9
│ │ │ └── ms@2.0.0
│ │ ├── depd@1.1.2
│ │ ├── encodeurl@1.0.2
│ │ ├── escape-html@1.0.3
│ │ ├── etag@1.8.1
│ │ ├─┬ finalhandler@1.1.2
│ │ │ ├─┬ debug@2.6.9
│ │ │ │ └── ms@2.0.0
│ │ │ ├── encodeurl@1.0.2 deduped
│ │ │ ├── escape-html@1.0.3 deduped
│ │ │ ├── on-finished@2.3.0 deduped
│ │ │ ├── parseurl@1.3.3 deduped
│ │ │ ├── statuses@1.5.0 deduped
│ │ │ └── unpipe@1.0.0
│ │ ├── fresh@0.5.2
│ │ ├── merge-descriptors@1.0.1
│ │ ├── methods@1.1.2
│ │ ├─┬ on-finished@2.3.0
│ │ │ └── ee-first@1.1.1
│ │ ├── parseurl@1.3.3
│ │ ├── path-to-regexp@0.1.7
│ │ ├─┬ proxy-addr@2.0.7
│ │ │ ├── forwarded@0.2.0
│ │ │ └── ipaddr.js@1.9.1
│ │ ├── qs@6.9.6
│ │ ├── range-parser@1.2.1
│ │ ├── safe-buffer@5.2.1
│ │ ├─┬ send@0.17.2
│ │ │ ├─┬ debug@2.6.9
│ │ │ │ └── ms@2.0.0
│ │ │ ├── depd@1.1.2 deduped
│ │ │ ├── destroy@1.0.4
│ │ │ ├── encodeurl@1.0.2 deduped
│ │ │ ├── escape-html@1.0.3 deduped
│ │ │ ├── etag@1.8.1 deduped
│ │ │ ├── fresh@0.5.2 deduped
│ │ │ ├── http-errors@1.8.1 deduped
│ │ │ ├── mime@1.6.0
│ │ │ ├── ms@2.1.3
│ │ │ ├── on-finished@2.3.0 deduped
│ │ │ ├── range-parser@1.2.1 deduped
│ │ │ └── statuses@1.5.0 deduped
│ │ ├─┬ serve-static@1.14.2
│ │ │ ├── encodeurl@1.0.2 deduped
│ │ │ ├── escape-html@1.0.3 deduped
│ │ │ ├── parseurl@1.3.3 deduped
│ │ │ └── send@0.17.2 deduped
│ │ ├── setprototypeof@1.2.0
│ │ ├── statuses@1.5.0
│ │ ├─┬ type-is@1.6.18
│ │ │ ├── media-typer@0.3.0
│ │ │ └── mime-types@2.1.34 deduped
│ │ ├── utils-merge@1.0.1
│ │ └── vary@1.1.2 deduped
│ └── lodash@4.17.21 deduped
├─┬ firebase-functions-test@0.3.3
│ ├── @types/lodash@4.14.178
│ └── lodash@4.17.21 deduped
├─┬ jasmine@3.10.0
│ ├── glob@7.2.0 deduped
│ └── jasmine-core@3.10.1
├─┬ node-fetch@2.6.6
│ └─┬ whatwg-url@5.0.0
│   ├── tr46@0.0.3
│   └── webidl-conversions@3.0.1
├─┬ ts-node@10.4.0
│ ├─┬ @cspotcode/source-map-support@0.7.0
│ │ └── @cspotcode/source-map-consumer@0.8.0
│ ├── @tsconfig/node10@1.0.8
│ ├── @tsconfig/node12@1.0.9
│ ├── @tsconfig/node14@1.0.1
│ ├── @tsconfig/node16@1.0.2
│ ├── acorn@8.6.0
│ ├── acorn-walk@8.2.0
│ ├── arg@4.1.3
│ ├── create-require@1.1.1
│ ├── diff@4.0.2
│ ├── make-error@1.3.6
│ └── yn@3.1.1
├─┬ tsconfig-paths@3.12.0
│ ├── @types/json5@0.0.29
│ ├─┬ json5@1.0.1
│ │ └── minimist@1.2.5 deduped
│ ├── minimist@1.2.5
│ └── strip-bom@3.0.0
├─┬ tslint@6.1.3
│ ├─┬ @babel/code-frame@7.16.0
│ │ └─┬ @babel/highlight@7.16.0
│ │   ├── @babel/helper-validator-identifier@7.15.7 deduped
│ │   ├─┬ chalk@2.4.2
│ │   │ ├─┬ ansi-styles@3.2.1
│ │   │ │ └─┬ color-convert@1.9.3
│ │   │ │   └── color-name@1.1.3
│ │   │ ├── escape-string-regexp@1.0.5
│ │   │ └─┬ supports-color@5.5.0
│ │   │   └── has-flag@3.0.0
│ │   └── js-tokens@4.0.0
│ ├── builtin-modules@1.1.1
│ ├─┬ chalk@2.4.2
│ │ ├─┬ ansi-styles@3.2.1
│ │ │ └─┬ color-convert@1.9.3
│ │ │   └── color-name@1.1.3
│ │ ├── escape-string-regexp@1.0.5
│ │ └─┬ supports-color@5.5.0
│ │   └── has-flag@3.0.0
│ ├── commander@2.20.3
│ ├── diff@4.0.2 deduped
│ ├── glob@7.2.0 deduped
│ ├─┬ js-yaml@3.14.1
│ │ ├─┬ argparse@1.0.10
│ │ │ └── sprintf-js@1.0.3
│ │ └── esprima@4.0.1
│ ├── minimatch@3.0.4 deduped
│ ├─┬ mkdirp@0.5.5
│ │ └── minimist@1.2.5 deduped
│ ├── resolve@1.20.0 deduped
│ ├── semver@5.7.1
│ ├── tslib@1.14.1
│ └─┬ tsutils@2.29.0
│   └── tslib@1.14.1 deduped
└── typescript@4.5.4

@schmidt-sebastian
Copy link
Contributor

You have both "@google-cloud/firestore@4.15.1" and "@google-cloud/firestore@5.0.1". Until firebase-admin pulls in 5.x, you should depend on "@google-cloud/firestore@4.15.1".

@rgant
Copy link

rgant commented Dec 23, 2021

That is listed as an optional dependency in firebase-admin's package.json. I wouldn't have expected that to cause issues. Seems like something in npm should warn me about these conflicts.

Thanks!

@devinrhode2

This comment was marked as outdated.

@devinrhode2
Copy link

Sorry for spamming, jamie has a v9 article here: https://plainenglish.io/blog/using-firestore-with-typescript-in-the-v9-sdk-cf36851bb099

@ljrodriguez1
Copy link

I wanted to know why is this closed, I am using "firebase-admin": "^11.11.0" And i still find this issue when using dot notation with withconverter

@0x80
Copy link

0x80 commented Nov 25, 2023

@ljrodriguez1 I think it's because UpdateData is now included / exported from firebase-admin since it upgraded its @google-cloud/firestore dependency.

import type { UpdateData } from "firebase-admin/firestore";

So you can use that to type your data as described in this article by @JamieCurnow

PS: Tantiantially related, does anyone here know if/when the firestore API from firebase-admin / @google-cloud/firestore will transition to the same "modular" API as the web SDK >=v9?

@ljrodriguez1
Copy link

@0x80 But that UpdateData does not support dot notation, and differs from the UpdateData implementation from the article.

because of that when doing db.collection("example").doc("exampleId").update(updates) it throws a typescript error if i use the UpdateData from the article that supports dot notation

@0x80
Copy link

0x80 commented Nov 29, 2023

@ljrodriguez1 Ah ok that's good to know! I didn't fully understand your problem then.

Maybe @schmidt-sebastian or @thebrianchen can chime in...?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: firestore Issues related to the googleapis/nodejs-firestore API. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

No branches or pull requests

7 participants