From 0716bd7511dba2b068aa6b31800772b6b387b9ef Mon Sep 17 00:00:00 2001 From: Mila <107142260+milaGGL@users.noreply.github.com> Date: Mon, 10 Mar 2025 13:17:54 -0400 Subject: [PATCH 01/77] Add new BSON types to public API (#330) --- common/api-review/firestore-lite.api.md | 79 ++++ common/api-review/firestore.api.md | 79 ++++ packages/firestore/lite/index.ts | 23 +- packages/firestore/src/api.ts | 23 +- .../firestore/src/api/field_value_impl.ts | 9 +- packages/firestore/src/core/target.ts | 20 +- .../src/index/firestore_index_value_writer.ts | 1 + .../src/lite-api/bson_binary_data.ts | 59 +++ .../firestore/src/lite-api/bson_object_Id.ts | 35 ++ .../src/lite-api/bson_timestamp_value.ts | 35 ++ .../src/lite-api/field_value_impl.ts | 89 ++++ .../firestore/src/lite-api/int32_value.ts | 35 ++ packages/firestore/src/lite-api/max_key.ts | 36 ++ packages/firestore/src/lite-api/min_key.ts | 36 ++ .../firestore/src/lite-api/regex_value.ts | 35 ++ .../src/lite-api/user_data_reader.ts | 149 +++++- .../src/lite-api/user_data_writer.ts | 90 +++- packages/firestore/src/model/object_value.ts | 5 +- packages/firestore/src/model/type_order.ts | 27 +- packages/firestore/src/model/values.ts | 332 ++++++++++--- .../test/integration/api/database.test.ts | 440 +++++++++++++++++- .../test/integration/api/type.test.ts | 395 +++++++++++++++- .../firestore/test/lite/integration.test.ts | 48 ++ .../test/unit/model/document.test.ts | 37 ++ .../test/unit/model/object_value.test.ts | 153 +++++- .../firestore/test/unit/model/target.test.ts | 12 +- .../firestore/test/unit/model/values.test.ts | 158 ++++++- .../test/unit/remote/serializer.helper.ts | 62 ++- 28 files changed, 2386 insertions(+), 116 deletions(-) create mode 100644 packages/firestore/src/lite-api/bson_binary_data.ts create mode 100644 packages/firestore/src/lite-api/bson_object_Id.ts create mode 100644 packages/firestore/src/lite-api/bson_timestamp_value.ts create mode 100644 packages/firestore/src/lite-api/int32_value.ts create mode 100644 packages/firestore/src/lite-api/max_key.ts create mode 100644 packages/firestore/src/lite-api/min_key.ts create mode 100644 packages/firestore/src/lite-api/regex_value.ts diff --git a/common/api-review/firestore-lite.api.md b/common/api-review/firestore-lite.api.md index 4a9ef4c0171..04faa9c47c6 100644 --- a/common/api-review/firestore-lite.api.md +++ b/common/api-review/firestore-lite.api.md @@ -65,6 +65,41 @@ export function arrayUnion(...elements: unknown[]): FieldValue; // @public export function average(field: string | FieldPath): AggregateField; +// @public +export class BsonBinaryData { + constructor(subtype: number, data: Uint8Array); + readonly data: Uint8Array; + isEqual(other: BsonBinaryData): boolean; + readonly subtype: number; +} + +// @public +export function bsonBinaryData(subtype: number, data: Uint8Array): BsonBinaryData; + +// @public +export class BsonObjectId { + constructor(value: string); + isEqual(other: BsonObjectId): boolean; + // (undocumented) + readonly value: string; +} + +// @public +export function bsonObjectId(value: string): BsonObjectId; + +// @public +export function bsonTimestamp(seconds: number, increment: number): BsonTimestampValue; + +// @public +export class BsonTimestampValue { + constructor(seconds: number, increment: number); + // (undocumented) + readonly increment: number; + isEqual(other: BsonTimestampValue): boolean; + // (undocumented) + readonly seconds: number; +} + // @public export class Bytes { static fromBase64String(base64: string): Bytes; @@ -249,6 +284,17 @@ export function initializeFirestore(app: FirebaseApp, settings: Settings): Fires // @beta export function initializeFirestore(app: FirebaseApp, settings: Settings, databaseId?: string): Firestore; +// @public +export function int32(value: number): Int32Value; + +// @public +export class Int32Value { + constructor(value: number); + isEqual(other: Int32Value): boolean; + // (undocumented) + readonly value: number; +} + // @public export function limit(limit: number): QueryLimitConstraint; @@ -257,6 +303,26 @@ export function limitToLast(limit: number): QueryLimitConstraint; export { LogLevel } +// @public +export class MaxKey { + // (undocumented) + static instance(): MaxKey; + readonly type = "MaxKey"; +} + +// @public +export function maxKey(): MaxKey; + +// @public +export class MinKey { + // (undocumented) + static instance(): MinKey; + readonly type = "MinKey"; +} + +// @public +export function minKey(): MinKey; + // @public export type NestedUpdateFields> = UnionToIntersection<{ [K in keyof T & string]: ChildUpdateFields; @@ -360,6 +426,19 @@ export class QueryStartAtConstraint extends QueryConstraint { // @public export function refEqual(left: DocumentReference | CollectionReference, right: DocumentReference | CollectionReference): boolean; +// @public +export function regex(pattern: string, options: string): RegexValue; + +// @public +export class RegexValue { + constructor(pattern: string, options: string); + isEqual(other: RegexValue): boolean; + // (undocumented) + readonly options: string; + // (undocumented) + readonly pattern: string; +} + // @public export function runTransaction(firestore: Firestore, updateFunction: (transaction: Transaction) => Promise, options?: TransactionOptions): Promise; diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 34b56b97f21..5d3c2286859 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -65,6 +65,41 @@ export function arrayUnion(...elements: unknown[]): FieldValue; // @public export function average(field: string | FieldPath): AggregateField; +// @public +export class BsonBinaryData { + constructor(subtype: number, data: Uint8Array); + readonly data: Uint8Array; + isEqual(other: BsonBinaryData): boolean; + readonly subtype: number; +} + +// @public +export function bsonBinaryData(subtype: number, data: Uint8Array): BsonBinaryData; + +// @public +export class BsonObjectId { + constructor(value: string); + isEqual(other: BsonObjectId): boolean; + // (undocumented) + readonly value: string; +} + +// @public +export function bsonObjectId(value: string): BsonObjectId; + +// @public +export function bsonTimestamp(seconds: number, increment: number): BsonTimestampValue; + +// @public +export class BsonTimestampValue { + constructor(seconds: number, increment: number); + // (undocumented) + readonly increment: number; + isEqual(other: BsonTimestampValue): boolean; + // (undocumented) + readonly seconds: number; +} + // @public export class Bytes { static fromBase64String(base64: string): Bytes; @@ -344,6 +379,17 @@ export interface IndexField { // @public export function initializeFirestore(app: FirebaseApp, settings: FirestoreSettings, databaseId?: string): Firestore; +// @public +export function int32(value: number): Int32Value; + +// @public +export class Int32Value { + constructor(value: number); + isEqual(other: Int32Value): boolean; + // (undocumented) + readonly value: number; +} + // @public export function limit(limit: number): QueryLimitConstraint; @@ -374,6 +420,16 @@ export interface LoadBundleTaskProgress { export { LogLevel } +// @public +export class MaxKey { + // (undocumented) + static instance(): MaxKey; + readonly type = "MaxKey"; +} + +// @public +export function maxKey(): MaxKey; + // @public export interface MemoryCacheSettings { garbageCollector?: MemoryGarbageCollector; @@ -411,6 +467,16 @@ export function memoryLruGarbageCollector(settings?: { cacheSizeBytes?: number; }): MemoryLruGarbageCollector; +// @public +export class MinKey { + // (undocumented) + static instance(): MinKey; + readonly type = "MinKey"; +} + +// @public +export function minKey(): MinKey; + // @public export function namedQuery(firestore: Firestore, name: string): Promise; @@ -620,6 +686,19 @@ export class QueryStartAtConstraint extends QueryConstraint { // @public export function refEqual(left: DocumentReference | CollectionReference, right: DocumentReference | CollectionReference): boolean; +// @public +export function regex(pattern: string, options: string): RegexValue; + +// @public +export class RegexValue { + constructor(pattern: string, options: string); + isEqual(other: RegexValue): boolean; + // (undocumented) + readonly options: string; + // (undocumented) + readonly pattern: string; +} + // @public export function runTransaction(firestore: Firestore, updateFunction: (transaction: Transaction) => Promise, options?: TransactionOptions): Promise; diff --git a/packages/firestore/lite/index.ts b/packages/firestore/lite/index.ts index b751f0a8254..6d1d6c01998 100644 --- a/packages/firestore/lite/index.ts +++ b/packages/firestore/lite/index.ts @@ -128,7 +128,14 @@ export { arrayUnion, serverTimestamp, deleteField, - vector + vector, + int32, + regex, + bsonBinaryData, + bsonObjectId, + bsonTimestamp, + minKey, + maxKey } from '../src/lite-api/field_value_impl'; export { @@ -141,6 +148,20 @@ export { export { VectorValue } from '../src/lite-api/vector_value'; +export { Int32Value } from '../src/lite-api/int32_value'; + +export { RegexValue } from '../src/lite-api/regex_value'; + +export { BsonBinaryData } from '../src/lite-api/bson_binary_data'; + +export { BsonObjectId } from '../src/lite-api/bson_object_Id'; + +export { BsonTimestampValue } from '../src/lite-api/bson_timestamp_value'; + +export { MinKey } from '../src/lite-api/min_key'; + +export { MaxKey } from '../src/lite-api/max_key'; + export { WriteBatch, writeBatch } from '../src/lite-api/write_batch'; export { TransactionOptions } from '../src/lite-api/transaction_options'; diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index ea969c6b94c..46fb1b3bba3 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -173,11 +173,32 @@ export { deleteField, increment, serverTimestamp, - vector + vector, + int32, + regex, + bsonBinaryData, + bsonObjectId, + bsonTimestamp, + minKey, + maxKey } from './api/field_value_impl'; export { VectorValue } from './lite-api/vector_value'; +export { Int32Value } from './lite-api/int32_value'; + +export { RegexValue } from './lite-api/regex_value'; + +export { BsonBinaryData } from './lite-api/bson_binary_data'; + +export { BsonObjectId } from './lite-api/bson_object_Id'; + +export { BsonTimestampValue } from './lite-api/bson_timestamp_value'; + +export { MinKey } from './lite-api/min_key'; + +export { MaxKey } from './lite-api/max_key'; + export { LogLevelString as LogLevel, setLogLevel } from './util/log'; export { Bytes } from './api/bytes'; diff --git a/packages/firestore/src/api/field_value_impl.ts b/packages/firestore/src/api/field_value_impl.ts index 1b1283a3543..6e65d273259 100644 --- a/packages/firestore/src/api/field_value_impl.ts +++ b/packages/firestore/src/api/field_value_impl.ts @@ -21,5 +21,12 @@ export { arrayUnion, serverTimestamp, deleteField, - vector + vector, + int32, + regex, + bsonBinaryData, + bsonObjectId, + bsonTimestamp, + minKey, + maxKey } from '../lite-api/field_value_impl'; diff --git a/packages/firestore/src/core/target.ts b/packages/firestore/src/core/target.ts index 4b12857fc2a..664a2ef9a08 100644 --- a/packages/firestore/src/core/target.ts +++ b/packages/firestore/src/core/target.ts @@ -25,8 +25,8 @@ import { import { FieldPath, ResourcePath } from '../model/path'; import { canonicalId, - MAX_VALUE, - MIN_VALUE, + INTERNAL_MAX_VALUE, + INTERNAL_MIN_VALUE, lowerBoundCompare, upperBoundCompare, valuesGetLowerBound, @@ -302,7 +302,7 @@ export function targetGetNotInValues( /** * Returns a lower bound of field values that can be used as a starting point to - * scan the index defined by `fieldIndex`. Returns `MIN_VALUE` if no lower bound + * scan the index defined by `fieldIndex`. Returns `INTERNAL_MIN_VALUE` if no lower bound * exists. */ export function targetGetLowerBound( @@ -328,7 +328,7 @@ export function targetGetLowerBound( /** * Returns an upper bound of field values that can be used as an ending point - * when scanning the index defined by `fieldIndex`. Returns `MAX_VALUE` if no + * when scanning the index defined by `fieldIndex`. Returns `INTERNAL_MAX_VALUE` if no * upper bound exists. */ export function targetGetUpperBound( @@ -362,13 +362,13 @@ function targetGetAscendingBound( fieldPath: FieldPath, bound: Bound | null ): { value: ProtoValue; inclusive: boolean } { - let value: ProtoValue = MIN_VALUE; + let value: ProtoValue = INTERNAL_MIN_VALUE; let inclusive = true; // Process all filters to find a value for the current field segment for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) { - let filterValue: ProtoValue = MIN_VALUE; + let filterValue: ProtoValue = INTERNAL_MIN_VALUE; let filterInclusive = true; switch (fieldFilter.op) { @@ -387,7 +387,7 @@ function targetGetAscendingBound( break; case Operator.NOT_EQUAL: case Operator.NOT_IN: - filterValue = MIN_VALUE; + filterValue = INTERNAL_MIN_VALUE; break; default: // Remaining filters cannot be used as lower bounds. @@ -437,12 +437,12 @@ function targetGetDescendingBound( fieldPath: FieldPath, bound: Bound | null ): { value: ProtoValue; inclusive: boolean } { - let value: ProtoValue = MAX_VALUE; + let value: ProtoValue = INTERNAL_MAX_VALUE; let inclusive = true; // Process all filters to find a value for the current field segment for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) { - let filterValue: ProtoValue = MAX_VALUE; + let filterValue: ProtoValue = INTERNAL_MAX_VALUE; let filterInclusive = true; switch (fieldFilter.op) { @@ -462,7 +462,7 @@ function targetGetDescendingBound( break; case Operator.NOT_EQUAL: case Operator.NOT_IN: - filterValue = MAX_VALUE; + filterValue = INTERNAL_MAX_VALUE; break; default: // Remaining filters cannot be used as upper bounds. diff --git a/packages/firestore/src/index/firestore_index_value_writer.ts b/packages/firestore/src/index/firestore_index_value_writer.ts index dfdb3836578..f831862a0de 100644 --- a/packages/firestore/src/index/firestore_index_value_writer.ts +++ b/packages/firestore/src/index/firestore_index_value_writer.ts @@ -124,6 +124,7 @@ export class FirestoreIndexValueWriter { encoder.writeNumber(geoPoint.latitude || 0); encoder.writeNumber(geoPoint.longitude || 0); } else if ('mapValue' in indexValue) { + // TODO(Mila/BSON): add bson types for indexing if (isMaxValue(indexValue)) { this.writeValueTypeLabel(encoder, Number.MAX_SAFE_INTEGER); } else if (isVectorValue(indexValue)) { diff --git a/packages/firestore/src/lite-api/bson_binary_data.ts b/packages/firestore/src/lite-api/bson_binary_data.ts new file mode 100644 index 00000000000..233dd790aec --- /dev/null +++ b/packages/firestore/src/lite-api/bson_binary_data.ts @@ -0,0 +1,59 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ByteString } from '../util/byte_string'; +import { Code, FirestoreError } from '../util/error'; + +/** + * Represents a BSON Binary Data type in Firestore documents. + * + * @class BsonBinaryData + */ +export class BsonBinaryData { + /** The subtype for the data */ + readonly subtype: number; + + /** The binary data as a byte array */ + readonly data: Uint8Array; + + constructor(subtype: number, data: Uint8Array) { + if (subtype < 0 || subtype > 255) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'The subtype for BsonBinaryData must be a value in the inclusive [0, 255] range.' + ); + } + this.subtype = subtype; + // Make a copy of the data. + this.data = Uint8Array.from(data); + } + + /** + * Returns true if this `BsonBinaryData` is equal to the provided one. + * + * @param other - The `BsonBinaryData` to compare against. + * @return 'true' if this `BsonBinaryData` is equal to the provided one. + */ + isEqual(other: BsonBinaryData): boolean { + return ( + this.subtype === other.subtype && + ByteString.fromUint8Array(this.data).isEqual( + ByteString.fromUint8Array(other.data) + ) + ); + } +} diff --git a/packages/firestore/src/lite-api/bson_object_Id.ts b/packages/firestore/src/lite-api/bson_object_Id.ts new file mode 100644 index 00000000000..71ee13d8860 --- /dev/null +++ b/packages/firestore/src/lite-api/bson_object_Id.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Represents a BSON ObjectId type in Firestore documents. + * + * @class BsonObjectId + */ +export class BsonObjectId { + constructor(readonly value: string) {} + + /** + * Returns true if this `BsonObjectId` is equal to the provided one. + * + * @param other - The `BsonObjectId` to compare against. + * @return 'true' if this `BsonObjectId` is equal to the provided one. + */ + isEqual(other: BsonObjectId): boolean { + return this.value === other.value; + } +} diff --git a/packages/firestore/src/lite-api/bson_timestamp_value.ts b/packages/firestore/src/lite-api/bson_timestamp_value.ts new file mode 100644 index 00000000000..60b48157906 --- /dev/null +++ b/packages/firestore/src/lite-api/bson_timestamp_value.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Represents a BSON Timestamp type in Firestore documents. + * + * @class BsonTimestampValue + */ +export class BsonTimestampValue { + constructor(readonly seconds: number, readonly increment: number) {} + + /** + * Returns true if this `BsonTimestampValue` is equal to the provided one. + * + * @param other - The `BsonTimestampValue` to compare against. + * @return 'true' if this `BsonTimestampValue` is equal to the provided one. + */ + isEqual(other: BsonTimestampValue): boolean { + return this.seconds === other.seconds && this.increment === other.increment; + } +} diff --git a/packages/firestore/src/lite-api/field_value_impl.ts b/packages/firestore/src/lite-api/field_value_impl.ts index 2c910bdace5..2cc1e3522b0 100644 --- a/packages/firestore/src/lite-api/field_value_impl.ts +++ b/packages/firestore/src/lite-api/field_value_impl.ts @@ -15,7 +15,14 @@ * limitations under the License. */ +import { BsonBinaryData } from './bson_binary_data'; +import { BsonObjectId } from './bson_object_Id'; +import { BsonTimestampValue } from './bson_timestamp_value'; import { FieldValue } from './field_value'; +import { Int32Value } from './int32_value'; +import { MaxKey } from './max_key'; +import { MinKey } from './min_key'; +import { RegexValue } from './regex_value'; import { ArrayRemoveFieldValueImpl, ArrayUnionFieldValueImpl, @@ -109,3 +116,85 @@ export function increment(n: number): FieldValue { export function vector(values?: number[]): VectorValue { return new VectorValue(values); } + +/** + * Creates a new `Int32Value` constructed with the given number. + * + * @param value - The 32-bit number to be used for constructing the Int32Value + * + * @returns A new `Int32Value` constructed with the given number. + */ +export function int32(value: number): Int32Value { + return new Int32Value(value); +} + +/** + * Creates a new `RegexValue` constructed with the given pattern and options. + * + * @param subtype - The subtype of the BSON binary data. + * @param data - The data to use for the BSON binary data. + * + * @returns A new `RegexValue` constructed with the given pattern and options. + */ +export function regex(pattern: string, options: string): RegexValue { + return new RegexValue(pattern, options); +} + +/** + * Creates a new `BsonBinaryData` constructed with the given subtype and data. + * + * @param subtype - Create a `BsonBinaryData` instance with the given subtype. + * @param data - Create a `BsonBinaryData` instance with a copy of this array of numbers. + * + * @returns A new `BsonBinaryData` constructed with the given subtype and data. + */ +export function bsonBinaryData( + subtype: number, + data: Uint8Array +): BsonBinaryData { + return new BsonBinaryData(subtype, data); +} + +/** + * Creates a new `BsonObjectId` constructed with the given string. + * + * @param value - The 24-character hex string representing the ObjectId. + * + * @returns A new `BsonObjectId` constructed with the given string. + */ +export function bsonObjectId(value: string): BsonObjectId { + return new BsonObjectId(value); +} + +/** + * Creates a new `BsonTimestampValue` constructed with the given seconds and increment. + * + * @param seconds - The underlying unsigned 32-bit integer for seconds. + * @param seconds - The underlying unsigned 32-bit integer for increment. + * + * @returns A new `BsonTimestampValue` constructed with the given seconds and increment. + */ +export function bsonTimestamp( + seconds: number, + increment: number +): BsonTimestampValue { + return new BsonTimestampValue(seconds, increment); +} + +/** + * Creates or returns a `MinKey` instance. + * + * @returns A `MinKey` instance. + */ +export function minKey(): MinKey { + return MinKey.instance(); +} + +/** + * Creates or returns a `MaxKey` instance. + * + * @returns A `MaxKey` instance. + */ +export function maxKey(): MaxKey { + return MaxKey.instance(); +} diff --git a/packages/firestore/src/lite-api/int32_value.ts b/packages/firestore/src/lite-api/int32_value.ts new file mode 100644 index 00000000000..cfa0003c0f6 --- /dev/null +++ b/packages/firestore/src/lite-api/int32_value.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Represents a 32-bit integer type in Firestore documents. + * + * @class Int32Value + */ +export class Int32Value { + constructor(readonly value: number) {} + + /** + * Returns true if this `Int32Value` is equal to the provided one. + * + * @param other - The `Int32Value` to compare against. + * @return 'true' if this `Int32Value` is equal to the provided one. + */ + isEqual(other: Int32Value): boolean { + return this.value === other.value; + } +} diff --git a/packages/firestore/src/lite-api/max_key.ts b/packages/firestore/src/lite-api/max_key.ts new file mode 100644 index 00000000000..3f37986315e --- /dev/null +++ b/packages/firestore/src/lite-api/max_key.ts @@ -0,0 +1,36 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Represent a "Max Key" type in Firestore documents. + * + * @class MaxKey + */ +export class MaxKey { + private static MAX_KEY_VALUE_INSTANCE: MaxKey | null = null; + /** A type string to uniquely identify instances of this class. */ + readonly type = 'MaxKey'; + + private constructor() {} + + static instance(): MaxKey { + if (!MaxKey.MAX_KEY_VALUE_INSTANCE) { + MaxKey.MAX_KEY_VALUE_INSTANCE = new MaxKey(); + } + return MaxKey.MAX_KEY_VALUE_INSTANCE; + } +} diff --git a/packages/firestore/src/lite-api/min_key.ts b/packages/firestore/src/lite-api/min_key.ts new file mode 100644 index 00000000000..a901b9611a5 --- /dev/null +++ b/packages/firestore/src/lite-api/min_key.ts @@ -0,0 +1,36 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Represent a "Min Key" type in Firestore documents. + * + * @class MinKey + */ +export class MinKey { + private static MIN_KEY_VALUE_INSTANCE: MinKey | null = null; + /** A type string to uniquely identify instances of this class. */ + readonly type = 'MinKey'; + + private constructor() {} + + static instance(): MinKey { + if (!MinKey.MIN_KEY_VALUE_INSTANCE) { + MinKey.MIN_KEY_VALUE_INSTANCE = new MinKey(); + } + return MinKey.MIN_KEY_VALUE_INSTANCE; + } +} diff --git a/packages/firestore/src/lite-api/regex_value.ts b/packages/firestore/src/lite-api/regex_value.ts new file mode 100644 index 00000000000..b4d4f70962b --- /dev/null +++ b/packages/firestore/src/lite-api/regex_value.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Represents a regular expression type in Firestore documents. + * + * @class RegexValue + */ +export class RegexValue { + constructor(readonly pattern: string, readonly options: string) {} + + /** + * Returns true if this `RegexValue` is equal to the provided one. + * + * @param other - The `RegexValue` to compare against. + * @return 'true' if this `RegexValue` is equal to the provided one. + */ + isEqual(other: RegexValue): boolean { + return this.pattern === other.pattern && this.options === other.options; + } +} diff --git a/packages/firestore/src/lite-api/user_data_reader.ts b/packages/firestore/src/lite-api/user_data_reader.ts index ebd4b49085f..3d0ce031599 100644 --- a/packages/firestore/src/lite-api/user_data_reader.ts +++ b/packages/firestore/src/lite-api/user_data_reader.ts @@ -44,14 +44,25 @@ import { import { TYPE_KEY, VECTOR_MAP_VECTORS_KEY, - VECTOR_VALUE_SENTINEL + RESERVED_VECTOR_KEY, + RESERVED_REGEX_KEY, + RESERVED_REGEX_PATTERN_KEY, + RESERVED_REGEX_OPTIONS_KEY, + RESERVED_BSON_OBJECT_ID_KEY, + RESERVED_INT32_KEY, + RESERVED_BSON_TIMESTAMP_KEY, + RESERVED_BSON_TIMESTAMP_SECONDS_KEY, + RESERVED_BSON_TIMESTAMP_INCREMENT_KEY, + RESERVED_BSON_BINARY_KEY, + RESERVED_MIN_KEY, + RESERVED_MAX_KEY } from '../model/values'; import { newSerializer } from '../platform/serializer'; import { MapValue as ProtoMapValue, Value as ProtoValue } from '../protos/firestore_proto_api'; -import { toDouble, toNumber } from '../remote/number_serializer'; +import { toDouble, toInteger, toNumber } from '../remote/number_serializer'; import { JsonProtoSerializer, toBytes, @@ -59,20 +70,28 @@ import { toTimestamp } from '../remote/serializer'; import { debugAssert, fail } from '../util/assert'; +import { ByteString } from '../util/byte_string'; import { Code, FirestoreError } from '../util/error'; import { isPlainObject, valueDescription } from '../util/input_validation'; import { Dict, forEach, isEmpty } from '../util/obj'; +import { BsonBinaryData } from './bson_binary_data'; +import { BsonObjectId } from './bson_object_Id'; +import { BsonTimestampValue } from './bson_timestamp_value'; import { Bytes } from './bytes'; import { Firestore } from './database'; import { FieldPath } from './field_path'; import { FieldValue } from './field_value'; import { GeoPoint } from './geo_point'; +import { Int32Value } from './int32_value'; +import { MaxKey } from './max_key'; +import { MinKey } from './min_key'; import { DocumentReference, PartialWithFieldValue, WithFieldValue } from './reference'; +import { RegexValue } from './regex_value'; import { Timestamp } from './timestamp'; import { VectorValue } from './vector_value'; @@ -909,6 +928,20 @@ function parseScalarValue( }; } else if (value instanceof VectorValue) { return parseVectorValue(value, context); + } else if (value instanceof RegexValue) { + return parseRegexValue(value); + } else if (value instanceof BsonObjectId) { + return parseBsonObjectId(value); + } else if (value instanceof Int32Value) { + return parseInt32Value(value); + } else if (value instanceof BsonTimestampValue) { + return parseBsonTimestamp(value); + } else if (value instanceof BsonBinaryData) { + return parseBsonBinaryData(context.serializer, value); + } else if (value instanceof MinKey) { + return parseMinKey(); + } else if (value instanceof MaxKey) { + return parseMaxKey(); } else { throw context.createError( `Unsupported field value: ${valueDescription(value)}` @@ -926,7 +959,7 @@ export function parseVectorValue( const mapValue: ProtoMapValue = { fields: { [TYPE_KEY]: { - stringValue: VECTOR_VALUE_SENTINEL + stringValue: RESERVED_VECTOR_KEY }, [VECTOR_MAP_VECTORS_KEY]: { arrayValue: { @@ -947,6 +980,107 @@ export function parseVectorValue( return { mapValue }; } +export function parseRegexValue(value: RegexValue): ProtoValue { + const mapValue: ProtoMapValue = { + fields: { + [RESERVED_REGEX_KEY]: { + mapValue: { + fields: { + [RESERVED_REGEX_PATTERN_KEY]: { + stringValue: value.pattern + }, + [RESERVED_REGEX_OPTIONS_KEY]: { + stringValue: value.options + } + } + } + } + } + }; + + return { mapValue }; +} + +export function parseMinKey(): ProtoValue { + const mapValue: ProtoMapValue = { + fields: { + [RESERVED_MIN_KEY]: { + nullValue: 'NULL_VALUE' + } + } + }; + return { mapValue }; +} + +export function parseMaxKey(): ProtoValue { + const mapValue: ProtoMapValue = { + fields: { + [RESERVED_MAX_KEY]: { + nullValue: 'NULL_VALUE' + } + } + }; + return { mapValue }; +} + +export function parseBsonObjectId(value: BsonObjectId): ProtoValue { + const mapValue: ProtoMapValue = { + fields: { + [RESERVED_BSON_OBJECT_ID_KEY]: { + stringValue: value.value + } + } + }; + return { mapValue }; +} + +export function parseInt32Value(value: Int32Value): ProtoValue { + const mapValue: ProtoMapValue = { + fields: { + [RESERVED_INT32_KEY]: toInteger(value.value) + } + }; + return { mapValue }; +} + +export function parseBsonTimestamp(value: BsonTimestampValue): ProtoValue { + const mapValue: ProtoMapValue = { + fields: { + [RESERVED_BSON_TIMESTAMP_KEY]: { + mapValue: { + fields: { + [RESERVED_BSON_TIMESTAMP_SECONDS_KEY]: toInteger(value.seconds), + [RESERVED_BSON_TIMESTAMP_INCREMENT_KEY]: toInteger(value.increment) + } + } + } + } + }; + return { mapValue }; +} + +export function parseBsonBinaryData( + serializer: JsonProtoSerializer, + value: BsonBinaryData +): ProtoValue { + const subtypeAndData = new Uint8Array(value.data.length + 1); + // This converts the subtype from `number` to a byte. + subtypeAndData[0] = value.subtype; + // Concatenate the rest of the data starting at index 1. + subtypeAndData.set(value.data, /* offset */ 1); + + const mapValue: ProtoMapValue = { + fields: { + [RESERVED_BSON_BINARY_KEY]: { + bytesValue: toBytes( + serializer, + ByteString.fromUint8Array(subtypeAndData) + ) + } + } + }; + return { mapValue }; +} /** * Checks whether an object looks like a JSON object that should be converted * into a struct. Normal class/prototype instances are considered to look like @@ -965,7 +1099,14 @@ function looksLikeJsonObject(input: unknown): boolean { !(input instanceof Bytes) && !(input instanceof DocumentReference) && !(input instanceof FieldValue) && - !(input instanceof VectorValue) + !(input instanceof VectorValue) && + !(input instanceof MinKey) && + !(input instanceof MaxKey) && + !(input instanceof Int32Value) && + !(input instanceof RegexValue) && + !(input instanceof BsonObjectId) && + !(input instanceof BsonTimestampValue) && + !(input instanceof BsonBinaryData) ); } diff --git a/packages/firestore/src/lite-api/user_data_writer.ts b/packages/firestore/src/lite-api/user_data_writer.ts index e903991cb58..0de02b822b2 100644 --- a/packages/firestore/src/lite-api/user_data_writer.ts +++ b/packages/firestore/src/lite-api/user_data_writer.ts @@ -30,7 +30,19 @@ import { getPreviousValue } from '../model/server_timestamps'; import { TypeOrder } from '../model/type_order'; -import { VECTOR_MAP_VECTORS_KEY, typeOrder } from '../model/values'; +import { + RESERVED_BSON_BINARY_KEY, + RESERVED_INT32_KEY, + RESERVED_BSON_OBJECT_ID_KEY, + RESERVED_REGEX_KEY, + RESERVED_REGEX_OPTIONS_KEY, + RESERVED_REGEX_PATTERN_KEY, + RESERVED_BSON_TIMESTAMP_INCREMENT_KEY, + RESERVED_BSON_TIMESTAMP_KEY, + RESERVED_BSON_TIMESTAMP_SECONDS_KEY, + typeOrder, + VECTOR_MAP_VECTORS_KEY +} from '../model/values'; import { ApiClientObjectMap, ArrayValue as ProtoArrayValue, @@ -46,7 +58,13 @@ import { ByteString } from '../util/byte_string'; import { logError } from '../util/log'; import { forEach } from '../util/obj'; +import { BsonBinaryData } from './bson_binary_data'; +import { BsonObjectId } from './bson_object_Id'; +import { BsonTimestampValue } from './bson_timestamp_value'; +import { maxKey, minKey } from './field_value_impl'; import { GeoPoint } from './geo_point'; +import { Int32Value } from './int32_value'; +import { RegexValue } from './regex_value'; import { Timestamp } from './timestamp'; import { VectorValue } from './vector_value'; @@ -65,10 +83,16 @@ export abstract class AbstractUserDataWriter { ): unknown { switch (typeOrder(value)) { case TypeOrder.NullValue: + if ('mapValue' in value) { + return minKey(); + } return null; case TypeOrder.BooleanValue: return value.booleanValue!; case TypeOrder.NumberValue: + if ('mapValue' in value) { + return this.convertToInt32Value(value.mapValue!); + } return normalizeNumber(value.integerValue || value.doubleValue); case TypeOrder.TimestampValue: return this.convertTimestamp(value.timestampValue!); @@ -88,6 +112,16 @@ export abstract class AbstractUserDataWriter { return this.convertObject(value.mapValue!, serverTimestampBehavior); case TypeOrder.VectorValue: return this.convertVectorValue(value.mapValue!); + case TypeOrder.RegexValue: + return this.convertToRegexValue(value.mapValue!); + case TypeOrder.BsonObjectIdValue: + return this.convertToBsonObjectIdValue(value.mapValue!); + case TypeOrder.BsonBinaryValue: + return this.convertToBsonBinaryValue(value.mapValue!); + case TypeOrder.BsonTimestampValue: + return this.convertToBsonTimestampValue(value.mapValue!); + case TypeOrder.MaxKeyValue: + return maxKey(); default: throw fail('Invalid value type: ' + JSON.stringify(value)); } @@ -127,6 +161,60 @@ export abstract class AbstractUserDataWriter { return new VectorValue(values); } + private convertToBsonObjectIdValue(mapValue: ProtoMapValue): BsonObjectId { + const oid = + mapValue!.fields?.[RESERVED_BSON_OBJECT_ID_KEY]?.stringValue ?? ''; + return new BsonObjectId(oid); + } + + private convertToBsonBinaryValue(mapValue: ProtoMapValue): BsonBinaryData { + const fields = mapValue!.fields?.[RESERVED_BSON_BINARY_KEY]; + const subtypeAndData = fields?.bytesValue; + if (!subtypeAndData) { + throw new Error('Received incorrect bytesValue for BsonBinaryData'); + } + + const bytes = normalizeByteString(subtypeAndData).toUint8Array(); + if (bytes.length === 0) { + throw new Error('Received empty bytesValue for BsonBinaryData'); + } + const subtype = bytes.at(0); + const data = bytes.slice(1); + return new BsonBinaryData(Number(subtype), data); + } + + private convertToBsonTimestampValue( + mapValue: ProtoMapValue + ): BsonTimestampValue { + const fields = mapValue!.fields?.[RESERVED_BSON_TIMESTAMP_KEY]; + const seconds = Number( + fields?.mapValue?.fields?.[RESERVED_BSON_TIMESTAMP_SECONDS_KEY] + ?.integerValue + ); + const increment = Number( + fields?.mapValue?.fields?.[RESERVED_BSON_TIMESTAMP_INCREMENT_KEY] + ?.integerValue + ); + return new BsonTimestampValue(seconds, increment); + } + + private convertToRegexValue(mapValue: ProtoMapValue): RegexValue { + const pattern = + mapValue!.fields?.[RESERVED_REGEX_KEY]?.mapValue?.fields?.[ + RESERVED_REGEX_PATTERN_KEY + ]?.stringValue ?? ''; + const options = + mapValue!.fields?.[RESERVED_REGEX_KEY]?.mapValue?.fields?.[ + RESERVED_REGEX_OPTIONS_KEY + ]?.stringValue ?? ''; + return new RegexValue(pattern, options); + } + + private convertToInt32Value(mapValue: ProtoMapValue): Int32Value { + const value = Number(mapValue!.fields?.[RESERVED_INT32_KEY]?.integerValue); + return new Int32Value(value); + } + private convertGeoPoint(value: ProtoLatLng): GeoPoint { return new GeoPoint( normalizeNumber(value.latitude), diff --git a/packages/firestore/src/model/object_value.ts b/packages/firestore/src/model/object_value.ts index d5cb273eb9d..35d8733e40a 100644 --- a/packages/firestore/src/model/object_value.ts +++ b/packages/firestore/src/model/object_value.ts @@ -25,7 +25,7 @@ import { forEach } from '../util/obj'; import { FieldMask } from './field_mask'; import { FieldPath } from './path'; import { isServerTimestamp } from './server_timestamps'; -import { deepClone, isMapValue, valueEquals } from './values'; +import { deepClone, isBsonType, isMapValue, valueEquals } from './values'; export interface JsonObject { [name: string]: T; @@ -188,7 +188,8 @@ export function extractFieldMask(value: ProtoMapValue): FieldMask { const fields: FieldPath[] = []; forEach(value!.fields, (key, value) => { const currentPath = new FieldPath([key]); - if (isMapValue(value)) { + // BSON types do not need to extract reserved keys, ie,__regex__. + if (isMapValue(value) && !isBsonType(value)) { const nestedMask = extractFieldMask(value.mapValue!); const nestedFields = nestedMask.fields; if (nestedFields.length === 0) { diff --git a/packages/firestore/src/model/type_order.ts b/packages/firestore/src/model/type_order.ts index 749b8e8036d..7ac6ed11e47 100644 --- a/packages/firestore/src/model/type_order.ts +++ b/packages/firestore/src/model/type_order.ts @@ -24,18 +24,27 @@ */ export const enum TypeOrder { // This order is based on the backend's ordering, but modified to support - // server timestamps and `MAX_VALUE`. + // server timestamps and `MAX_VALUE` inside the SDK. + // NULL and MIN_KEY sort the same. NullValue = 0, + MinKeyValue = 0, BooleanValue = 1, NumberValue = 2, TimestampValue = 3, - ServerTimestampValue = 4, - StringValue = 5, - BlobValue = 6, - RefValue = 7, - GeoPointValue = 8, - ArrayValue = 9, - VectorValue = 10, - ObjectValue = 11, + // TODO(Mila/BSON): which should come first considering indexes? + BsonTimestampValue = 4, + ServerTimestampValue = 5, + StringValue = 6, + BlobValue = 7, + BsonBinaryValue = 8, + RefValue = 9, + BsonObjectIdValue = 10, + GeoPointValue = 11, + RegexValue = 12, + ArrayValue = 13, + VectorValue = 14, + ObjectValue = 15, + // TODO(Mila/BSON):should MaxKeyValue and MaxValue combined? how would this affect indexes? + MaxKeyValue = 16, MaxValue = 9007199254740991 // Number.MAX_SAFE_INTEGER } diff --git a/packages/firestore/src/model/values.ts b/packages/firestore/src/model/values.ts index 1977767515e..d6344409ed9 100644 --- a/packages/firestore/src/model/values.ts +++ b/packages/firestore/src/model/values.ts @@ -35,28 +35,54 @@ import { normalizeNumber, normalizeTimestamp } from './normalize'; -import { - getLocalWriteTime, - getPreviousValue, - isServerTimestamp -} from './server_timestamps'; +import { getLocalWriteTime, getPreviousValue } from './server_timestamps'; import { TypeOrder } from './type_order'; export const TYPE_KEY = '__type__'; -const MAX_VALUE_TYPE = '__max__'; -export const MAX_VALUE: Value = { + +export const RESERVED_VECTOR_KEY = '__vector__'; +export const VECTOR_MAP_VECTORS_KEY = 'value'; + +const RESERVED_SERVER_TIMESTAMP_KEY = 'server_timestamp'; + +export const RESERVED_MIN_KEY = '__min__'; +export const RESERVED_MAX_KEY = '__max__'; + +export const RESERVED_REGEX_KEY = '__regex__'; +export const RESERVED_REGEX_PATTERN_KEY = 'pattern'; +export const RESERVED_REGEX_OPTIONS_KEY = 'options'; + +export const RESERVED_BSON_OBJECT_ID_KEY = '__oid__'; + +export const RESERVED_INT32_KEY = '__int__'; + +export const RESERVED_BSON_TIMESTAMP_KEY = '__request_timestamp__'; +export const RESERVED_BSON_TIMESTAMP_SECONDS_KEY = 'seconds'; +export const RESERVED_BSON_TIMESTAMP_INCREMENT_KEY = 'increment'; + +export const RESERVED_BSON_BINARY_KEY = '__binary__'; + +export const INTERNAL_MIN_VALUE: Value = { + nullValue: 'NULL_VALUE' +}; + +export const INTERNAL_MAX_VALUE: Value = { mapValue: { fields: { - '__type__': { stringValue: MAX_VALUE_TYPE } + '__type__': { stringValue: RESERVED_MAX_KEY } } } }; -export const VECTOR_VALUE_SENTINEL = '__vector__'; -export const VECTOR_MAP_VECTORS_KEY = 'value'; - -export const MIN_VALUE: Value = { - nullValue: 'NULL_VALUE' +export const MIN_VECTOR_VALUE = { + mapValue: { + fields: { + [TYPE_KEY]: { stringValue: RESERVED_VECTOR_KEY }, + [VECTOR_MAP_VECTORS_KEY]: { + arrayValue: {} + } + } + } }; /** Extracts the backend's type order for the provided value. */ @@ -80,14 +106,31 @@ export function typeOrder(value: Value): TypeOrder { } else if ('arrayValue' in value) { return TypeOrder.ArrayValue; } else if ('mapValue' in value) { - if (isServerTimestamp(value)) { - return TypeOrder.ServerTimestampValue; - } else if (isMaxValue(value)) { - return TypeOrder.MaxValue; - } else if (isVectorValue(value)) { - return TypeOrder.VectorValue; + const valueType = detectSpecialMapType(value); + switch (valueType) { + case 'serverTimestampValue': + return TypeOrder.ServerTimestampValue; + case 'maxValue': + return TypeOrder.MaxValue; + case 'vectorValue': + return TypeOrder.VectorValue; + case 'regexValue': + return TypeOrder.RegexValue; + case 'bsonObjectIdValue': + return TypeOrder.BsonObjectIdValue; + case 'int32Value': + return TypeOrder.NumberValue; + case 'bsonTimestampValue': + return TypeOrder.BsonTimestampValue; + case 'bsonBinaryValue': + return TypeOrder.BsonBinaryValue; + case 'minKeyValue': + return TypeOrder.MinKeyValue; + case 'maxKeyValue': + return TypeOrder.MaxKeyValue; + default: + return TypeOrder.ObjectValue; } - return TypeOrder.ObjectValue; } else { return fail('Invalid value type: ' + JSON.stringify(value)); } @@ -107,6 +150,11 @@ export function valueEquals(left: Value, right: Value): boolean { switch (leftType) { case TypeOrder.NullValue: + case TypeOrder.MaxValue: + // MaxKeys are all equal. + case TypeOrder.MaxKeyValue: + // MinKeys are all equal. + case TypeOrder.MinKeyValue: return true; case TypeOrder.BooleanValue: return left.booleanValue === right.booleanValue; @@ -133,8 +181,14 @@ export function valueEquals(left: Value, right: Value): boolean { case TypeOrder.VectorValue: case TypeOrder.ObjectValue: return objectEquals(left, right); - case TypeOrder.MaxValue: - return true; + case TypeOrder.BsonBinaryValue: + return compareBsonBinaryData(left, right) === 0; + case TypeOrder.BsonTimestampValue: + return compareBsonTimestamps(left, right) === 0; + case TypeOrder.RegexValue: + return compareRegex(left, right) === 0; + case TypeOrder.BsonObjectIdValue: + return compareBsonObjectIds(left, right) === 0; default: return fail('Unexpected value type: ' + JSON.stringify(left)); } @@ -174,10 +228,12 @@ function blobEquals(left: Value, right: Value): boolean { } export function numberEquals(left: Value, right: Value): boolean { - if ('integerValue' in left && 'integerValue' in right) { - return ( - normalizeNumber(left.integerValue) === normalizeNumber(right.integerValue) - ); + if ( + ('integerValue' in left && 'integerValue' in right) || + (detectSpecialMapType(left) === 'int32Value' && + detectSpecialMapType(right) === 'int32Value') + ) { + return extractNumber(left) === extractNumber(right); } else if ('doubleValue' in left && 'doubleValue' in right) { const n1 = normalizeNumber(left.doubleValue!); const n2 = normalizeNumber(right.doubleValue!); @@ -237,6 +293,8 @@ export function valueCompare(left: Value, right: Value): number { switch (leftType) { case TypeOrder.NullValue: + case TypeOrder.MinKeyValue: + case TypeOrder.MaxKeyValue: case TypeOrder.MaxValue: return 0; case TypeOrder.BooleanValue: @@ -264,14 +322,33 @@ export function valueCompare(left: Value, right: Value): number { return compareVectors(left.mapValue!, right.mapValue!); case TypeOrder.ObjectValue: return compareMaps(left.mapValue!, right.mapValue!); + case TypeOrder.BsonTimestampValue: + return compareBsonTimestamps(left, right); + case TypeOrder.BsonBinaryValue: + return compareBsonBinaryData(left, right); + case TypeOrder.RegexValue: + return compareRegex(left, right); + case TypeOrder.BsonObjectIdValue: + return compareBsonObjectIds(left, right); + default: throw fail('Invalid value type: ' + leftType); } } +export function extractNumber(value: Value): number { + let numberValue; + if (detectSpecialMapType(value) === 'int32Value') { + numberValue = value.mapValue!.fields![RESERVED_INT32_KEY].integerValue!; + } else { + numberValue = value.integerValue || value.doubleValue; + } + return normalizeNumber(numberValue); +} + function compareNumbers(left: Value, right: Value): number { - const leftNumber = normalizeNumber(left.integerValue || left.doubleValue); - const rightNumber = normalizeNumber(right.integerValue || right.doubleValue); + const leftNumber = extractNumber(left); + const rightNumber = extractNumber(right); if (leftNumber < rightNumber) { return -1; @@ -379,11 +456,14 @@ function compareVectors(left: MapValue, right: MapValue): number { } function compareMaps(left: MapValue, right: MapValue): number { - if (left === MAX_VALUE.mapValue && right === MAX_VALUE.mapValue) { + if ( + left === INTERNAL_MAX_VALUE.mapValue && + right === INTERNAL_MAX_VALUE.mapValue + ) { return 0; - } else if (left === MAX_VALUE.mapValue) { + } else if (left === INTERNAL_MAX_VALUE.mapValue) { return 1; - } else if (right === MAX_VALUE.mapValue) { + } else if (right === INTERNAL_MAX_VALUE.mapValue) { return -1; } @@ -413,6 +493,80 @@ function compareMaps(left: MapValue, right: MapValue): number { return primitiveComparator(leftKeys.length, rightKeys.length); } +function compareBsonTimestamps(left: Value, right: Value): number { + const leftSecondField = + left.mapValue!.fields?.[RESERVED_BSON_TIMESTAMP_KEY].mapValue?.fields?.[ + RESERVED_BSON_TIMESTAMP_SECONDS_KEY + ]; + const rightSecondField = + right.mapValue!.fields?.[RESERVED_BSON_TIMESTAMP_KEY].mapValue?.fields?.[ + RESERVED_BSON_TIMESTAMP_SECONDS_KEY + ]; + + const leftIncrementField = + left.mapValue!.fields?.[RESERVED_BSON_TIMESTAMP_KEY].mapValue?.fields?.[ + RESERVED_BSON_TIMESTAMP_INCREMENT_KEY + ]; + const rightIncrementField = + right.mapValue!.fields?.[RESERVED_BSON_TIMESTAMP_KEY].mapValue?.fields?.[ + RESERVED_BSON_TIMESTAMP_INCREMENT_KEY + ]; + + const secondsDiff = compareNumbers(leftSecondField!, rightSecondField!); + return secondsDiff !== 0 + ? secondsDiff + : compareNumbers(leftIncrementField!, rightIncrementField!); +} + +function compareBsonBinaryData(left: Value, right: Value): number { + const leftBytes = + left.mapValue!.fields?.[RESERVED_BSON_BINARY_KEY]?.bytesValue; + const rightBytes = + right.mapValue!.fields?.[RESERVED_BSON_BINARY_KEY]?.bytesValue; + if (!rightBytes || !leftBytes) { + throw new Error('Received incorrect bytesValue for BsonBinaryData'); + } + return compareBlobs(leftBytes, rightBytes); +} + +function compareRegex(left: Value, right: Value): number { + const leftFields = left.mapValue!.fields; + const leftPattern = + leftFields?.[RESERVED_REGEX_KEY]?.mapValue?.fields?.[ + RESERVED_REGEX_PATTERN_KEY + ]?.stringValue ?? ''; + const leftOptions = + leftFields?.[RESERVED_REGEX_KEY]?.mapValue?.fields?.[ + RESERVED_REGEX_OPTIONS_KEY + ]?.stringValue ?? ''; + + const rightFields = right.mapValue!.fields; + const rightPattern = + rightFields?.[RESERVED_REGEX_KEY]?.mapValue?.fields?.[ + RESERVED_REGEX_PATTERN_KEY + ]?.stringValue ?? ''; + const rightOptions = + rightFields?.[RESERVED_REGEX_KEY]?.mapValue?.fields?.[ + RESERVED_REGEX_OPTIONS_KEY + ]?.stringValue ?? ''; + + // First order by patterns, and then options. + const patternDiff = primitiveComparator(leftPattern, rightPattern); + return patternDiff !== 0 + ? patternDiff + : primitiveComparator(leftOptions, rightOptions); +} + +function compareBsonObjectIds(left: Value, right: Value): number { + const leftOid = + left.mapValue!.fields?.[RESERVED_BSON_OBJECT_ID_KEY]?.stringValue ?? ''; + const rightOid = + right.mapValue!.fields?.[RESERVED_BSON_OBJECT_ID_KEY]?.stringValue ?? ''; + + // TODO(Mila/BSON): use compareUtf8Strings once the bug fix is merged. + return primitiveComparator(leftOid, rightOid); +} + /** * Generates the canonical ID for the provided field value (as used in Target * serialization). @@ -443,6 +597,10 @@ function canonifyValue(value: Value): string { } else if ('arrayValue' in value) { return canonifyArray(value.arrayValue!); } else if ('mapValue' in value) { + // BsonBinaryValue contains an array of bytes, and needs to extract `subtype` and `data` from it before canonifying. + if (detectSpecialMapType(value) === 'bsonBinaryValue') { + return canonifyBsonBinaryData(value.mapValue!); + } return canonifyMap(value.mapValue!); } else { return fail('Invalid value type: ' + JSON.stringify(value)); @@ -466,6 +624,19 @@ function canonifyReference(referenceValue: string): string { return DocumentKey.fromName(referenceValue).toString(); } +function canonifyBsonBinaryData(mapValue: MapValue): string { + const fields = mapValue!.fields?.[RESERVED_BSON_BINARY_KEY]; + const subtypeAndData = fields?.bytesValue; + if (!subtypeAndData) { + throw new Error('Received incorrect bytesValue for BsonBinaryData'); + } + // Normalize the bytesValue to Uint8Array before extracting subtype and data. + const bytes = normalizeByteString(subtypeAndData).toUint8Array(); + return `{__binary__:{subType:${bytes.at(0)},data:${canonifyByteString( + bytes.slice(1) + )}}}`; +} + function canonifyMap(mapValue: MapValue): string { // Iteration order in JavaScript is not guaranteed. To ensure that we generate // matching canonical IDs for identical maps, we need to sort the keys. @@ -508,10 +679,16 @@ function canonifyArray(arrayValue: ArrayValue): string { export function estimateByteSize(value: Value): number { switch (typeOrder(value)) { case TypeOrder.NullValue: + // MinKeyValue and NullValue has same TypeOrder number, but MinKeyValue is encoded as MapValue + // and its size should be estimated differently. + if ('mapValue' in value) { + return estimateMapByteSize(value.mapValue!); + } return 4; case TypeOrder.BooleanValue: return 4; case TypeOrder.NumberValue: + // TODO(Mila/BSON): return 16 if the value is 128 decimal value return 8; case TypeOrder.TimestampValue: // Timestamps are made up of two distinct numbers (seconds + nanoseconds) @@ -535,6 +712,11 @@ export function estimateByteSize(value: Value): number { return estimateArrayByteSize(value.arrayValue!); case TypeOrder.VectorValue: case TypeOrder.ObjectValue: + case TypeOrder.RegexValue: + case TypeOrder.BsonObjectIdValue: + case TypeOrder.BsonBinaryValue: + case TypeOrder.BsonTimestampValue: + case TypeOrder.MaxKeyValue: return estimateMapByteSize(value.mapValue!); default: throw fail('Invalid value type: ' + JSON.stringify(value)); @@ -619,10 +801,67 @@ export function isMapValue( return !!value && 'mapValue' in value; } -/** Returns true if `value` is a VetorValue. */ +/** Returns true if `value` is a VectorValue. */ export function isVectorValue(value: ProtoValue | null): boolean { - const type = (value?.mapValue?.fields || {})[TYPE_KEY]?.stringValue; - return type === VECTOR_VALUE_SENTINEL; + return !!value && detectSpecialMapType(value) === 'vectorValue'; +} + +/** Returns true if the `Value` represents the canonical {@link #INTERNAL_MAX_VALUE} . */ +export function isMaxValue(value: Value): boolean { + return detectSpecialMapType(value) === 'maxValue'; +} + +function detectSpecialMapType(value: Value): string { + if (!value || !value.mapValue || !value.mapValue.fields) { + return ''; // Not a special map type + } + + const fields = value.mapValue.fields; + + // Check for type-based mappings + const type = fields[TYPE_KEY]?.stringValue; + if (type) { + const typeMap: Record = { + [RESERVED_VECTOR_KEY]: 'vectorValue', + [RESERVED_MAX_KEY]: 'maxValue', + [RESERVED_SERVER_TIMESTAMP_KEY]: 'serverTimestampValue' + }; + if (typeMap[type]) { + return typeMap[type]; + } + } + + // Check for BSON-related mappings + const bsonMap: Record = { + [RESERVED_REGEX_KEY]: 'regexValue', + [RESERVED_BSON_OBJECT_ID_KEY]: 'bsonObjectIdValue', + [RESERVED_INT32_KEY]: 'int32Value', + [RESERVED_BSON_TIMESTAMP_KEY]: 'bsonTimestampValue', + [RESERVED_BSON_BINARY_KEY]: 'bsonBinaryValue', + [RESERVED_MIN_KEY]: 'minKeyValue', + [RESERVED_MAX_KEY]: 'maxKeyValue' + }; + + for (const key in bsonMap) { + if (fields[key]) { + return bsonMap[key]; + } + } + + return ''; +} + +export function isBsonType(value: Value): boolean { + const bsonTypes = new Set([ + 'regexValue', + 'bsonObjectIdValue', + 'int32Value', + 'bsonTimestampValue', + 'bsonBinaryValue', + 'minKeyValue', + 'maxKeyValue' + ]); + return bsonTypes.has(detectSpecialMapType(value)); } /** Creates a deep copy of `source`. */ @@ -652,29 +891,10 @@ export function deepClone(source: Value): Value { } } -/** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */ -export function isMaxValue(value: Value): boolean { - return ( - (((value.mapValue || {}).fields || {})['__type__'] || {}).stringValue === - MAX_VALUE_TYPE - ); -} - -export const MIN_VECTOR_VALUE = { - mapValue: { - fields: { - [TYPE_KEY]: { stringValue: VECTOR_VALUE_SENTINEL }, - [VECTOR_MAP_VECTORS_KEY]: { - arrayValue: {} - } - } - } -}; - /** Returns the lowest value for the given value type (inclusive). */ export function valuesGetLowerBound(value: Value): Value { if ('nullValue' in value) { - return MIN_VALUE; + return INTERNAL_MIN_VALUE; } else if ('booleanValue' in value) { return { booleanValue: false }; } else if ('integerValue' in value || 'doubleValue' in value) { @@ -692,6 +912,7 @@ export function valuesGetLowerBound(value: Value): Value { } else if ('arrayValue' in value) { return { arrayValue: {} }; } else if ('mapValue' in value) { + // TODO(Mila/BSON): add lower bound for bson types for indexing if (isVectorValue(value)) { return MIN_VECTOR_VALUE; } @@ -722,10 +943,11 @@ export function valuesGetUpperBound(value: Value): Value { } else if ('arrayValue' in value) { return MIN_VECTOR_VALUE; } else if ('mapValue' in value) { + // TODO(Mila/BSON): add upper bound for bson types for indexing if (isVectorValue(value)) { return { mapValue: {} }; } - return MAX_VALUE; + return INTERNAL_MAX_VALUE; } else { return fail('Invalid value type: ' + JSON.stringify(value)); } diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index 1cda49d9229..2856862f6de 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -20,6 +20,7 @@ import { Deferred } from '@firebase/util'; import { expect, use } from 'chai'; import chaiAsPromised from 'chai-as-promised'; +import { AutoId } from '../../../src/util/misc'; import { EventsAccumulator } from '../util/events_accumulator'; import { addDoc, @@ -67,7 +68,15 @@ import { FirestoreError, QuerySnapshot, vector, - getDocsFromServer + getDocsFromServer, + bsonBinaryData, + bsonObjectId, + bsonTimestamp, + int32, + maxKey, + minKey, + regex, + or } from '../util/firebase_export'; import { apiDescribe, @@ -2424,4 +2433,433 @@ apiDescribe('Database', persistence => { }); }); }); + + describe('BSON types', () => { + // TODO(Mila/BSON): simplify the test setup once prod support BSON + const NIGHTLY_PROJECT_ID = 'firestore-sdk-nightly'; + const settings = { + ...DEFAULT_SETTINGS, + host: 'test-firestore.sandbox.googleapis.com' + }; + + it('can write and read BSON types', async () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const coll = collection(dbs[0], AutoId.newId()); + + const docRef = await addDoc(coll, { + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), + objectId: bsonObjectId('507f191e810c19729de860ea'), + int32: int32(1), + min: minKey(), + max: maxKey(), + regex: regex('^foo', 'i') + }); + + await setDoc( + docRef, + { + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: bsonTimestamp(1, 2), + int32: int32(2) + }, + { merge: true } + ); + + const snapshot = await getDoc(docRef); + expect( + snapshot + .get('objectId') + .isEqual(bsonObjectId('507f191e810c19729de860ea')) + ).to.be.true; + expect(snapshot.get('int32').isEqual(int32(2))).to.be.true; + expect(snapshot.get('min') === minKey()).to.be.true; + expect(snapshot.get('max') === maxKey()).to.be.true; + expect( + snapshot + .get('binary') + .isEqual(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ).to.be.true; + expect(snapshot.get('timestamp').isEqual(bsonTimestamp(1, 2))).to.be + .true; + expect(snapshot.get('regex').isEqual(regex('^foo', 'i'))).to.be.true; + } + ); + }); + + it('can filter and order objectIds', async () => { + const testDocs = { + a: { key: bsonObjectId('507f191e810c19729de860ea') }, + b: { key: bsonObjectId('507f191e810c19729de860eb') }, + c: { key: bsonObjectId('507f191e810c19729de860ec') } + }; + + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const coll = collection(dbs[0], AutoId.newId()); + await addDoc(coll, testDocs['a']); + await addDoc(coll, testDocs['b']); + await addDoc(coll, testDocs['c']); + + let orderedQuery = query( + coll, + where('key', '>', bsonObjectId('507f191e810c19729de860ea')), + orderBy('key', 'desc') + ); + + let snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['c'], + testDocs['b'] + ]); + + orderedQuery = query( + coll, + where('key', 'in', [ + bsonObjectId('507f191e810c19729de860ea'), + bsonObjectId('507f191e810c19729de860eb') + ]), + orderBy('key', 'desc') + ); + + snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['b'], + testDocs['a'] + ]); + } + ); + }); + + it('can filter and order Int32 values', async () => { + const testDocs = { + a: { key: int32(-1) }, + b: { key: int32(1) }, + c: { key: int32(2) } + }; + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const coll = collection(dbs[0], AutoId.newId()); + await addDoc(coll, testDocs['a']); + await addDoc(coll, testDocs['b']); + await addDoc(coll, testDocs['c']); + + let orderedQuery = query( + coll, + where('key', '>=', int32(1)), + orderBy('key', 'desc') + ); + + let snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['c'], + testDocs['b'] + ]); + + orderedQuery = query( + coll, + where('key', 'not-in', [int32(1)]), + orderBy('key', 'desc') + ); + + snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['c'], + testDocs['a'] + ]); + } + ); + }); + + it('can filter and order Timestamp values', async () => { + const testDocs = { + a: { key: bsonTimestamp(1, 1) }, + b: { key: bsonTimestamp(1, 2) }, + c: { key: bsonTimestamp(2, 1) } + }; + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const coll = collection(dbs[0], AutoId.newId()); + await addDoc(coll, testDocs['a']); + await addDoc(coll, testDocs['b']); + await addDoc(coll, testDocs['c']); + + let orderedQuery = query( + coll, + where('key', '>', bsonTimestamp(1, 1)), + orderBy('key', 'desc') + ); + + let snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['c'], + testDocs['b'] + ]); + + orderedQuery = query( + coll, + where('key', '!=', bsonTimestamp(1, 1)), + orderBy('key', 'desc') + ); + + snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['c'], + testDocs['b'] + ]); + } + ); + }); + + it('can filter and order Binary values', async () => { + const testDocs = { + a: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }, + b: { key: bsonBinaryData(1, new Uint8Array([1, 2, 4])) }, + c: { key: bsonBinaryData(2, new Uint8Array([1, 2, 3])) } + }; + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const coll = collection(dbs[0], AutoId.newId()); + await addDoc(coll, testDocs['a']); + await addDoc(coll, testDocs['b']); + await addDoc(coll, testDocs['c']); + + let orderedQuery = query( + coll, + where('key', '>', bsonBinaryData(1, new Uint8Array([1, 2, 3]))), + orderBy('key', 'desc') + ); + + let snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['c'], + testDocs['b'] + ]); + + orderedQuery = query( + coll, + where('key', '>=', bsonBinaryData(1, new Uint8Array([1, 2, 3]))), + where('key', '<', bsonBinaryData(2, new Uint8Array([1, 2, 3]))), + orderBy('key', 'desc') + ); + + snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['b'], + testDocs['a'] + ]); + } + ); + }); + + it('can filter and order Regex values', async () => { + const testDocs = { + a: { key: regex('^bar', 'i') }, + b: { key: regex('^bar', 'x') }, + c: { key: regex('^baz', 'i') } + }; + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const coll = collection(dbs[0], AutoId.newId()); + await addDoc(coll, testDocs['a']); + await addDoc(coll, testDocs['b']); + await addDoc(coll, testDocs['c']); + + const orderedQuery = query( + coll, + or( + where('key', '>', regex('^bar', 'x')), + where('key', '!=', regex('^bar', 'x')) + ), + orderBy('key', 'desc') + ); + + const snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['c'], + testDocs['a'] + ]); + } + ); + }); + + it('can filter and order minKey values', async () => { + const testDocs = { + a: { key: minKey() }, + b: { key: minKey() }, + c: { key: maxKey() } + }; + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const coll = collection(dbs[0], AutoId.newId()); + await addDoc(coll, testDocs['a']); + await addDoc(coll, testDocs['b']); + await addDoc(coll, testDocs['c']); + + const orderedQuery = query( + coll, + where('key', '==', minKey()), + orderBy('key', 'desc') // minKeys are equal, would sort by documentId as secondary order + ); + const snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['b'], + testDocs['a'] + ]); + } + ); + }); + + it('can filter and order maxKey values', async () => { + const testDocs = { + a: { key: minKey() }, + b: { key: maxKey() }, + c: { key: maxKey() } + }; + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const coll = collection(dbs[0], AutoId.newId()); + await addDoc(coll, testDocs['a']); + await addDoc(coll, testDocs['b']); + await addDoc(coll, testDocs['c']); + + const orderedQuery = query( + coll, + where('key', '==', maxKey()), + orderBy('key', 'desc') // maxKeys are equal, would sort by documentId as secondary order + ); + const snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['c'], + testDocs['b'] + ]); + } + ); + }); + + it('can listen to documents with bson types', async () => { + const testDocs = { + a: { key: maxKey() }, + b: { key: minKey() }, + c: { key: bsonTimestamp(1, 2) }, + d: { key: bsonObjectId('507f191e810c19729de860ea') }, + e: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }, + f: { key: regex('^foo', 'i') } + }; + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const coll = collection(dbs[0], AutoId.newId()); + await addDoc(coll, testDocs['a']); + await addDoc(coll, testDocs['b']); + await addDoc(coll, testDocs['c']); + await addDoc(coll, testDocs['d']); + await addDoc(coll, testDocs['e']); + await addDoc(coll, testDocs['f']); + + const orderedQuery = query(coll, orderBy('key', 'asc')); + + const storeEvent = new EventsAccumulator(); + const unsubscribe = onSnapshot(orderedQuery, storeEvent.storeEvent); + + let listenSnapshot = await storeEvent.awaitEvent(); + expect(toDataArray(listenSnapshot)).to.deep.equal([ + testDocs['b'], + testDocs['c'], + testDocs['e'], + testDocs['d'], + testDocs['f'], + testDocs['a'] + ]); + + const newData = { key: int32(2) }; + await setDoc(doc(coll, 'g'), newData); + listenSnapshot = await storeEvent.awaitEvent(); + expect(toDataArray(listenSnapshot)).to.deep.equal([ + testDocs['b'], + newData, + testDocs['c'], + testDocs['e'], + testDocs['d'], + testDocs['f'], + testDocs['a'] + ]); + + unsubscribe(); + } + ); + }); + + // TODO(Mila/BSON): Skip the runTransaction tests against nightly when running on browsers. remove when it is supported by prod + // eslint-disable-next-line no-restricted-properties + it.skip('can run transactions on documents with bson types', async () => { + const testDocs = { + a: { key: bsonTimestamp(1, 2) }, + b: { key: regex('^foo', 'i') }, + c: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) } + }; + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const coll = collection(dbs[0], AutoId.newId()); + const docA = await addDoc(coll, testDocs['a']); + const docB = await addDoc(coll, { key: 'place holder' }); + const docC = await addDoc(coll, testDocs['c']); + + await runTransaction(dbs[0], async transaction => { + const docSnapshot = await transaction.get(docA); + expect(docSnapshot.data()).to.deep.equal(testDocs['a']); + transaction.set(docB, testDocs['b']); + transaction.delete(docC); + }); + + const orderedQuery = query(coll, orderBy('key', 'asc')); + const snapshot = await getDocs(orderedQuery); + + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['a'], + testDocs['b'] + ]); + } + ); + }); + }); }); diff --git a/packages/firestore/test/integration/api/type.test.ts b/packages/firestore/test/integration/api/type.test.ts index 0fd9c19ccad..a6218f6a1ad 100644 --- a/packages/firestore/test/integration/api/type.test.ts +++ b/packages/firestore/test/integration/api/type.test.ts @@ -17,25 +17,46 @@ import { expect } from 'chai'; +import { AutoId } from '../../../src/util/misc'; import { addEqualityMatcher } from '../../util/equality_matcher'; import { EventsAccumulator } from '../util/events_accumulator'; import { + bsonBinaryData, + bsonObjectId, + bsonTimestamp, Bytes, collection, doc, + DocumentData, + DocumentReference, DocumentSnapshot, Firestore, + FirestoreError, GeoPoint, getDoc, getDocs, + int32, + maxKey, + minKey, onSnapshot, + orderBy, + query, QuerySnapshot, + refEqual, + regex, runTransaction, setDoc, Timestamp, - updateDoc + updateDoc, + vector } from '../util/firebase_export'; -import { apiDescribe, withTestDb, withTestDoc } from '../util/helpers'; +import { + apiDescribe, + withTestDb, + withTestDbsSettings, + withTestDoc +} from '../util/helpers'; +import { DEFAULT_SETTINGS } from '../util/settings'; apiDescribe('Firestore', persistence => { addEqualityMatcher(); @@ -82,6 +103,43 @@ apiDescribe('Firestore', persistence => { return docSnapshot; } + // TODO(Mila/BSON): Transactions against nightly is having issue, remove this after prod supports BSON + async function expectRoundtripWithoutTransaction( + db: Firestore, + data: {}, + validateSnapshots = true, + expectedData?: {} + ): Promise { + expectedData = expectedData ?? data; + + const collRef = collection(db, doc(collection(db, 'a')).id); + const docRef = doc(collRef); + + await setDoc(docRef, data); + let docSnapshot = await getDoc(docRef); + expect(docSnapshot.data()).to.deep.equal(expectedData); + + await updateDoc(docRef, data); + docSnapshot = await getDoc(docRef); + expect(docSnapshot.data()).to.deep.equal(expectedData); + + if (validateSnapshots) { + let querySnapshot = await getDocs(collRef); + docSnapshot = querySnapshot.docs[0]; + expect(docSnapshot.data()).to.deep.equal(expectedData); + + const eventsAccumulator = new EventsAccumulator(); + const unlisten = onSnapshot(collRef, eventsAccumulator.storeEvent); + querySnapshot = await eventsAccumulator.awaitEvent(); + docSnapshot = querySnapshot.docs[0]; + expect(docSnapshot.data()).to.deep.equal(expectedData); + + unlisten(); + } + + return docSnapshot; + } + it('can read and write null fields', () => { return withTestDb(persistence, async db => { await expectRoundtrip(db, { a: 1, b: null }); @@ -177,4 +235,337 @@ apiDescribe('Firestore', persistence => { await expectRoundtrip(db, { a: 42, refs: [doc] }); }); }); + + it('can read and write vector fields', () => { + return withTestDoc(persistence, async (doc, db) => { + await expectRoundtrip(db, { vector: vector([1, 2, 3]) }); + }); + }); + + // TODO(Mila/BSON): simplify the test setup once prod support BSON + const NIGHTLY_PROJECT_ID = 'firestore-sdk-nightly'; + const settings = { + ...DEFAULT_SETTINGS, + host: 'test-firestore.sandbox.googleapis.com' + }; + + it('can read and write minKey fields', () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + await expectRoundtripWithoutTransaction(dbs[0], { min: minKey() }); + } + ); + }); + + it('can read and write maxKey fields', () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + await expectRoundtripWithoutTransaction(dbs[0], { max: maxKey() }); + } + ); + }); + + it('can read and write regex fields', () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + await expectRoundtripWithoutTransaction(dbs[0], { + regex: regex('^foo', 'i') + }); + } + ); + }); + + it('can read and write int32 fields', () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + await expectRoundtripWithoutTransaction(dbs[0], { int32: int32(1) }); + } + ); + }); + + it('can read and write bsonTimestamp fields', () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + await expectRoundtripWithoutTransaction(dbs[0], { + bsonTimestamp: bsonTimestamp(1, 2) + }); + } + ); + }); + + it('can read and write bsonObjectId fields', () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + await expectRoundtripWithoutTransaction(dbs[0], { + objectId: bsonObjectId('507f191e810c19729de860ea') + }); + } + ); + }); + + it('can read and write bsonBinaryData fields', () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + await expectRoundtripWithoutTransaction(dbs[0], { + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + }); + } + ); + }); + + it('can read and write bson fields in an array', () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + await expectRoundtripWithoutTransaction(dbs[0], { + array: [ + bsonBinaryData(1, new Uint8Array([1, 2, 3])), + bsonObjectId('507f191e810c19729de860ea'), + int32(1), + minKey(), + maxKey(), + regex('^foo', 'i') + ] + }); + } + ); + }); + + it('can read and write bson fields in an object', () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + await expectRoundtripWithoutTransaction(dbs[0], { + object: { + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), + objectId: bsonObjectId('507f191e810c19729de860ea'), + int32: int32(1), + min: minKey(), + max: maxKey(), + regex: regex('^foo', 'i') + } + }); + } + ); + }); + + it('invalid 32-bit integer gets rejected', async () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const docRef = doc(dbs[0], 'test-collection/test-doc'); + let errorMessage; + try { + await setDoc(docRef, { key: int32(2147483648) }); + } catch (err) { + errorMessage = (err as FirestoreError)?.message; + } + expect(errorMessage).to.contains( + "The field '__int__' value (2,147,483,648) is too large to be converted to a 32-bit integer." + ); + + try { + await setDoc(docRef, { key: int32(-2147483650) }); + } catch (err) { + errorMessage = (err as FirestoreError)?.message; + } + expect(errorMessage).to.contains( + "The field '__int__' value (-2,147,483,650) is too large to be converted to a 32-bit integer." + ); + } + ); + }); + + it('invalid BSON timestamp gets rejected', async () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const docRef = doc(dbs[0], 'test-collection/test-doc'); + let errorMessage; + try { + // BSON timestamp larger than 32-bit integer gets rejected + await setDoc(docRef, { key: bsonTimestamp(4294967296, 2) }); + } catch (err) { + errorMessage = (err as FirestoreError)?.message; + } + expect(errorMessage).to.contains( + "The field 'seconds' value (4,294,967,296) does not represent an unsigned 32-bit integer." + ); + + try { + // negative BSON timestamp gets rejected + await setDoc(docRef, { key: bsonTimestamp(-1, 2) }); + } catch (err) { + errorMessage = (err as FirestoreError)?.message; + } + expect(errorMessage).to.contains( + "The field 'seconds' value (-1) does not represent an unsigned 32-bit integer." + ); + } + ); + }); + + it('invalid regex value gets rejected', async () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const docRef = doc(dbs[0], 'test-collection/test-doc'); + let errorMessage; + try { + await setDoc(docRef, { key: regex('foo', 'a') }); + } catch (err) { + errorMessage = (err as FirestoreError)?.message; + } + expect(errorMessage).to.contains( + "Invalid regex option 'a'. Supported options are 'i', 'm', 's', 'u', and 'x'." + ); + } + ); + }); + + it('invalid bsonObjectId value gets rejected', async () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const docRef = doc(dbs[0], 'test-collection/test-doc'); + + let errorMessage; + try { + // bsonObjectId with length not equal to 24 gets rejected + await setDoc(docRef, { key: bsonObjectId('foo') }); + } catch (err) { + errorMessage = (err as FirestoreError)?.message; + } + expect(errorMessage).to.contains( + 'Object ID hex string has incorrect length.' + ); + } + ); + }); + + it('invalid bsonBinaryData value gets rejected', async () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const docRef = doc(dbs[0], 'test-collection/test-doc'); + let errorMessage; + try { + await setDoc(docRef, { + key: bsonBinaryData(1234, new Uint8Array([1, 2, 3])) + }); + } catch (err) { + errorMessage = (err as FirestoreError)?.message; + } + expect(errorMessage).to.contains( + 'The subtype for BsonBinaryData must be a value in the inclusive [0, 255] range.' + ); + } + ); + }); + + it('can order values of different TypeOrder together', async () => { + const testDocs: { [key: string]: DocumentData } = { + nullValue: { key: null }, + minValue: { key: minKey() }, + booleanValue: { key: true }, + nanValue: { key: NaN }, + int32Value: { key: int32(1) }, + doubleValue: { key: 2.0 }, + integerValue: { key: 3 }, + timestampValue: { key: new Timestamp(100, 123456000) }, + bsonTimestampValue: { key: bsonTimestamp(1, 2) }, + stringValue: { key: 'string' }, + bytesValue: { key: Bytes.fromUint8Array(new Uint8Array([0, 1, 255])) }, + bsonBinaryValue: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }, + // referenceValue: {key: ref('coll/doc')}, + referenceValue: { key: 'placeholder' }, + objectIdValue: { key: bsonObjectId('507f191e810c19729de860ea') }, + geoPointValue: { key: new GeoPoint(0, 0) }, + regexValue: { key: regex('^foo', 'i') }, + arrayValue: { key: [1, 2] }, + vectorValue: { key: vector([1, 2]) }, + objectValue: { key: { a: 1 } }, + maxValue: { key: maxKey() } + }; + + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + const coll = collection(dbs[0], AutoId.newId()); + for (const key of Object.keys(testDocs)) { + await setDoc(doc(coll, key), testDocs[key]); + } + + // TODO(Mila/BSON): replace after prod supports bson + const docRef = doc(coll, 'doc'); + await setDoc(doc(coll, 'referenceValue'), { key: docRef }); + + const orderedQuery = query(coll, orderBy('key')); + const snapshot = await getDocs(orderedQuery); + for (let i = 0; i < snapshot.docs.length; i++) { + const actualDoc = snapshot.docs[i].data().key; + const expectedDoc = + testDocs[snapshot.docs[i].id as keyof typeof testDocs].key; + if (actualDoc instanceof DocumentReference) { + // deep.equal doesn't work with DocumentReference + expect(refEqual(actualDoc, docRef)).to.be.true; + } else { + expect(actualDoc).to.deep.equal(expectedDoc); + } + } + } + ); + }); }); diff --git a/packages/firestore/test/lite/integration.test.ts b/packages/firestore/test/lite/integration.test.ts index 780db5f4f9c..9b647587503 100644 --- a/packages/firestore/test/lite/integration.test.ts +++ b/packages/firestore/test/lite/integration.test.ts @@ -40,8 +40,15 @@ import { FieldValue } from '../../src/lite-api/field_value'; import { arrayRemove, arrayUnion, + bsonBinaryData, + bsonObjectId, + bsonTimestamp, deleteField, increment, + int32, + maxKey, + minKey, + regex, serverTimestamp, vector } from '../../src/lite-api/field_value_impl'; @@ -2960,3 +2967,44 @@ describe('Vectors', () => { }); }); }); + +// eslint-disable-next-line no-restricted-properties +describe.skip('BSON types', () => { + // TODO(Mila/BSON): enable this test once prod supports bson + it('can be read and written using the lite SDK', async () => { + return withTestCollection(async coll => { + const ref = await addDoc(coll, { + objectId: bsonObjectId('507f191e810c19729de860ea'), + int32: int32(1), + min: minKey(), + max: maxKey(), + regex: regex('^foo', 'i') + }); + + await setDoc( + ref, + { + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: bsonTimestamp(1, 2), + int32: int32(2) + }, + { merge: true } + ); + + const snap1 = await getDoc(ref); + expect( + snap1.get('objectId').isEqual(bsonObjectId('507f191e810c19729de860ea')) + ).to.be.true; + expect(snap1.get('int32').isEqual(int32(2))).to.be.true; + expect(snap1.get('min') === minKey()).to.be.true; + expect(snap1.get('max') === maxKey()).to.be.true; + expect( + snap1 + .get('binary') + .isEqual(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ).to.be.true; + expect(snap1.get('timestamp').isEqual(bsonTimestamp(1, 2))).to.be.true; + expect(snap1.get('regex').isEqual(regex('^foo', 'i'))).to.be.true; + }); + }); +}); diff --git a/packages/firestore/test/unit/model/document.test.ts b/packages/firestore/test/unit/model/document.test.ts index cfb93d15e6f..2c2387cca63 100644 --- a/packages/firestore/test/unit/model/document.test.ts +++ b/packages/firestore/test/unit/model/document.test.ts @@ -17,6 +17,15 @@ import { expect } from 'chai'; +import { + bsonBinaryData, + bsonObjectId, + bsonTimestamp, + int32, + maxKey, + minKey, + regex +} from '../../../src/lite-api/field_value_impl'; import { doc, expectEqual, @@ -44,6 +53,34 @@ describe('Document', () => { expect(document.hasLocalMutations).to.equal(false); }); + it('can be constructed with bson types', () => { + const data = { + objectId: bsonObjectId('foo'), + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: bsonTimestamp(1, 2), + min: minKey(), + max: maxKey(), + regex: regex('a', 'b'), + int32: int32(1) + }; + const document = doc('rooms/Eros', 1, data); + + const value = document.data; + expect(value.value).to.deep.equal( + wrap({ + objectId: bsonObjectId('foo'), + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: bsonTimestamp(1, 2), + min: minKey(), + max: maxKey(), + regex: regex('a', 'b'), + int32: int32(1) + }) + ); + expect(value).not.to.equal(data); + expect(document.hasLocalMutations).to.equal(false); + }); + it('returns fields correctly', () => { const data = { desc: 'Discuss all the project related stuff', diff --git a/packages/firestore/test/unit/model/object_value.test.ts b/packages/firestore/test/unit/model/object_value.test.ts index 9e96056d957..13cfa02131b 100644 --- a/packages/firestore/test/unit/model/object_value.test.ts +++ b/packages/firestore/test/unit/model/object_value.test.ts @@ -17,7 +17,16 @@ import { expect } from 'chai'; -import { vector } from '../../../src/lite-api/field_value_impl'; +import { + vector, + bsonObjectId, + bsonBinaryData, + bsonTimestamp, + int32, + regex, + minKey, + maxKey +} from '../../../src/lite-api/field_value_impl'; import { extractFieldMask, ObjectValue } from '../../../src/model/object_value'; import { TypeOrder } from '../../../src/model/type_order'; import { typeOrder } from '../../../src/model/values'; @@ -27,7 +36,16 @@ describe('ObjectValue', () => { it('can extract fields', () => { const objValue = wrapObject({ foo: { a: 1, b: true, c: 'string' }, - embedding: vector([1]) + embedding: vector([1]), + bson: { + objectId: bsonObjectId('foo'), + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: bsonTimestamp(1, 2), + min: minKey(), + max: maxKey(), + regex: regex('a', 'b'), + int32: int32(1) + } }); expect(typeOrder(objValue.field(field('foo'))!)).to.equal( @@ -45,6 +63,27 @@ describe('ObjectValue', () => { expect(typeOrder(objValue.field(field('embedding'))!)).to.equal( TypeOrder.VectorValue ); + expect(typeOrder(objValue.field(field('bson.objectId'))!)).to.equal( + TypeOrder.BsonObjectIdValue + ); + expect(typeOrder(objValue.field(field('bson.binary'))!)).to.equal( + TypeOrder.BsonBinaryValue + ); + expect(typeOrder(objValue.field(field('bson.timestamp'))!)).to.equal( + TypeOrder.BsonTimestampValue + ); + expect(typeOrder(objValue.field(field('bson.min'))!)).to.equal( + TypeOrder.MinKeyValue + ); + expect(typeOrder(objValue.field(field('bson.max'))!)).to.equal( + TypeOrder.MaxKeyValue + ); + expect(typeOrder(objValue.field(field('bson.regex'))!)).to.equal( + TypeOrder.RegexValue + ); + expect(typeOrder(objValue.field(field('bson.int32'))!)).to.equal( + TypeOrder.NumberValue + ); expect(objValue.field(field('foo.a.b'))).to.be.null; expect(objValue.field(field('bar'))).to.be.null; @@ -60,13 +99,42 @@ describe('ObjectValue', () => { expect(objValue.field(field('foo.a'))).to.deep.equal(wrap(1)); expect(objValue.field(field('foo.b'))).to.deep.equal(wrap(true)); expect(objValue.field(field('foo.c'))).to.deep.equal(wrap('string')); + + expect(objValue.field(field('bson'))!).to.deep.equal( + wrap({ + objectId: bsonObjectId('foo'), + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: bsonTimestamp(1, 2), + min: minKey(), + max: maxKey(), + regex: regex('a', 'b'), + int32: int32(1) + }) + ); + expect(objValue.field(field('bson.objectId'))!).to.deep.equal( + wrap(bsonObjectId('foo')) + ); + expect(objValue.field(field('bson.binary'))!).to.deep.equal( + wrap(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ); + expect(objValue.field(field('bson.timestamp'))!).to.deep.equal( + wrap(bsonTimestamp(1, 2)) + ); + expect(objValue.field(field('bson.min'))!).to.deep.equal(wrap(minKey())); + expect(objValue.field(field('bson.max'))!).to.deep.equal(wrap(maxKey())); + expect(objValue.field(field('bson.regex'))!).to.deep.equal( + wrap(regex('a', 'b')) + ); + expect(objValue.field(field('bson.int32'))!).to.deep.equal(wrap(int32(1))); }); it('can overwrite existing fields', () => { const objValue = wrapObject({ foo: 'foo-value' }); objValue.set(field('foo'), wrap('new-foo-value')); - assertObjectEquals(objValue, { foo: 'new-foo-value' }); + assertObjectEquals(objValue, { + foo: 'new-foo-value' + }); }); it('can add new fields', () => { @@ -163,11 +231,77 @@ describe('ObjectValue', () => { assertObjectEquals(objValue, {}); }); + it('can handle bson types in ObjectValue', () => { + const objValue = ObjectValue.empty(); + // Add new fields + objValue.set(field('objectId'), wrap(bsonObjectId('foo-value'))); + objValue.set( + field('binary'), + wrap(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ); + objValue.set(field('timestamp'), wrap(bsonTimestamp(1, 2))); + objValue.set(field('regex'), wrap(regex('a', 'b'))); + objValue.set(field('int32'), wrap(int32(1))); + objValue.set(field('min'), wrap(minKey())); + objValue.set(field('max'), wrap(maxKey())); + + assertObjectEquals(objValue, { + objectId: bsonObjectId('foo-value'), + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: bsonTimestamp(1, 2), + regex: regex('a', 'b'), + int32: int32(1), + min: minKey(), + max: maxKey() + }); + + // Overwrite existing fields + objValue.set(field('objectId'), wrap(bsonObjectId('new-foo-value'))); + + // Create nested objects + objValue.set( + field('foo.binary'), + wrap(bsonBinaryData(2, new Uint8Array([1, 2, 3]))) + ); + objValue.set(field('foo.timestamp'), wrap(bsonTimestamp(1, 2))); + + // Delete fields + objValue.delete(field('binary')); + + // overwrite nested objects + objValue.set(field('foo.timestamp'), wrap(bsonTimestamp(2, 1))); + + // Overwrite primitive values to create objects + objValue.set(field('min'), wrap(null)); + + assertObjectEquals(objValue, { + objectId: bsonObjectId('new-foo-value'), + timestamp: bsonTimestamp(1, 2), + regex: regex('a', 'b'), + int32: int32(1), + min: null, + max: maxKey(), + foo: { + binary: bsonBinaryData(2, new Uint8Array([1, 2, 3])), + timestamp: bsonTimestamp(2, 1) + } + }); + }); + it('provides field mask', () => { const objValue = wrapObject({ a: 'b', map: { a: 1, b: true, c: 'string', nested: { d: 'e' } }, - emptymap: {} + emptymap: {}, + bar: { + objectId: bsonObjectId('foo'), + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: bsonTimestamp(1, 2), + min: minKey(), + max: maxKey(), + regex: regex('a', 'b'), + int32: int32(1) + } }); const expectedMask = mask( 'a', @@ -175,7 +309,14 @@ describe('ObjectValue', () => { 'map.b', 'map.c', 'map.nested.d', - 'emptymap' + 'emptymap', + 'bar.objectId', + 'bar.binary', + 'bar.timestamp', + 'bar.min', + 'bar.max', + 'bar.regex', + 'bar.int32' ); const actualMask = extractFieldMask(objValue.value.mapValue); expect(actualMask.isEqual(expectedMask)).to.be.true; @@ -185,6 +326,6 @@ describe('ObjectValue', () => { objValue: ObjectValue, data: { [k: string]: unknown } ): void { - expect(objValue.isEqual(wrapObject(data))); + expect(objValue.isEqual(wrapObject(data))).to.be.true; } }); diff --git a/packages/firestore/test/unit/model/target.test.ts b/packages/firestore/test/unit/model/target.test.ts index bbeea5dec83..1fa2e58b298 100644 --- a/packages/firestore/test/unit/model/target.test.ts +++ b/packages/firestore/test/unit/model/target.test.ts @@ -31,8 +31,8 @@ import { import { IndexKind } from '../../../src/model/field_index'; import { canonicalId, - MAX_VALUE, - MIN_VALUE, + INTERNAL_MAX_VALUE, + INTERNAL_MIN_VALUE, valueEquals } from '../../../src/model/values'; import { @@ -207,11 +207,11 @@ describe('Target Bounds', () => { const index = fieldIndex('c', { fields: [['foo', IndexKind.ASCENDING]] }); const lowerBound = targetGetLowerBound(target, index); - expect(lowerBound?.position[0]).to.equal(MIN_VALUE); + expect(lowerBound?.position[0]).to.equal(INTERNAL_MIN_VALUE); expect(lowerBound?.inclusive).to.be.true; const upperBound = targetGetUpperBound(target, index); - expect(upperBound?.position[0]).to.equal(MAX_VALUE); + expect(upperBound?.position[0]).to.equal(INTERNAL_MAX_VALUE); expect(upperBound?.inclusive).to.be.true; }); @@ -241,7 +241,7 @@ describe('Target Bounds', () => { verifyBound(lowerBound, true, 'bar'); const upperBound = targetGetUpperBound(target, index); - expect(upperBound?.position[0]).to.equal(MAX_VALUE); + expect(upperBound?.position[0]).to.equal(INTERNAL_MAX_VALUE); expect(upperBound?.inclusive).to.be.true; }); @@ -337,7 +337,7 @@ describe('Target Bounds', () => { const index = fieldIndex('c', { fields: [['foo', IndexKind.ASCENDING]] }); const lowerBound = targetGetLowerBound(target, index); - expect(lowerBound?.position[0]).to.equal(MIN_VALUE); + expect(lowerBound?.position[0]).to.equal(INTERNAL_MIN_VALUE); expect(lowerBound?.inclusive).to.be.true; const upperBound = targetGetUpperBound(target, index); diff --git a/packages/firestore/test/unit/model/values.test.ts b/packages/firestore/test/unit/model/values.test.ts index 722d2db6fa5..bf46386c800 100644 --- a/packages/firestore/test/unit/model/values.test.ts +++ b/packages/firestore/test/unit/model/values.test.ts @@ -19,7 +19,23 @@ import { expect } from 'chai'; import { GeoPoint, Timestamp } from '../../../src'; import { DatabaseId } from '../../../src/core/database_info'; -import { vector } from '../../../src/lite-api/field_value_impl'; +import { BsonBinaryData } from '../../../src/lite-api/bson_binary_data'; +import { BsonObjectId } from '../../../src/lite-api/bson_object_Id'; +import { BsonTimestampValue } from '../../../src/lite-api/bson_timestamp_value'; +import { + vector, + regex, + bsonTimestamp, + int32, + bsonBinaryData, + bsonObjectId, + minKey, + maxKey +} from '../../../src/lite-api/field_value_impl'; +import { Int32Value } from '../../../src/lite-api/int32_value'; +import { MaxKey } from '../../../src/lite-api/max_key'; +import { MinKey } from '../../../src/lite-api/min_key'; +import { RegexValue } from '../../../src/lite-api/regex_value'; import { serverTimestamp } from '../../../src/model/server_timestamps'; import { canonicalId, @@ -31,7 +47,7 @@ import { valuesGetLowerBound, valuesGetUpperBound, TYPE_KEY, - VECTOR_VALUE_SENTINEL, + RESERVED_VECTOR_KEY, VECTOR_MAP_VECTORS_KEY } from '../../../src/model/values'; import * as api from '../../../src/protos/firestore_proto_api'; @@ -55,7 +71,14 @@ describe('Values', () => { const values: api.Value[][] = [ [wrap(true), wrap(true)], [wrap(false), wrap(false)], - [wrap(null), wrap(null)], + // MinKeys are all equal, and sort the same as null. + [ + wrap(null), + wrap(null), + wrap(minKey()), + wrap(minKey()), + wrap(MinKey.instance()) + ], [wrap(0 / 0), wrap(Number.NaN), wrap(NaN)], // -0.0 and 0.0 order the same but are not considered equal. [wrap(-0.0)], @@ -92,7 +115,21 @@ describe('Values', () => { [wrap({ bar: 1, foo: 1 })], [wrap({ foo: 1 })], [wrap(vector([]))], - [wrap(vector([1, 2.3, -4.0]))] + [wrap(vector([1, 2.3, -4.0]))], + [wrap(regex('^foo', 'i')), wrap(new RegexValue('^foo', 'i'))], + [wrap(bsonTimestamp(57, 4)), wrap(new BsonTimestampValue(57, 4))], + [ + wrap(bsonBinaryData(128, Uint8Array.from([7, 8, 9]))), + wrap(new BsonBinaryData(128, Uint8Array.from([7, 8, 9]))), + wrap(bsonBinaryData(128, Buffer.from([7, 8, 9]))), + wrap(new BsonBinaryData(128, Buffer.from([7, 8, 9]))) + ], + [ + wrap(bsonObjectId('123456789012')), + wrap(new BsonObjectId('123456789012')) + ], + [wrap(int32(255)), wrap(new Int32Value(255))], + [wrap(maxKey()), wrap(maxKey()), wrap(MaxKey.instance())] ]; expectEqualitySets(values, (v1, v2) => valueEquals(v1, v2)); }); @@ -129,7 +166,7 @@ describe('Values', () => { it('orders types correctly', () => { const groups = [ // null first - [wrap(null)], + [wrap(null), wrap(minKey())], // booleans [wrap(false)], @@ -141,15 +178,24 @@ describe('Values', () => { [wrap(-Number.MAX_VALUE)], [wrap(Number.MIN_SAFE_INTEGER - 1)], [wrap(Number.MIN_SAFE_INTEGER)], + // 64-bit and 32-bit integers order together numerically. + [{ integerValue: -2147483648 }, wrap(int32(-2147483648))], [wrap(-1.1)], - // Integers and Doubles order the same. - [{ integerValue: -1 }, { doubleValue: -1 }], + // Integers, Int32Values and Doubles order the same. + [{ integerValue: -1 }, { doubleValue: -1 }, wrap(int32(-1))], [wrap(-Number.MIN_VALUE)], // zeros all compare the same. - [{ integerValue: 0 }, { doubleValue: 0 }, { doubleValue: -0 }], + [ + { integerValue: 0 }, + { doubleValue: 0 }, + { doubleValue: -0 }, + wrap(int32(0)) + ], [wrap(Number.MIN_VALUE)], - [{ integerValue: 1 }, { doubleValue: 1 }], + [{ integerValue: 1 }, { doubleValue: 1.0 }, wrap(int32(1))], [wrap(1.1)], + [wrap(int32(2))], + [wrap(int32(2147483647))], [wrap(Number.MAX_SAFE_INTEGER)], [wrap(Number.MAX_SAFE_INTEGER + 1)], [wrap(Infinity)], @@ -164,6 +210,11 @@ describe('Values', () => { { timestampValue: '2020-04-05T14:30:01.000000000Z' } ], + // request timestamp + [wrap(bsonTimestamp(123, 4))], + [wrap(bsonTimestamp(123, 5))], + [wrap(bsonTimestamp(124, 0))], + // server timestamps come after all concrete timestamps. [serverTimestamp(Timestamp.fromDate(date1), null)], [serverTimestamp(Timestamp.fromDate(date2), null)], @@ -187,6 +238,13 @@ describe('Values', () => { [wrap(blob(0, 1, 2, 4, 3))], [wrap(blob(255))], + [ + wrap(bsonBinaryData(5, Buffer.from([1, 2, 3]))), + wrap(bsonBinaryData(5, new Uint8Array([1, 2, 3]))) + ], + [wrap(bsonBinaryData(7, Buffer.from([1])))], + [wrap(bsonBinaryData(7, new Uint8Array([2])))], + // reference values [refValue(dbId('p1', 'd1'), key('c1/doc1'))], [refValue(dbId('p1', 'd1'), key('c1/doc2'))], @@ -195,6 +253,13 @@ describe('Values', () => { [refValue(dbId('p1', 'd2'), key('c1/doc1'))], [refValue(dbId('p2', 'd1'), key('c1/doc1'))], + // ObjectId + [wrap(bsonObjectId('foo')), wrap(bsonObjectId('foo'))], + // TODO(Mila/BSON): uncomment after string sort bug is fixed + // [wrap(bsonObjectId('Ḟoo'))], // with latin capital letter f with dot above + // [wrap(bsonObjectId('foo\u0301'))], // with combining acute accent + [wrap(bsonObjectId('xyz'))], + // geo points [wrap(new GeoPoint(-90, -180))], [wrap(new GeoPoint(-90, 0))], @@ -209,6 +274,12 @@ describe('Values', () => { [wrap(new GeoPoint(90, 0))], [wrap(new GeoPoint(90, 180))], + // regular expressions + [wrap(regex('a', 'bar1'))], + [wrap(regex('foo', 'bar1'))], + [wrap(regex('foo', 'bar2'))], + [wrap(regex('go', 'bar1'))], + // arrays [wrap([])], [wrap(['bar'])], @@ -227,7 +298,10 @@ describe('Values', () => { [wrap({ bar: 0, foo: 1 })], [wrap({ foo: 1 })], [wrap({ foo: 2 })], - [wrap({ foo: '0' })] + [wrap({ foo: '0' })], + + // MaxKey + [wrap(maxKey())] ]; expectCorrectComparisonGroups( @@ -331,7 +405,31 @@ describe('Values', () => { { expectedByteSize: 49, elements: [wrap(vector([1, 2])), wrap(vector([-100, 20000098.123445]))] - } + }, + { + expectedByteSize: 27, + elements: [wrap(regex('a', 'b')), wrap(regex('c', 'd'))] + }, + { + expectedByteSize: 13, + elements: [wrap(bsonObjectId('foo')), wrap(bsonObjectId('bar'))] + }, + { + expectedByteSize: 53, + elements: [wrap(bsonTimestamp(1, 2)), wrap(bsonTimestamp(3, 4))] + }, + { + expectedByteSize: 8, + elements: [wrap(int32(1)), wrap(int32(2147483647))] + }, + { + expectedByteSize: 16, + elements: [ + wrap(bsonBinaryData(1, new Uint8Array([127, 128]))), + wrap(bsonBinaryData(128, new Uint8Array([1, 2]))) + ] + }, + { expectedByteSize: 11, elements: [wrap(minKey()), wrap(maxKey())] } ]; for (const group of equalityGroups) { @@ -361,7 +459,13 @@ describe('Values', () => { [wrap({ a: 'a', b: 'b' }), wrap({ a: 'a', bc: 'b' })], [wrap({ a: 'a', b: 'b' }), wrap({ a: 'a', b: 'b', c: 'c' })], [wrap({ a: 'a', b: 'b' }), wrap({ a: 'a', b: 'b', c: 'c' })], - [wrap(vector([2, 3])), wrap(vector([1, 2, 3]))] + [wrap(vector([2, 3])), wrap(vector([1, 2, 3]))], + [wrap(regex('a', 'b')), wrap(regex('cc', 'dd'))], + [wrap(bsonObjectId('foo')), wrap(bsonObjectId('foobar'))], + [ + wrap(bsonBinaryData(128, new Uint8Array([127, 128]))), + wrap(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ] ]; for (const group of relativeGroups) { @@ -376,9 +480,14 @@ describe('Values', () => { }); it('computes lower bound', () => { + // TODO(Mila/BSON):add cases for bson types const groups = [ - // null first - [valuesGetLowerBound({ nullValue: 'NULL_VALUE' }), wrap(null)], + // null and minKey first + [ + valuesGetLowerBound({ nullValue: 'NULL_VALUE' }), + wrap(null), + wrap(minKey()) + ], // booleans [valuesGetLowerBound({ booleanValue: true }), wrap(false)], @@ -420,7 +529,7 @@ describe('Values', () => { valuesGetLowerBound({ mapValue: { fields: { - [TYPE_KEY]: { stringValue: VECTOR_VALUE_SENTINEL }, + [TYPE_KEY]: { stringValue: RESERVED_VECTOR_KEY }, [VECTOR_MAP_VECTORS_KEY]: { arrayValue: { values: [{ doubleValue: 1 }] @@ -433,7 +542,10 @@ describe('Values', () => { ], // objects - [valuesGetLowerBound({ mapValue: {} }), wrap({})] + [valuesGetLowerBound({ mapValue: {} }), wrap({})], + + // MaxKey + [wrap(maxKey())] ]; expectCorrectComparisonGroups( @@ -445,6 +557,7 @@ describe('Values', () => { }); it('computes upper bound', () => { + // TODO(Mila/BSON):add cases for bson types const groups = [ // null first [wrap(null)], @@ -526,6 +639,19 @@ describe('Values', () => { expect( canonicalId(wrap({ 'a': ['b', { 'c': new GeoPoint(30, 60) }] })) ).to.equal('{a:[b,{c:geo(30,60)}]}'); + expect(canonicalId(wrap(regex('a', 'b')))).to.equal( + '{__regex__:{options:b,pattern:a}}' + ); + expect(canonicalId(wrap(bsonObjectId('foo')))).to.equal('{__oid__:foo}'); + expect(canonicalId(wrap(bsonTimestamp(1, 2)))).to.equal( + '{__request_timestamp__:{increment:2,seconds:1}}' + ); + expect(canonicalId(wrap(int32(1)))).to.equal('{__int__:1}'); + expect( + canonicalId(wrap(bsonBinaryData(1, new Uint8Array([1, 2, 3])))) + ).to.equal('{__binary__:{subType:1,data:AQID}}'); + expect(canonicalId(wrap(minKey()))).to.equal('{__min__:null}'); + expect(canonicalId(wrap(maxKey()))).to.equal('{__max__:null}'); }); it('canonical IDs ignore sort order', () => { diff --git a/packages/firestore/test/unit/remote/serializer.helper.ts b/packages/firestore/test/unit/remote/serializer.helper.ts index d523c8fab83..9c116549928 100644 --- a/packages/firestore/test/unit/remote/serializer.helper.ts +++ b/packages/firestore/test/unit/remote/serializer.helper.ts @@ -52,7 +52,16 @@ import { } from '../../../src/core/query'; import { SnapshotVersion } from '../../../src/core/snapshot_version'; import { Target, targetEquals, TargetImpl } from '../../../src/core/target'; -import { vector } from '../../../src/lite-api/field_value_impl'; +import { + bsonBinaryData, + bsonObjectId, + bsonTimestamp, + int32, + maxKey, + minKey, + regex, + vector +} from '../../../src/lite-api/field_value_impl'; import { parseQueryValue } from '../../../src/lite-api/user_data_reader'; import { TargetData, TargetPurpose } from '../../../src/local/target_data'; import { FieldMask } from '../../../src/model/field_mask'; @@ -565,6 +574,57 @@ export function serializerTest( jsonValue: expectedJson.mapValue }); }); + + it('converts BSON types in mapValue', () => { + const examples = [ + bsonObjectId('foo'), + bsonTimestamp(1, 2), + minKey(), + maxKey(), + regex('a', 'b'), + int32(1) + ]; + + for (const example of examples) { + expect(userDataWriter.convertValue(wrap(example))).to.deep.equal( + example + ); + + verifyFieldValueRoundTrip({ + value: example, + valueType: 'mapValue', + jsonValue: wrap(example).mapValue + }); + } + + // BsonBinaryData will be serialized differently Proto3Json VS. regular Protobuf format + const bsonBinary = bsonBinaryData(1, new Uint8Array([1, 2, 3])); + const expectedJson: api.Value = { + mapValue: { + fields: { + '__binary__': { + 'bytesValue': 'AQECAw==' + } + } + } + }; + + const expectedProtoJson: api.Value = { + mapValue: { + fields: { + '__binary__': { + 'bytesValue': new Uint8Array([1, 1, 2, 3]) + } + } + } + }; + verifyFieldValueRoundTrip({ + value: bsonBinary, + valueType: 'mapValue', + jsonValue: expectedJson.mapValue, + protoJsValue: expectedProtoJson.mapValue + }); + }); }); describe('toKey', () => { From 3c743b25c77fa076f6bc4db254e5e02036773de3 Mon Sep 17 00:00:00 2001 From: Mila <107142260+milaGGL@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:38:22 -0400 Subject: [PATCH 02/77] Implement indexing for bson types (#331) --- packages/firestore/src/core/target.ts | 6 +- .../src/index/firestore_index_value_writer.ts | 102 ++- packages/firestore/src/lite-api/query.ts | 27 + .../src/lite-api/user_data_writer.ts | 5 +- packages/firestore/src/model/type_order.ts | 39 +- packages/firestore/src/model/values.ts | 231 +++++-- .../test/integration/api/database.test.ts | 424 +++++++++--- .../test/integration/api/query.test.ts | 14 - .../test/integration/api/type.test.ts | 55 +- .../test/integration/api/validation.test.ts | 14 + .../test/integration/util/helpers.ts | 74 ++- .../firestore_index_value_writer.test.ts | 366 ++++++++++- .../test/unit/local/index_manager.test.ts | 607 +++++++++++++++++- .../firestore/test/unit/model/values.test.ts | 117 +++- 14 files changed, 1823 insertions(+), 258 deletions(-) diff --git a/packages/firestore/src/core/target.ts b/packages/firestore/src/core/target.ts index 664a2ef9a08..cc2732e8f8a 100644 --- a/packages/firestore/src/core/target.ts +++ b/packages/firestore/src/core/target.ts @@ -28,6 +28,8 @@ import { INTERNAL_MAX_VALUE, INTERNAL_MIN_VALUE, lowerBoundCompare, + MAX_KEY_VALUE, + MIN_KEY_VALUE, upperBoundCompare, valuesGetLowerBound, valuesGetUpperBound @@ -387,7 +389,7 @@ function targetGetAscendingBound( break; case Operator.NOT_EQUAL: case Operator.NOT_IN: - filterValue = INTERNAL_MIN_VALUE; + filterValue = MIN_KEY_VALUE; break; default: // Remaining filters cannot be used as lower bounds. @@ -462,7 +464,7 @@ function targetGetDescendingBound( break; case Operator.NOT_EQUAL: case Operator.NOT_IN: - filterValue = INTERNAL_MAX_VALUE; + filterValue = MAX_KEY_VALUE; break; default: // Remaining filters cannot be used as upper bounds. diff --git a/packages/firestore/src/index/firestore_index_value_writer.ts b/packages/firestore/src/index/firestore_index_value_writer.ts index f831862a0de..d02a07313fe 100644 --- a/packages/firestore/src/index/firestore_index_value_writer.ts +++ b/packages/firestore/src/index/firestore_index_value_writer.ts @@ -22,9 +22,16 @@ import { normalizeTimestamp } from '../model/normalize'; import { - isVectorValue, VECTOR_MAP_VECTORS_KEY, - isMaxValue + detectSpecialMapType, + RESERVED_BSON_TIMESTAMP_KEY, + RESERVED_REGEX_KEY, + RESERVED_BSON_OBJECT_ID_KEY, + RESERVED_BSON_BINARY_KEY, + SpecialMapValueType, + RESERVED_REGEX_PATTERN_KEY, + RESERVED_REGEX_OPTIONS_KEY, + RESERVED_INT32_KEY } from '../model/values'; import { ArrayValue, MapValue, Value } from '../protos/firestore_proto_api'; import { fail } from '../util/assert'; @@ -32,22 +39,28 @@ import { isNegativeZero } from '../util/types'; import { DirectionalIndexByteEncoder } from './directional_index_byte_encoder'; -// Note: This code is copied from the backend. Code that is not used by -// Firestore was removed. +// Note: This file is copied from the backend. Code that is not used by +// Firestore was removed. Code that has different behavior was modified. const INDEX_TYPE_NULL = 5; +const INDEX_TYPE_MIN_KEY = 7; const INDEX_TYPE_BOOLEAN = 10; const INDEX_TYPE_NAN = 13; const INDEX_TYPE_NUMBER = 15; const INDEX_TYPE_TIMESTAMP = 20; +const INDEX_TYPE_BSON_TIMESTAMP = 22; const INDEX_TYPE_STRING = 25; const INDEX_TYPE_BLOB = 30; +const INDEX_TYPE_BSON_BINARY = 31; const INDEX_TYPE_REFERENCE = 37; +const INDEX_TYPE_BSON_OBJECT_ID = 43; const INDEX_TYPE_GEOPOINT = 45; +const INDEX_TYPE_REGEX = 47; const INDEX_TYPE_ARRAY = 50; const INDEX_TYPE_VECTOR = 53; const INDEX_TYPE_MAP = 55; const INDEX_TYPE_REFERENCE_SEGMENT = 60; +const INDEX_TYPE_MAX_VALUE = 999; // A terminator that indicates that a truncatable value was not truncated. // This must be smaller than all other type labels. @@ -124,11 +137,30 @@ export class FirestoreIndexValueWriter { encoder.writeNumber(geoPoint.latitude || 0); encoder.writeNumber(geoPoint.longitude || 0); } else if ('mapValue' in indexValue) { - // TODO(Mila/BSON): add bson types for indexing - if (isMaxValue(indexValue)) { + const type = detectSpecialMapType(indexValue); + if (type === SpecialMapValueType.INTERNAL_MAX) { this.writeValueTypeLabel(encoder, Number.MAX_SAFE_INTEGER); - } else if (isVectorValue(indexValue)) { + } else if (type === SpecialMapValueType.VECTOR) { this.writeIndexVector(indexValue.mapValue!, encoder); + } else if (type === SpecialMapValueType.MAX_KEY) { + this.writeValueTypeLabel(encoder, INDEX_TYPE_MAX_VALUE); + } else if (type === SpecialMapValueType.MIN_KEY) { + this.writeValueTypeLabel(encoder, INDEX_TYPE_MIN_KEY); + } else if (type === SpecialMapValueType.BSON_BINARY) { + this.writeIndexBsonBinaryData(indexValue.mapValue!, encoder); + } else if (type === SpecialMapValueType.REGEX) { + this.writeIndexRegex(indexValue.mapValue!, encoder); + } else if (type === SpecialMapValueType.BSON_TIMESTAMP) { + this.writeIndexBsonTimestamp(indexValue.mapValue!, encoder); + } else if (type === SpecialMapValueType.BSON_OBJECT_ID) { + this.writeIndexBsonObjectId(indexValue.mapValue!, encoder); + } else if (type === SpecialMapValueType.INT32) { + this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER); + encoder.writeNumber( + normalizeNumber( + indexValue.mapValue!.fields![RESERVED_INT32_KEY]!.integerValue! + ) + ); } else { this.writeIndexMap(indexValue.mapValue!, encoder); this.writeTruncationMarker(encoder); @@ -202,7 +234,10 @@ export class FirestoreIndexValueWriter { encoder: DirectionalIndexByteEncoder ): void { this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE); - const path = DocumentKey.fromName(referenceValue).path; + const segments: string[] = referenceValue + .split('/') + .filter(segment => segment.length > 0); + const path = DocumentKey.fromSegments(segments.slice(5)).path; path.forEach(segment => { this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE_SEGMENT); this.writeUnlabeledIndexString(segment, encoder); @@ -222,4 +257,55 @@ export class FirestoreIndexValueWriter { // references, arrays and maps). encoder.writeNumber(NOT_TRUNCATED); } + + private writeIndexBsonTimestamp( + mapValue: MapValue, + encoder: DirectionalIndexByteEncoder + ): void { + this.writeValueTypeLabel(encoder, INDEX_TYPE_BSON_TIMESTAMP); + const fields = mapValue.fields || {}; + if (fields) { + // The JS SDK encodes BSON timestamps differently than the backend. + // This is due to the limitation of `number` in JS which handles up to 53-bit precision. + this.writeIndexMap( + fields[RESERVED_BSON_TIMESTAMP_KEY].mapValue!, + encoder + ); + } + } + + private writeIndexBsonObjectId( + mapValue: MapValue, + encoder: DirectionalIndexByteEncoder + ): void { + this.writeValueTypeLabel(encoder, INDEX_TYPE_BSON_OBJECT_ID); + const fields = mapValue.fields || {}; + const oid = fields[RESERVED_BSON_OBJECT_ID_KEY]?.stringValue || ''; + encoder.writeBytes(normalizeByteString(oid)); + } + + private writeIndexBsonBinaryData( + mapValue: MapValue, + encoder: DirectionalIndexByteEncoder + ): void { + this.writeValueTypeLabel(encoder, INDEX_TYPE_BSON_BINARY); + const fields = mapValue.fields || {}; + const binary = fields[RESERVED_BSON_BINARY_KEY]?.bytesValue || ''; + encoder.writeBytes(normalizeByteString(binary)); + this.writeTruncationMarker(encoder); + } + + private writeIndexRegex( + mapValue: MapValue, + encoder: DirectionalIndexByteEncoder + ): void { + this.writeValueTypeLabel(encoder, INDEX_TYPE_REGEX); + const fields = mapValue.fields || {}; + const regex = fields[RESERVED_REGEX_KEY]?.mapValue?.fields || {}; + if (regex) { + encoder.writeString(regex[RESERVED_REGEX_PATTERN_KEY]?.stringValue || ''); + encoder.writeString(regex[RESERVED_REGEX_OPTIONS_KEY]?.stringValue || ''); + } + this.writeTruncationMarker(encoder); + } } diff --git a/packages/firestore/src/lite-api/query.ts b/packages/firestore/src/lite-api/query.ts index f0a357b828c..67245f96d07 100644 --- a/packages/firestore/src/lite-api/query.ts +++ b/packages/firestore/src/lite-api/query.ts @@ -811,6 +811,8 @@ export function newQueryFilter( value: unknown ): FieldFilter { let fieldValue: ProtoValue; + validateQueryOperator(value, op); + if (fieldPath.isKeyField()) { if (op === Operator.ARRAY_CONTAINS || op === Operator.ARRAY_CONTAINS_ANY) { throw new FirestoreError( @@ -1064,6 +1066,31 @@ function validateDisjunctiveFilterElements( } } +/** + * Validates the input string as a field comparison operator. + */ +export function validateQueryOperator( + value: unknown, + operator: Operator +): void { + if ( + typeof value === 'number' && + isNaN(value) && + operator !== '==' && + operator !== '!=' + ) { + throw new Error( + "Invalid query. You can only perform '==' and '!=' comparisons on NaN." + ); + } + + if (value === null && operator !== '==' && operator !== '!=') { + throw new Error( + "Invalid query. You can only perform '==' and '!=' comparisons on Null." + ); + } +} + /** * Given an operator, returns the set of operators that cannot be used with it. * diff --git a/packages/firestore/src/lite-api/user_data_writer.ts b/packages/firestore/src/lite-api/user_data_writer.ts index 0de02b822b2..012b04874c3 100644 --- a/packages/firestore/src/lite-api/user_data_writer.ts +++ b/packages/firestore/src/lite-api/user_data_writer.ts @@ -83,9 +83,6 @@ export abstract class AbstractUserDataWriter { ): unknown { switch (typeOrder(value)) { case TypeOrder.NullValue: - if ('mapValue' in value) { - return minKey(); - } return null; case TypeOrder.BooleanValue: return value.booleanValue!; @@ -122,6 +119,8 @@ export abstract class AbstractUserDataWriter { return this.convertToBsonTimestampValue(value.mapValue!); case TypeOrder.MaxKeyValue: return maxKey(); + case TypeOrder.MinKeyValue: + return minKey(); default: throw fail('Invalid value type: ' + JSON.stringify(value)); } diff --git a/packages/firestore/src/model/type_order.ts b/packages/firestore/src/model/type_order.ts index 7ac6ed11e47..a13e16f4211 100644 --- a/packages/firestore/src/model/type_order.ts +++ b/packages/firestore/src/model/type_order.ts @@ -27,24 +27,25 @@ export const enum TypeOrder { // server timestamps and `MAX_VALUE` inside the SDK. // NULL and MIN_KEY sort the same. NullValue = 0, - MinKeyValue = 0, - BooleanValue = 1, - NumberValue = 2, - TimestampValue = 3, - // TODO(Mila/BSON): which should come first considering indexes? - BsonTimestampValue = 4, - ServerTimestampValue = 5, - StringValue = 6, - BlobValue = 7, - BsonBinaryValue = 8, - RefValue = 9, - BsonObjectIdValue = 10, - GeoPointValue = 11, - RegexValue = 12, - ArrayValue = 13, - VectorValue = 14, - ObjectValue = 15, - // TODO(Mila/BSON):should MaxKeyValue and MaxValue combined? how would this affect indexes? - MaxKeyValue = 16, + MinKeyValue = 1, + BooleanValue = 2, + // Note: all numbers (32-bit int, 64-bit int, 64-bit double, 128-bit decimal, + // etc.) are sorted together numerically. The `numberEquals` function + // distinguishes between different number types and compares them accordingly. + NumberValue = 3, + TimestampValue = 4, + BsonTimestampValue = 5, + ServerTimestampValue = 6, + StringValue = 7, + BlobValue = 8, + BsonBinaryValue = 9, + RefValue = 10, + BsonObjectIdValue = 11, + GeoPointValue = 12, + RegexValue = 13, + ArrayValue = 14, + VectorValue = 15, + ObjectValue = 16, + MaxKeyValue = 17, MaxValue = 9007199254740991 // Number.MAX_SAFE_INTEGER } diff --git a/packages/firestore/src/model/values.ts b/packages/firestore/src/model/values.ts index d6344409ed9..02404130a77 100644 --- a/packages/firestore/src/model/values.ts +++ b/packages/firestore/src/model/values.ts @@ -21,7 +21,6 @@ import { LatLng, MapValue, Timestamp, - Value as ProtoValue, Value } from '../protos/firestore_proto_api'; import { fail } from '../util/assert'; @@ -74,7 +73,7 @@ export const INTERNAL_MAX_VALUE: Value = { } }; -export const MIN_VECTOR_VALUE = { +export const MIN_VECTOR_VALUE: Value = { mapValue: { fields: { [TYPE_KEY]: { stringValue: RESERVED_VECTOR_KEY }, @@ -85,6 +84,96 @@ export const MIN_VECTOR_VALUE = { } }; +export const MIN_KEY_VALUE: Value = { + mapValue: { + fields: { + [RESERVED_MIN_KEY]: { + nullValue: 'NULL_VALUE' + } + } + } +}; + +export const MAX_KEY_VALUE: Value = { + mapValue: { + fields: { + [RESERVED_MAX_KEY]: { + nullValue: 'NULL_VALUE' + } + } + } +}; + +export const MIN_BSON_OBJECT_ID_VALUE: Value = { + mapValue: { + fields: { + [RESERVED_BSON_OBJECT_ID_KEY]: { + stringValue: '' + } + } + } +}; + +export const MIN_BSON_TIMESTAMP_VALUE: Value = { + mapValue: { + fields: { + [RESERVED_BSON_TIMESTAMP_KEY]: { + mapValue: { + fields: { + // Both seconds and increment are 32 bit unsigned integers + [RESERVED_BSON_TIMESTAMP_SECONDS_KEY]: { + integerValue: 0 + }, + [RESERVED_BSON_TIMESTAMP_INCREMENT_KEY]: { + integerValue: 0 + } + } + } + } + } + } +}; + +export const MIN_REGEX_VALUE: Value = { + mapValue: { + fields: { + [RESERVED_REGEX_KEY]: { + mapValue: { + fields: { + [RESERVED_REGEX_PATTERN_KEY]: { stringValue: '' }, + [RESERVED_REGEX_OPTIONS_KEY]: { stringValue: '' } + } + } + } + } + } +}; + +export const MIN_BSON_BINARY_VALUE: Value = { + mapValue: { + fields: { + [RESERVED_BSON_BINARY_KEY]: { + // bsonBinaryValue should have at least one byte as subtype + bytesValue: Uint8Array.from([0]) + } + } + } +}; + +export enum SpecialMapValueType { + REGEX = 'regexValue', + BSON_OBJECT_ID = 'bsonObjectIdValue', + INT32 = 'int32Value', + BSON_TIMESTAMP = 'bsonTimestampValue', + BSON_BINARY = 'bsonBinaryValue', + MIN_KEY = 'minKeyValue', + MAX_KEY = 'maxKeyValue', + INTERNAL_MAX = 'maxValue', + VECTOR = 'vectorValue', + SERVER_TIMESTAMP = 'serverTimestampValue', + REGULAR_MAP = 'regularMapValue' +} + /** Extracts the backend's type order for the provided value. */ export function typeOrder(value: Value): TypeOrder { if ('nullValue' in value) { @@ -108,25 +197,25 @@ export function typeOrder(value: Value): TypeOrder { } else if ('mapValue' in value) { const valueType = detectSpecialMapType(value); switch (valueType) { - case 'serverTimestampValue': + case SpecialMapValueType.SERVER_TIMESTAMP: return TypeOrder.ServerTimestampValue; - case 'maxValue': + case SpecialMapValueType.INTERNAL_MAX: return TypeOrder.MaxValue; - case 'vectorValue': + case SpecialMapValueType.VECTOR: return TypeOrder.VectorValue; - case 'regexValue': + case SpecialMapValueType.REGEX: return TypeOrder.RegexValue; - case 'bsonObjectIdValue': + case SpecialMapValueType.BSON_OBJECT_ID: return TypeOrder.BsonObjectIdValue; - case 'int32Value': + case SpecialMapValueType.INT32: return TypeOrder.NumberValue; - case 'bsonTimestampValue': + case SpecialMapValueType.BSON_TIMESTAMP: return TypeOrder.BsonTimestampValue; - case 'bsonBinaryValue': + case SpecialMapValueType.BSON_BINARY: return TypeOrder.BsonBinaryValue; - case 'minKeyValue': + case SpecialMapValueType.MIN_KEY: return TypeOrder.MinKeyValue; - case 'maxKeyValue': + case SpecialMapValueType.MAX_KEY: return TypeOrder.MaxKeyValue; default: return TypeOrder.ObjectValue; @@ -230,8 +319,8 @@ function blobEquals(left: Value, right: Value): boolean { export function numberEquals(left: Value, right: Value): boolean { if ( ('integerValue' in left && 'integerValue' in right) || - (detectSpecialMapType(left) === 'int32Value' && - detectSpecialMapType(right) === 'int32Value') + (detectSpecialMapType(left) === SpecialMapValueType.INT32 && + detectSpecialMapType(right) === SpecialMapValueType.INT32) ) { return extractNumber(left) === extractNumber(right); } else if ('doubleValue' in left && 'doubleValue' in right) { @@ -338,7 +427,7 @@ export function valueCompare(left: Value, right: Value): number { export function extractNumber(value: Value): number { let numberValue; - if (detectSpecialMapType(value) === 'int32Value') { + if (detectSpecialMapType(value) === SpecialMapValueType.INT32) { numberValue = value.mapValue!.fields![RESERVED_INT32_KEY].integerValue!; } else { numberValue = value.integerValue || value.doubleValue; @@ -598,7 +687,7 @@ function canonifyValue(value: Value): string { return canonifyArray(value.arrayValue!); } else if ('mapValue' in value) { // BsonBinaryValue contains an array of bytes, and needs to extract `subtype` and `data` from it before canonifying. - if (detectSpecialMapType(value) === 'bsonBinaryValue') { + if (detectSpecialMapType(value) === SpecialMapValueType.BSON_BINARY) { return canonifyBsonBinaryData(value.mapValue!); } return canonifyMap(value.mapValue!); @@ -679,11 +768,6 @@ function canonifyArray(arrayValue: ArrayValue): string { export function estimateByteSize(value: Value): number { switch (typeOrder(value)) { case TypeOrder.NullValue: - // MinKeyValue and NullValue has same TypeOrder number, but MinKeyValue is encoded as MapValue - // and its size should be estimated differently. - if ('mapValue' in value) { - return estimateMapByteSize(value.mapValue!); - } return 4; case TypeOrder.BooleanValue: return 4; @@ -716,6 +800,7 @@ export function estimateByteSize(value: Value): number { case TypeOrder.BsonObjectIdValue: case TypeOrder.BsonBinaryValue: case TypeOrder.BsonTimestampValue: + case TypeOrder.MinKeyValue: case TypeOrder.MaxKeyValue: return estimateMapByteSize(value.mapValue!); default: @@ -801,19 +886,9 @@ export function isMapValue( return !!value && 'mapValue' in value; } -/** Returns true if `value` is a VectorValue. */ -export function isVectorValue(value: ProtoValue | null): boolean { - return !!value && detectSpecialMapType(value) === 'vectorValue'; -} - -/** Returns true if the `Value` represents the canonical {@link #INTERNAL_MAX_VALUE} . */ -export function isMaxValue(value: Value): boolean { - return detectSpecialMapType(value) === 'maxValue'; -} - -function detectSpecialMapType(value: Value): string { +export function detectSpecialMapType(value: Value): SpecialMapValueType { if (!value || !value.mapValue || !value.mapValue.fields) { - return ''; // Not a special map type + return SpecialMapValueType.REGULAR_MAP; // Not a special map type } const fields = value.mapValue.fields; @@ -821,10 +896,10 @@ function detectSpecialMapType(value: Value): string { // Check for type-based mappings const type = fields[TYPE_KEY]?.stringValue; if (type) { - const typeMap: Record = { - [RESERVED_VECTOR_KEY]: 'vectorValue', - [RESERVED_MAX_KEY]: 'maxValue', - [RESERVED_SERVER_TIMESTAMP_KEY]: 'serverTimestampValue' + const typeMap: Record = { + [RESERVED_VECTOR_KEY]: SpecialMapValueType.VECTOR, + [RESERVED_MAX_KEY]: SpecialMapValueType.INTERNAL_MAX, + [RESERVED_SERVER_TIMESTAMP_KEY]: SpecialMapValueType.SERVER_TIMESTAMP }; if (typeMap[type]) { return typeMap[type]; @@ -832,14 +907,14 @@ function detectSpecialMapType(value: Value): string { } // Check for BSON-related mappings - const bsonMap: Record = { - [RESERVED_REGEX_KEY]: 'regexValue', - [RESERVED_BSON_OBJECT_ID_KEY]: 'bsonObjectIdValue', - [RESERVED_INT32_KEY]: 'int32Value', - [RESERVED_BSON_TIMESTAMP_KEY]: 'bsonTimestampValue', - [RESERVED_BSON_BINARY_KEY]: 'bsonBinaryValue', - [RESERVED_MIN_KEY]: 'minKeyValue', - [RESERVED_MAX_KEY]: 'maxKeyValue' + const bsonMap: Record = { + [RESERVED_REGEX_KEY]: SpecialMapValueType.REGEX, + [RESERVED_BSON_OBJECT_ID_KEY]: SpecialMapValueType.BSON_OBJECT_ID, + [RESERVED_INT32_KEY]: SpecialMapValueType.INT32, + [RESERVED_BSON_TIMESTAMP_KEY]: SpecialMapValueType.BSON_TIMESTAMP, + [RESERVED_BSON_BINARY_KEY]: SpecialMapValueType.BSON_BINARY, + [RESERVED_MIN_KEY]: SpecialMapValueType.MIN_KEY, + [RESERVED_MAX_KEY]: SpecialMapValueType.MAX_KEY }; for (const key in bsonMap) { @@ -848,18 +923,18 @@ function detectSpecialMapType(value: Value): string { } } - return ''; + return SpecialMapValueType.REGULAR_MAP; } export function isBsonType(value: Value): boolean { const bsonTypes = new Set([ - 'regexValue', - 'bsonObjectIdValue', - 'int32Value', - 'bsonTimestampValue', - 'bsonBinaryValue', - 'minKeyValue', - 'maxKeyValue' + SpecialMapValueType.REGEX, + SpecialMapValueType.BSON_OBJECT_ID, + SpecialMapValueType.INT32, + SpecialMapValueType.BSON_TIMESTAMP, + SpecialMapValueType.BSON_BINARY, + SpecialMapValueType.MIN_KEY, + SpecialMapValueType.MAX_KEY ]); return bsonTypes.has(detectSpecialMapType(value)); } @@ -912,9 +987,24 @@ export function valuesGetLowerBound(value: Value): Value { } else if ('arrayValue' in value) { return { arrayValue: {} }; } else if ('mapValue' in value) { - // TODO(Mila/BSON): add lower bound for bson types for indexing - if (isVectorValue(value)) { + const type = detectSpecialMapType(value); + if (type === SpecialMapValueType.VECTOR) { return MIN_VECTOR_VALUE; + } else if (type === SpecialMapValueType.BSON_OBJECT_ID) { + return MIN_BSON_OBJECT_ID_VALUE; + } else if (type === SpecialMapValueType.BSON_TIMESTAMP) { + return MIN_BSON_TIMESTAMP_VALUE; + } else if (type === SpecialMapValueType.BSON_BINARY) { + return MIN_BSON_BINARY_VALUE; + } else if (type === SpecialMapValueType.REGEX) { + return MIN_REGEX_VALUE; + } else if (type === SpecialMapValueType.INT32) { + // int32Value is treated the same as integerValue and doubleValue + return { doubleValue: NaN }; + } else if (type === SpecialMapValueType.MIN_KEY) { + return MIN_KEY_VALUE; + } else if (type === SpecialMapValueType.MAX_KEY) { + return MAX_KEY_VALUE; } return { mapValue: {} }; } else { @@ -925,29 +1015,44 @@ export function valuesGetLowerBound(value: Value): Value { /** Returns the largest value for the given value type (exclusive). */ export function valuesGetUpperBound(value: Value): Value { if ('nullValue' in value) { - return { booleanValue: false }; + return MIN_KEY_VALUE; } else if ('booleanValue' in value) { return { doubleValue: NaN }; } else if ('integerValue' in value || 'doubleValue' in value) { return { timestampValue: { seconds: Number.MIN_SAFE_INTEGER } }; } else if ('timestampValue' in value) { - return { stringValue: '' }; + return MIN_BSON_TIMESTAMP_VALUE; } else if ('stringValue' in value) { return { bytesValue: '' }; } else if ('bytesValue' in value) { - return refValue(DatabaseId.empty(), DocumentKey.empty()); + return MIN_BSON_BINARY_VALUE; } else if ('referenceValue' in value) { - return { geoPointValue: { latitude: -90, longitude: -180 } }; + return MIN_BSON_OBJECT_ID_VALUE; } else if ('geoPointValue' in value) { - return { arrayValue: {} }; + return MIN_REGEX_VALUE; } else if ('arrayValue' in value) { return MIN_VECTOR_VALUE; } else if ('mapValue' in value) { - // TODO(Mila/BSON): add upper bound for bson types for indexing - if (isVectorValue(value)) { + const type = detectSpecialMapType(value); + if (type === SpecialMapValueType.VECTOR) { return { mapValue: {} }; + } else if (type === SpecialMapValueType.BSON_OBJECT_ID) { + return { geoPointValue: { latitude: -90, longitude: -180 } }; + } else if (type === SpecialMapValueType.BSON_TIMESTAMP) { + return { stringValue: '' }; + } else if (type === SpecialMapValueType.BSON_BINARY) { + return refValue(DatabaseId.empty(), DocumentKey.empty()); + } else if (type === SpecialMapValueType.REGEX) { + return { arrayValue: {} }; + } else if (type === SpecialMapValueType.INT32) { + // int32Value is treated the same as integerValue and doubleValue + return { timestampValue: { seconds: Number.MIN_SAFE_INTEGER } }; + } else if (type === SpecialMapValueType.MIN_KEY) { + return { booleanValue: false }; + } else if (type === SpecialMapValueType.MAX_KEY) { + return INTERNAL_MAX_VALUE; } - return INTERNAL_MAX_VALUE; + return MAX_KEY_VALUE; } else { return fail('Invalid value type: ' + JSON.stringify(value)); } diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index 2856862f6de..d4091768e7c 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -62,7 +62,6 @@ import { WithFieldValue, Timestamp, FieldPath, - newTestFirestore, SnapshotOptions, newTestApp, FirestoreError, @@ -76,7 +75,10 @@ import { maxKey, minKey, regex, - or + or, + newTestFirestore, + GeoPoint, + Bytes } from '../util/firebase_export'; import { apiDescribe, @@ -89,7 +91,9 @@ import { withNamedTestDbsOrSkipUnlessUsingEmulator, toDataArray, checkOnlineAndOfflineResultsMatch, - toIds + toIds, + withTestProjectIdAndCollectionSettings, + checkCacheRoundTrip } from '../util/helpers'; import { DEFAULT_SETTINGS, DEFAULT_PROJECT_ID } from '../util/settings'; @@ -2443,14 +2447,12 @@ apiDescribe('Database', persistence => { }; it('can write and read BSON types', async () => { - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const coll = collection(dbs[0], AutoId.newId()); - + {}, + async coll => { const docRef = await addDoc(coll, { binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), objectId: bsonObjectId('507f191e810c19729de860ea'), @@ -2491,6 +2493,49 @@ apiDescribe('Database', persistence => { ); }); + it('can write and read BSON types offline', async () => { + return withTestProjectIdAndCollectionSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + {}, + async (coll, db) => { + await disableNetwork(db); + const docRef = doc(coll, 'testDoc'); + + // Adding docs to cache, do not wait for promise to resolve. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + setDoc(docRef, { + binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), + objectId: bsonObjectId('507f191e810c19729de860ea'), + int32: int32(1), + regex: regex('^foo', 'i'), + timestamp: bsonTimestamp(1, 2), + min: minKey(), + max: maxKey() + }); + + const snapshot = await getDocFromCache(docRef); + expect( + snapshot + .get('binary') + .isEqual(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ).to.be.true; + expect( + snapshot + .get('objectId') + .isEqual(bsonObjectId('507f191e810c19729de860ea')) + ).to.be.true; + expect(snapshot.get('int32').isEqual(int32(1))).to.be.true; + expect(snapshot.get('regex').isEqual(regex('^foo', 'i'))).to.be.true; + expect(snapshot.get('timestamp').isEqual(bsonTimestamp(1, 2))).to.be + .true; + expect(snapshot.get('min') === minKey()).to.be.true; + expect(snapshot.get('max') === maxKey()).to.be.true; + } + ); + }); + it('can filter and order objectIds', async () => { const testDocs = { a: { key: bsonObjectId('507f191e810c19729de860ea') }, @@ -2498,17 +2543,12 @@ apiDescribe('Database', persistence => { c: { key: bsonObjectId('507f191e810c19729de860ec') } }; - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const coll = collection(dbs[0], AutoId.newId()); - await addDoc(coll, testDocs['a']); - await addDoc(coll, testDocs['b']); - await addDoc(coll, testDocs['c']); - + testDocs, + async (coll, db) => { let orderedQuery = query( coll, where('key', '>', bsonObjectId('507f191e810c19729de860ea')), @@ -2520,6 +2560,7 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['b'] ]); + await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); orderedQuery = query( coll, @@ -2535,6 +2576,7 @@ apiDescribe('Database', persistence => { testDocs['b'], testDocs['a'] ]); + await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); } ); }); @@ -2545,17 +2587,12 @@ apiDescribe('Database', persistence => { b: { key: int32(1) }, c: { key: int32(2) } }; - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const coll = collection(dbs[0], AutoId.newId()); - await addDoc(coll, testDocs['a']); - await addDoc(coll, testDocs['b']); - await addDoc(coll, testDocs['c']); - + testDocs, + async (coll, db) => { let orderedQuery = query( coll, where('key', '>=', int32(1)), @@ -2567,6 +2604,7 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['b'] ]); + await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); orderedQuery = query( coll, @@ -2579,6 +2617,7 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['a'] ]); + await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); } ); }); @@ -2589,17 +2628,12 @@ apiDescribe('Database', persistence => { b: { key: bsonTimestamp(1, 2) }, c: { key: bsonTimestamp(2, 1) } }; - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const coll = collection(dbs[0], AutoId.newId()); - await addDoc(coll, testDocs['a']); - await addDoc(coll, testDocs['b']); - await addDoc(coll, testDocs['c']); - + testDocs, + async (coll, db) => { let orderedQuery = query( coll, where('key', '>', bsonTimestamp(1, 1)), @@ -2611,6 +2645,7 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['b'] ]); + await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); orderedQuery = query( coll, @@ -2623,6 +2658,7 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['b'] ]); + await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); } ); }); @@ -2633,17 +2669,12 @@ apiDescribe('Database', persistence => { b: { key: bsonBinaryData(1, new Uint8Array([1, 2, 4])) }, c: { key: bsonBinaryData(2, new Uint8Array([1, 2, 3])) } }; - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const coll = collection(dbs[0], AutoId.newId()); - await addDoc(coll, testDocs['a']); - await addDoc(coll, testDocs['b']); - await addDoc(coll, testDocs['c']); - + testDocs, + async (coll, db) => { let orderedQuery = query( coll, where('key', '>', bsonBinaryData(1, new Uint8Array([1, 2, 3]))), @@ -2655,6 +2686,7 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['b'] ]); + await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); orderedQuery = query( coll, @@ -2668,6 +2700,7 @@ apiDescribe('Database', persistence => { testDocs['b'], testDocs['a'] ]); + await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); } ); }); @@ -2678,17 +2711,12 @@ apiDescribe('Database', persistence => { b: { key: regex('^bar', 'x') }, c: { key: regex('^baz', 'i') } }; - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const coll = collection(dbs[0], AutoId.newId()); - await addDoc(coll, testDocs['a']); - await addDoc(coll, testDocs['b']); - await addDoc(coll, testDocs['c']); - + testDocs, + async (coll, db) => { const orderedQuery = query( coll, or( @@ -2703,6 +2731,7 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['a'] ]); + await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); } ); }); @@ -2711,29 +2740,62 @@ apiDescribe('Database', persistence => { const testDocs = { a: { key: minKey() }, b: { key: minKey() }, - c: { key: maxKey() } + c: { key: null }, + d: { key: 1 }, + e: { key: maxKey() } }; - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const coll = collection(dbs[0], AutoId.newId()); - await addDoc(coll, testDocs['a']); - await addDoc(coll, testDocs['b']); - await addDoc(coll, testDocs['c']); + testDocs, + async (coll, db) => { + let filteredQuery = query(coll, where('key', '==', minKey())); + let snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['a'], + testDocs['b'] + ]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); - const orderedQuery = query( - coll, - where('key', '==', minKey()), - orderBy('key', 'desc') // minKeys are equal, would sort by documentId as secondary order - ); - const snapshot = await getDocs(orderedQuery); + filteredQuery = query(coll, where('key', '!=', minKey())); + snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ - testDocs['b'], - testDocs['a'] + testDocs['d'], + testDocs['e'] ]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '>=', minKey())); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['a'], + testDocs['b'] + ]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '<=', minKey())); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['a'], + testDocs['b'] + ]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '>', minKey())); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '<', minKey())); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '<', 1)); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); } ); }); @@ -2741,30 +2803,98 @@ apiDescribe('Database', persistence => { it('can filter and order maxKey values', async () => { const testDocs = { a: { key: minKey() }, - b: { key: maxKey() }, - c: { key: maxKey() } + b: { key: 1 }, + c: { key: maxKey() }, + d: { key: maxKey() }, + e: { key: null } }; - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const coll = collection(dbs[0], AutoId.newId()); - await addDoc(coll, testDocs['a']); - await addDoc(coll, testDocs['b']); - await addDoc(coll, testDocs['c']); - - const orderedQuery = query( - coll, - where('key', '==', maxKey()), - orderBy('key', 'desc') // maxKeys are equal, would sort by documentId as secondary order - ); - const snapshot = await getDocs(orderedQuery); + testDocs, + async (coll, db) => { + let filteredQuery = query(coll, where('key', '==', maxKey())); + let snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ testDocs['c'], + testDocs['d'] + ]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '!=', maxKey())); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['a'], testDocs['b'] ]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '>=', maxKey())); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['c'], + testDocs['d'] + ]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '<=', maxKey())); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['c'], + testDocs['d'] + ]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '>', maxKey())); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '<', maxKey())); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '>', 1)); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + } + ); + }); + + it('can handle null with bson values', async () => { + const testDocs = { + a: { key: minKey() }, + b: { key: null }, + c: { key: null }, + d: { key: 1 }, + e: { key: maxKey() } + }; + + return withTestProjectIdAndCollectionSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + testDocs, + async (coll, db) => { + let filteredQuery = query(coll, where('key', '==', null)); + let snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['b'], + testDocs['c'] + ]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + + filteredQuery = query(coll, where('key', '!=', null)); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['a'], + testDocs['d'], + testDocs['e'] + ]); + await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); } ); }); @@ -2778,20 +2908,12 @@ apiDescribe('Database', persistence => { e: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }, f: { key: regex('^foo', 'i') } }; - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const coll = collection(dbs[0], AutoId.newId()); - await addDoc(coll, testDocs['a']); - await addDoc(coll, testDocs['b']); - await addDoc(coll, testDocs['c']); - await addDoc(coll, testDocs['d']); - await addDoc(coll, testDocs['e']); - await addDoc(coll, testDocs['f']); - + testDocs, + async (coll, db) => { const orderedQuery = query(coll, orderBy('key', 'asc')); const storeEvent = new EventsAccumulator(); @@ -2861,5 +2983,125 @@ apiDescribe('Database', persistence => { } ); }); + + // eslint-disable-next-line no-restricted-properties + (persistence.gc === 'lru' ? describe : describe.skip)('From Cache', () => { + it('SDK orders different value types together the same way online and offline', async () => { + const testDocs: { [key: string]: DocumentData } = { + a: { key: null }, + b: { key: minKey() }, + c: { key: true }, + d: { key: NaN }, + e: { key: int32(1) }, + f: { key: 2.0 }, + g: { key: 3 }, + h: { key: new Timestamp(100, 123456000) }, + i: { key: bsonTimestamp(1, 2) }, + j: { key: 'string' }, + k: { key: Bytes.fromUint8Array(new Uint8Array([0, 1, 255])) }, + l: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }, + n: { key: bsonObjectId('507f191e810c19729de860ea') }, + o: { key: new GeoPoint(0, 0) }, + p: { key: regex('^foo', 'i') }, + q: { key: [1, 2] }, + r: { key: vector([1, 2]) }, + s: { key: { a: 1 } }, + t: { key: maxKey() } + }; + + return withTestProjectIdAndCollectionSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + testDocs, + async coll => { + // TODO(Mila/BSON): remove after prod supports bson, and use `ref` helper function instead + const docRef = doc(coll, 'doc'); + await setDoc(doc(coll, 'm'), { key: docRef }); + + const orderedQuery = query(coll, orderBy('key', 'desc')); + await checkOnlineAndOfflineResultsMatch( + orderedQuery, + 't', + 's', + 'r', + 'q', + 'p', + 'o', + 'n', + 'm', + 'l', + 'k', + 'j', + 'i', + 'h', + 'g', + 'f', + 'e', + 'd', + 'c', + 'b', + 'a' + ); + } + ); + }); + + it('SDK orders bson types the same way online and offline', async () => { + const testDocs: { [key: string]: DocumentData } = { + a: { key: maxKey() }, // maxKeys are all equal + b: { key: maxKey() }, + c: { key: int32(1) }, + d: { key: int32(-1) }, + e: { key: int32(0) }, + f: { key: bsonTimestamp(1, 1) }, + g: { key: bsonTimestamp(2, 1) }, + h: { key: bsonTimestamp(1, 2) }, + i: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }, + j: { key: bsonBinaryData(1, new Uint8Array([1, 1, 4])) }, + k: { key: bsonBinaryData(2, new Uint8Array([1, 0, 0])) }, + l: { key: bsonObjectId('507f191e810c19729de860eb') }, + m: { key: bsonObjectId('507f191e810c19729de860ea') }, + n: { key: bsonObjectId('407f191e810c19729de860ea') }, + o: { key: regex('^foo', 'i') }, + p: { key: regex('^foo', 'm') }, + q: { key: regex('^bar', 'i') }, + r: { key: minKey() }, // minKeys are all equal + s: { key: minKey() } + }; + + return withTestProjectIdAndCollectionSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + testDocs, + async coll => { + const orderedQuery = query(coll, orderBy('key')); + await checkOnlineAndOfflineResultsMatch( + orderedQuery, + 'r', + 's', + 'd', + 'e', + 'c', + 'f', + 'h', + 'g', + 'j', + 'i', + 'k', + 'n', + 'm', + 'l', + 'q', + 'o', + 'p', + 'a', + 'b' + ); + } + ); + }); + }); }); }); diff --git a/packages/firestore/test/integration/api/query.test.ts b/packages/firestore/test/integration/api/query.test.ts index 01fd0e47e35..91090d04fa9 100644 --- a/packages/firestore/test/integration/api/query.test.ts +++ b/packages/firestore/test/integration/api/query.test.ts @@ -925,20 +925,6 @@ apiDescribe('Queries', persistence => { { array: ['a', 42, 'c'] }, { array: [42], array2: ['bingo'] } ]); - - // NOTE: The backend doesn't currently support null, NaN, objects, or - // arrays, so there isn't much of anything else interesting to test. - // With null. - const snapshot3 = await getDocs( - query(coll, where('zip', 'array-contains', null)) - ); - expect(toDataArray(snapshot3)).to.deep.equal([]); - - // With NaN. - const snapshot4 = await getDocs( - query(coll, where('zip', 'array-contains', Number.NaN)) - ); - expect(toDataArray(snapshot4)).to.deep.equal([]); }); }); diff --git a/packages/firestore/test/integration/api/type.test.ts b/packages/firestore/test/integration/api/type.test.ts index a6218f6a1ad..156eba426f8 100644 --- a/packages/firestore/test/integration/api/type.test.ts +++ b/packages/firestore/test/integration/api/type.test.ts @@ -17,7 +17,6 @@ import { expect } from 'chai'; -import { AutoId } from '../../../src/util/misc'; import { addEqualityMatcher } from '../../util/equality_matcher'; import { EventsAccumulator } from '../util/events_accumulator'; import { @@ -52,6 +51,7 @@ import { } from '../util/firebase_export'; import { apiDescribe, + withTestProjectIdAndCollectionSettings, withTestDb, withTestDbsSettings, withTestDoc @@ -384,13 +384,13 @@ apiDescribe('Firestore', persistence => { }); it('invalid 32-bit integer gets rejected', async () => { - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const docRef = doc(dbs[0], 'test-collection/test-doc'); + {}, + async coll => { + const docRef = doc(coll, 'test-doc'); let errorMessage; try { await setDoc(docRef, { key: int32(2147483648) }); @@ -414,13 +414,13 @@ apiDescribe('Firestore', persistence => { }); it('invalid BSON timestamp gets rejected', async () => { - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const docRef = doc(dbs[0], 'test-collection/test-doc'); + {}, + async coll => { + const docRef = doc(coll, 'test-doc'); let errorMessage; try { // BSON timestamp larger than 32-bit integer gets rejected @@ -446,13 +446,13 @@ apiDescribe('Firestore', persistence => { }); it('invalid regex value gets rejected', async () => { - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const docRef = doc(dbs[0], 'test-collection/test-doc'); + {}, + async coll => { + const docRef = doc(coll, 'test-doc'); let errorMessage; try { await setDoc(docRef, { key: regex('foo', 'a') }); @@ -467,13 +467,13 @@ apiDescribe('Firestore', persistence => { }); it('invalid bsonObjectId value gets rejected', async () => { - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const docRef = doc(dbs[0], 'test-collection/test-doc'); + {}, + async coll => { + const docRef = doc(coll, 'test-doc'); let errorMessage; try { @@ -490,13 +490,13 @@ apiDescribe('Firestore', persistence => { }); it('invalid bsonBinaryData value gets rejected', async () => { - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const docRef = doc(dbs[0], 'test-collection/test-doc'); + {}, + async coll => { + const docRef = doc(coll, 'test-doc'); let errorMessage; try { await setDoc(docRef, { @@ -537,18 +537,13 @@ apiDescribe('Firestore', persistence => { maxValue: { key: maxKey() } }; - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const coll = collection(dbs[0], AutoId.newId()); - for (const key of Object.keys(testDocs)) { - await setDoc(doc(coll, key), testDocs[key]); - } - - // TODO(Mila/BSON): replace after prod supports bson + testDocs, + async coll => { + // TODO(Mila/BSON): remove after prod supports bson const docRef = doc(coll, 'doc'); await setDoc(doc(coll, 'referenceValue'), { key: docRef }); diff --git a/packages/firestore/test/integration/api/validation.test.ts b/packages/firestore/test/integration/api/validation.test.ts index 9c74634affa..72978f71fe3 100644 --- a/packages/firestore/test/integration/api/validation.test.ts +++ b/packages/firestore/test/integration/api/validation.test.ts @@ -856,6 +856,20 @@ apiDescribe('Validation:', persistence => { ).to.throw("Invalid query. You cannot use more than one '!=' filter."); }); + validationIt(persistence, 'rejects invalid NaN filter', db => { + const coll = collection(db, 'test'); + expect(() => query(coll, where('foo', '>', NaN))).to.throw( + "Invalid query. You can only perform '==' and '!=' comparisons on NaN." + ); + }); + + validationIt(persistence, 'rejects invalid Null filter', db => { + const coll = collection(db, 'test'); + expect(() => query(coll, where('foo', '>', null))).to.throw( + "Invalid query. You can only perform '==' and '!=' comparisons on Null." + ); + }); + validationIt(persistence, 'with != and not-in filters fail', db => { expect(() => query( diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 465bc8edd61..2c789ec9151 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -44,7 +44,9 @@ import { Query, getDocsFromServer, getDocsFromCache, - _AutoId + _AutoId, + disableNetwork, + enableNetwork } from './firebase_export'; import { ALT_PROJECT_ID, @@ -444,10 +446,27 @@ export function withTestCollectionSettings( settings: PrivateSettings, docs: { [key: string]: DocumentData }, fn: (collection: CollectionReference, db: Firestore) => Promise +): Promise { + return withTestProjectIdAndCollectionSettings( + persistence, + DEFAULT_PROJECT_ID, + settings, + docs, + fn + ); +} + +export function withTestProjectIdAndCollectionSettings( + persistence: PersistenceMode | typeof PERSISTENCE_MODE_UNSPECIFIED, + projectId: string, + settings: PrivateSettings, + docs: { [key: string]: DocumentData }, + fn: (collection: CollectionReference, db: Firestore) => Promise ): Promise { const collectionId = _AutoId.newId(); - return batchCommitDocsToCollection( + return batchCommitDocsToCollectionWithSettings( persistence, + projectId, settings, docs, collectionId, @@ -462,10 +481,28 @@ export function batchCommitDocsToCollection( collectionId: string, fn: (collection: CollectionReference, db: Firestore) => Promise ): Promise { - return withTestDbsSettings( + return batchCommitDocsToCollectionWithSettings( persistence, DEFAULT_PROJECT_ID, settings, + docs, + collectionId, + fn + ); +} + +export function batchCommitDocsToCollectionWithSettings( + persistence: PersistenceMode | typeof PERSISTENCE_MODE_UNSPECIFIED, + projectId: string, + settings: PrivateSettings, + docs: { [key: string]: DocumentData }, + collectionId: string, + fn: (collection: CollectionReference, db: Firestore) => Promise +): Promise { + return withTestDbsSettings( + persistence, + projectId, + settings, 2, ([testDb, setupDb]) => { const testCollection = collection(testDb, collectionId); @@ -557,3 +594,34 @@ export async function checkOnlineAndOfflineResultsMatch( const docsFromCache = await getDocsFromCache(query); expect(toIds(docsFromServer)).to.deep.equal(toIds(docsFromCache)); } + +/** + * Checks that documents fetched from the server and stored in the cache can be + * successfully retrieved from the cache and matches the expected documents. + * + * This function performs the following steps: + * 1. Fetch documents from the server for provided query and populate the cache. + * 2. Disables the network connection to simulate offline mode. + * 3. Retrieves the documents from the cache using the same query. + * 4. Compares the cached documents with the expected documents. + * + * @param query The query to check. + * @param db The Firestore database instance. + * @param expectedDocs Optional ordered list of document data that are expected to be retrieved from the cache. + */ +export async function checkCacheRoundTrip( + query: Query, + db: Firestore, + expectedDocs: DocumentData[] +): Promise { + await getDocsFromServer(query); + + await disableNetwork(db); + const docsFromCache = await getDocsFromCache(query); + + if (expectedDocs.length !== 0) { + expect(expectedDocs).to.deep.equal(toDataArray(docsFromCache)); + } + + await enableNetwork(db); +} diff --git a/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts index 8daa97eb77d..c646726feeb 100644 --- a/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts +++ b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts @@ -16,13 +16,35 @@ */ import { expect } from 'chai'; +import { + bsonBinaryData, + bsonObjectId, + bsonTimestamp, + int32, + regex +} from '../../../lite'; import { FirestoreIndexValueWriter } from '../../../src/index/firestore_index_value_writer'; import { IndexByteEncoder } from '../../../src/index/index_byte_encoder'; import { Timestamp } from '../../../src/lite-api/timestamp'; +import { + parseBsonBinaryData, + parseInt32Value, + parseMaxKey, + parseMinKey, + parseBsonObjectId, + parseRegexValue, + parseBsonTimestamp +} from '../../../src/lite-api/user_data_reader'; import { IndexKind } from '../../../src/model/field_index'; import type { Value } from '../../../src/protos/firestore_proto_api'; -import { toTimestamp } from '../../../src/remote/serializer'; -import { JSON_SERIALIZER } from '../local/persistence_test_helpers'; +import { + JsonProtoSerializer, + toTimestamp +} from '../../../src/remote/serializer'; +import { + JSON_SERIALIZER, + TEST_DATABASE_ID +} from '../local/persistence_test_helpers'; import { compare } from './ordered_code_writer.test'; @@ -247,4 +269,344 @@ describe('Firestore Index Value Writer', () => { ).to.equal(1); }); }); + + describe('can gracefully handle BSON types', () => { + it('can compare BSON ObjectIds', () => { + const value1 = { + mapValue: { + fields: { + '__oid__': { stringValue: '507f191e810c19729de860ea' } + } + } + }; + const value2 = { + mapValue: { + fields: { + '__oid__': { stringValue: '507f191e810c19729de860eb' } + } + } + }; + const value3 = parseBsonObjectId( + bsonObjectId('507f191e810c19729de860ea') + ); + + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value1, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value1, value1, IndexKind.ASCENDING) + ).to.equal(0); + + expect( + compareIndexEncodedValues(value3, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value3, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value3, value1, IndexKind.ASCENDING) + ).to.equal(0); + }); + + it('can compare BSON Timestamps', () => { + const value1 = { + mapValue: { + fields: { + '__request_timestamp__': { + mapValue: { + fields: { + seconds: { integerValue: 1 }, + increment: { integerValue: 2 } + } + } + } + } + } + }; + const value2 = { + mapValue: { + fields: { + '__request_timestamp__': { + mapValue: { + fields: { + seconds: { integerValue: 1 }, + increment: { integerValue: 3 } + } + } + } + } + } + }; + const value3 = parseBsonTimestamp(bsonTimestamp(1, 2)); + const value4 = parseBsonTimestamp(bsonTimestamp(2, 1)); + + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value1, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value1, value1, IndexKind.ASCENDING) + ).to.equal(0); + + expect( + compareIndexEncodedValues(value3, value1, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value3, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value3, IndexKind.ASCENDING) + ).to.equal(1); + + expect( + compareIndexEncodedValues(value4, value1, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value4, value2, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value4, value3, IndexKind.ASCENDING) + ).to.equal(1); + }); + + it('can compare BSON Binary', () => { + const value1 = { + mapValue: { + fields: { + '__binary__': { + bytesValue: 'AQECAw==' // 1, 1, 2, 3 + } + } + } + }; + const value2 = { + mapValue: { + fields: { + '__binary__': { + bytesValue: 'AQECBA==' // 1, 1, 2, 4 + } + } + } + }; + + const serializer = new JsonProtoSerializer( + TEST_DATABASE_ID, + /* useProto3Json= */ false + ); + const value3 = parseBsonBinaryData( + serializer, + bsonBinaryData(1, new Uint8Array([1, 2, 3])) + ); + + const jsonSerializer = new JsonProtoSerializer( + TEST_DATABASE_ID, + /* useProto3Json= */ true + ); + + const value4 = parseBsonBinaryData( + jsonSerializer, + bsonBinaryData(1, new Uint8Array([1, 2, 3])) + ); + + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value1, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value1, value1, IndexKind.ASCENDING) + ).to.equal(0); + + expect( + compareIndexEncodedValues(value3, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value3, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value3, value1, IndexKind.ASCENDING) + ).to.equal(0); + + expect( + compareIndexEncodedValues(value4, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value4, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value4, value1, IndexKind.ASCENDING) + ).to.equal(0); + }); + + it('can compare BSON Regex', () => { + const value1 = { + mapValue: { + fields: { + '__regex__': { + mapValue: { + fields: { + 'pattern': { stringValue: '^foo' }, + 'options': { stringValue: 'i' } + } + } + } + } + } + }; + const value2 = { + mapValue: { + fields: { + '__regex__': { + mapValue: { + fields: { + 'pattern': { stringValue: '^foo' }, + 'options': { stringValue: 'm' } + } + } + } + } + } + }; + const value3 = parseRegexValue(regex('^foo', 'i')); + const value4 = parseRegexValue(regex('^zoo', 'i')); + + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value1, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value1, value1, IndexKind.ASCENDING) + ).to.equal(0); + + expect( + compareIndexEncodedValues(value3, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value3, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value3, value1, IndexKind.ASCENDING) + ).to.equal(0); + + expect( + compareIndexEncodedValues(value4, value1, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value4, value2, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value4, value3, IndexKind.ASCENDING) + ).to.equal(1); + }); + + it('can compare BSON Int32', () => { + const value1 = { + mapValue: { + fields: { + '__int__': { integerValue: 1 } + } + } + }; + const value2 = { + mapValue: { + fields: { + '__int__': { integerValue: 2 } + } + } + }; + const value3 = parseInt32Value(int32(1)); + + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value1, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value1, value1, IndexKind.ASCENDING) + ).to.equal(0); + + expect( + compareIndexEncodedValues(value3, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value3, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value3, value1, IndexKind.ASCENDING) + ).to.equal(0); + }); + + it('can compare BSON MinKey', () => { + const value1 = { + mapValue: { + fields: { + '__min__': { + nullValue: 'NULL_VALUE' as const + } + } + } + }; + const value2 = { + mapValue: { + fields: { + '__min__': { + nullValue: 'NULL_VALUE' as const + } + } + } + }; + const value3 = parseMinKey(); + + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value3, IndexKind.DESCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value1, IndexKind.ASCENDING) + ).to.equal(0); + }); + + it('can compare BSON MaxKey', () => { + const value1 = { + mapValue: { + fields: { + '__max__': { + nullValue: 'NULL_VALUE' as const + } + } + } + }; + const value2 = { + mapValue: { + fields: { + '__max__': { + nullValue: 'NULL_VALUE' as const + } + } + } + }; + const value3 = parseMaxKey(); + + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value3, IndexKind.DESCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value1, IndexKind.ASCENDING) + ).to.equal(0); + }); + }); }); diff --git a/packages/firestore/test/unit/local/index_manager.test.ts b/packages/firestore/test/unit/local/index_manager.test.ts index 2521be99bf5..51bef76b31e 100644 --- a/packages/firestore/test/unit/local/index_manager.test.ts +++ b/packages/firestore/test/unit/local/index_manager.test.ts @@ -17,6 +17,7 @@ import { expect } from 'chai'; +import { Bytes, GeoPoint } from '../../../src/'; import { User } from '../../../src/auth/user'; import { FieldFilter } from '../../../src/core/filter'; import { @@ -30,7 +31,16 @@ import { queryWithLimit, queryWithStartAt } from '../../../src/core/query'; -import { vector } from '../../../src/lite-api/field_value_impl'; +import { + bsonBinaryData, + bsonObjectId, + bsonTimestamp, + int32, + maxKey, + minKey, + regex, + vector +} from '../../../src/lite-api/field_value_impl'; import { Timestamp } from '../../../src/lite-api/timestamp'; import { displayNameForIndexType, @@ -71,6 +81,7 @@ import { orFilter, path, query, + ref, version, wrap } from '../../util/helpers'; @@ -327,6 +338,14 @@ describe('IndexedDbIndexManager', async () => { await addDoc('coll/doc2', {}); }); + it('adds string', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['exists', IndexKind.ASCENDING]] }) + ); + await addDoc('coll/doc1', { 'exists': 'a' }); + await addDoc('coll/doc2', { 'exists': 'b' }); + }); + it('applies orderBy', async () => { await indexManager.addFieldIndex( fieldIndex('coll', { fields: [['count', IndexKind.ASCENDING]] }) @@ -1856,6 +1875,592 @@ describe('IndexedDbIndexManager', async () => { await validateIsNoneIndex(query2); }); + describe('BSON type indexing', () => { + it('can index BSON ObjectId fields', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) + ); + + await addDoc('coll/doc1', { + key: bsonObjectId('507f191e810c19729de860ea') + }); + await addDoc('coll/doc2', { + key: bsonObjectId('507f191e810c19729de860eb') + }); + await addDoc('coll/doc3', { + key: bsonObjectId('507f191e810c19729de860ec') + }); + + const fieldIndexes = await indexManager.getFieldIndexes('coll'); + expect(fieldIndexes).to.have.length(1); + + let q = queryWithAddedOrderBy(query('coll'), orderBy('key')); + await verifyResults(q, 'coll/doc1', 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '==', bsonObjectId('507f191e810c19729de860ea')) + ); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '!=', bsonObjectId('507f191e810c19729de860ea')) + ); + await verifyResults(q, 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>=', bsonObjectId('507f191e810c19729de860eb')) + ); + await verifyResults(q, 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<=', bsonObjectId('507f191e810c19729de860eb')) + ); + await verifyResults(q, 'coll/doc1', 'coll/doc2'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', bsonObjectId('507f191e810c19729de860eb')) + ); + await verifyResults(q, 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', bsonObjectId('507f191e810c19729de860eb')) + ); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', bsonObjectId('507f191e810c19729de860ec')) + ); + await verifyResults(q); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', bsonObjectId('507f191e810c19729de860ea')) + ); + await verifyResults(q); + }); + + it('can index BSON Binary Data fields', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) + ); + await addDoc('coll/doc1', { + key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + }); + await addDoc('coll/doc2', { + key: bsonBinaryData(1, new Uint8Array([1, 2, 4])) + }); + await addDoc('coll/doc3', { + key: bsonBinaryData(1, new Uint8Array([2, 1, 2])) + }); + + const fieldIndexes = await indexManager.getFieldIndexes('coll'); + expect(fieldIndexes).to.have.length(1); + + let q = queryWithAddedOrderBy(query('coll'), orderBy('key')); + await verifyResults(q, 'coll/doc1', 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '==', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '!=', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ); + await verifyResults(q, 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>=', bsonBinaryData(1, new Uint8Array([1, 2, 4]))) + ); + await verifyResults(q, 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<=', bsonBinaryData(1, new Uint8Array([1, 2, 4]))) + ); + await verifyResults(q, 'coll/doc1', 'coll/doc2'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', bsonBinaryData(1, new Uint8Array([1, 2, 4]))) + ); + await verifyResults(q, 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', bsonBinaryData(1, new Uint8Array([1, 2, 4]))) + ); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', bsonBinaryData(1, new Uint8Array([2, 1, 2]))) + ); + await verifyResults(q); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ); + await verifyResults(q); + }); + + it('can index BSON Timestamp fields', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) + ); + await addDoc('coll/doc1', { + key: bsonTimestamp(1, 1) + }); + await addDoc('coll/doc2', { + key: bsonTimestamp(1, 2) + }); + await addDoc('coll/doc3', { + key: bsonTimestamp(2, 1) + }); + + const fieldIndexes = await indexManager.getFieldIndexes('coll'); + expect(fieldIndexes).to.have.length(1); + + let q = queryWithAddedOrderBy(query('coll'), orderBy('key')); + await verifyResults(q, 'coll/doc1', 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '==', bsonTimestamp(1, 1)) + ); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '!=', bsonTimestamp(1, 1)) + ); + await verifyResults(q, 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>=', bsonTimestamp(1, 2)) + ); + await verifyResults(q, 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<=', bsonTimestamp(1, 2)) + ); + await verifyResults(q, 'coll/doc1', 'coll/doc2'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', bsonTimestamp(1, 2)) + ); + await verifyResults(q, 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', bsonTimestamp(1, 2)) + ); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', bsonTimestamp(2, 1)) + ); + await verifyResults(q); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', bsonTimestamp(1, 1)) + ); + await verifyResults(q); + }); + + it('can index Int32 fields', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) + ); + await addDoc('coll/doc1', { + key: int32(1) + }); + await addDoc('coll/doc2', { + key: int32(2) + }); + await addDoc('coll/doc3', { + key: int32(3) + }); + const fieldIndexes = await indexManager.getFieldIndexes('coll'); + expect(fieldIndexes).to.have.length(1); + + let q = queryWithAddedOrderBy(query('coll'), orderBy('key')); + await verifyResults(q, 'coll/doc1', 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter(query('coll'), filter('key', '==', int32(1))); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter(query('coll'), filter('key', '!=', int32(1))); + await verifyResults(q, 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter(query('coll'), filter('key', '>=', int32(2))); + await verifyResults(q, 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter(query('coll'), filter('key', '<=', int32(2))); + await verifyResults(q, 'coll/doc1', 'coll/doc2'); + + q = queryWithAddedFilter(query('coll'), filter('key', '>', int32(2))); + await verifyResults(q, 'coll/doc3'); + + q = queryWithAddedFilter(query('coll'), filter('key', '<', int32(2))); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter(query('coll'), filter('key', '>', int32(3))); + await verifyResults(q); + + q = queryWithAddedFilter(query('coll'), filter('key', '<', int32(1))); + await verifyResults(q); + }); + + it('can index regex fields', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) + ); + await addDoc('coll/doc1', { + key: regex('a', 'i') + }); + await addDoc('coll/doc2', { + key: regex('a', 'm') + }); + await addDoc('coll/doc3', { + key: regex('b', 'i') + }); + const fieldIndexes = await indexManager.getFieldIndexes('coll'); + expect(fieldIndexes).to.have.length(1); + let q = queryWithAddedOrderBy(query('coll'), orderBy('key')); + await verifyResults(q, 'coll/doc1', 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '==', regex('a', 'i')) + ); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '!=', regex('a', 'i')) + ); + await verifyResults(q, 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>=', regex('a', 'm')) + ); + await verifyResults(q, 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<=', regex('a', 'm')) + ); + await verifyResults(q, 'coll/doc1', 'coll/doc2'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', regex('a', 'm')) + ); + await verifyResults(q, 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', regex('a', 'm')) + ); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', regex('b', 'i')) + ); + await verifyResults(q); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', regex('a', 'i')) + ); + await verifyResults(q); + }); + + it('can index minKey fields', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) + ); + await addDoc('coll/doc1', { + key: minKey() + }); + await addDoc('coll/doc2', { + key: minKey() + }); + await addDoc('coll/doc3', { + key: null + }); + await addDoc('coll/doc4', { + key: 1 + }); + await addDoc('coll/doc5', { + key: maxKey() + }); + + const fieldIndexes = await indexManager.getFieldIndexes('coll'); + expect(fieldIndexes).to.have.length(1); + + let q = queryWithAddedOrderBy(query('coll'), orderBy('key')); + await verifyResults( + q, + 'coll/doc3', + 'coll/doc1', + 'coll/doc2', + 'coll/doc4', + 'coll/doc5' + ); + + q = queryWithAddedFilter(query('coll'), filter('key', '==', minKey())); + await verifyResults(q, 'coll/doc1', 'coll/doc2'); + + q = queryWithAddedFilter(query('coll'), filter('key', '!=', minKey())); + await verifyResults(q, 'coll/doc4', 'coll/doc5'); + + q = queryWithAddedFilter(query('coll'), filter('key', '>=', minKey())); + await verifyResults(q, 'coll/doc1', 'coll/doc2'); + + q = queryWithAddedFilter(query('coll'), filter('key', '<=', minKey())); + await verifyResults(q, 'coll/doc1', 'coll/doc2'); + + q = queryWithAddedFilter(query('coll'), filter('key', '>', minKey())); + await verifyResults(q); + + q = queryWithAddedFilter(query('coll'), filter('key', '<', minKey())); + await verifyResults(q); + }); + + it('can index maxKey fields', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) + ); + await addDoc('coll/doc1', { + key: minKey() + }); + await addDoc('coll/doc2', { + key: 1 + }); + await addDoc('coll/doc3', { + key: maxKey() + }); + await addDoc('coll/doc4', { + key: maxKey() + }); + await addDoc('coll/doc5', { + key: null + }); + + const fieldIndexes = await indexManager.getFieldIndexes('coll'); + expect(fieldIndexes).to.have.length(1); + + let q = queryWithAddedOrderBy(query('coll'), orderBy('key')); + await verifyResults( + q, + 'coll/doc5', + 'coll/doc1', + 'coll/doc2', + 'coll/doc3', + 'coll/doc4' + ); + + q = queryWithAddedFilter(query('coll'), filter('key', '==', maxKey())); + await verifyResults(q, 'coll/doc3', 'coll/doc4'); + + q = queryWithAddedFilter(query('coll'), filter('key', '!=', maxKey())); + await verifyResults(q, 'coll/doc1', 'coll/doc2'); + + q = queryWithAddedFilter(query('coll'), filter('key', '>=', maxKey())); + await verifyResults(q, 'coll/doc3', 'coll/doc4'); + + q = queryWithAddedFilter(query('coll'), filter('key', '<=', maxKey())); + await verifyResults(q, 'coll/doc3', 'coll/doc4'); + + q = queryWithAddedFilter(query('coll'), filter('key', '>', maxKey())); + await verifyResults(q); + + q = queryWithAddedFilter(query('coll'), filter('key', '<', maxKey())); + await verifyResults(q); + }); + + it('can index fields of BSON types together', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['key', IndexKind.DESCENDING]] }) + ); + await addDoc('coll/doc1', { + key: minKey() + }); + + await addDoc('coll/doc2', { + key: int32(2) + }); + await addDoc('coll/doc3', { + key: int32(1) + }); + + await addDoc('coll/doc4', { + key: bsonTimestamp(1, 2) + }); + await addDoc('coll/doc5', { + key: bsonTimestamp(1, 1) + }); + + await addDoc('coll/doc6', { + key: bsonBinaryData(1, new Uint8Array([1, 2, 4])) + }); + await addDoc('coll/doc7', { + key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + }); + await addDoc('coll/doc8', { + key: bsonObjectId('507f191e810c19729de860eb') + }); + await addDoc('coll/doc9', { + key: bsonObjectId('507f191e810c19729de860ea') + }); + + await addDoc('coll/doc10', { + key: regex('a', 'm') + }); + await addDoc('coll/doc11', { + key: regex('a', 'i') + }); + + await addDoc('coll/doc12', { + key: maxKey() + }); + + const fieldIndexes = await indexManager.getFieldIndexes('coll'); + expect(fieldIndexes).to.have.length(1); + + const q = queryWithAddedOrderBy(query('coll'), orderBy('key', 'desc')); + await verifyResults( + q, + 'coll/doc12', + 'coll/doc10', + 'coll/doc11', + 'coll/doc8', + 'coll/doc9', + 'coll/doc6', + 'coll/doc7', + 'coll/doc4', + 'coll/doc5', + 'coll/doc2', + 'coll/doc3', + 'coll/doc1' + ); + }); + }); + + it('can index fields of all types together', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['key', IndexKind.DESCENDING]] }) + ); + await addDoc('coll/a', { + key: null + }); + await addDoc('coll/b', { + key: minKey() + }); + await addDoc('coll/c', { + key: true + }); + await addDoc('coll/d', { + key: NaN + }); + await addDoc('coll/e', { + key: int32(1) + }); + await addDoc('coll/f', { + key: 2.0 + }); + await addDoc('coll/g', { + key: 3 + }); + await addDoc('coll/h', { + key: new Timestamp(100, 123456000) + }); + await addDoc('coll/i', { + key: bsonTimestamp(1, 2) + }); + await addDoc('coll/j', { + key: 'string' + }); + await addDoc('coll/k', { + key: Bytes.fromUint8Array(new Uint8Array([0, 1, 255])) as Bytes + }); + await addDoc('coll/l', { + key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + }); + await addDoc('coll/m', { + key: ref('coll/doc') + }); + await addDoc('coll/n', { + key: bsonObjectId('507f191e810c19729de860ea') + }); + await addDoc('coll/o', { + key: new GeoPoint(0, 1) + }); + await addDoc('coll/p', { + key: regex('^foo', 'i') + }); + await addDoc('coll/q', { + key: [1, 2] + }); + await addDoc('coll/r', { + key: vector([1, 2]) + }); + await addDoc('coll/s', { + key: { a: 1 } + }); + await addDoc('coll/t', { + key: maxKey() + }); + + const fieldIndexes = await indexManager.getFieldIndexes('coll'); + expect(fieldIndexes).to.have.length(1); + + const q = queryWithAddedOrderBy(query('coll'), orderBy('key', 'desc')); + await verifyResults( + q, + 'coll/t', + 'coll/s', + 'coll/r', + 'coll/q', + 'coll/p', + 'coll/o', + 'coll/n', + 'coll/m', + 'coll/l', + 'coll/k', + 'coll/j', + 'coll/i', + 'coll/h', + 'coll/g', + 'coll/f', + 'coll/e', + 'coll/d', + 'coll/c', + 'coll/b', + 'coll/a' + ); + }); + async function validateIsPartialIndex(query: Query): Promise { await validateIndexType(query, IndexType.PARTIAL); } diff --git a/packages/firestore/test/unit/model/values.test.ts b/packages/firestore/test/unit/model/values.test.ts index bf46386c800..7a2558d2461 100644 --- a/packages/firestore/test/unit/model/values.test.ts +++ b/packages/firestore/test/unit/model/values.test.ts @@ -48,7 +48,14 @@ import { valuesGetUpperBound, TYPE_KEY, RESERVED_VECTOR_KEY, - VECTOR_MAP_VECTORS_KEY + VECTOR_MAP_VECTORS_KEY, + MIN_BSON_TIMESTAMP_VALUE, + MIN_VECTOR_VALUE, + RESERVED_INT32_KEY, + MIN_BSON_BINARY_VALUE, + MIN_KEY_VALUE, + MIN_REGEX_VALUE, + MIN_BSON_OBJECT_ID_VALUE } from '../../../src/model/values'; import * as api from '../../../src/protos/firestore_proto_api'; import { primitiveComparator } from '../../../src/util/misc'; @@ -71,14 +78,8 @@ describe('Values', () => { const values: api.Value[][] = [ [wrap(true), wrap(true)], [wrap(false), wrap(false)], - // MinKeys are all equal, and sort the same as null. - [ - wrap(null), - wrap(null), - wrap(minKey()), - wrap(minKey()), - wrap(MinKey.instance()) - ], + [wrap(null), wrap(null)], + [wrap(minKey()), wrap(minKey()), wrap(MinKey.instance())], [wrap(0 / 0), wrap(Number.NaN), wrap(NaN)], // -0.0 and 0.0 order the same but are not considered equal. [wrap(-0.0)], @@ -166,7 +167,10 @@ describe('Values', () => { it('orders types correctly', () => { const groups = [ // null first - [wrap(null), wrap(minKey())], + [wrap(null)], + + // MinKey is after null + [wrap(minKey())], // booleans [wrap(false)], @@ -480,21 +484,25 @@ describe('Values', () => { }); it('computes lower bound', () => { - // TODO(Mila/BSON):add cases for bson types const groups = [ - // null and minKey first - [ - valuesGetLowerBound({ nullValue: 'NULL_VALUE' }), - wrap(null), - wrap(minKey()) - ], + // lower bound of null is null + [valuesGetLowerBound({ nullValue: 'NULL_VALUE' }), wrap(null)], + + // lower bound of MinKey is MinKey + [valuesGetLowerBound(MIN_KEY_VALUE), wrap(minKey())], // booleans [valuesGetLowerBound({ booleanValue: true }), wrap(false)], [wrap(true)], // numbers - [valuesGetLowerBound({ doubleValue: 0 }), wrap(NaN)], + [ + valuesGetLowerBound({ doubleValue: 0 }), + valuesGetLowerBound({ + mapValue: { fields: { [RESERVED_INT32_KEY]: { integerValue: 0 } } } + }), + wrap(NaN) + ], [wrap(Number.NEGATIVE_INFINITY)], [wrap(Number.MIN_VALUE)], @@ -502,10 +510,31 @@ describe('Values', () => { [valuesGetLowerBound({ timestampValue: {} })], [wrap(date1)], + // bson timestamps + [ + valuesGetLowerBound(wrap(bsonTimestamp(4294967295, 4294967295))), + MIN_BSON_TIMESTAMP_VALUE, + wrap(bsonTimestamp(0, 0)) + ], + [wrap(bsonTimestamp(1, 1))], + // strings - [valuesGetLowerBound({ stringValue: '' }), wrap('')], + [valuesGetLowerBound({ stringValue: 'Z' }), wrap('')], [wrap('\u0000')], + // blobs + [valuesGetLowerBound({ bytesValue: 'Z' }), wrap(blob())], + [wrap(blob(0))], + + // bson binary data + [ + valuesGetLowerBound( + wrap(bsonBinaryData(128, new Uint8Array([128, 128]))) + ), + MIN_BSON_BINARY_VALUE + ], + [wrap(bsonBinaryData(0, new Uint8Array([0])))], + // resource names [ valuesGetLowerBound({ referenceValue: '' }), @@ -513,6 +542,14 @@ describe('Values', () => { ], [refValue(DatabaseId.empty(), key('a/a'))], + // bson object ids + [ + valuesGetLowerBound(wrap(bsonObjectId('ZZZ'))), + wrap(bsonObjectId('')), + MIN_BSON_OBJECT_ID_VALUE + ], + [wrap(bsonObjectId('a'))], + // geo points [ valuesGetLowerBound({ geoPointValue: {} }), @@ -520,6 +557,14 @@ describe('Values', () => { ], [wrap(new GeoPoint(-90, 0))], + // regular expressions + [ + valuesGetLowerBound(wrap(regex('ZZZ', 'i'))), + wrap(regex('', '')), + MIN_REGEX_VALUE + ], + [wrap(regex('a', 'i'))], + // arrays [valuesGetLowerBound({ arrayValue: {} }), wrap([])], [wrap([false])], @@ -557,17 +602,22 @@ describe('Values', () => { }); it('computes upper bound', () => { - // TODO(Mila/BSON):add cases for bson types const groups = [ // null first [wrap(null)], - [valuesGetUpperBound({ nullValue: 'NULL_VALUE' })], + + // upper value of null is MinKey + [valuesGetUpperBound({ nullValue: 'NULL_VALUE' }), wrap(minKey())], + + // upper value of MinKey is boolean `false` + [valuesGetUpperBound(MIN_KEY_VALUE), wrap(false)], // booleans [wrap(true)], [valuesGetUpperBound({ booleanValue: false })], // numbers + [wrap(int32(2147483647))], //largest int32 value [wrap(Number.MAX_SAFE_INTEGER)], [wrap(Number.POSITIVE_INFINITY)], [valuesGetUpperBound({ doubleValue: NaN })], @@ -576,6 +626,10 @@ describe('Values', () => { [wrap(date1)], [valuesGetUpperBound({ timestampValue: {} })], + // bson timestamps + [wrap(bsonTimestamp(4294967295, 4294967295))], // largest bson timestamp value + [valuesGetUpperBound(MIN_BSON_TIMESTAMP_VALUE)], + // strings [wrap('\u0000')], [valuesGetUpperBound({ stringValue: '' })], @@ -584,20 +638,39 @@ describe('Values', () => { [wrap(blob(255))], [valuesGetUpperBound({ bytesValue: '' })], + // bson binary data + [wrap(bsonBinaryData(128, new Uint8Array([255, 255, 255])))], + [valuesGetUpperBound(MIN_BSON_BINARY_VALUE)], + // resource names [refValue(dbId('', ''), key('a/a'))], [valuesGetUpperBound({ referenceValue: '' })], + // bson object ids + [wrap(bsonObjectId('foo'))], + [valuesGetUpperBound(MIN_BSON_OBJECT_ID_VALUE)], + // geo points [wrap(new GeoPoint(90, 180))], [valuesGetUpperBound({ geoPointValue: {} })], + // regular expressions + [wrap(regex('a', 'i'))], + [valuesGetUpperBound(MIN_REGEX_VALUE)], + // arrays [wrap([false])], [valuesGetUpperBound({ arrayValue: {} })], + // vectors + [wrap(vector([1, 2, 3]))], + [valuesGetUpperBound(MIN_VECTOR_VALUE)], + // objects - [wrap({ 'a': 'b' })] + [wrap({ 'a': 'b' })], + + // MaxKey + [wrap(maxKey())] ]; expectCorrectComparisonGroups( From 82f32ca7de9508352e26be16ad7b1ab2f0eb0d46 Mon Sep 17 00:00:00 2001 From: Mila <107142260+milaGGL@users.noreply.github.com> Date: Fri, 14 Mar 2025 13:44:38 -0400 Subject: [PATCH 03/77] rename BsonTimestampValue class to BsonTimestamp (#333) --- packages/firestore/lite/index.ts | 2 +- .../src/lite-api/bson_timestamp_value.ts | 12 ++++++------ .../firestore/src/lite-api/field_value_impl.ts | 10 +++++----- .../firestore/src/lite-api/user_data_reader.ts | 8 ++++---- .../firestore/src/lite-api/user_data_writer.ts | 18 ++++++++---------- .../firestore/test/unit/model/values.test.ts | 4 ++-- 6 files changed, 26 insertions(+), 28 deletions(-) diff --git a/packages/firestore/lite/index.ts b/packages/firestore/lite/index.ts index 6d1d6c01998..48e0bdae068 100644 --- a/packages/firestore/lite/index.ts +++ b/packages/firestore/lite/index.ts @@ -156,7 +156,7 @@ export { BsonBinaryData } from '../src/lite-api/bson_binary_data'; export { BsonObjectId } from '../src/lite-api/bson_object_Id'; -export { BsonTimestampValue } from '../src/lite-api/bson_timestamp_value'; +export { BsonTimestamp } from '../src/lite-api/bson_timestamp_value'; export { MinKey } from '../src/lite-api/min_key'; diff --git a/packages/firestore/src/lite-api/bson_timestamp_value.ts b/packages/firestore/src/lite-api/bson_timestamp_value.ts index 60b48157906..0b317f9042c 100644 --- a/packages/firestore/src/lite-api/bson_timestamp_value.ts +++ b/packages/firestore/src/lite-api/bson_timestamp_value.ts @@ -18,18 +18,18 @@ /** * Represents a BSON Timestamp type in Firestore documents. * - * @class BsonTimestampValue + * @class BsonTimestamp */ -export class BsonTimestampValue { +export class BsonTimestamp { constructor(readonly seconds: number, readonly increment: number) {} /** - * Returns true if this `BsonTimestampValue` is equal to the provided one. + * Returns true if this `BsonTimestamp` is equal to the provided one. * - * @param other - The `BsonTimestampValue` to compare against. - * @return 'true' if this `BsonTimestampValue` is equal to the provided one. + * @param other - The `BsonTimestamp` to compare against. + * @return 'true' if this `BsonTimestamp` is equal to the provided one. */ - isEqual(other: BsonTimestampValue): boolean { + isEqual(other: BsonTimestamp): boolean { return this.seconds === other.seconds && this.increment === other.increment; } } diff --git a/packages/firestore/src/lite-api/field_value_impl.ts b/packages/firestore/src/lite-api/field_value_impl.ts index 2cc1e3522b0..ade0656e0d3 100644 --- a/packages/firestore/src/lite-api/field_value_impl.ts +++ b/packages/firestore/src/lite-api/field_value_impl.ts @@ -17,7 +17,7 @@ import { BsonBinaryData } from './bson_binary_data'; import { BsonObjectId } from './bson_object_Id'; -import { BsonTimestampValue } from './bson_timestamp_value'; +import { BsonTimestamp } from './bson_timestamp_value'; import { FieldValue } from './field_value'; import { Int32Value } from './int32_value'; import { MaxKey } from './max_key'; @@ -167,18 +167,18 @@ export function bsonObjectId(value: string): BsonObjectId { } /** - * Creates a new `BsonTimestampValue` constructed with the given seconds and increment. + * Creates a new `BsonTimestamp` constructed with the given seconds and increment. * * @param seconds - The underlying unsigned 32-bit integer for seconds. * @param seconds - The underlying unsigned 32-bit integer for increment. * - * @returns A new `BsonTimestampValue` constructed with the given seconds and increment. + * @returns A new `BsonTimestamp` constructed with the given seconds and increment. */ export function bsonTimestamp( seconds: number, increment: number -): BsonTimestampValue { - return new BsonTimestampValue(seconds, increment); +): BsonTimestamp { + return new BsonTimestamp(seconds, increment); } /** diff --git a/packages/firestore/src/lite-api/user_data_reader.ts b/packages/firestore/src/lite-api/user_data_reader.ts index 3d0ce031599..9d7e6fa79f1 100644 --- a/packages/firestore/src/lite-api/user_data_reader.ts +++ b/packages/firestore/src/lite-api/user_data_reader.ts @@ -77,7 +77,7 @@ import { Dict, forEach, isEmpty } from '../util/obj'; import { BsonBinaryData } from './bson_binary_data'; import { BsonObjectId } from './bson_object_Id'; -import { BsonTimestampValue } from './bson_timestamp_value'; +import { BsonTimestamp } from './bson_timestamp_value'; import { Bytes } from './bytes'; import { Firestore } from './database'; import { FieldPath } from './field_path'; @@ -934,7 +934,7 @@ function parseScalarValue( return parseBsonObjectId(value); } else if (value instanceof Int32Value) { return parseInt32Value(value); - } else if (value instanceof BsonTimestampValue) { + } else if (value instanceof BsonTimestamp) { return parseBsonTimestamp(value); } else if (value instanceof BsonBinaryData) { return parseBsonBinaryData(context.serializer, value); @@ -1043,7 +1043,7 @@ export function parseInt32Value(value: Int32Value): ProtoValue { return { mapValue }; } -export function parseBsonTimestamp(value: BsonTimestampValue): ProtoValue { +export function parseBsonTimestamp(value: BsonTimestamp): ProtoValue { const mapValue: ProtoMapValue = { fields: { [RESERVED_BSON_TIMESTAMP_KEY]: { @@ -1105,7 +1105,7 @@ function looksLikeJsonObject(input: unknown): boolean { !(input instanceof Int32Value) && !(input instanceof RegexValue) && !(input instanceof BsonObjectId) && - !(input instanceof BsonTimestampValue) && + !(input instanceof BsonTimestamp) && !(input instanceof BsonBinaryData) ); } diff --git a/packages/firestore/src/lite-api/user_data_writer.ts b/packages/firestore/src/lite-api/user_data_writer.ts index 012b04874c3..e4719591b4c 100644 --- a/packages/firestore/src/lite-api/user_data_writer.ts +++ b/packages/firestore/src/lite-api/user_data_writer.ts @@ -60,7 +60,7 @@ import { forEach } from '../util/obj'; import { BsonBinaryData } from './bson_binary_data'; import { BsonObjectId } from './bson_object_Id'; -import { BsonTimestampValue } from './bson_timestamp_value'; +import { BsonTimestamp } from './bson_timestamp_value'; import { maxKey, minKey } from './field_value_impl'; import { GeoPoint } from './geo_point'; import { Int32Value } from './int32_value'; @@ -112,11 +112,11 @@ export abstract class AbstractUserDataWriter { case TypeOrder.RegexValue: return this.convertToRegexValue(value.mapValue!); case TypeOrder.BsonObjectIdValue: - return this.convertToBsonObjectIdValue(value.mapValue!); + return this.convertToBsonObjectId(value.mapValue!); case TypeOrder.BsonBinaryValue: - return this.convertToBsonBinaryValue(value.mapValue!); + return this.convertToBsonBinaryData(value.mapValue!); case TypeOrder.BsonTimestampValue: - return this.convertToBsonTimestampValue(value.mapValue!); + return this.convertToBsonTimestamp(value.mapValue!); case TypeOrder.MaxKeyValue: return maxKey(); case TypeOrder.MinKeyValue: @@ -160,13 +160,13 @@ export abstract class AbstractUserDataWriter { return new VectorValue(values); } - private convertToBsonObjectIdValue(mapValue: ProtoMapValue): BsonObjectId { + private convertToBsonObjectId(mapValue: ProtoMapValue): BsonObjectId { const oid = mapValue!.fields?.[RESERVED_BSON_OBJECT_ID_KEY]?.stringValue ?? ''; return new BsonObjectId(oid); } - private convertToBsonBinaryValue(mapValue: ProtoMapValue): BsonBinaryData { + private convertToBsonBinaryData(mapValue: ProtoMapValue): BsonBinaryData { const fields = mapValue!.fields?.[RESERVED_BSON_BINARY_KEY]; const subtypeAndData = fields?.bytesValue; if (!subtypeAndData) { @@ -182,9 +182,7 @@ export abstract class AbstractUserDataWriter { return new BsonBinaryData(Number(subtype), data); } - private convertToBsonTimestampValue( - mapValue: ProtoMapValue - ): BsonTimestampValue { + private convertToBsonTimestamp(mapValue: ProtoMapValue): BsonTimestamp { const fields = mapValue!.fields?.[RESERVED_BSON_TIMESTAMP_KEY]; const seconds = Number( fields?.mapValue?.fields?.[RESERVED_BSON_TIMESTAMP_SECONDS_KEY] @@ -194,7 +192,7 @@ export abstract class AbstractUserDataWriter { fields?.mapValue?.fields?.[RESERVED_BSON_TIMESTAMP_INCREMENT_KEY] ?.integerValue ); - return new BsonTimestampValue(seconds, increment); + return new BsonTimestamp(seconds, increment); } private convertToRegexValue(mapValue: ProtoMapValue): RegexValue { diff --git a/packages/firestore/test/unit/model/values.test.ts b/packages/firestore/test/unit/model/values.test.ts index 7a2558d2461..4054dd6481d 100644 --- a/packages/firestore/test/unit/model/values.test.ts +++ b/packages/firestore/test/unit/model/values.test.ts @@ -21,7 +21,7 @@ import { GeoPoint, Timestamp } from '../../../src'; import { DatabaseId } from '../../../src/core/database_info'; import { BsonBinaryData } from '../../../src/lite-api/bson_binary_data'; import { BsonObjectId } from '../../../src/lite-api/bson_object_Id'; -import { BsonTimestampValue } from '../../../src/lite-api/bson_timestamp_value'; +import { BsonTimestamp } from '../../../src/lite-api/bson_timestamp_value'; import { vector, regex, @@ -118,7 +118,7 @@ describe('Values', () => { [wrap(vector([]))], [wrap(vector([1, 2.3, -4.0]))], [wrap(regex('^foo', 'i')), wrap(new RegexValue('^foo', 'i'))], - [wrap(bsonTimestamp(57, 4)), wrap(new BsonTimestampValue(57, 4))], + [wrap(bsonTimestamp(57, 4)), wrap(new BsonTimestamp(57, 4))], [ wrap(bsonBinaryData(128, Uint8Array.from([7, 8, 9]))), wrap(new BsonBinaryData(128, Uint8Array.from([7, 8, 9]))), From 1df3d26fbfb4db24b74d5d779825017e9ec40eaa Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Fri, 4 Apr 2025 11:55:48 -0700 Subject: [PATCH 04/77] Fix Data Connect Types (#8898) --- .changeset/hungry-snails-drive.md | 5 +++++ common/api-review/data-connect.api.md | 18 +++++++++++++++++- packages/data-connect/src/api/index.ts | 1 + packages/data-connect/src/core/error.ts | 1 - 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 .changeset/hungry-snails-drive.md diff --git a/.changeset/hungry-snails-drive.md b/.changeset/hungry-snails-drive.md new file mode 100644 index 00000000000..1a29782a04d --- /dev/null +++ b/.changeset/hungry-snails-drive.md @@ -0,0 +1,5 @@ +--- +"@firebase/data-connect": patch +--- + +Fix DataConnectOperationError. diff --git a/common/api-review/data-connect.api.md b/common/api-review/data-connect.api.md index 786714361af..9e3d2424876 100644 --- a/common/api-review/data-connect.api.md +++ b/common/api-review/data-connect.api.md @@ -24,6 +24,20 @@ export const CallerSdkTypeEnum: { readonly GeneratedAngular: "GeneratedAngular"; }; +// @public (undocumented) +export type Code = DataConnectErrorCode; + +// @public (undocumented) +export const Code: { + OTHER: DataConnectErrorCode; + ALREADY_INITIALIZED: DataConnectErrorCode; + NOT_INITIALIZED: DataConnectErrorCode; + NOT_SUPPORTED: DataConnectErrorCode; + INVALID_ARGUMENT: DataConnectErrorCode; + PARTIAL_ERROR: DataConnectErrorCode; + UNAUTHORIZED: DataConnectErrorCode; +}; + // @public export function connectDataConnectEmulator(dc: DataConnect, host: string, port?: number, sslEnabled?: boolean): void; @@ -54,7 +68,9 @@ export class DataConnect { // @public export class DataConnectError extends FirebaseError { - } + /* Excluded from this release type: name */ + constructor(code: Code, message: string); +} // @public (undocumented) export type DataConnectErrorCode = 'other' | 'already-initialized' | 'not-initialized' | 'not-supported' | 'invalid-argument' | 'partial-error' | 'unauthorized'; diff --git a/packages/data-connect/src/api/index.ts b/packages/data-connect/src/api/index.ts index dcd48485571..72ee8b313e5 100644 --- a/packages/data-connect/src/api/index.ts +++ b/packages/data-connect/src/api/index.ts @@ -24,6 +24,7 @@ export { setLogLevel } from '../logger'; export { validateArgs } from '../util/validateArgs'; export { DataConnectErrorCode, + Code, DataConnectError, DataConnectOperationError, DataConnectOperationFailureResponse, diff --git a/packages/data-connect/src/core/error.ts b/packages/data-connect/src/core/error.ts index b1246969e48..bbf1e299e0d 100644 --- a/packages/data-connect/src/core/error.ts +++ b/packages/data-connect/src/core/error.ts @@ -43,7 +43,6 @@ export class DataConnectError extends FirebaseError { /** @internal */ readonly name: string = 'DataConnectError'; - /** @hideconstructor */ constructor(code: Code, message: string) { super(code, message); From 66a09f247603519768c6b6a3e1c13732a48781f8 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Tue, 8 Apr 2025 13:17:02 -0400 Subject: [PATCH 05/77] test(vertexai): update mock responses to v8 (#8904) --- scripts/update_vertexai_responses.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update_vertexai_responses.sh b/scripts/update_vertexai_responses.sh index 0d1f1a2c6f6..de55ac176ce 100755 --- a/scripts/update_vertexai_responses.sh +++ b/scripts/update_vertexai_responses.sh @@ -17,7 +17,7 @@ # This script replaces mock response files for Vertex AI unit tests with a fresh # clone of the shared repository of Vertex AI test data. -RESPONSES_VERSION='v7.*' # The major version of mock responses to use +RESPONSES_VERSION='v8.*' # The major version of mock responses to use REPO_NAME="vertexai-sdk-test-data" REPO_LINK="https://github.com/FirebaseExtended/$REPO_NAME.git" From b3328250d516142c8bec4f5c9bfe8663523ffcb4 Mon Sep 17 00:00:00 2001 From: Mila <107142260+milaGGL@users.noreply.github.com> Date: Wed, 9 Apr 2025 10:26:26 -0400 Subject: [PATCH 06/77] Fix: display WebChannel error message (#8907) --- .../firestore/src/platform/browser/webchannel_connection.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/firestore/src/platform/browser/webchannel_connection.ts b/packages/firestore/src/platform/browser/webchannel_connection.ts index 6813b88f65a..206e5829c41 100644 --- a/packages/firestore/src/platform/browser/webchannel_connection.ts +++ b/packages/firestore/src/platform/browser/webchannel_connection.ts @@ -326,8 +326,10 @@ export class WebChannelConnection extends RestConnection { closed = true; logWarn( LOG_TAG, - `RPC '${rpcName}' stream ${streamId} transport errored:`, - err + `RPC '${rpcName}' stream ${streamId} transport errored. Name:`, + err.name, + 'Message:', + err.message ); streamBridge.callOnClose( new FirestoreError( From 4e0f630e714cd63e1e39ffb0b56918a88951fe8e Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Wed, 9 Apr 2025 14:19:49 -0400 Subject: [PATCH 07/77] test(vertexai): add `backendName` param to mock response getters (#8906) `backendName` can be either `googleAI` or `vertexAI`. This can be passed to `getMockResponse` or `getMockResponseStreaming` to specify whether the mock response lookup should be done in the set of vertexAI or googleAI mock files. Modified the `convert-mocks.ts` script to read mock responses from the 'developerapi' directory, and add an export to the generated file for the new lookup object with those mock responses. --- .../vertexai/src/methods/count-tokens.test.ts | 12 +- .../src/methods/generate-content.test.ts | 26 +++- .../src/models/generative-model.test.ts | 11 +- .../vertexai/src/models/imagen-model.test.ts | 3 + .../vertexai/src/requests/request.test.ts | 1 + .../src/requests/response-helpers.test.ts | 5 + .../src/requests/stream-reader.test.ts | 16 ++- packages/vertexai/test-utils/convert-mocks.ts | 131 +++++++++++++----- packages/vertexai/test-utils/mock-response.ts | 27 +++- 9 files changed, 186 insertions(+), 46 deletions(-) diff --git a/packages/vertexai/src/methods/count-tokens.test.ts b/packages/vertexai/src/methods/count-tokens.test.ts index a3d7c99b4ba..9eccbf702fe 100644 --- a/packages/vertexai/src/methods/count-tokens.test.ts +++ b/packages/vertexai/src/methods/count-tokens.test.ts @@ -45,7 +45,10 @@ describe('countTokens()', () => { restore(); }); it('total tokens', async () => { - const mockResponse = getMockResponse('unary-success-total-tokens.json'); + const mockResponse = getMockResponse( + 'vertexAI', + 'unary-success-total-tokens.json' + ); const makeRequestStub = stub(request, 'makeRequest').resolves( mockResponse as Response ); @@ -69,6 +72,7 @@ describe('countTokens()', () => { }); it('total tokens with modality details', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-detailed-token-response.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -96,6 +100,7 @@ describe('countTokens()', () => { }); it('total tokens no billable characters', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-no-billable-characters.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -120,7 +125,10 @@ describe('countTokens()', () => { ); }); it('model not found', async () => { - const mockResponse = getMockResponse('unary-failure-model-not-found.json'); + const mockResponse = getMockResponse( + 'vertexAI', + 'unary-failure-model-not-found.json' + ); const mockFetch = stub(globalThis, 'fetch').resolves({ ok: false, status: 404, diff --git a/packages/vertexai/src/methods/generate-content.test.ts b/packages/vertexai/src/methods/generate-content.test.ts index 426bd5176db..1d15632f828 100644 --- a/packages/vertexai/src/methods/generate-content.test.ts +++ b/packages/vertexai/src/methods/generate-content.test.ts @@ -61,6 +61,7 @@ describe('generateContent()', () => { }); it('short response', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-basic-reply-short.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -84,7 +85,10 @@ describe('generateContent()', () => { ); }); it('long response', async () => { - const mockResponse = getMockResponse('unary-success-basic-reply-long.json'); + const mockResponse = getMockResponse( + 'vertexAI', + 'unary-success-basic-reply-long.json' + ); const makeRequestStub = stub(request, 'makeRequest').resolves( mockResponse as Response ); @@ -105,6 +109,7 @@ describe('generateContent()', () => { }); it('long response with token details', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-basic-response-long-usage-metadata.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -138,7 +143,10 @@ describe('generateContent()', () => { ); }); it('citations', async () => { - const mockResponse = getMockResponse('unary-success-citations.json'); + const mockResponse = getMockResponse( + 'vertexAI', + 'unary-success-citations.json' + ); const makeRequestStub = stub(request, 'makeRequest').resolves( mockResponse as Response ); @@ -163,6 +171,7 @@ describe('generateContent()', () => { }); it('blocked prompt', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-failure-prompt-blocked-safety.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -184,6 +193,7 @@ describe('generateContent()', () => { }); it('finishReason safety', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-failure-finish-reason-safety.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -204,7 +214,10 @@ describe('generateContent()', () => { ); }); it('empty content', async () => { - const mockResponse = getMockResponse('unary-failure-empty-content.json'); + const mockResponse = getMockResponse( + 'vertexAI', + 'unary-failure-empty-content.json' + ); const makeRequestStub = stub(request, 'makeRequest').resolves( mockResponse as Response ); @@ -224,6 +237,7 @@ describe('generateContent()', () => { }); it('unknown enum - should ignore', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-unknown-enum-safety-ratings.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -244,7 +258,10 @@ describe('generateContent()', () => { ); }); it('image rejected (400)', async () => { - const mockResponse = getMockResponse('unary-failure-image-rejected.json'); + const mockResponse = getMockResponse( + 'vertexAI', + 'unary-failure-image-rejected.json' + ); const mockFetch = stub(globalThis, 'fetch').resolves({ ok: false, status: 400, @@ -257,6 +274,7 @@ describe('generateContent()', () => { }); it('api not enabled (403)', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-failure-firebasevertexai-api-not-enabled.json' ); const mockFetch = stub(globalThis, 'fetch').resolves({ diff --git a/packages/vertexai/src/models/generative-model.test.ts b/packages/vertexai/src/models/generative-model.test.ts index 26dff4e04c6..987f9b115e2 100644 --- a/packages/vertexai/src/models/generative-model.test.ts +++ b/packages/vertexai/src/models/generative-model.test.ts @@ -60,6 +60,7 @@ describe('GenerativeModel', () => { ); expect(genModel.systemInstruction?.parts[0].text).to.equal('be friendly'); const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-basic-reply-short.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -89,6 +90,7 @@ describe('GenerativeModel', () => { }); expect(genModel.systemInstruction?.parts[0].text).to.equal('be friendly'); const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-basic-reply-short.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -129,6 +131,7 @@ describe('GenerativeModel', () => { ); expect(genModel.systemInstruction?.parts[0].text).to.equal('be friendly'); const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-basic-reply-short.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -177,6 +180,7 @@ describe('GenerativeModel', () => { ); expect(genModel.systemInstruction?.parts[0].text).to.equal('be friendly'); const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-basic-reply-short.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -206,6 +210,7 @@ describe('GenerativeModel', () => { }); expect(genModel.systemInstruction?.parts[0].text).to.equal('be friendly'); const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-basic-reply-short.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -239,6 +244,7 @@ describe('GenerativeModel', () => { ); expect(genModel.systemInstruction?.parts[0].text).to.equal('be friendly'); const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-basic-reply-short.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -277,7 +283,10 @@ describe('GenerativeModel', () => { }); it('calls countTokens', async () => { const genModel = new GenerativeModel(fakeVertexAI, { model: 'my-model' }); - const mockResponse = getMockResponse('unary-success-total-tokens.json'); + const mockResponse = getMockResponse( + 'vertexAI', + 'unary-success-total-tokens.json' + ); const makeRequestStub = stub(request, 'makeRequest').resolves( mockResponse as Response ); diff --git a/packages/vertexai/src/models/imagen-model.test.ts b/packages/vertexai/src/models/imagen-model.test.ts index c566a88e5b0..9e534f2195a 100644 --- a/packages/vertexai/src/models/imagen-model.test.ts +++ b/packages/vertexai/src/models/imagen-model.test.ts @@ -47,6 +47,7 @@ const fakeVertexAI: VertexAI = { describe('ImagenModel', () => { it('generateImages makes a request to predict with default parameters', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-generate-images-base64.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -90,6 +91,7 @@ describe('ImagenModel', () => { }); const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-generate-images-base64.json' ); const makeRequestStub = stub(request, 'makeRequest').resolves( @@ -133,6 +135,7 @@ describe('ImagenModel', () => { }); it('throws if prompt blocked', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-failure-generate-images-prompt-blocked.json' ); diff --git a/packages/vertexai/src/requests/request.test.ts b/packages/vertexai/src/requests/request.test.ts index 499f06c848b..cd39a0f8ae5 100644 --- a/packages/vertexai/src/requests/request.test.ts +++ b/packages/vertexai/src/requests/request.test.ts @@ -414,6 +414,7 @@ describe('request methods', () => { }); it('Network error, API not enabled', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-failure-firebasevertexai-api-not-enabled.json' ); const fetchStub = stub(globalThis, 'fetch').resolves( diff --git a/packages/vertexai/src/requests/response-helpers.test.ts b/packages/vertexai/src/requests/response-helpers.test.ts index 4cab8cde047..5371d040253 100644 --- a/packages/vertexai/src/requests/response-helpers.test.ts +++ b/packages/vertexai/src/requests/response-helpers.test.ts @@ -257,6 +257,7 @@ describe('response-helpers methods', () => { describe('handlePredictResponse', () => { it('returns base64 images', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-generate-images-base64.json' ) as Response; const res = await handlePredictResponse(mockResponse); @@ -270,6 +271,7 @@ describe('response-helpers methods', () => { }); it('returns GCS images', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-success-generate-images-gcs.json' ) as Response; const res = await handlePredictResponse(mockResponse); @@ -284,6 +286,7 @@ describe('response-helpers methods', () => { }); it('has filtered reason and no images if all images were filtered', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-failure-generate-images-all-filtered.json' ) as Response; const res = await handlePredictResponse(mockResponse); @@ -294,6 +297,7 @@ describe('response-helpers methods', () => { }); it('has filtered reason and no images if all base64 images were filtered', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-failure-generate-images-base64-some-filtered.json' ) as Response; const res = await handlePredictResponse(mockResponse); @@ -308,6 +312,7 @@ describe('response-helpers methods', () => { }); it('has filtered reason and no images if all GCS images were filtered', async () => { const mockResponse = getMockResponse( + 'vertexAI', 'unary-failure-generate-images-gcs-some-filtered.json' ) as Response; const res = await handlePredictResponse(mockResponse); diff --git a/packages/vertexai/src/requests/stream-reader.test.ts b/packages/vertexai/src/requests/stream-reader.test.ts index b68c2423066..bf959276a93 100644 --- a/packages/vertexai/src/requests/stream-reader.test.ts +++ b/packages/vertexai/src/requests/stream-reader.test.ts @@ -72,6 +72,7 @@ describe('processStream', () => { }); it('streaming response - short', async () => { const fakeResponse = getMockResponseStreaming( + 'vertexAI', 'streaming-success-basic-reply-short.txt' ); const result = processStream(fakeResponse as Response); @@ -83,6 +84,7 @@ describe('processStream', () => { }); it('streaming response - long', async () => { const fakeResponse = getMockResponseStreaming( + 'vertexAI', 'streaming-success-basic-reply-long.txt' ); const result = processStream(fakeResponse as Response); @@ -95,6 +97,7 @@ describe('processStream', () => { }); it('streaming response - long - big chunk', async () => { const fakeResponse = getMockResponseStreaming( + 'vertexAI', 'streaming-success-basic-reply-long.txt', 1e6 ); @@ -107,7 +110,10 @@ describe('processStream', () => { expect(aggregatedResponse.text()).to.include('to their owners.'); }); it('streaming response - utf8', async () => { - const fakeResponse = getMockResponseStreaming('streaming-success-utf8.txt'); + const fakeResponse = getMockResponseStreaming( + 'vertexAI', + 'streaming-success-utf8.txt' + ); const result = processStream(fakeResponse as Response); for await (const response of result.stream) { expect(response.text()).to.not.be.empty; @@ -118,6 +124,7 @@ describe('processStream', () => { }); it('streaming response - functioncall', async () => { const fakeResponse = getMockResponseStreaming( + 'vertexAI', 'streaming-success-function-call-short.txt' ); const result = processStream(fakeResponse as Response); @@ -141,6 +148,7 @@ describe('processStream', () => { }); it('candidate had finishReason', async () => { const fakeResponse = getMockResponseStreaming( + 'vertexAI', 'streaming-failure-finish-reason-safety.txt' ); const result = processStream(fakeResponse as Response); @@ -153,6 +161,7 @@ describe('processStream', () => { }); it('prompt was blocked', async () => { const fakeResponse = getMockResponseStreaming( + 'vertexAI', 'streaming-failure-prompt-blocked-safety.txt' ); const result = processStream(fakeResponse as Response); @@ -165,6 +174,7 @@ describe('processStream', () => { }); it('empty content', async () => { const fakeResponse = getMockResponseStreaming( + 'vertexAI', 'streaming-failure-empty-content.txt' ); const result = processStream(fakeResponse as Response); @@ -176,6 +186,7 @@ describe('processStream', () => { }); it('unknown enum - should ignore', async () => { const fakeResponse = getMockResponseStreaming( + 'vertexAI', 'streaming-success-unknown-safety-enum.txt' ); const result = processStream(fakeResponse as Response); @@ -187,6 +198,7 @@ describe('processStream', () => { }); it('recitation ending with a missing content field', async () => { const fakeResponse = getMockResponseStreaming( + 'vertexAI', 'streaming-failure-recitation-no-content.txt' ); const result = processStream(fakeResponse as Response); @@ -205,6 +217,7 @@ describe('processStream', () => { }); it('handles citations', async () => { const fakeResponse = getMockResponseStreaming( + 'vertexAI', 'streaming-success-citations.txt' ); const result = processStream(fakeResponse as Response); @@ -224,6 +237,7 @@ describe('processStream', () => { }); it('removes empty text parts', async () => { const fakeResponse = getMockResponseStreaming( + 'vertexAI', 'streaming-success-empty-text-part.txt' ); const result = processStream(fakeResponse as Response); diff --git a/packages/vertexai/test-utils/convert-mocks.ts b/packages/vertexai/test-utils/convert-mocks.ts index c306bec312f..851d6017b6d 100644 --- a/packages/vertexai/test-utils/convert-mocks.ts +++ b/packages/vertexai/test-utils/convert-mocks.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,42 +15,111 @@ * limitations under the License. */ +/* eslint-disable @typescript-eslint/no-require-imports */ +const { readdirSync, readFileSync, writeFileSync } = require('node:fs'); +const { join } = require('node:path'); + +const MOCK_RESPONSES_DIR_PATH = join( + __dirname, + 'vertexai-sdk-test-data', + 'mock-responses' +); +const MOCK_LOOKUP_OUTPUT_PATH = join(__dirname, 'mocks-lookup.ts'); + +type BackendName = 'vertexAI' | 'googleAI'; + +const mockDirs: Record = { + vertexAI: join(MOCK_RESPONSES_DIR_PATH, 'vertexai'), + // Note: the dirname is developerapi is legacy. It should be updated to googleai. + googleAI: join(MOCK_RESPONSES_DIR_PATH, 'developerapi') +}; + /** - * Converts mock text files into a js file that karma can read without - * using fs. + * Generates a JS file that exports maps from filenames to JSON mock responses (as strings) + * for each backend. + * + * This allows tests that run in a browser to access the mock responses without having to + * read from local disk and requiring 'fs'. */ +function generateMockLookupFile(): void { + console.log('Generating mock lookup file...'); + const vertexAIMockLookupText = generateMockLookup('vertexAI'); + const googleAIMockLookupText = generateMockLookup('googleAI'); -// eslint-disable-next-line @typescript-eslint/no-require-imports -const fs = require('fs'); -// eslint-disable-next-line @typescript-eslint/no-require-imports -const { join } = require('path'); + const fileText = ` +/** + * DO NOT EDIT - This file was generated by the packages/vertexai/test-utils/convert-mocks.ts script. + * + * These objects map mock response filenames to their JSON contents. + * + * Mock response files are pulled from https://github.com/FirebaseExtended/vertexai-sdk-test-data. + */ -const mockResponseDir = join( - __dirname, - 'vertexai-sdk-test-data/mock-responses/vertexai' -); +// Automatically generated at: ${new Date().toISOString()} + +${vertexAIMockLookupText} -async function main(): Promise { - const list = fs.readdirSync(mockResponseDir); +${googleAIMockLookupText} +`; + try { + writeFileSync(MOCK_LOOKUP_OUTPUT_PATH, fileText, 'utf-8'); + console.log( + `Successfully generated mock lookup file at: ${MOCK_LOOKUP_OUTPUT_PATH}` + ); + } catch (err) { + console.error( + `Error writing mock lookup file to ${MOCK_LOOKUP_OUTPUT_PATH}:`, + err + ); + process.exit(1); + } +} + +/** + * Given a directory that contains mock response files, reads through all the files, + * maps file names to file contents, and returns a string of typescript code + * that exports that map as an object. + */ +function generateMockLookup(backendName: BackendName): string { const lookup: Record = {}; - // eslint-disable-next-line guard-for-in - for (const fileName of list) { - const fullText = fs.readFileSync(join(mockResponseDir, fileName), 'utf-8'); - lookup[fileName] = fullText; + const mockDir = mockDirs[backendName]; + let mockFilenames: string[]; + + console.log( + `Processing mocks for "${backendName}" from directory: ${mockDir}` + ); + + try { + mockFilenames = readdirSync(mockDir); + } catch (err) { + console.error( + `Error reading directory ${mockDir} for ${backendName}:`, + err + ); + return `export const ${backendName}MocksLookup: Record = {};`; } - let fileText = `// Generated from mocks text files.`; - - fileText += '\n\n'; - fileText += `export const mocksLookup: Record = ${JSON.stringify( - lookup, - null, - 2 - )}`; - fileText += ';\n'; - fs.writeFileSync(join(__dirname, 'mocks-lookup.ts'), fileText, 'utf-8'); + + if (mockFilenames.length === 0) { + console.warn(`No .json files found in ${mockDir} for ${backendName}.`); + } + + for (const mockFilename of mockFilenames) { + const mockFilepath = `${mockDir}/${mockFilename}`; + try { + const fullText = readFileSync(mockFilepath, 'utf-8'); + lookup[mockFilename] = fullText; + } catch (err) { + console.error( + `Error reading mock file ${mockFilepath} for ${backendName}:`, + err + ); + } + } + + // Use JSON.stringify with indentation for readable output in the generated file + const lookupJsonString = JSON.stringify(lookup, null, 2); + + return `export const ${backendName}MocksLookup: Record = ${lookupJsonString};`; } -main().catch(e => { - console.error(e); - process.exit(1); -}); +generateMockLookupFile(); diff --git a/packages/vertexai/test-utils/mock-response.ts b/packages/vertexai/test-utils/mock-response.ts index 9b42c93427b..5128ddabe74 100644 --- a/packages/vertexai/test-utils/mock-response.ts +++ b/packages/vertexai/test-utils/mock-response.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,12 @@ * limitations under the License. */ -import { mocksLookup } from './mocks-lookup'; +import { vertexAIMocksLookup, googleAIMocksLookup } from './mocks-lookup'; + +const mockSetMaps: Record> = { + 'vertexAI': vertexAIMocksLookup, + 'googleAI': googleAIMocksLookup +}; /** * Mock native Response.body @@ -45,25 +50,33 @@ export function getChunkedStream( return stream; } + export function getMockResponseStreaming( + backendName: BackendName, filename: string, chunkLength: number = 20 ): Partial { - if (!(filename in mocksLookup)) { - throw Error(`Mock response file '${filename}' not found.`); + const mocksMap = mockSetMaps[backendName]; + if (!(filename in mocksMap)) { + throw Error(`${backendName} mock response file '${filename}' not found.`); } - const fullText = mocksLookup[filename]; + const fullText = mocksMap[filename]; return { body: getChunkedStream(fullText, chunkLength) }; } -export function getMockResponse(filename: string): Partial { +export function getMockResponse( + backendName: BackendName, + filename: string +): Partial { + const mocksLookup = mockSetMaps[backendName]; if (!(filename in mocksLookup)) { - throw Error(`Mock response file '${filename}' not found.`); + throw Error(`${backendName} mock response file '${filename}' not found.`); } const fullText = mocksLookup[filename]; + return { ok: true, json: () => Promise.resolve(JSON.parse(fullText)) From ed0803a29791cc0cecd0153f95e814ddcee7efd8 Mon Sep 17 00:00:00 2001 From: Mila <107142260+milaGGL@users.noreply.github.com> Date: Fri, 11 Apr 2025 11:16:52 -0400 Subject: [PATCH 08/77] fix: remove `null` value inclusion from `!=` and `not-in` filter results (#8915) --- .changeset/cyan-frogs-relate.md | 6 ++ packages/firestore/src/core/filter.ts | 7 +- .../test/integration/api/query.test.ts | 94 +++++++++++++++++++ .../firestore/test/unit/core/query.test.ts | 4 +- 4 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 .changeset/cyan-frogs-relate.md diff --git a/.changeset/cyan-frogs-relate.md b/.changeset/cyan-frogs-relate.md new file mode 100644 index 00000000000..08af27593a9 --- /dev/null +++ b/.changeset/cyan-frogs-relate.md @@ -0,0 +1,6 @@ +--- +'@firebase/firestore': patch +'firebase': patch +--- + +Fixed the `null` value handling in `!=` and `not-in` filters. diff --git a/packages/firestore/src/core/filter.ts b/packages/firestore/src/core/filter.ts index 06e2740c315..12b57729f81 100644 --- a/packages/firestore/src/core/filter.ts +++ b/packages/firestore/src/core/filter.ts @@ -141,6 +141,7 @@ export class FieldFilter extends Filter { if (this.op === Operator.NOT_EQUAL) { return ( other !== null && + other.nullValue === undefined && this.matchesComparison(valueCompare(other!, this.value)) ); } @@ -495,7 +496,11 @@ export class NotInFilter extends FieldFilter { return false; } const other = doc.data.field(this.field); - return other !== null && !arrayValueContains(this.value.arrayValue!, other); + return ( + other !== null && + other.nullValue === undefined && + !arrayValueContains(this.value.arrayValue!, other) + ); } } diff --git a/packages/firestore/test/integration/api/query.test.ts b/packages/firestore/test/integration/api/query.test.ts index 01fd0e47e35..5871607eb03 100644 --- a/packages/firestore/test/integration/api/query.test.ts +++ b/packages/firestore/test/integration/api/query.test.ts @@ -1751,6 +1751,100 @@ apiDescribe('Queries', persistence => { ); }); }); + + it('sdk uses != filter same as backend', async () => { + const testDocs = { + a: { zip: Number.NaN }, + b: { zip: 91102 }, + c: { zip: 98101 }, + d: { zip: '98101' }, + e: { zip: [98101] }, + f: { zip: [98101, 98102] }, + g: { zip: ['98101', { zip: 98101 }] }, + h: { zip: { code: 500 } }, + i: { zip: null }, + j: { code: 500 } + }; + + await withTestCollection(persistence, testDocs, async coll => { + // populate cache with all documents first to ensure getDocsFromCache() scans all docs + await getDocs(coll); + + let testQuery = query(coll, where('zip', '!=', 98101)); + await checkOnlineAndOfflineResultsMatch( + testQuery, + 'a', + 'b', + 'd', + 'e', + 'f', + 'g', + 'h' + ); + + testQuery = query(coll, where('zip', '!=', Number.NaN)); + await checkOnlineAndOfflineResultsMatch( + testQuery, + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h' + ); + + testQuery = query(coll, where('zip', '!=', null)); + await checkOnlineAndOfflineResultsMatch( + testQuery, + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h' + ); + }); + }); + + it('sdk uses not-in filter same as backend', async () => { + const testDocs = { + a: { zip: Number.NaN }, + b: { zip: 91102 }, + c: { zip: 98101 }, + d: { zip: '98101' }, + e: { zip: [98101] }, + f: { zip: [98101, 98102] }, + g: { zip: ['98101', { zip: 98101 }] }, + h: { zip: { code: 500 } }, + i: { zip: null }, + j: { code: 500 } + }; + + await withTestCollection(persistence, testDocs, async coll => { + // populate cache with all documents first to ensure getDocsFromCache() scans all docs + await getDocs(coll); + + let testQuery = query( + coll, + where('zip', 'not-in', [98101, 98103, [98101, 98102]]) + ); + await checkOnlineAndOfflineResultsMatch( + testQuery, + 'a', + 'b', + 'd', + 'e', + 'g', + 'h' + ); + + testQuery = query(coll, where('zip', 'not-in', [null])); + await checkOnlineAndOfflineResultsMatch(testQuery); + }); + }); }); // Reproduces https://github.com/firebase/firebase-js-sdk/issues/5873 diff --git a/packages/firestore/test/unit/core/query.test.ts b/packages/firestore/test/unit/core/query.test.ts index fd0e6884c66..e589e7bf027 100644 --- a/packages/firestore/test/unit/core/query.test.ts +++ b/packages/firestore/test/unit/core/query.test.ts @@ -256,7 +256,7 @@ describe('Query', () => { document = doc('collection/1', 0, { zip: null }); - expect(queryMatches(query1, document)).to.be.true; + expect(queryMatches(query1, document)).to.be.false; // NaN match. document = doc('collection/1', 0, { @@ -354,7 +354,7 @@ describe('Query', () => { expect(queryMatches(query2, doc3)).to.equal(true); expect(queryMatches(query2, doc4)).to.equal(true); expect(queryMatches(query2, doc5)).to.equal(true); - expect(queryMatches(query2, doc6)).to.equal(true); + expect(queryMatches(query2, doc6)).to.equal(false); }); it('matches null for filters', () => { From e055e9057caab4d9f73734307fe4e0be2098249b Mon Sep 17 00:00:00 2001 From: Mark Duckworth <1124037+MarkDuckworth@users.noreply.github.com> Date: Mon, 14 Apr 2025 10:52:23 -0600 Subject: [PATCH 09/77] Add assertion IDs that will be included in production log statements for fail and hardAsserts (#8313) --- .changeset/odd-wolves-sit.md | 5 + packages/firestore/package.json | 10 +- .../firestore/scripts/assertion-id-tool.js | 17 + .../firestore/scripts/assertion-id-tool.ts | 381 ++++++++++++++++++ packages/firestore/scripts/build-bundle.js | 2 +- packages/firestore/scripts/build-bundle.ts | 26 +- packages/firestore/scripts/extract-api.js | 2 +- packages/firestore/scripts/remove-asserts.js | 2 +- packages/firestore/scripts/remove-asserts.ts | 4 +- .../firestore/scripts/rename-internals.js | 2 +- packages/firestore/scripts/run-tests.js | 17 + packages/firestore/scripts/run-tests.ts | 42 +- packages/firestore/src/api/credentials.ts | 22 +- packages/firestore/src/api/snapshot.ts | 2 +- .../firestore/src/core/component_provider.ts | 1 + packages/firestore/src/core/filter.ts | 6 +- packages/firestore/src/core/query.ts | 2 +- .../firestore/src/core/sync_engine_impl.ts | 9 +- packages/firestore/src/core/transaction.ts | 4 +- packages/firestore/src/core/view.ts | 2 +- packages/firestore/src/core/view_snapshot.ts | 10 +- .../src/index/firestore_index_value_writer.ts | 2 +- .../firestore/src/lite-api/reference_impl.ts | 6 +- .../firestore/src/lite-api/transaction.ts | 8 +- .../src/lite-api/user_data_reader.ts | 4 +- .../src/lite-api/user_data_writer.ts | 8 +- .../src/local/encoded_resource_path.ts | 10 +- .../src/local/indexeddb_index_manager.ts | 3 +- .../local/indexeddb_mutation_batch_impl.ts | 7 +- .../src/local/indexeddb_mutation_queue.ts | 49 ++- .../local/indexeddb_remote_document_cache.ts | 2 +- .../src/local/indexeddb_schema_converter.ts | 6 +- .../src/local/indexeddb_sentinels.ts | 2 +- .../src/local/indexeddb_target_cache.ts | 3 +- .../firestore/src/local/local_serializer.ts | 4 +- .../firestore/src/local/local_store_impl.ts | 7 +- .../src/local/memory_mutation_queue.ts | 1 + .../firestore/src/local/memory_persistence.ts | 5 +- .../src/local/memory_remote_document_cache.ts | 2 +- .../src/local/persistence_promise.ts | 2 +- .../src/local/shared_client_state.ts | 4 +- packages/firestore/src/model/document.ts | 5 +- packages/firestore/src/model/mutation.ts | 8 +- .../firestore/src/model/mutation_batch.ts | 10 +- packages/firestore/src/model/normalize.ts | 6 +- packages/firestore/src/model/path.ts | 10 +- .../src/model/target_index_matcher.ts | 1 + packages/firestore/src/model/values.ts | 14 +- .../platform/browser/webchannel_connection.ts | 20 +- .../src/platform/node/grpc_connection.ts | 1 + packages/firestore/src/remote/datastore.ts | 5 +- .../firestore/src/remote/persistent_stream.ts | 3 + packages/firestore/src/remote/rpc_error.ts | 8 +- packages/firestore/src/remote/serializer.ts | 63 ++- packages/firestore/src/remote/watch_change.ts | 17 +- packages/firestore/src/util/assert.ts | 95 ++++- .../firestore/src/util/async_queue_impl.ts | 4 +- .../firestore/src/util/input_validation.ts | 2 +- packages/firestore/src/util/logic_utils.ts | 10 + packages/firestore/src/util/sorted_map.ts | 22 +- .../unit/index/ordered_code_writer.test.ts | 9 +- .../unit/local/indexeddb_persistence.test.ts | 2 +- .../test/unit/local/simple_db.test.ts | 2 +- .../firestore/test/unit/specs/spec_builder.ts | 14 +- .../test/unit/specs/spec_test_components.ts | 2 +- .../test/unit/specs/spec_test_runner.ts | 4 +- .../firestore/test/unit/util/assert.test.ts | 88 ++++ .../test/unit/util/async_queue.test.ts | 4 +- packages/firestore/test/util/helpers.ts | 2 + .../firestore/test/util/spec_test_helpers.ts | 7 +- packages/firestore/test/util/test_platform.ts | 2 +- 71 files changed, 955 insertions(+), 188 deletions(-) create mode 100644 .changeset/odd-wolves-sit.md create mode 100644 packages/firestore/scripts/assertion-id-tool.js create mode 100644 packages/firestore/scripts/assertion-id-tool.ts create mode 100644 packages/firestore/scripts/run-tests.js create mode 100644 packages/firestore/test/unit/util/assert.test.ts diff --git a/.changeset/odd-wolves-sit.md b/.changeset/odd-wolves-sit.md new file mode 100644 index 00000000000..fc63acc005e --- /dev/null +++ b/.changeset/odd-wolves-sit.md @@ -0,0 +1,5 @@ +--- +"@firebase/firestore": patch +--- + +Add unique IDs and state information into fatal error messages instead of the generic "unexpected state" message. diff --git a/packages/firestore/package.json b/packages/firestore/package.json index d9cb6c263b5..0c9ddeee843 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -20,14 +20,14 @@ "dev": "rollup -c -w", "lint": "eslint -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'", "lint:fix": "eslint --fix -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'", - "prettier": "prettier --write '*.js' '@(lite|src|test)/**/*.ts' 'test/unit/remote/bloom_filter_golden_test_data/*.json'", + "prettier": "prettier --write '*.js' '@(lite|src|test|scripts)/**/*.ts' 'test/unit/remote/bloom_filter_golden_test_data/*.json'", "test:lite": "ts-node ./scripts/run-tests.ts --emulator --platform node_lite --main=lite/index.ts 'test/lite/**/*.test.ts'", "test:lite:prod": "ts-node ./scripts/run-tests.ts --platform node_lite --main=lite/index.ts 'test/lite/**/*.test.ts'", "test:lite:prod:nameddb": "ts-node ./scripts/run-tests.ts --platform node_lite --databaseId=test-db --main=lite/index.ts 'test/lite/**/*.test.ts'", "test:lite:browser": "karma start --lite", "test:lite:browser:nameddb": "karma start --lite --databaseId=test-db", "test:lite:browser:debug": "karma start --browsers=Chrome --lite --auto-watch", - "test": "run-s --npm-path npm lint test:all", + "test": "run-s --npm-path npm lint assertion-id:check test:all", "test:ci": "node ../../scripts/run_tests_in_ci.js -s test:all:ci", "test:all:ci": "run-s --npm-path npm test:browser test:travis test:lite:browser test:browser:prod:nameddb test:lite:browser:nameddb", "test:all": "run-p --npm-path npm test:browser test:lite:browser test:travis test:minified test:browser:prod:nameddb test:lite:browser:nameddb", @@ -52,7 +52,11 @@ "api-report:api-json": "rm -rf temp && api-extractor run --local --verbose", "api-report": "run-s --npm-path npm api-report:main api-report:lite && yarn api-report:api-json", "doc": "api-documenter markdown --input temp --output docs", - "typings:public": "node ../../scripts/build/use_typings.js ./dist/index.d.ts" + "typings:public": "node ../../scripts/build/use_typings.js ./dist/index.d.ts", + "assertion-id:check": "ts-node scripts/assertion-id-tool.ts --dir=src --check", + "assertion-id:new": "ts-node scripts/assertion-id-tool.ts --dir=src --new", + "assertion-id:list": "ts-node scripts/assertion-id-tool.ts --dir=src --list", + "assertion-id:find": "ts-node scripts/assertion-id-tool.ts --dir=src --find" }, "exports": { ".": { diff --git a/packages/firestore/scripts/assertion-id-tool.js b/packages/firestore/scripts/assertion-id-tool.js new file mode 100644 index 00000000000..f29d88a1bf2 --- /dev/null +++ b/packages/firestore/scripts/assertion-id-tool.js @@ -0,0 +1,17 @@ +"use strict"; +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */var __awaiter=this&&this.__awaiter||function(thisArg,_arguments,P,generator){function adopt(value){return value instanceof P?value:new P((function(resolve){resolve(value)}))}return new(P||(P=Promise))((function(resolve,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator["throw"](value))}catch(e){reject(e)}}function step(result){result.done?resolve(result.value):adopt(result.value).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())}))};var __generator=this&&this.__generator||function(thisArg,body){var _={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},f,y,t,g;return g={next:verb(0),throw:verb(1),return:verb(2)},typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(g&&(g=0,op[0]&&(_=0)),_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]0){node.arguments.forEach((function(arg){argsText_1.push(arg.getText(sourceFile_1));if(ts.isStringLiteral(arg)){errorMessage_1=arg.getText(sourceFile_1)}else if(ts.isNumericLiteral(arg)){assertionId_1=parseInt(arg.getText(sourceFile_1),10)}}))}foundCalls.push({fileName:filePath,functionName:functionName,line:line+1,character:character+1,argumentsText:argsText_1,errorMessage:errorMessage_1,assertionId:assertionId_1!==null&&assertionId_1!==void 0?assertionId_1:-1})}}ts.forEachChild(node,visit_1)};visit_1(sourceFile_1)}catch(error){console.error("Error processing file ".concat(filePath,": ").concat(error.message))}};for(var _i=0,filePaths_1=filePaths;_i1){duplicatesFound=true;console.error('\nDuplicate assertion id "'.concat(code,'" found at ').concat(locations.length," locations:"));locations.forEach((function(loc){var relativePath=path.relative(process.cwd(),loc.fileName);console.error("- ".concat(relativePath,":").concat(loc.line,":").concat(loc.character))}))}}));if(!duplicatesFound){log("No duplicate assertion ids found.")}else{process.exit(1)}}function handleNew(occurrences){var maxCode=0;occurrences.forEach((function(occ){if(occ.assertionId>maxCode){maxCode=occ.assertionId}}));if(occurrences.length===0){log("0");return}var newCode=maxCode+1;console.log(newCode)}function main(){return __awaiter(this,void 0,void 0,(function(){var argv,targetDirectory,stats,filesToScan,allOccurrences;return __generator(this,(function(_a){argv=(0,yargs_1.default)((0,helpers_1.hideBin)(process.argv)).usage("Usage: $0 [options]").option("dir",{alias:"D",describe:"Directory to scan recursively for TS files",type:"string",demandOption:true}).option("verbose",{alias:"V",describe:"verbose",type:"boolean"}).option("find",{alias:"F",describe:"Find locations of a specific {assertionId}",type:"string",nargs:1}).option("list",{alias:"L",describe:"List all unique assertion ids found (default action)",type:"boolean"}).option("new",{alias:"N",describe:"Suggest a new assertion id based on existing ones",type:"boolean"}).option("check",{alias:"C",describe:"Check for duplicate usage of assertion ids",type:"boolean"}).check((function(argv){var options=[argv.F,argv.L,argv.N,argv.C].filter(Boolean).length;if(options>1){throw new Error("Options -F, -L, -N, -C are mutually exclusive.")}return true})).help().alias("help","h").strict().parse();targetDirectory=path.resolve(argv["dir"]);isVerbose=!!argv["verbose"];try{stats=fs.statSync(targetDirectory);if(!stats.isDirectory()){console.error("Error: Provided path is not a directory: ".concat(targetDirectory));process.exit(1)}}catch(error){console.error("Error accessing directory ".concat(targetDirectory,": ").concat(error.message));process.exit(1)}log("Scanning directory: ".concat(targetDirectory));filesToScan=getTsFilesRecursive(targetDirectory);if(filesToScan.length===0){log("No relevant .ts or .tsx files found.");process.exit(0)}log("Found ".concat(filesToScan.length," files. Analyzing for assertion ids..."));allOccurrences=findFunctionCalls(filesToScan);log("Scan complete. Found ".concat(allOccurrences.length," potential assertion id occurrences."));if(argv["find"]){handleFind(allOccurrences,argv["find"])}else if(argv["new"]){handleNew(allOccurrences)}else if(argv["check"]){handleCheck(allOccurrences)}else{handleList(allOccurrences)}return[2]}))}))}main().catch((function(error){console.error("\nAn unexpected error occurred:");console.error(error);process.exit(1)})); \ No newline at end of file diff --git a/packages/firestore/scripts/assertion-id-tool.ts b/packages/firestore/scripts/assertion-id-tool.ts new file mode 100644 index 00000000000..8bde791cc6d --- /dev/null +++ b/packages/firestore/scripts/assertion-id-tool.ts @@ -0,0 +1,381 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-console */ + +import * as fs from 'fs'; +import { getRandomValues } from 'node:crypto'; +import * as path from 'path'; + +import * as ts from 'typescript'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; + +let isVerbose: boolean = false; + +function log(message: any): void { + if (isVerbose) { + console.log(message); + } +} + +// Define the names of the functions we are looking for +const targetFunctionNames: Set = new Set(['fail', 'hardAssert']); + +// Interface to store information about found call sites +interface CallSiteInfo { + fileName: string; + functionName: string; + line: number; + character: number; + argumentsText: string[]; // Added to store argument text + errorMessage: string | undefined; + assertionId: string; +} + +/** + * Recursively finds all files with .ts extensions in a directory. + * @param dirPath The absolute path to the directory to scan. + * @returns An array of absolute paths to the found TypeScript files. + */ +function getTsFilesRecursive(dirPath: string): string[] { + let tsFiles: string[] = []; + try { + const entries = fs.readdirSync(dirPath, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dirPath, entry.name); + + if (entry.isDirectory()) { + // Ignore node_modules for performance and relevance + if (entry.name === 'node_modules') { + continue; + } + // Recursively scan subdirectories + tsFiles = tsFiles.concat(getTsFilesRecursive(fullPath)); + } else if (entry.isFile() && entry.name.endsWith('.ts')) { + // Exclude declaration files (.d.ts) as they usually don't contain implementation + if (!entry.name.endsWith('.d.ts')) { + tsFiles.push(fullPath); + } + } + } + } catch (error: any) { + console.error(`Error reading directory ${dirPath}: ${error.message}`); + throw error; + } + return tsFiles; +} + +/** + * Analyzes TypeScript source files to find calls to specific functions. + * @param filePaths An array of absolute paths to the TypeScript files to scan. + * @returns An array of objects detailing the found call sites. + */ +function findFunctionCalls(filePaths: string[]): CallSiteInfo[] { + const foundCalls: CallSiteInfo[] = []; + + for (const filePath of filePaths) { + // Read the file content + const sourceText = fs.readFileSync(filePath, 'utf8'); + + // Create the SourceFile AST node + const sourceFile = ts.createSourceFile( + path.basename(filePath), // Use basename for AST node name + sourceText, + ts.ScriptTarget.ESNext, + true, // Set parent pointers + ts.ScriptKind.Unknown // Detect TS vs TSX automatically + ); + + // Define the visitor function + const visit = (node: ts.Node): void => { + // Check if the node is a CallExpression (e.g., myFunction(...)) + if (ts.isCallExpression(node)) { + let functionName: string | null = null; + const expression = node.expression; + + // Check if the call is directly to an identifier (e.g., fail()) + if (ts.isIdentifier(expression)) { + functionName = expression.text; + } + + // If we found a function name, and it's one we're looking for + if (functionName && targetFunctionNames.has(functionName)) { + // Get line and character number + const { line, character } = ts.getLineAndCharacterOfPosition( + sourceFile, + node.getStart() // Get start position of the call expression + ); + + // --- Extract Arguments --- + const argsText: string[] = []; + let errorMessage: string | undefined; + let assertionId: string | undefined; + if (node.arguments && node.arguments.length > 0) { + node.arguments.forEach((arg: ts.Expression) => { + // Get the source text of the argument node + argsText.push(arg.getText(sourceFile)); + + if (ts.isStringLiteral(arg)) { + errorMessage = arg.getText(sourceFile); + } else if (ts.isNumericLiteral(arg)) { + assertionId = arg.getText(sourceFile); + } + }); + } + + // Store the information (add 1 to line/char for 1-based indexing) + foundCalls.push({ + fileName: filePath, // Store the full path + functionName, + line: line + 1, + character: character + 1, + argumentsText: argsText, // Store the extracted arguments, + errorMessage, + assertionId: assertionId ?? 'INVALID' + }); + } + } + + // Continue traversing down the AST + ts.forEachChild(node, visit); + }; + + // Start traversal from the root SourceFile node + visit(sourceFile); + } // End loop through filePaths + + return foundCalls; +} + +// --- Action Handlers --- + +function handleList(occurrences: CallSiteInfo[]): void { + if (occurrences.length === 0) { + log('No assertion ids found.'); + return; + } + + occurrences + .sort((a, b) => a.assertionId.localeCompare(b.assertionId)) + .forEach(call => { + console.log( + `ID: ${call.assertionId}; MESSAGE: ${call.errorMessage}; SOURCE: '${ + call.functionName + }' call at ${path.relative(process.cwd(), call.fileName)}:${ + call.line + }:${call.character}` + ); + }); +} + +function find( + occurrences: CallSiteInfo[], + targetId: string | number +): CallSiteInfo[] { + const target = + typeof targetId === 'number' ? targetId.toString(16) : targetId; + return occurrences.filter(o => String(o.assertionId) === String(target)); +} + +function handleFind( + occurrences: CallSiteInfo[], + targetId: string | number +): void { + const foundLocations = find(occurrences, targetId); + + if (foundLocations.length === 0) { + log(`Assertion id "${targetId}" not found.`); + process.exit(1); + } + + handleList(foundLocations); +} + +function handleCheck(occurrences: CallSiteInfo[]): void { + if (occurrences.length === 0) { + log('No assertion ids found to check for duplicates.'); + return; + } + const idCounts: { [id: string]: CallSiteInfo[] } = {}; + + occurrences.forEach(occ => { + // Count ID occurrences + const codeStr = String(occ.assertionId); // Use string representation as key + if (!idCounts[codeStr]) { + idCounts[codeStr] = []; + } + idCounts[codeStr].push(occ); + + // validate formats + if (!/^0x[0-9a-f]{4}$/.test(occ.assertionId)) { + console.error( + `Invalid assertion ID '${occ.assertionId}'. Must match /^0x[0-9a-f]{4}$/` + ); + + const relativePath = path.relative(process.cwd(), occ.fileName); + console.error(`- at '${relativePath}:${occ.line}:${occ.character}`); + } + }); + + let duplicatesFound = false; + log('Checking for duplicate assertion id usage:'); + Object.entries(idCounts).forEach(([code, locations]) => { + if (locations.length > 1) { + duplicatesFound = true; + console.error( + `\nDuplicate assertion id "${code}" found at ${locations.length} locations:` + ); + locations.forEach(loc => { + const relativePath = path.relative(process.cwd(), loc.fileName); + console.error(`- ${relativePath}:${loc.line}:${loc.character}`); + }); + } + }); + + if (!duplicatesFound) { + log('No duplicate assertion ids found.'); + } else { + process.exit(1); + } +} + +function randomId(): string { + const randomBytes = new Uint8Array(2); + getRandomValues(randomBytes); + + return ( + '0x' + + Array.from(randomBytes) + .map(byte => byte.toString(16).padStart(2, '0')) + .join('') + ); +} + +function handleNew(occurrences: CallSiteInfo[]): void { + let newCode: string = randomId(); + + // If we find this code already is used, regenerate it. + while (find(occurrences, newCode).length > 0) { + newCode = randomId(); + } + + console.log(newCode); +} + +// --- Main Execution --- +async function main(): Promise { + const argv = yargs(hideBin(process.argv)) + .usage('Usage: $0 [options]') + .option('dir', { + alias: 'D', + describe: 'Directory to scan recursively for TS files', + type: 'string', + demandOption: true + }) + .option('verbose', { + alias: 'V', + describe: 'verbose', + type: 'boolean' + }) + .option('find', { + alias: 'F', + describe: 'Find locations of a specific {assertionId}', + type: 'string', + nargs: 1 + }) + .option('list', { + alias: 'L', + describe: 'List all unique assertion ids found (default action)', + type: 'boolean' + }) + .option('new', { + alias: 'N', + describe: 'Suggest a new assertion id based on existing ones', + type: 'boolean' + }) + .option('check', { + alias: 'C', + describe: 'Check for duplicate usage of assertion ids', + type: 'boolean' + }) + .check(argv => { + // Enforce mutual exclusivity among options *within* the scan command + const options = [argv.F, argv.L, argv.N, argv.C].filter(Boolean).length; + if (options > 1) { + throw new Error('Options -F, -L, -N, -C are mutually exclusive.'); + } + return true; + }) + .help() + .alias('help', 'h') + .strict() + .parse(); // Execute parsing + + // Extract directory path (safe due to demandOption) + const targetDirectory = path.resolve(argv['dir'] as string); + + // set verbosity + isVerbose = !!argv['verbose']; + + // Validate directory + try { + const stats = fs.statSync(targetDirectory); + if (!stats.isDirectory()) { + console.error( + `Error: Provided path is not a directory: ${targetDirectory}` + ); + process.exit(1); + } + } catch (error: any) { + console.error( + `Error accessing directory ${targetDirectory}: ${error.message}` + ); + process.exit(1); + } + + log(`Scanning directory: ${targetDirectory}`); + const filesToScan = getTsFilesRecursive(targetDirectory); + + if (filesToScan.length === 0) { + log('No relevant .ts or .tsx files found.'); + process.exit(0); + } + log(`Found ${filesToScan.length} files. Analyzing for assertion ids...`); + + const allOccurrences = findFunctionCalls(filesToScan); + log( + `Scan complete. Found ${allOccurrences.length} potential assertion id occurrences.` + ); + + // Determine action based on flags + if (argv['find']) { + handleFind(allOccurrences, argv['find']); + } else if (argv['new']) { + handleNew(allOccurrences); + } else if (argv['check']) { + handleCheck(allOccurrences); + } else { + // Default action is List (-L or no flag) + handleList(allOccurrences); + } +} + +// Run the main function +void main(); diff --git a/packages/firestore/scripts/build-bundle.js b/packages/firestore/scripts/build-bundle.js index b44311571e3..f8ba283a5a8 100644 --- a/packages/firestore/scripts/build-bundle.js +++ b/packages/firestore/scripts/build-bundle.js @@ -14,4 +14,4 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */var __awaiter=this&&this.__awaiter||function(thisArg,_arguments,P,generator){function adopt(value){return value instanceof P?value:new P((function(resolve){resolve(value)}))}return new(P||(P=Promise))((function(resolve,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator["throw"](value))}catch(e){reject(e)}}function step(result){result.done?resolve(result.value):adopt(result.value).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())}))};var __generator=this&&this.__generator||function(thisArg,body){var _={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},f,y,t,g;return g={next:verb(0),throw:verb(1),return:verb(2)},typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(g&&(g=0,op[0]&&(_=0)),_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]=0){var method=declaration.name.text;if(method==="debugAssert"){updatedNode=ts.factory.createOmittedExpression()}else if(method==="hardAssert"){updatedNode=ts.factory.createCallExpression(declaration.name,undefined,[node.arguments[0]])}else if(method==="fail"){updatedNode=ts.factory.createCallExpression(declaration.name,undefined,[])}}}}if(updatedNode){ts.setSourceMapRange(updatedNode,ts.getSourceMapRange(node));return updatedNode}else{return node}};return RemoveAsserts}(); \ No newline at end of file + */Object.defineProperty(exports,"__esModule",{value:true});exports.removeAsserts=removeAsserts;var ts=require("typescript");var ASSERT_LOCATION="packages/firestore/src/util/assert.ts";function removeAsserts(program){var removeAsserts=new RemoveAsserts(program.getTypeChecker());return function(context){return function(file){return removeAsserts.visitNodeAndChildren(file,context)}}}var RemoveAsserts=function(){function RemoveAsserts(typeChecker){this.typeChecker=typeChecker}RemoveAsserts.prototype.visitNodeAndChildren=function(node,context){var _this=this;return ts.visitEachChild(this.visitNode(node),(function(childNode){return _this.visitNodeAndChildren(childNode,context)}),context)};RemoveAsserts.prototype.visitNode=function(node){var updatedNode=null;if(ts.isCallExpression(node)){var signature=this.typeChecker.getResolvedSignature(node);if(signature&&signature.declaration&&signature.declaration.kind===ts.SyntaxKind.FunctionDeclaration){var declaration=signature.declaration;if(declaration&&declaration.getSourceFile().fileName.indexOf(ASSERT_LOCATION)>=0){var method=declaration.name.text;if(method==="debugAssert"){updatedNode=ts.factory.createOmittedExpression()}else if(method==="hardAssert"){updatedNode=ts.factory.createCallExpression(declaration.name,undefined,node.arguments.filter((function(value){return!ts.isStringLiteral(value)})))}else if(method==="fail"){updatedNode=ts.factory.createCallExpression(declaration.name,undefined,node.arguments.filter((function(value){return!ts.isStringLiteral(value)})))}}}}if(updatedNode){ts.setSourceMapRange(updatedNode,ts.getSourceMapRange(node));return updatedNode}else{return node}};return RemoveAsserts}(); \ No newline at end of file diff --git a/packages/firestore/scripts/remove-asserts.ts b/packages/firestore/scripts/remove-asserts.ts index fb3e2af6a34..f55907ec152 100644 --- a/packages/firestore/scripts/remove-asserts.ts +++ b/packages/firestore/scripts/remove-asserts.ts @@ -70,14 +70,14 @@ class RemoveAsserts { updatedNode = ts.factory.createCallExpression( declaration.name!, /*typeArgs*/ undefined, - [node.arguments[0]] + node.arguments.filter(value => !ts.isStringLiteral(value)) ); } else if (method === 'fail') { // Remove the log message updatedNode = ts.factory.createCallExpression( declaration.name!, /*typeArgs*/ undefined, - [] + node.arguments.filter(value => !ts.isStringLiteral(value)) ); } } diff --git a/packages/firestore/scripts/rename-internals.js b/packages/firestore/scripts/rename-internals.js index fa8394a2261..4d5ebf1a838 100644 --- a/packages/firestore/scripts/rename-internals.js +++ b/packages/firestore/scripts/rename-internals.js @@ -14,4 +14,4 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */exports.__esModule=true;exports.renameInternals=void 0;var ts=require("typescript");var ignoredIdentifiers=["undefined"];var RenameInternals=function(){function RenameInternals(publicApi,prefix){this.publicApi=publicApi;this.prefix=prefix}RenameInternals.prototype.visitNodeAndChildren=function(node,context){var _this=this;return ts.visitEachChild(this.visitNode(node),(function(childNode){return _this.visitNodeAndChildren(childNode,context)}),context)};RenameInternals.prototype.visitNode=function(node){if(ts.isIdentifier(node)){var name_1=node.escapedText.toString();if(!this.publicApi.has(name_1)&&ignoredIdentifiers.indexOf(node.escapedText.toString())===-1){var newIdentifier=ts.factory.createIdentifier(this.prefix+name_1);ts.setSourceMapRange(newIdentifier,ts.getSourceMapRange(node));return newIdentifier}}return node};return RenameInternals}();var DEFAULT_PREFIX="_";function renameInternals(program,config){var _a;var prefix=(_a=config.prefix)!==null&&_a!==void 0?_a:DEFAULT_PREFIX;var renamer=new RenameInternals(config.publicIdentifiers,prefix);return function(context){return function(file){return renamer.visitNodeAndChildren(file,context)}}}exports.renameInternals=renameInternals; \ No newline at end of file + */Object.defineProperty(exports,"__esModule",{value:true});exports.renameInternals=renameInternals;var ts=require("typescript");var ignoredIdentifiers=["undefined"];var RenameInternals=function(){function RenameInternals(publicApi,prefix){this.publicApi=publicApi;this.prefix=prefix}RenameInternals.prototype.visitNodeAndChildren=function(node,context){var _this=this;return ts.visitEachChild(this.visitNode(node),(function(childNode){return _this.visitNodeAndChildren(childNode,context)}),context)};RenameInternals.prototype.visitNode=function(node){if(ts.isIdentifier(node)){var name_1=node.escapedText.toString();if(!this.publicApi.has(name_1)&&ignoredIdentifiers.indexOf(node.escapedText.toString())===-1){var newIdentifier=ts.factory.createIdentifier(this.prefix+name_1);ts.setSourceMapRange(newIdentifier,ts.getSourceMapRange(node));return newIdentifier}}return node};return RenameInternals}();var DEFAULT_PREFIX="_";function renameInternals(program,config){var _a;var prefix=(_a=config.prefix)!==null&&_a!==void 0?_a:DEFAULT_PREFIX;var renamer=new RenameInternals(config.publicIdentifiers,prefix);return function(context){return function(file){return renamer.visitNodeAndChildren(file,context)}}} \ No newline at end of file diff --git a/packages/firestore/scripts/run-tests.js b/packages/firestore/scripts/run-tests.js new file mode 100644 index 00000000000..3a2e171b649 --- /dev/null +++ b/packages/firestore/scripts/run-tests.js @@ -0,0 +1,17 @@ +"use strict"; +/** + * @license + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */Object.defineProperty(exports,"__esModule",{value:true});var path_1=require("path");var child_process_promise_1=require("child-process-promise");var yargs=require("yargs");var argv=yargs.options({main:{type:"string",demandOption:true},platform:{type:"string",default:"node"},emulator:{type:"boolean"},persistence:{type:"boolean"},databaseId:{type:"string"}}).parseSync();var nyc=(0,path_1.resolve)(__dirname,"../../../node_modules/.bin/nyc");var mocha=(0,path_1.resolve)(__dirname,"../../../node_modules/.bin/mocha");var babel=(0,path_1.resolve)(__dirname,"../babel-register.js");process.env.NO_TS_NODE="true";process.env.TEST_PLATFORM=argv.platform;var args=["--reporter","lcovonly",mocha,"--require",babel,"--require",argv.main,"--config","../../config/mocharc.node.js"];if(argv.emulator){process.env.FIRESTORE_TARGET_BACKEND="emulator"}if(argv.persistence){process.env.USE_MOCK_PERSISTENCE="YES";args.push("--require","test/util/node_persistence.ts")}if(argv.databaseId){process.env.FIRESTORE_TARGET_DB_ID=argv.databaseId}args=args.concat(argv._);var childProcess=(0,child_process_promise_1.spawn)(nyc,args,{stdio:"inherit",cwd:process.cwd()}).childProcess;process.once("exit",(function(){return childProcess.kill()}));process.once("SIGINT",(function(){return childProcess.kill("SIGINT")}));process.once("SIGTERM",(function(){return childProcess.kill("SIGTERM")})); \ No newline at end of file diff --git a/packages/firestore/scripts/run-tests.ts b/packages/firestore/scripts/run-tests.ts index 7e5cdf8fc80..bd721944647 100644 --- a/packages/firestore/scripts/run-tests.ts +++ b/packages/firestore/scripts/run-tests.ts @@ -20,32 +20,34 @@ import { resolve } from 'path'; import { spawn } from 'child-process-promise'; import * as yargs from 'yargs'; -const argv = yargs.options({ - main: { - type: 'string', - demandOption: true - }, - platform: { - type: 'string', - default: 'node' - }, - emulator: { - type: 'boolean' - }, - persistence: { - type: 'boolean' - }, - databaseId: { - type: 'string' - } -}).parseSync(); +const argv = yargs + .options({ + main: { + type: 'string', + demandOption: true + }, + platform: { + type: 'string', + default: 'node' + }, + emulator: { + type: 'boolean' + }, + persistence: { + type: 'boolean' + }, + databaseId: { + type: 'string' + } + }) + .parseSync(); const nyc = resolve(__dirname, '../../../node_modules/.bin/nyc'); const mocha = resolve(__dirname, '../../../node_modules/.bin/mocha'); const babel = resolve(__dirname, '../babel-register.js'); // used in '../../config/mocharc.node.js' to disable ts-node -process.env.NO_TS_NODE = "true"; +process.env.NO_TS_NODE = 'true'; process.env.TEST_PLATFORM = argv.platform; let args = [ diff --git a/packages/firestore/src/api/credentials.ts b/packages/firestore/src/api/credentials.ts index b542ec80b91..c1b10d61057 100644 --- a/packages/firestore/src/api/credentials.ts +++ b/packages/firestore/src/api/credentials.ts @@ -207,7 +207,9 @@ export class LiteAuthCredentialsProvider implements CredentialsProvider { if (tokenData) { hardAssert( typeof tokenData.accessToken === 'string', - 'Invalid tokenData returned from getToken():' + tokenData + 0xa539, + 'Invalid tokenData returned from getToken()', + { tokenData } ); return new OAuthToken( tokenData.accessToken, @@ -259,6 +261,7 @@ export class FirebaseAuthCredentialsProvider ): void { hardAssert( this.tokenListener === undefined, + 0xa540, 'Token listener already added' ); let lastTokenId = this.tokenCounter; @@ -357,7 +360,9 @@ export class FirebaseAuthCredentialsProvider if (tokenData) { hardAssert( typeof tokenData.accessToken === 'string', - 'Invalid tokenData returned from getToken():' + tokenData + 0x7c5d, + 'Invalid tokenData returned from getToken()', + { tokenData } ); return new OAuthToken(tokenData.accessToken, this.currentUser); } else { @@ -386,7 +391,9 @@ export class FirebaseAuthCredentialsProvider const currentUid = this.auth && this.auth.getUid(); hardAssert( currentUid === null || typeof currentUid === 'string', - 'Received invalid UID: ' + currentUid + 0x0807, + 'Received invalid UID', + { currentUid } ); return new User(currentUid); } @@ -513,6 +520,7 @@ export class FirebaseAppCheckTokenProvider ): void { hardAssert( this.tokenListener === undefined, + 0x0db8, 'Token listener already added' ); @@ -588,7 +596,9 @@ export class FirebaseAppCheckTokenProvider if (tokenResult) { hardAssert( typeof tokenResult.token === 'string', - 'Invalid tokenResult returned from getToken():' + tokenResult + 0xae0e, + 'Invalid tokenResult returned from getToken()', + { tokenResult } ); this.latestAppCheckToken = tokenResult.token; return new AppCheckToken(tokenResult.token); @@ -659,7 +669,9 @@ export class LiteAppCheckTokenProvider implements CredentialsProvider { if (tokenResult) { hardAssert( typeof tokenResult.token === 'string', - 'Invalid tokenResult returned from getToken():' + tokenResult + 0x0d8e, + 'Invalid tokenResult returned from getToken()', + { tokenResult } ); return new AppCheckToken(tokenResult.token); } else { diff --git a/packages/firestore/src/api/snapshot.ts b/packages/firestore/src/api/snapshot.ts index 29e1616b61c..669ac26cafe 100644 --- a/packages/firestore/src/api/snapshot.ts +++ b/packages/firestore/src/api/snapshot.ts @@ -749,7 +749,7 @@ export function resultChangeType(type: ChangeType): DocumentChangeType { case ChangeType.Removed: return 'removed'; default: - return fail('Unknown change type: ' + type); + return fail(0xf03d, 'Unknown change type', { type }); } } diff --git a/packages/firestore/src/core/component_provider.ts b/packages/firestore/src/core/component_provider.ts index 8a63509232c..183a1046cc9 100644 --- a/packages/firestore/src/core/component_provider.ts +++ b/packages/firestore/src/core/component_provider.ts @@ -197,6 +197,7 @@ export class LruGcMemoryOfflineComponentProvider extends MemoryOfflineComponentP ): Scheduler | null { hardAssert( this.persistence.referenceDelegate instanceof MemoryLruDelegate, + 0xb743, 'referenceDelegate is expected to be an instance of MemoryLruDelegate.' ); diff --git a/packages/firestore/src/core/filter.ts b/packages/firestore/src/core/filter.ts index 12b57729f81..b3ae3d0619b 100644 --- a/packages/firestore/src/core/filter.ts +++ b/packages/firestore/src/core/filter.ts @@ -169,7 +169,9 @@ export class FieldFilter extends Filter { case Operator.GREATER_THAN_OR_EQUAL: return comparison >= 0; default: - return fail('Unknown FieldFilter operator: ' + this.op); + return fail(0xb8a2, 'Unknown FieldFilter operator', { + operator: this.op + }); } } @@ -316,7 +318,7 @@ export function filterEquals(f1: Filter, f2: Filter): boolean { } else if (f1 instanceof CompositeFilter) { return compositeFilterEquals(f1, f2); } else { - fail('Only FieldFilters and CompositeFilters can be compared'); + fail(0x4bef, 'Only FieldFilters and CompositeFilters can be compared'); } } diff --git a/packages/firestore/src/core/query.ts b/packages/firestore/src/core/query.ts index b13296ad7ee..ca875bbb14d 100644 --- a/packages/firestore/src/core/query.ts +++ b/packages/firestore/src/core/query.ts @@ -578,6 +578,6 @@ export function compareDocs( case Direction.DESCENDING: return -1 * comparison; default: - return fail('Unknown direction: ' + orderBy.dir); + return fail(0x4d4e, 'Unknown direction', { direction: orderBy.dir }); } } diff --git a/packages/firestore/src/core/sync_engine_impl.ts b/packages/firestore/src/core/sync_engine_impl.ts index f96cbea0f00..404d4663a47 100644 --- a/packages/firestore/src/core/sync_engine_impl.ts +++ b/packages/firestore/src/core/sync_engine_impl.ts @@ -579,6 +579,7 @@ export async function syncEngineApplyRemoteEvent( targetChange.modifiedDocuments.size + targetChange.removedDocuments.size <= 1, + 0x5858, 'Limbo resolution for single document contains multiple changes.' ); if (targetChange.addedDocuments.size > 0) { @@ -586,11 +587,13 @@ export async function syncEngineApplyRemoteEvent( } else if (targetChange.modifiedDocuments.size > 0) { hardAssert( limboResolution.receivedDocument, + 0x390f, 'Received change for limbo target document without add.' ); } else if (targetChange.removedDocuments.size > 0) { hardAssert( limboResolution.receivedDocument, + 0xa4f3, 'Received remove for limbo target document without add.' ); limboResolution.receivedDocument = false; @@ -994,7 +997,7 @@ function updateTrackedLimbos( removeLimboTarget(syncEngineImpl, limboChange.key); } } else { - fail('Unknown limbo change: ' + JSON.stringify(limboChange)); + fail(0x4d4f, 'Unknown limbo change', { limboChange }); } } } @@ -1317,7 +1320,7 @@ export async function syncEngineApplyBatchState( batchId ); } else { - fail(`Unknown batchState: ${batchState}`); + fail(0x1a40, `Unknown batchState`, { batchState }); } await syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, documents); @@ -1560,7 +1563,7 @@ export async function syncEngineApplyTargetState( break; } default: - fail('Unexpected target state: ' + state); + fail(0xfa9b, 'Unexpected target state', state); } } } diff --git a/packages/firestore/src/core/transaction.ts b/packages/firestore/src/core/transaction.ts index 471c64e13bd..d3dc6ee8a89 100644 --- a/packages/firestore/src/core/transaction.ts +++ b/packages/firestore/src/core/transaction.ts @@ -124,7 +124,9 @@ export class Transaction { // Represent a deleted doc using SnapshotVersion.min(). docVersion = SnapshotVersion.min(); } else { - throw fail('Document in a transaction was a ' + doc.constructor.name); + throw fail(0xc542, 'Document in a transaction was a ', { + documentName: doc.constructor.name + }); } const existingVersion = this.readVersions.get(doc.key.toString()); diff --git a/packages/firestore/src/core/view.ts b/packages/firestore/src/core/view.ts index b0a07bd783c..e0909de938f 100644 --- a/packages/firestore/src/core/view.ts +++ b/packages/firestore/src/core/view.ts @@ -505,7 +505,7 @@ function compareChangeType(c1: ChangeType, c2: ChangeType): number { case ChangeType.Removed: return 0; default: - return fail('Unknown ChangeType: ' + change); + return fail(0x4f35, 'Unknown ChangeType', { change }); } }; diff --git a/packages/firestore/src/core/view_snapshot.ts b/packages/firestore/src/core/view_snapshot.ts index f15c5ccb409..3bb6e8ae134 100644 --- a/packages/firestore/src/core/view_snapshot.ts +++ b/packages/firestore/src/core/view_snapshot.ts @@ -118,10 +118,12 @@ export class DocumentChangeSet { // Metadata->Added // Removed->Metadata fail( - 'unsupported combination of changes: ' + - JSON.stringify(change) + - ' after ' + - JSON.stringify(oldChange) + 0xf76d, + 'unsupported combination of changes: `change` after `oldChange`', + { + change, + oldChange + } ); } } diff --git a/packages/firestore/src/index/firestore_index_value_writer.ts b/packages/firestore/src/index/firestore_index_value_writer.ts index dfdb3836578..b76ca7a930a 100644 --- a/packages/firestore/src/index/firestore_index_value_writer.ts +++ b/packages/firestore/src/index/firestore_index_value_writer.ts @@ -136,7 +136,7 @@ export class FirestoreIndexValueWriter { this.writeIndexArray(indexValue.arrayValue!, encoder); this.writeTruncationMarker(encoder); } else { - fail('unknown index value type ' + indexValue); + fail(0x4a4e, 'unknown index value type', { indexValue }); } } diff --git a/packages/firestore/src/lite-api/reference_impl.ts b/packages/firestore/src/lite-api/reference_impl.ts index 6876ed0a877..6d92ccab479 100644 --- a/packages/firestore/src/lite-api/reference_impl.ts +++ b/packages/firestore/src/lite-api/reference_impl.ts @@ -133,7 +133,11 @@ export function getDoc( return invokeBatchGetDocumentsRpc(datastore, [reference._key]).then( result => { - hardAssert(result.length === 1, 'Expected a single document result'); + hardAssert( + result.length === 1, + 0x3d02, + 'Expected a single document result' + ); const document = result[0]; return new DocumentSnapshot( reference.firestore, diff --git a/packages/firestore/src/lite-api/transaction.ts b/packages/firestore/src/lite-api/transaction.ts index 10a07ef252c..9a405feebea 100644 --- a/packages/firestore/src/lite-api/transaction.ts +++ b/packages/firestore/src/lite-api/transaction.ts @@ -94,7 +94,7 @@ export class Transaction { const userDataWriter = new LiteUserDataWriter(this._firestore); return this._transaction.lookup([ref._key]).then(docs => { if (!docs || docs.length !== 1) { - return fail('Mismatch in docs returned from document lookup.'); + return fail(0x5de9, 'Mismatch in docs returned from document lookup.'); } const doc = docs[0]; if (doc.isFoundDocument()) { @@ -115,7 +115,11 @@ export class Transaction { ); } else { throw fail( - `BatchGetDocumentsRequest returned unexpected document: ${doc}` + 0x4801, + 'BatchGetDocumentsRequest returned unexpected document', + { + doc + } ); } }); diff --git a/packages/firestore/src/lite-api/user_data_reader.ts b/packages/firestore/src/lite-api/user_data_reader.ts index ebd4b49085f..aa5f9eeb5bf 100644 --- a/packages/firestore/src/lite-api/user_data_reader.ts +++ b/packages/firestore/src/lite-api/user_data_reader.ts @@ -175,7 +175,9 @@ function isWrite(dataSource: UserDataSource): boolean { case UserDataSource.ArrayArgument: return false; default: - throw fail(`Unexpected case for UserDataSource: ${dataSource}`); + throw fail(0x9c4b, 'Unexpected case for UserDataSource', { + dataSource + }); } } diff --git a/packages/firestore/src/lite-api/user_data_writer.ts b/packages/firestore/src/lite-api/user_data_writer.ts index e903991cb58..070c71c7832 100644 --- a/packages/firestore/src/lite-api/user_data_writer.ts +++ b/packages/firestore/src/lite-api/user_data_writer.ts @@ -89,7 +89,9 @@ export abstract class AbstractUserDataWriter { case TypeOrder.VectorValue: return this.convertVectorValue(value.mapValue!); default: - throw fail('Invalid value type: ' + JSON.stringify(value)); + throw fail(0xf2a2, 'Invalid value type', { + value + }); } } @@ -173,7 +175,9 @@ export abstract class AbstractUserDataWriter { const resourcePath = ResourcePath.fromString(name); hardAssert( isValidResourceName(resourcePath), - 'ReferenceValue is not valid ' + name + 0x25d8, + 'ReferenceValue is not valid', + { name } ); const databaseId = new DatabaseId(resourcePath.get(1), resourcePath.get(3)); const key = new DocumentKey(resourcePath.popFirst(5)); diff --git a/packages/firestore/src/local/encoded_resource_path.ts b/packages/firestore/src/local/encoded_resource_path.ts index b52b8bd6805..497a65fdf8c 100644 --- a/packages/firestore/src/local/encoded_resource_path.ts +++ b/packages/firestore/src/local/encoded_resource_path.ts @@ -118,11 +118,13 @@ export function decodeResourcePath(path: EncodedResourcePath): ResourcePath { // Event the empty path must encode as a path of at least length 2. A path // with exactly 2 must be the empty path. const length = path.length; - hardAssert(length >= 2, 'Invalid path ' + path); + hardAssert(length >= 2, 0xfb98, 'Invalid path', { path }); if (length === 2) { hardAssert( path.charAt(0) === escapeChar && path.charAt(1) === encodedSeparatorChar, - 'Non-empty path ' + path + ' had length 2' + 0xdb51, + 'Non-empty path had length 2', + { path } ); return ResourcePath.emptyPath(); } @@ -139,7 +141,7 @@ export function decodeResourcePath(path: EncodedResourcePath): ResourcePath { // there must be an end to this segment. const end = path.indexOf(escapeChar, start); if (end < 0 || end > lastReasonableEscapeIndex) { - fail('Invalid encoded resource path: "' + path + '"'); + fail(0xc553, 'Invalid encoded resource path', { path }); } const next = path.charAt(end + 1); @@ -167,7 +169,7 @@ export function decodeResourcePath(path: EncodedResourcePath): ResourcePath { segmentBuilder += path.substring(start, end + 1); break; default: - fail('Invalid encoded resource path: "' + path + '"'); + fail(0xeeef, 'Invalid encoded resource path', { path }); } start = end + 2; diff --git a/packages/firestore/src/local/indexeddb_index_manager.ts b/packages/firestore/src/local/indexeddb_index_manager.ts index 04a380601b3..d2b8bc47163 100644 --- a/packages/firestore/src/local/indexeddb_index_manager.ts +++ b/packages/firestore/src/local/indexeddb_index_manager.ts @@ -1066,7 +1066,7 @@ export class IndexedDbIndexManager implements IndexManager { this.getSubTargets(target), (subTarget: Target) => this.getFieldIndex(transaction, subTarget).next(index => - index ? index : fail('Target cannot be served from index') + index ? index : fail(0xad8a, 'Target cannot be served from index') ) ).next(getMinOffsetFromFieldIndexes); } @@ -1118,6 +1118,7 @@ function indexStateStore( function getMinOffsetFromFieldIndexes(fieldIndexes: FieldIndex[]): IndexOffset { hardAssert( fieldIndexes.length !== 0, + 0x7099, 'Found empty index group when looking for least recent index offset.' ); diff --git a/packages/firestore/src/local/indexeddb_mutation_batch_impl.ts b/packages/firestore/src/local/indexeddb_mutation_batch_impl.ts index 16b157accb2..64b6df20b7e 100644 --- a/packages/firestore/src/local/indexeddb_mutation_batch_impl.ts +++ b/packages/firestore/src/local/indexeddb_mutation_batch_impl.ts @@ -64,8 +64,9 @@ export function removeMutationBatch( removePromise.next(() => { hardAssert( numDeleted === 1, - 'Dangling document-mutation reference found: Missing batch ' + - batch.batchId + 0xb7de, + 'Dangling document-mutation reference found: Missing batch', + { batchId: batch.batchId } ); }) ); @@ -100,7 +101,7 @@ export function dbDocumentSize( } else if (doc.noDocument) { value = doc.noDocument; } else { - throw fail('Unknown remote document type'); + throw fail(0x398b, 'Unknown remote document type'); } return JSON.stringify(value).length; } diff --git a/packages/firestore/src/local/indexeddb_mutation_queue.ts b/packages/firestore/src/local/indexeddb_mutation_queue.ts index 0aedf650769..bcdafa6aa36 100644 --- a/packages/firestore/src/local/indexeddb_mutation_queue.ts +++ b/packages/firestore/src/local/indexeddb_mutation_queue.ts @@ -105,7 +105,7 @@ export class IndexedDbMutationQueue implements MutationQueue { // In particular, are there any reserved characters? are empty ids allowed? // For the moment store these together in the same mutations table assuming // that empty userIDs aren't allowed. - hardAssert(user.uid !== '', 'UserID must not be an empty string.'); + hardAssert(user.uid !== '', 0xfb83, 'UserID must not be an empty string.'); const userId = user.isAuthenticated() ? user.uid! : ''; return new IndexedDbMutationQueue( userId, @@ -154,6 +154,7 @@ export class IndexedDbMutationQueue implements MutationQueue { return mutationStore.add({} as any).next(batchId => { hardAssert( typeof batchId === 'number', + 0xbf7b, 'Auto-generated key is not a number' ); @@ -206,7 +207,12 @@ export class IndexedDbMutationQueue implements MutationQueue { if (dbBatch) { hardAssert( dbBatch.userId === this.userId, - `Unexpected user '${dbBatch.userId}' for mutation batch ${batchId}` + 0x0030, + `Unexpected user for mutation batch`, + { + userId: dbBatch.userId, + batchId + } ); return fromDbMutationBatch(this.serializer, dbBatch); } @@ -257,7 +263,9 @@ export class IndexedDbMutationQueue implements MutationQueue { if (dbBatch.userId === this.userId) { hardAssert( dbBatch.batchId >= nextBatchId, - 'Should have found mutation after ' + nextBatchId + 0xb9a4, + 'Should have found mutation after `nextBatchId`', + { nextBatchId } ); foundBatch = fromDbMutationBatch(this.serializer, dbBatch); } @@ -336,15 +344,22 @@ export class IndexedDbMutationQueue implements MutationQueue { .next(mutation => { if (!mutation) { throw fail( - 'Dangling document-mutation reference found: ' + - indexKey + - ' which points to ' + + 0xf028, + 'Dangling document-mutation reference found: `indexKey` which points to `batchId`', + { + indexKey, batchId + } ); } hardAssert( mutation.userId === this.userId, - `Unexpected user '${mutation.userId}' for mutation batch ${batchId}` + 0x2907, + `Unexpected user for mutation batch`, + { + userId: mutation.userId, + batchId + } ); results.push(fromDbMutationBatch(this.serializer, mutation)); }); @@ -468,14 +483,18 @@ export class IndexedDbMutationQueue implements MutationQueue { .next(mutation => { if (mutation === null) { throw fail( - 'Dangling document-mutation reference found, ' + - 'which points to ' + + 0x89ca, + 'Dangling document-mutation reference found, which points to `batchId`', + { batchId + } ); } hardAssert( mutation.userId === this.userId, - `Unexpected user '${mutation.userId}' for mutation batch ${batchId}` + 0x2614, + `Unexpected user for mutation batch`, + { userId: mutation.userId, batchId } ); results.push(fromDbMutationBatch(this.serializer, mutation)); }) @@ -549,9 +568,13 @@ export class IndexedDbMutationQueue implements MutationQueue { .next(() => { hardAssert( danglingMutationReferences.length === 0, - 'Document leak -- detected dangling mutation references when queue is empty. ' + - 'Dangling keys: ' + - danglingMutationReferences.map(p => p.canonicalString()) + 0xdd90, + 'Document leak -- detected dangling mutation references when queue is empty.', + { + danglingKeys: danglingMutationReferences.map(p => + p.canonicalString() + ) + } ); }); }); diff --git a/packages/firestore/src/local/indexeddb_remote_document_cache.ts b/packages/firestore/src/local/indexeddb_remote_document_cache.ts index 9b23c64fcf5..fffe935c4f9 100644 --- a/packages/firestore/src/local/indexeddb_remote_document_cache.ts +++ b/packages/firestore/src/local/indexeddb_remote_document_cache.ts @@ -381,7 +381,7 @@ class IndexedDbRemoteDocumentCacheImpl implements IndexedDbRemoteDocumentCache { return documentGlobalStore(txn) .get(DbRemoteDocumentGlobalKey) .next(metadata => { - hardAssert(!!metadata, 'Missing document cache metadata'); + hardAssert(!!metadata, 0x4e35, 'Missing document cache metadata'); return metadata!; }); } diff --git a/packages/firestore/src/local/indexeddb_schema_converter.ts b/packages/firestore/src/local/indexeddb_schema_converter.ts index 9d7485f4a92..7446ae7ae20 100644 --- a/packages/firestore/src/local/indexeddb_schema_converter.ts +++ b/packages/firestore/src/local/indexeddb_schema_converter.ts @@ -326,7 +326,9 @@ export class SchemaConverter implements SimpleDbSchemaConverter { (dbBatch: DbMutationBatch) => { hardAssert( dbBatch.userId === queue.userId, - `Cannot process batch ${dbBatch.batchId} from unexpected user` + 0x48da, + `Cannot process batch from unexpected user`, + { batchId: dbBatch.batchId } ); const batch = fromDbMutationBatch(this.serializer, dbBatch); @@ -772,6 +774,6 @@ function extractKey(remoteDoc: DbRemoteDocumentLegacy): DocumentKey { } else if (remoteDoc.unknownDocument) { return DocumentKey.fromSegments(remoteDoc.unknownDocument.path); } else { - return fail('Unexpected DbRemoteDocument'); + return fail(0x8faf, 'Unexpected DbRemoteDocument'); } } diff --git a/packages/firestore/src/local/indexeddb_sentinels.ts b/packages/firestore/src/local/indexeddb_sentinels.ts index e1e3ead3aa2..cb6ebcb664a 100644 --- a/packages/firestore/src/local/indexeddb_sentinels.ts +++ b/packages/firestore/src/local/indexeddb_sentinels.ts @@ -450,6 +450,6 @@ export function getObjectStores(schemaVersion: number): string[] { } else if (schemaVersion === 11) { return V11_STORES; } else { - fail('Only schema version 11 and 12 and 13 are supported'); + fail(0xeb55, 'Only schema version 11 and 12 and 13 are supported'); } } diff --git a/packages/firestore/src/local/indexeddb_target_cache.ts b/packages/firestore/src/local/indexeddb_target_cache.ts index 9e93cc68838..1d5ed8f0c8b 100644 --- a/packages/firestore/src/local/indexeddb_target_cache.ts +++ b/packages/firestore/src/local/indexeddb_target_cache.ts @@ -144,6 +144,7 @@ export class IndexedDbTargetCache implements TargetCache { .next(metadata => { hardAssert( metadata.targetCount > 0, + 0x1f81, 'Removing from an empty target cache' ); metadata.targetCount -= 1; @@ -197,7 +198,7 @@ export class IndexedDbTargetCache implements TargetCache { return globalTargetStore(transaction) .get(DbTargetGlobalKey) .next(metadata => { - hardAssert(metadata !== null, 'Missing metadata row.'); + hardAssert(metadata !== null, 0x0b48, 'Missing metadata row.'); return metadata; }); } diff --git a/packages/firestore/src/local/local_serializer.ts b/packages/firestore/src/local/local_serializer.ts index b8916608711..bb1658caa52 100644 --- a/packages/firestore/src/local/local_serializer.ts +++ b/packages/firestore/src/local/local_serializer.ts @@ -101,7 +101,7 @@ export function fromDbRemoteDocument( const version = fromDbTimestamp(remoteDoc.unknownDocument.version); doc = MutableDocument.newUnknownDocument(key, version); } else { - return fail('Unexpected DbRemoteDocument'); + return fail(0xdd85, 'Unexpected DbRemoteDocument'); } if (remoteDoc.readTime) { @@ -138,7 +138,7 @@ export function toDbRemoteDocument( version: toDbTimestamp(document.version) }; } else { - return fail('Unexpected Document ' + document); + return fail(0xe230, 'Unexpected Document', { document }); } return remoteDoc; } diff --git a/packages/firestore/src/local/local_store_impl.ts b/packages/firestore/src/local/local_store_impl.ts index 56f2b96f8d1..31d2a46c326 100644 --- a/packages/firestore/src/local/local_store_impl.ts +++ b/packages/firestore/src/local/local_store_impl.ts @@ -494,7 +494,11 @@ export function localStoreRejectBatch( return localStoreImpl.mutationQueue .lookupMutationBatch(txn, batchId) .next((batch: MutationBatch | null) => { - hardAssert(batch !== null, 'Attempt to reject nonexistent batch!'); + hardAssert( + batch !== null, + 0x90f9, + 'Attempt to reject nonexistent batch!' + ); affectedKeys = batch.keys(); return localStoreImpl.mutationQueue.removeMutationBatch(txn, batch); }) @@ -1137,6 +1141,7 @@ function applyWriteToRemoteDocuments( const ackVersion = batchResult.docVersions.get(docKey); hardAssert( ackVersion !== null, + 0xbd9d, 'ackVersions should contain every doc in the write.' ); if (doc.version.compareTo(ackVersion!) < 0) { diff --git a/packages/firestore/src/local/memory_mutation_queue.ts b/packages/firestore/src/local/memory_mutation_queue.ts index e3902cc96fc..f136fb7ad15 100644 --- a/packages/firestore/src/local/memory_mutation_queue.ts +++ b/packages/firestore/src/local/memory_mutation_queue.ts @@ -246,6 +246,7 @@ export class MemoryMutationQueue implements MutationQueue { const batchIndex = this.indexOfExistingBatchId(batch.batchId, 'removed'); hardAssert( batchIndex === 0, + 0xd6db, 'Can only remove the first entry of the mutation queue' ); this.mutationQueue.shift(); diff --git a/packages/firestore/src/local/memory_persistence.ts b/packages/firestore/src/local/memory_persistence.ts index 30d4f2bd19a..fcb6db42059 100644 --- a/packages/firestore/src/local/memory_persistence.ts +++ b/packages/firestore/src/local/memory_persistence.ts @@ -235,7 +235,10 @@ export class MemoryEagerDelegate implements MemoryReferenceDelegate { private get orphanedDocuments(): Set { if (!this._orphanedDocuments) { - throw fail('orphanedDocuments is only valid during a transaction.'); + throw fail( + 0xee44, + 'orphanedDocuments is only valid during a transaction.' + ); } else { return this._orphanedDocuments; } diff --git a/packages/firestore/src/local/memory_remote_document_cache.ts b/packages/firestore/src/local/memory_remote_document_cache.ts index 42a0010d4ac..0daf80b6a19 100644 --- a/packages/firestore/src/local/memory_remote_document_cache.ts +++ b/packages/firestore/src/local/memory_remote_document_cache.ts @@ -219,7 +219,7 @@ class MemoryRemoteDocumentCacheImpl implements MemoryRemoteDocumentCache { ): PersistencePromise { // This method should only be called from the IndexBackfiller if persistence // is enabled. - fail('getAllFromCollectionGroup() is not supported.'); + fail(0x251c, 'getAllFromCollectionGroup() is not supported.'); } forEachDocumentKey( diff --git a/packages/firestore/src/local/persistence_promise.ts b/packages/firestore/src/local/persistence_promise.ts index 4678650fa84..812cc0fca85 100644 --- a/packages/firestore/src/local/persistence_promise.ts +++ b/packages/firestore/src/local/persistence_promise.ts @@ -86,7 +86,7 @@ export class PersistencePromise { catchFn?: RejectedHandler ): PersistencePromise { if (this.callbackAttached) { - fail('Called next() or catch() twice for PersistencePromise'); + fail(0xe830, 'Called next() or catch() twice for PersistencePromise'); } this.callbackAttached = true; if (this.isDone) { diff --git a/packages/firestore/src/local/shared_client_state.ts b/packages/firestore/src/local/shared_client_state.ts index 7c033cedb41..1000e63a0f6 100644 --- a/packages/firestore/src/local/shared_client_state.ts +++ b/packages/firestore/src/local/shared_client_state.ts @@ -1085,7 +1085,9 @@ function fromWebStorageSequenceNumber( const parsed = JSON.parse(seqString); hardAssert( typeof parsed === 'number', - 'Found non-numeric sequence number' + 0x77ac, + 'Found non-numeric sequence number', + { seqString } ); sequenceNumber = parsed; } catch (e) { diff --git a/packages/firestore/src/model/document.ts b/packages/firestore/src/model/document.ts index 830983aec43..ac454704776 100644 --- a/packages/firestore/src/model/document.ts +++ b/packages/firestore/src/model/document.ts @@ -397,6 +397,9 @@ export function compareDocumentsByField( if (v1 !== null && v2 !== null) { return valueCompare(v1, v2); } else { - return fail("Trying to compare documents on fields that don't exist"); + return fail( + 0xa786, + "Trying to compare documents on fields that don't exist" + ); } } diff --git a/packages/firestore/src/model/mutation.ts b/packages/firestore/src/model/mutation.ts index 119e9b9731b..0bcd1345b01 100644 --- a/packages/firestore/src/model/mutation.ts +++ b/packages/firestore/src/model/mutation.ts @@ -623,8 +623,12 @@ function serverTransformResults( const transformResults = new Map(); hardAssert( fieldTransforms.length === serverTransformResults.length, - `server transform result count (${serverTransformResults.length}) ` + - `should match field transform count (${fieldTransforms.length})` + 0x7f90, + 'server transform result count should match field transform count', + { + serverTransformResultCount: serverTransformResults.length, + fieldTransformCount: fieldTransforms.length + } ); for (let i = 0; i < serverTransformResults.length; i++) { diff --git a/packages/firestore/src/model/mutation_batch.ts b/packages/firestore/src/model/mutation_batch.ts index 56d5f4d2cd3..703623da01a 100644 --- a/packages/firestore/src/model/mutation_batch.ts +++ b/packages/firestore/src/model/mutation_batch.ts @@ -219,10 +219,12 @@ export class MutationBatchResult { ): MutationBatchResult { hardAssert( batch.mutations.length === results.length, - 'Mutations sent ' + - batch.mutations.length + - ' must equal results received ' + - results.length + 0xe5da, + 'Mutations sent must equal results received', + { + mutationsSent: batch.mutations.length, + resultsReceived: results.length + } ); let versionMap = documentVersionMap(); diff --git a/packages/firestore/src/model/normalize.ts b/packages/firestore/src/model/normalize.ts index 2061601d23e..986eeed1e48 100644 --- a/packages/firestore/src/model/normalize.ts +++ b/packages/firestore/src/model/normalize.ts @@ -32,7 +32,7 @@ export function normalizeTimestamp(date: Timestamp): { seconds: number; nanos: number; } { - hardAssert(!!date, 'Cannot normalize null or undefined timestamp.'); + hardAssert(!!date, 0x986a, 'Cannot normalize null or undefined timestamp.'); // The json interface (for the browser) will return an iso timestamp string, // while the proto js library (for node) will return a @@ -44,7 +44,9 @@ export function normalizeTimestamp(date: Timestamp): { // Parse the nanos right out of the string. let nanos = 0; const fraction = ISO_TIMESTAMP_REG_EXP.exec(date); - hardAssert(!!fraction, 'invalid timestamp: ' + date); + hardAssert(!!fraction, 0xb5de, 'invalid timestamp', { + timestamp: date + }); if (fraction[1]) { // Pad the fraction out to 9 digits (nanos). let nanoStr = fraction[1]; diff --git a/packages/firestore/src/model/path.ts b/packages/firestore/src/model/path.ts index c375b4c56d2..0f4581da8d8 100644 --- a/packages/firestore/src/model/path.ts +++ b/packages/firestore/src/model/path.ts @@ -35,13 +35,19 @@ abstract class BasePath> { if (offset === undefined) { offset = 0; } else if (offset > segments.length) { - fail('offset ' + offset + ' out of range ' + segments.length); + fail(0x027d, 'offset out of range', { + offset, + range: segments.length + }); } if (length === undefined) { length = segments.length - offset; } else if (length > segments.length - offset) { - fail('length ' + length + ' out of range ' + (segments.length - offset)); + fail(0x06d2, 'length out of range', { + length, + range: segments.length - offset + }); } this.segments = segments; this.offset = offset; diff --git a/packages/firestore/src/model/target_index_matcher.ts b/packages/firestore/src/model/target_index_matcher.ts index df80ffa419a..407eae337c7 100644 --- a/packages/firestore/src/model/target_index_matcher.ts +++ b/packages/firestore/src/model/target_index_matcher.ts @@ -111,6 +111,7 @@ export class TargetIndexMatcher { servedByIndex(index: FieldIndex): boolean { hardAssert( index.collectionGroup === this.collectionId, + 0xc07f, 'Collection IDs do not match' ); diff --git a/packages/firestore/src/model/values.ts b/packages/firestore/src/model/values.ts index 30d8688b776..1ef54a98ad6 100644 --- a/packages/firestore/src/model/values.ts +++ b/packages/firestore/src/model/values.ts @@ -93,7 +93,7 @@ export function typeOrder(value: Value): TypeOrder { } return TypeOrder.ObjectValue; } else { - return fail('Invalid value type: ' + JSON.stringify(value)); + return fail(0x6e87, 'Invalid value type', { value }); } } @@ -140,7 +140,7 @@ export function valueEquals(left: Value, right: Value): boolean { case TypeOrder.MaxValue: return true; default: - return fail('Unexpected value type: ' + JSON.stringify(left)); + return fail(0xcbf8, 'Unexpected value type', { left }); } } @@ -269,7 +269,7 @@ export function valueCompare(left: Value, right: Value): number { case TypeOrder.ObjectValue: return compareMaps(left.mapValue!, right.mapValue!); default: - throw fail('Invalid value type: ' + leftType); + throw fail(0x5ae0, 'Invalid value type', { leftType }); } } @@ -449,7 +449,7 @@ function canonifyValue(value: Value): string { } else if ('mapValue' in value) { return canonifyMap(value.mapValue!); } else { - return fail('Invalid value type: ' + JSON.stringify(value)); + return fail(0xee4d, 'Invalid value type', { value }); } } @@ -541,7 +541,7 @@ export function estimateByteSize(value: Value): number { case TypeOrder.ObjectValue: return estimateMapByteSize(value.mapValue!); default: - throw fail('Invalid value type: ' + JSON.stringify(value)); + throw fail(0x34ae, 'Invalid value type', { value }); } } @@ -701,7 +701,7 @@ export function valuesGetLowerBound(value: Value): Value { } return { mapValue: {} }; } else { - return fail('Invalid value type: ' + JSON.stringify(value)); + return fail(0x8c66, 'Invalid value type', { value }); } } @@ -731,7 +731,7 @@ export function valuesGetUpperBound(value: Value): Value { } return MAX_VALUE; } else { - return fail('Invalid value type: ' + JSON.stringify(value)); + return fail(0xf207, 'Invalid value type', { value }); } } diff --git a/packages/firestore/src/platform/browser/webchannel_connection.ts b/packages/firestore/src/platform/browser/webchannel_connection.ts index 206e5829c41..5223285c5a4 100644 --- a/packages/firestore/src/platform/browser/webchannel_connection.ts +++ b/packages/firestore/src/platform/browser/webchannel_connection.ts @@ -142,12 +142,14 @@ export class WebChannelConnection extends RestConnection { break; default: fail( - `RPC '${rpcName}' ${streamId} ` + - 'failed with unanticipated webchannel error: ' + - xhr.getLastErrorCode() + - ': ' + - xhr.getLastError() + - ', giving up.' + 0x235f, + 'RPC failed with unanticipated webchannel error. Giving up.', + { + rpcName, + streamId, + lastErrorCode: xhr.getLastErrorCode(), + lastError: xhr.getLastError() + } ); } } finally { @@ -353,7 +355,11 @@ export class WebChannelConnection extends RestConnection { msg => { if (!closed) { const msgData = msg.data[0]; - hardAssert(!!msgData, 'Got a webchannel message without data.'); + hardAssert( + !!msgData, + 0x3fdd, + 'Got a webchannel message without data.' + ); // TODO(b/35143891): There is a bug in One Platform that caused errors // (and only errors) to be wrapped in an extra array. To be forward // compatible with the bug we need to check either condition. The latter diff --git a/packages/firestore/src/platform/node/grpc_connection.ts b/packages/firestore/src/platform/node/grpc_connection.ts index dec3137af76..d50a3149416 100644 --- a/packages/firestore/src/platform/node/grpc_connection.ts +++ b/packages/firestore/src/platform/node/grpc_connection.ts @@ -48,6 +48,7 @@ function createMetadata( ): grpc.Metadata { hardAssert( authToken === null || authToken.type === 'OAuth', + 0x9048, 'If provided, token must be OAuth' ); const metadata = new grpc.Metadata(); diff --git a/packages/firestore/src/remote/datastore.ts b/packages/firestore/src/remote/datastore.ts index ac47f0cb931..f790ede0d5c 100644 --- a/packages/firestore/src/remote/datastore.ts +++ b/packages/firestore/src/remote/datastore.ts @@ -228,7 +228,9 @@ export async function invokeBatchGetDocumentsRpc( const result: Document[] = []; keys.forEach(key => { const doc = docs.get(key.toString()); - hardAssert(!!doc, 'Missing entity in write response for ' + key); + hardAssert(!!doc, 0xd7c2, 'Missing entity in write response for `key`', { + key + }); result.push(doc); }); return result; @@ -290,6 +292,7 @@ export async function invokeRunAggregationQueryRpc( hardAssert( filteredResult.length === 1, + 0xfcd7, 'Aggregation fields are missing from result.' ); debugAssert( diff --git a/packages/firestore/src/remote/persistent_stream.ts b/packages/firestore/src/remote/persistent_stream.ts index f9f64bec7f6..4f3b91652ad 100644 --- a/packages/firestore/src/remote/persistent_stream.ts +++ b/packages/firestore/src/remote/persistent_stream.ts @@ -809,6 +809,7 @@ export class PersistentWriteStream extends PersistentStream< // Always capture the last stream token. hardAssert( !!responseProto.streamToken, + 0x7a5a, 'Got a write handshake response without a stream token' ); this.lastStreamToken = responseProto.streamToken; @@ -816,6 +817,7 @@ export class PersistentWriteStream extends PersistentStream< // The first response is always the handshake response hardAssert( !responseProto.writeResults || responseProto.writeResults.length === 0, + 0xda08, 'Got mutation results for handshake' ); return this.listener!.onHandshakeComplete(); @@ -825,6 +827,7 @@ export class PersistentWriteStream extends PersistentStream< // Always capture the last stream token. hardAssert( !!responseProto.streamToken, + 0x3186, 'Got a write response without a stream token' ); this.lastStreamToken = responseProto.streamToken; diff --git a/packages/firestore/src/remote/rpc_error.ts b/packages/firestore/src/remote/rpc_error.ts index 479ceea36c9..2efee40f223 100644 --- a/packages/firestore/src/remote/rpc_error.ts +++ b/packages/firestore/src/remote/rpc_error.ts @@ -58,7 +58,7 @@ enum RpcCode { export function isPermanentError(code: Code): boolean { switch (code) { case Code.OK: - return fail('Treated status OK as error'); + return fail(0xfdaa, 'Treated status OK as error'); case Code.CANCELLED: case Code.UNKNOWN: case Code.DEADLINE_EXCEEDED: @@ -83,7 +83,7 @@ export function isPermanentError(code: Code): boolean { case Code.DATA_LOSS: return true; default: - return fail('Unknown status code: ' + code); + return fail(0x3c6b, 'Unknown status code', { code }); } } @@ -171,7 +171,7 @@ export function mapCodeFromRpcCode(code: number | undefined): Code { case RpcCode.DATA_LOSS: return Code.DATA_LOSS; default: - return fail('Unknown status code: ' + code); + return fail(0x999b, 'Unknown status code', { code }); } } @@ -220,7 +220,7 @@ export function mapRpcCodeFromCode(code: Code | undefined): number { case Code.DATA_LOSS: return RpcCode.DATA_LOSS; default: - return fail('Unknown status code: ' + code); + return fail(0x3019, 'Unknown status code', { code }); } } diff --git a/packages/firestore/src/remote/serializer.ts b/packages/firestore/src/remote/serializer.ts index 811c2ac4df6..aabdb263c1a 100644 --- a/packages/firestore/src/remote/serializer.ts +++ b/packages/firestore/src/remote/serializer.ts @@ -257,6 +257,7 @@ export function fromBytes( if (serializer.useProto3Json) { hardAssert( value === undefined || typeof value === 'string', + 0xe30b, 'value must be undefined or a string when using proto3 Json' ); return ByteString.fromBase64String(value ? value : ''); @@ -269,6 +270,7 @@ export function fromBytes( // does not indicate that it extends Uint8Array. value instanceof Buffer || value instanceof Uint8Array, + 0x3f41, 'value must be undefined, Buffer, or Uint8Array' ); return ByteString.fromUint8Array(value ? value : new Uint8Array()); @@ -283,7 +285,7 @@ export function toVersion( } export function fromVersion(version: ProtoTimestamp): SnapshotVersion { - hardAssert(!!version, "Trying to deserialize version that isn't set"); + hardAssert(!!version, 0xc050, "Trying to deserialize version that isn't set"); return SnapshotVersion.fromTimestamp(fromTimestamp(version)); } @@ -306,7 +308,9 @@ function fromResourceName(name: string): ResourcePath { const resource = ResourcePath.fromString(name); hardAssert( isValidResourceName(resource), - 'Tried to deserialize invalid key ' + resource.toString() + 0x27ce, + 'Tried to deserialize invalid key', + { key: resource.toString() } ); return resource; } @@ -389,7 +393,9 @@ function extractLocalPathFromResourceName( ): ResourcePath { hardAssert( resourceName.length > 4 && resourceName.get(4) === 'documents', - 'tried to deserialize invalid key ' + resourceName.toString() + 0x71a3, + 'tried to deserialize invalid key', + { key: resourceName.toString() } ); return resourceName.popFirst(5); } @@ -454,6 +460,7 @@ function fromFound( ): MutableDocument { hardAssert( !!doc.found, + 0xaa33, 'Tried to deserialize a found document from a missing document.' ); assertPresent(doc.found.name, 'doc.found.name'); @@ -473,10 +480,12 @@ function fromMissing( ): MutableDocument { hardAssert( !!result.missing, + 0x0f36, 'Tried to deserialize a missing document from a found document.' ); hardAssert( !!result.readTime, + 0x5995, 'Tried to deserialize a missing document without a read time.' ); const key = fromName(serializer, result.missing); @@ -493,7 +502,7 @@ export function fromBatchGetDocumentsResponse( } else if ('missing' in result) { return fromMissing(serializer, result); } - return fail('invalid batch get response: ' + JSON.stringify(result)); + return fail(0x1c42, 'invalid batch get response', { result }); } export function fromWatchChange( @@ -578,7 +587,7 @@ export function fromWatchChange( const targetId = filter.targetId; watchChange = new ExistenceFilterChange(targetId, existenceFilter); } else { - return fail('Unknown change type ' + JSON.stringify(change)); + return fail(0x2d51, 'Unknown change type', { change }); } return watchChange; } @@ -597,7 +606,7 @@ function fromWatchTargetChangeState( } else if (state === 'RESET') { return WatchTargetChangeState.Reset; } else { - return fail('Got unexpected TargetChange.state: ' + state); + return fail(0x9991, 'Got unexpected TargetChange.state', { state }); } } @@ -641,7 +650,9 @@ export function toMutation( verify: toName(serializer, mutation.key) }; } else { - return fail('Unknown mutation type ' + mutation.type); + return fail(0x40d7, 'Unknown mutation type', { + mutationType: mutation.type + }); } if (mutation.fieldTransforms.length > 0) { @@ -697,7 +708,7 @@ export function fromMutation( const key = fromName(serializer, proto.verify); return new VerifyMutation(key, precondition); } else { - return fail('unknown mutation proto: ' + JSON.stringify(proto)); + return fail(0x05b7, 'unknown mutation proto', { proto }); } } @@ -713,7 +724,7 @@ function toPrecondition( } else if (precondition.exists !== undefined) { return { exists: precondition.exists }; } else { - return fail('Unknown precondition'); + return fail(0x6b69, 'Unknown precondition'); } } @@ -755,6 +766,7 @@ export function fromWriteResults( if (protos && protos.length > 0) { hardAssert( commitTime !== undefined, + 0x3811, 'Received a write result without a commit time' ); return protos.map(proto => fromWriteResult(proto, commitTime)); @@ -793,7 +805,9 @@ function toFieldTransform( increment: transform.operand }; } else { - throw fail('Unknown transform: ' + fieldTransform.transform); + throw fail(0x51c2, 'Unknown transform', { + transform: fieldTransform.transform + }); } } @@ -805,7 +819,9 @@ function fromFieldTransform( if ('setToServerValue' in proto) { hardAssert( proto.setToServerValue === 'REQUEST_TIME', - 'Unknown server value transform proto: ' + JSON.stringify(proto) + 0x40f6, + 'Unknown server value transform proto', + { proto } ); transform = new ServerTimestampTransform(); } else if ('appendMissingElements' in proto) { @@ -820,7 +836,7 @@ function fromFieldTransform( proto.increment! ); } else { - fail('Unknown transform proto: ' + JSON.stringify(proto)); + fail(0x40c8, 'Unknown transform proto', { proto }); } const fieldPath = FieldPath.fromServerFormat(proto.fieldPath!); return new FieldTransform(fieldPath, transform!); @@ -839,7 +855,11 @@ export function fromDocumentsTarget( const count = documentsTarget.documents!.length; hardAssert( count === 1, - 'DocumentsTarget contained other than 1 document: ' + count + 0x07ae, + 'DocumentsTarget contained other than 1 document', + { + count + } ); const name = documentsTarget.documents![0]; return queryToTarget(newQueryForPath(fromQueryPath(name))); @@ -969,6 +989,7 @@ export function convertQueryTargetToQuery(target: ProtoQueryTarget): Query { if (fromCount > 0) { hardAssert( fromCount === 1, + 0xfe26, 'StructuredQuery.from with more than one collection is not supported.' ); const from = query.from![0]; @@ -1045,7 +1066,7 @@ export function toLabel(purpose: TargetPurpose): string | null { case TargetPurpose.LimboResolution: return 'limbo-document'; default: - return fail('Unrecognized query purpose: ' + purpose); + return fail(0x713b, 'Unrecognized query purpose', { purpose }); } } @@ -1116,7 +1137,7 @@ function fromFilter(filter: ProtoFilter): Filter { } else if (filter.compositeFilter !== undefined) { return fromCompositeFilter(filter); } else { - return fail('Unknown filter: ' + JSON.stringify(filter)); + return fail(0x7591, 'Unknown filter', { filter }); } } @@ -1210,9 +1231,9 @@ export function fromOperatorName(op: ProtoFieldFilterOp): Operator { case 'ARRAY_CONTAINS_ANY': return Operator.ARRAY_CONTAINS_ANY; case 'OPERATOR_UNSPECIFIED': - return fail('Unspecified operator'); + return fail(0xe2fe, 'Unspecified operator'); default: - return fail('Unknown operator'); + return fail(0xc54a, 'Unknown operator'); } } @@ -1225,7 +1246,7 @@ export function fromCompositeOperatorName( case 'OR': return CompositeOperator.OR; default: - return fail('Unknown operator'); + return fail(0x0402, 'Unknown operator'); } } @@ -1261,7 +1282,7 @@ export function toFilter(filter: Filter): ProtoFilter { } else if (filter instanceof CompositeFilter) { return toCompositeFilter(filter); } else { - return fail('Unrecognized filter type ' + JSON.stringify(filter)); + return fail(0xd65d, 'Unrecognized filter type', { filter }); } } @@ -1346,9 +1367,9 @@ export function fromUnaryFilter(filter: ProtoFilter): Filter { nullValue: 'NULL_VALUE' }); case 'OPERATOR_UNSPECIFIED': - return fail('Unspecified filter'); + return fail(0xef81, 'Unspecified filter'); default: - return fail('Unknown filter'); + return fail(0xed36, 'Unknown filter'); } } diff --git a/packages/firestore/src/remote/watch_change.ts b/packages/firestore/src/remote/watch_change.ts index 0c69163095f..a656d8fdf6e 100644 --- a/packages/firestore/src/remote/watch_change.ts +++ b/packages/firestore/src/remote/watch_change.ts @@ -203,7 +203,7 @@ class TargetState { removedDocuments = removedDocuments.add(key); break; default: - fail('Encountered invalid change type: ' + changeType); + fail(0x9481, 'Encountered invalid change type', { changeType }); } }); @@ -242,10 +242,9 @@ class TargetState { this.pendingResponses -= 1; hardAssert( this.pendingResponses >= 0, - '`pendingResponses` is less than 0. Actual value: ' + - this.pendingResponses + - '. This indicates that the SDK received more target acks from the ' + - 'server than expected. The SDK should not continue to operate.' + 0x0ca9, + '`pendingResponses` is less than 0. This indicates that the SDK received more target acks from the server than expected. The SDK should not continue to operate.', + { pendingResponses: this.pendingResponses } ); } @@ -378,7 +377,9 @@ export class WatchChangeAggregator { } break; default: - fail('Unknown target watch change state: ' + targetChange.state); + fail(0xddd6, 'Unknown target watch change state', { + state: targetChange.state + }); } }); } @@ -432,7 +433,9 @@ export class WatchChangeAggregator { } else { hardAssert( expectedCount === 1, - 'Single document existence filter with count: ' + expectedCount + 0x4e2d, + 'Single document existence filter with count', + { expectedCount } ); } } else { diff --git a/packages/firestore/src/util/assert.ts b/packages/firestore/src/util/assert.ts index 6d65e6cd19b..07ebe775e9c 100644 --- a/packages/firestore/src/util/assert.ts +++ b/packages/firestore/src/util/assert.ts @@ -26,12 +26,61 @@ import { logError } from './log'; * Returns `never` and can be used in expressions: * @example * let futureVar = fail('not implemented yet'); + * + * @param code generate a new unique value with `yarn assertion-id:generate` + * Search for an existing value using `yarn assertion-id:find X` + */ +export function fail( + code: number, + message: string, + context?: Record +): never; + +/** + * Unconditionally fails, throwing an Error with the given message. + * Messages are stripped in production builds. + * + * Returns `never` and can be used in expressions: + * @example + * let futureVar = fail('not implemented yet'); + * + * @param id generate a new unique value with `yarn assertion-id:generate` + * Search for an existing value using `yarn assertion-id:find X` */ -export function fail(failure: string = 'Unexpected state'): never { +export function fail(id: number, context?: Record): never; + +export function fail( + id: number, + messageOrContext?: string | Record, + context?: Record +): never { + let message = 'Unexpected state'; + if (typeof messageOrContext === 'string') { + message = messageOrContext; + } else { + context = messageOrContext; + } + _fail(id, message, context); +} + +function _fail( + id: number, + failure: string, + context?: Record +): never { // Log the failure in addition to throw an exception, just in case the // exception is swallowed. - const message = - `FIRESTORE (${SDK_VERSION}) INTERNAL ASSERTION FAILED: ` + failure; + let message = `FIRESTORE (${SDK_VERSION}) INTERNAL ASSERTION FAILED: ${failure} (ID: ${id.toString( + 16 + )})`; + if (context !== undefined) { + try { + const stringContext = JSON.stringify(context); + message += ' CONTEXT: ' + stringContext; + } catch (e) { + message += ' CONTEXT: ' + context; + } + } logError(message); // NOTE: We don't use FirestoreError here because these are internal failures @@ -45,13 +94,47 @@ export function fail(failure: string = 'Unexpected state'): never { * given message if it did. * * Messages are stripped in production builds. + * + * @param id generate a new unique value with `yarn assertion-idgenerate`. + * Search for an existing value using `yarn assertion-id:find X` + */ +export function hardAssert( + assertion: boolean, + id: number, + message: string, + context?: Record +): asserts assertion; + +/** + * Fails if the given assertion condition is false, throwing an Error with the + * given message if it did. + * + * Messages are stripped in production builds. + * + * @param id generate a new unique value with `yarn assertion-id:generate`. + * Search for an existing value using `yarn assertion-id:find X` */ export function hardAssert( assertion: boolean, - message?: string + id: number, + context?: Record +): asserts assertion; + +export function hardAssert( + assertion: boolean, + id: number, + messageOrContext?: string | Record, + context?: Record ): asserts assertion { + let message = 'Unexpected state'; + if (typeof messageOrContext === 'string') { + message = messageOrContext; + } else { + context = messageOrContext; + } + if (!assertion) { - fail(message); + _fail(id, message, context); } } @@ -70,7 +153,7 @@ export function debugAssert( message: string ): asserts assertion { if (!assertion) { - fail(message); + fail(0xdeb6, message); } } diff --git a/packages/firestore/src/util/async_queue_impl.ts b/packages/firestore/src/util/async_queue_impl.ts index 79eb6c23850..f8c7a995761 100644 --- a/packages/firestore/src/util/async_queue_impl.ts +++ b/packages/firestore/src/util/async_queue_impl.ts @@ -236,7 +236,9 @@ export class AsyncQueueImpl implements AsyncQueue { private verifyNotFailed(): void { if (this.failure) { - fail('AsyncQueue is already failed: ' + getMessageOrStack(this.failure)); + fail(0xb815, 'AsyncQueue is already failed', { + messageOrStack: getMessageOrStack(this.failure) + }); } } diff --git a/packages/firestore/src/util/input_validation.ts b/packages/firestore/src/util/input_validation.ts index 37e349ce910..7fd9967b5a0 100644 --- a/packages/firestore/src/util/input_validation.ts +++ b/packages/firestore/src/util/input_validation.ts @@ -128,7 +128,7 @@ export function valueDescription(input: unknown): string { } else if (typeof input === 'function') { return 'a function'; } else { - return fail('Unknown wrong type: ' + typeof input); + return fail(0x3029, 'Unknown wrong type', { type: typeof input }); } } diff --git a/packages/firestore/src/util/logic_utils.ts b/packages/firestore/src/util/logic_utils.ts index 3c3a6b19fd8..b2167c385e9 100644 --- a/packages/firestore/src/util/logic_utils.ts +++ b/packages/firestore/src/util/logic_utils.ts @@ -44,6 +44,7 @@ import { hardAssert } from './assert'; export function computeInExpansion(filter: Filter): Filter { hardAssert( filter instanceof FieldFilter || filter instanceof CompositeFilter, + 0x4e2c, 'Only field filters and composite filters are accepted.' ); @@ -90,6 +91,7 @@ export function getDnfTerms(filter: CompositeFilter): Filter[] { hardAssert( isDisjunctiveNormalForm(result), + 0x1cdf, 'computeDistributedNormalForm did not result in disjunctive normal form' ); @@ -157,6 +159,7 @@ function isDisjunctionOfFieldFiltersAndFlatConjunctions( export function computeDistributedNormalForm(filter: Filter): Filter { hardAssert( filter instanceof FieldFilter || filter instanceof CompositeFilter, + 0x84e2, 'Only field filters and composite filters are accepted.' ); @@ -182,14 +185,17 @@ export function computeDistributedNormalForm(filter: Filter): Filter { hardAssert( newFilter instanceof CompositeFilter, + 0xfbf2, 'field filters are already in DNF form' ); hardAssert( compositeFilterIsConjunction(newFilter), + 0x9d3b, 'Disjunction of filters all of which are already in DNF form is itself in DNF form.' ); hardAssert( newFilter.filters.length > 1, + 0xe247, 'Single-filter composite filters are already in DNF form.' ); @@ -201,10 +207,12 @@ export function computeDistributedNormalForm(filter: Filter): Filter { export function applyDistribution(lhs: Filter, rhs: Filter): Filter { hardAssert( lhs instanceof FieldFilter || lhs instanceof CompositeFilter, + 0x95f4, 'Only field filters and composite filters are accepted.' ); hardAssert( rhs instanceof FieldFilter || rhs instanceof CompositeFilter, + 0x6381, 'Only field filters and composite filters are accepted.' ); @@ -245,6 +253,7 @@ function applyDistributionCompositeFilters( ): Filter { hardAssert( lhs.filters.length > 0 && rhs.filters.length > 0, + 0xbb85, 'Found an empty composite filter' ); @@ -306,6 +315,7 @@ function applyDistributionFieldAndCompositeFilters( export function applyAssociation(filter: Filter): Filter { hardAssert( filter instanceof FieldFilter || filter instanceof CompositeFilter, + 0x2e4a, 'Only field filters and composite filters are accepted.' ); diff --git a/packages/firestore/src/util/sorted_map.ts b/packages/firestore/src/util/sorted_map.ts index a24cf8802ca..023354173d3 100644 --- a/packages/firestore/src/util/sorted_map.ts +++ b/packages/firestore/src/util/sorted_map.ts @@ -511,14 +511,20 @@ export class LLRBNode { // leaves is equal on both sides. This function verifies that or asserts. protected check(): number { if (this.isRed() && this.left.isRed()) { - throw fail('Red node has red child(' + this.key + ',' + this.value + ')'); + throw fail(0xaad2, 'Red node has red child', { + key: this.key, + value: this.value + }); } if (this.right.isRed()) { - throw fail('Right child of (' + this.key + ',' + this.value + ') is red'); + throw fail(0x3721, 'Right child of (`key`, `value`) is red', { + key: this.key, + value: this.value + }); } const blackDepth = (this.left as LLRBNode).check(); if (blackDepth !== (this.right as LLRBNode).check()) { - throw fail('Black depths differ'); + throw fail(0x6d2d, 'Black depths differ'); } else { return blackDepth + (this.isRed() ? 0 : 1); } @@ -528,19 +534,19 @@ export class LLRBNode { // Represents an empty node (a leaf node in the Red-Black Tree). export class LLRBEmptyNode { get key(): never { - throw fail('LLRBEmptyNode has no key.'); + throw fail(0xe1a6, 'LLRBEmptyNode has no key.'); } get value(): never { - throw fail('LLRBEmptyNode has no value.'); + throw fail(0x3f0d, 'LLRBEmptyNode has no value.'); } get color(): never { - throw fail('LLRBEmptyNode has no color.'); + throw fail(0x4157, 'LLRBEmptyNode has no color.'); } get left(): never { - throw fail('LLRBEmptyNode has no left child.'); + throw fail(0x741e, 'LLRBEmptyNode has no left child.'); } get right(): never { - throw fail('LLRBEmptyNode has no right child.'); + throw fail(0x901e, 'LLRBEmptyNode has no right child.'); } size = 0; diff --git a/packages/firestore/test/unit/index/ordered_code_writer.test.ts b/packages/firestore/test/unit/index/ordered_code_writer.test.ts index 6d87ddb4849..27956c730ee 100644 --- a/packages/firestore/test/unit/index/ordered_code_writer.test.ts +++ b/packages/firestore/test/unit/index/ordered_code_writer.test.ts @@ -248,7 +248,14 @@ function getBytes(val: unknown): { asc: Uint8Array; desc: Uint8Array } { ascWriter.writeUtf8Ascending(val); descWriter.writeUtf8Descending(val); } else { - hardAssert(val instanceof Uint8Array); + hardAssert( + val instanceof Uint8Array, + 0xa10f, + 'val is not instance of Uint8Array', + { + val + } + ); ascWriter.writeBytesAscending(ByteString.fromUint8Array(val)); descWriter.writeBytesDescending(ByteString.fromUint8Array(val)); } diff --git a/packages/firestore/test/unit/local/indexeddb_persistence.test.ts b/packages/firestore/test/unit/local/indexeddb_persistence.test.ts index e44bb73e47b..1240c977cee 100644 --- a/packages/firestore/test/unit/local/indexeddb_persistence.test.ts +++ b/packages/firestore/test/unit/local/indexeddb_persistence.test.ts @@ -1609,6 +1609,6 @@ function toLegacyDbRemoteDocument( parentPath }; } else { - return fail('Unexpected Document ' + document); + return fail(0x6bb7, 'Unexpected Document ', { document }); } } diff --git a/packages/firestore/test/unit/local/simple_db.test.ts b/packages/firestore/test/unit/local/simple_db.test.ts index b2b7ed3f95a..207e454fb5b 100644 --- a/packages/firestore/test/unit/local/simple_db.test.ts +++ b/packages/firestore/test/unit/local/simple_db.test.ts @@ -363,7 +363,7 @@ describe('SimpleDb', () => { iterated.push(value); return PersistencePromise.reject(new Error('Expected error')); }) - .next(() => fail('Promise not rejected')) + .next(() => fail(0xb9b3, 'Promise not rejected')) .catch(err => { expect(err.message).to.eq('Expected error'); expect(iterated).to.deep.equal([testData[0]]); diff --git a/packages/firestore/test/unit/specs/spec_builder.ts b/packages/firestore/test/unit/specs/spec_builder.ts index 80dcd6519de..3e52c5873b9 100644 --- a/packages/firestore/test/unit/specs/spec_builder.ts +++ b/packages/firestore/test/unit/specs/spec_builder.ts @@ -674,7 +674,7 @@ export class SpecBuilder { } else if (doc.isNoDocument()) { // Don't send any updates } else { - fail('Unknown parameter: ' + doc); + fail(0xd71c, 'Unknown parameter', { doc }); } this.watchCurrents(query, 'resume-token-' + version); this.watchSnapshots(version); @@ -1149,7 +1149,7 @@ export class SpecBuilder { userDataWriter.convertValue(filter.value) ] as SpecQueryFilter; } else { - return fail('Unknown filter: ' + filter); + return fail(0x0e51, 'Unknown filter', { filter }); } }); } @@ -1210,7 +1210,10 @@ export class SpecBuilder { targetPurpose?: TargetPurpose ): void { if (!(resume?.resumeToken || resume?.readTime) && resume?.expectedCount) { - fail('Expected count is present without a resume token or read time.'); + fail( + 0xc9a1, + 'Expected count is present without a resume token or read time.' + ); } if (this.activeTargets[targetId]) { @@ -1273,7 +1276,10 @@ export class SpecBuilder { if (queryTargetId && limboTargetId) { // TODO(dimond): add support for query for doc and limbo doc at the same // time? - fail('Found both query and limbo doc with target ID, not supported yet'); + fail( + 0x6e17, + 'Found both query and limbo doc with target ID, not supported yet' + ); } const targetId = queryTargetId || limboTargetId; debugAssert( diff --git a/packages/firestore/test/unit/specs/spec_test_components.ts b/packages/firestore/test/unit/specs/spec_test_components.ts index 2a2e480de63..017afe1d924 100644 --- a/packages/firestore/test/unit/specs/spec_test_components.ts +++ b/packages/firestore/test/unit/specs/spec_test_components.ts @@ -415,7 +415,7 @@ export class MockConnection implements Connection { } else if (request.removeTarget) { delete this.activeTargets[request.removeTarget]; } else { - fail('Invalid listen request'); + fail(0x782d, 'Invalid listen request'); } }, closeFn: () => { diff --git a/packages/firestore/test/unit/specs/spec_test_runner.ts b/packages/firestore/test/unit/specs/spec_test_runner.ts index b34421d9e0a..ee0af0b8bf8 100644 --- a/packages/firestore/test/unit/specs/spec_test_runner.ts +++ b/packages/firestore/test/unit/specs/spec_test_runner.ts @@ -477,7 +477,7 @@ abstract class TestRunner { ? this.doFailDatabase(step.failDatabase!) : this.doRecoverDatabase(); } else { - return fail('Unknown step: ' + JSON.stringify(step)); + return fail(0x6bb3, 'Unknown step: ' + JSON.stringify(step)); } } @@ -724,7 +724,7 @@ abstract class TestRunner { ); return this.doWatchEvent(change); } else { - return fail('Either doc or docs must be set'); + return fail(0xdcc3, 'Either doc or docs must be set'); } } diff --git a/packages/firestore/test/unit/util/assert.test.ts b/packages/firestore/test/unit/util/assert.test.ts new file mode 100644 index 00000000000..e865337a3a3 --- /dev/null +++ b/packages/firestore/test/unit/util/assert.test.ts @@ -0,0 +1,88 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; + +import { fail, hardAssert } from '../../../src/util/assert'; + +describe('hardAssert', () => { + it('includes the error code as hex', () => { + expect(() => hardAssert(false, 0x1234, 'a message here')).to.throw('1234'); + }); + + it('includes the context', () => { + expect(() => + hardAssert(false, 0x1234, 'a message here', { foo: 'bar baz' }) + ).to.throw('bar baz'); + }); + + it('includes the message', () => { + expect(() => + hardAssert(false, 0x1234, 'a message here', { foo: 'bar baz' }) + ).to.throw('a message here'); + }); + + describe('without message', () => { + it('includes the error code as hex', () => { + expect(() => hardAssert(false, 0x1234)).to.throw('1234'); + }); + + it('includes the context', () => { + expect(() => hardAssert(false, 0x1234, { foo: 'bar baz' })).to.throw( + 'bar baz' + ); + }); + it('includes a default message', () => { + expect(() => hardAssert(false, 0x1234, { foo: 'bar baz' })).to.throw( + 'Unexpected state' + ); + }); + }); +}); + +describe('fail', () => { + it('includes the error code as hex', () => { + expect(() => fail(0x1234, 'a message here')).to.throw('1234'); + }); + + it('includes the context', () => { + expect(() => fail(0x1234, 'a message here', { foo: 'bar baz' })).to.throw( + 'bar baz' + ); + }); + + it('includes the message', () => { + expect(() => fail(0x1234, 'a message here', { foo: 'bar baz' })).to.throw( + 'a message here' + ); + }); + + describe('without message', () => { + it('includes the error code as hex', () => { + expect(() => fail(0x1234)).to.throw('1234'); + }); + + it('includes the context', () => { + expect(() => fail(0x1234, { foo: 'bar baz' })).to.throw('bar baz'); + }); + it('includes a default message', () => { + expect(() => fail(0x1234, { foo: 'bar baz' })).to.throw( + 'Unexpected state' + ); + }); + }); +}); diff --git a/packages/firestore/test/unit/util/async_queue.test.ts b/packages/firestore/test/unit/util/async_queue.test.ts index cc55879c88b..48aa02fe298 100644 --- a/packages/firestore/test/unit/util/async_queue.test.ts +++ b/packages/firestore/test/unit/util/async_queue.test.ts @@ -139,7 +139,7 @@ describe('AsyncQueue', () => { Promise.reject('dummyOp should not be run'); expect(() => { queue.enqueueAndForget(dummyOp); - }).to.throw(/already failed:.*Simulated Error/); + }).to.throw(/already failed.*Simulated Error/); // Finally, restore log level. setLogLevel(oldLogLevel as unknown as LogLevelString); @@ -247,7 +247,7 @@ describe('AsyncQueue', () => { const deferred = new Deferred(); queue.enqueueRetryable(async () => { deferred.resolve(); - throw fail('Simulated test failure'); + throw fail(0x1576, 'Simulated test failure'); }); await deferred.promise; await expect( diff --git a/packages/firestore/test/util/helpers.ts b/packages/firestore/test/util/helpers.ts index c5865c3e0f7..1ddaf174b19 100644 --- a/packages/firestore/test/util/helpers.ts +++ b/packages/firestore/test/util/helpers.ts @@ -867,11 +867,13 @@ export function expectEqual(left: any, right: any, message?: string): void { message = message || ''; if (typeof left.isEqual !== 'function') { return fail( + 0x8004, JSON.stringify(left) + ' does not support isEqual (left) ' + message ); } if (typeof right.isEqual !== 'function') { return fail( + 0xebc9, JSON.stringify(right) + ' does not support isEqual (right) ' + message ); } diff --git a/packages/firestore/test/util/spec_test_helpers.ts b/packages/firestore/test/util/spec_test_helpers.ts index 7e2f77da438..73378a361a3 100644 --- a/packages/firestore/test/util/spec_test_helpers.ts +++ b/packages/firestore/test/util/spec_test_helpers.ts @@ -100,7 +100,10 @@ export function encodeWatchChange( } }; } - return fail('Unrecognized watch change: ' + JSON.stringify(watchChange)); + return fail( + 0xf8e5, + 'Unrecognized watch change: ' + JSON.stringify(watchChange) + ); } function encodeTargetChangeTargetChangeType( @@ -118,6 +121,6 @@ function encodeTargetChangeTargetChangeType( case WatchTargetChangeState.Reset: return 'RESET'; default: - return fail('Unknown WatchTargetChangeState: ' + state); + return fail(0x368b, 'Unknown WatchTargetChangeState: ' + state); } } diff --git a/packages/firestore/test/util/test_platform.ts b/packages/firestore/test/util/test_platform.ts index 7803a2fb3c6..a0eac93cbc9 100644 --- a/packages/firestore/test/util/test_platform.ts +++ b/packages/firestore/test/util/test_platform.ts @@ -64,7 +64,7 @@ export class FakeWindow implements WindowLike { // listeners. break; default: - fail(`MockWindow doesn't support events of type '${type}'`); + fail(0xe53d, `MockWindow doesn't support events of type '${type}'`); } } From 8bb66c28b3b1a2486ae2574250e6f738bec671d1 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Tue, 15 Apr 2025 12:11:50 -0400 Subject: [PATCH 10/77] docs(vertexai): remove HTML `` tags (#8900) We wrap links that reference public APIs with `` tags so that they can be rendered as code in devsite. Unfortunately, VSCode's hover feature can not render these links at all. Since there are no other alternatives for creating code-formatted links, we should remove the problematic HTML `` tags. --- docs-devsite/vertexai.chatsession.md | 8 +-- docs-devsite/vertexai.citationmetadata.md | 2 +- docs-devsite/vertexai.counttokensrequest.md | 4 +- docs-devsite/vertexai.customerrordata.md | 4 +- docs-devsite/vertexai.filedatapart.md | 2 +- docs-devsite/vertexai.functioncall.md | 2 +- docs-devsite/vertexai.functioncallpart.md | 2 +- .../vertexai.functiondeclarationstool.md | 4 +- docs-devsite/vertexai.functionresponse.md | 2 +- docs-devsite/vertexai.functionresponsepart.md | 2 +- .../vertexai.generatecontentcandidate.md | 2 +- docs-devsite/vertexai.generationconfig.md | 4 +- docs-devsite/vertexai.generativemodel.md | 8 +-- docs-devsite/vertexai.imagengcsimage.md | 4 +- .../vertexai.imagengenerationconfig.md | 16 ++--- .../vertexai.imagengenerationresponse.md | 4 +- docs-devsite/vertexai.imagenimageformat.md | 14 ++--- docs-devsite/vertexai.imageninlineimage.md | 4 +- docs-devsite/vertexai.imagenmodel.md | 6 +- docs-devsite/vertexai.imagenmodelparams.md | 2 +- docs-devsite/vertexai.md | 60 +++++++++---------- docs-devsite/vertexai.modelparams.md | 2 +- .../vertexai.objectschemainterface.md | 2 +- docs-devsite/vertexai.requestoptions.md | 2 +- docs-devsite/vertexai.safetyrating.md | 2 +- docs-devsite/vertexai.schemainterface.md | 2 +- docs-devsite/vertexai.schemaparams.md | 2 +- docs-devsite/vertexai.schemarequest.md | 2 +- docs-devsite/vertexai.schemashared.md | 2 +- docs-devsite/vertexai.usagemetadata.md | 2 +- docs-devsite/vertexai.vertexai.md | 4 +- docs-devsite/vertexai.vertexaierror.md | 2 +- packages/vertexai/src/api.ts | 6 +- packages/vertexai/src/errors.ts | 2 +- packages/vertexai/src/methods/chat-session.ts | 4 +- .../vertexai/src/models/generative-model.ts | 4 +- packages/vertexai/src/models/imagen-model.ts | 6 +- packages/vertexai/src/public-types.ts | 2 +- .../src/requests/imagen-image-format.ts | 10 ++-- .../vertexai/src/requests/request-helpers.ts | 2 +- .../vertexai/src/requests/response-helpers.ts | 2 +- packages/vertexai/src/types/content.ts | 12 ++-- packages/vertexai/src/types/error.ts | 4 +- .../vertexai/src/types/imagen/requests.ts | 14 ++--- .../vertexai/src/types/imagen/responses.ts | 10 ++-- packages/vertexai/src/types/requests.ts | 14 ++--- packages/vertexai/src/types/responses.ts | 8 +-- packages/vertexai/src/types/schema.ts | 12 ++-- 48 files changed, 146 insertions(+), 146 deletions(-) diff --git a/docs-devsite/vertexai.chatsession.md b/docs-devsite/vertexai.chatsession.md index cc5a75ace16..ed359f7e08c 100644 --- a/docs-devsite/vertexai.chatsession.md +++ b/docs-devsite/vertexai.chatsession.md @@ -37,8 +37,8 @@ export declare class ChatSession | Method | Modifiers | Description | | --- | --- | --- | | [getHistory()](./vertexai.chatsession.md#chatsessiongethistory) | | Gets the chat history so far. Blocked prompts are not added to history. Neither blocked candidates nor the prompts that generated them are added to history. | -| [sendMessage(request)](./vertexai.chatsession.md#chatsessionsendmessage) | | Sends a chat message and receives a non-streaming [GenerateContentResult](./vertexai.generatecontentresult.md#generatecontentresult_interface) | -| [sendMessageStream(request)](./vertexai.chatsession.md#chatsessionsendmessagestream) | | Sends a chat message and receives the response as a [GenerateContentStreamResult](./vertexai.generatecontentstreamresult.md#generatecontentstreamresult_interface) containing an iterable stream and a response promise. | +| [sendMessage(request)](./vertexai.chatsession.md#chatsessionsendmessage) | | Sends a chat message and receives a non-streaming [GenerateContentResult](./vertexai.generatecontentresult.md#generatecontentresult_interface) | +| [sendMessageStream(request)](./vertexai.chatsession.md#chatsessionsendmessagestream) | | Sends a chat message and receives the response as a [GenerateContentStreamResult](./vertexai.generatecontentstreamresult.md#generatecontentstreamresult_interface) containing an iterable stream and a response promise. | ## ChatSession.(constructor) @@ -98,7 +98,7 @@ Promise<[Content](./vertexai.content.md#content_interface)\[\]> ## ChatSession.sendMessage() -Sends a chat message and receives a non-streaming [GenerateContentResult](./vertexai.generatecontentresult.md#generatecontentresult_interface) +Sends a chat message and receives a non-streaming [GenerateContentResult](./vertexai.generatecontentresult.md#generatecontentresult_interface) Signature: @@ -118,7 +118,7 @@ Promise<[GenerateContentResult](./vertexai.generatecontentresult.md#generatec ## ChatSession.sendMessageStream() -Sends a chat message and receives the response as a [GenerateContentStreamResult](./vertexai.generatecontentstreamresult.md#generatecontentstreamresult_interface) containing an iterable stream and a response promise. +Sends a chat message and receives the response as a [GenerateContentStreamResult](./vertexai.generatecontentstreamresult.md#generatecontentstreamresult_interface) containing an iterable stream and a response promise. Signature: diff --git a/docs-devsite/vertexai.citationmetadata.md b/docs-devsite/vertexai.citationmetadata.md index e3d41a37d98..c317160e64f 100644 --- a/docs-devsite/vertexai.citationmetadata.md +++ b/docs-devsite/vertexai.citationmetadata.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # CitationMetadata interface -Citation metadata that may be found on a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface). +Citation metadata that may be found on a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface). Signature: diff --git a/docs-devsite/vertexai.counttokensrequest.md b/docs-devsite/vertexai.counttokensrequest.md index f5875564588..740ae5feed4 100644 --- a/docs-devsite/vertexai.counttokensrequest.md +++ b/docs-devsite/vertexai.counttokensrequest.md @@ -25,7 +25,7 @@ export interface CountTokensRequest | [contents](./vertexai.counttokensrequest.md#counttokensrequestcontents) | [Content](./vertexai.content.md#content_interface)\[\] | | | [generationConfig](./vertexai.counttokensrequest.md#counttokensrequestgenerationconfig) | [GenerationConfig](./vertexai.generationconfig.md#generationconfig_interface) | Configuration options that control how the model generates a response. | | [systemInstruction](./vertexai.counttokensrequest.md#counttokensrequestsysteminstruction) | string \| [Part](./vertexai.md#part) \| [Content](./vertexai.content.md#content_interface) | Instructions that direct the model to behave a certain way. | -| [tools](./vertexai.counttokensrequest.md#counttokensrequesttools) | [Tool](./vertexai.md#tool)\[\] | [Tool](./vertexai.md#tool) configuration. | +| [tools](./vertexai.counttokensrequest.md#counttokensrequesttools) | [Tool](./vertexai.md#tool)\[\] | [Tool](./vertexai.md#tool) configuration. | ## CountTokensRequest.contents @@ -57,7 +57,7 @@ systemInstruction?: string | Part | Content; ## CountTokensRequest.tools -[Tool](./vertexai.md#tool) configuration. +[Tool](./vertexai.md#tool) configuration. Signature: diff --git a/docs-devsite/vertexai.customerrordata.md b/docs-devsite/vertexai.customerrordata.md index 701b1b84c49..100f4a85fd9 100644 --- a/docs-devsite/vertexai.customerrordata.md +++ b/docs-devsite/vertexai.customerrordata.md @@ -23,7 +23,7 @@ export interface CustomErrorData | Property | Type | Description | | --- | --- | --- | | [errorDetails](./vertexai.customerrordata.md#customerrordataerrordetails) | [ErrorDetails](./vertexai.errordetails.md#errordetails_interface)\[\] | Optional additional details about the error. | -| [response](./vertexai.customerrordata.md#customerrordataresponse) | [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface) | Response from a [GenerateContentRequest](./vertexai.generatecontentrequest.md#generatecontentrequest_interface) | +| [response](./vertexai.customerrordata.md#customerrordataresponse) | [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface) | Response from a [GenerateContentRequest](./vertexai.generatecontentrequest.md#generatecontentrequest_interface) | | [status](./vertexai.customerrordata.md#customerrordatastatus) | number | HTTP status code of the error response. | | [statusText](./vertexai.customerrordata.md#customerrordatastatustext) | string | HTTP status text of the error response. | @@ -39,7 +39,7 @@ errorDetails?: ErrorDetails[]; ## CustomErrorData.response -Response from a [GenerateContentRequest](./vertexai.generatecontentrequest.md#generatecontentrequest_interface) +Response from a [GenerateContentRequest](./vertexai.generatecontentrequest.md#generatecontentrequest_interface) Signature: diff --git a/docs-devsite/vertexai.filedatapart.md b/docs-devsite/vertexai.filedatapart.md index 74512fa6d29..76162227526 100644 --- a/docs-devsite/vertexai.filedatapart.md +++ b/docs-devsite/vertexai.filedatapart.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # FileDataPart interface -Content part interface if the part represents [FileData](./vertexai.filedata.md#filedata_interface) +Content part interface if the part represents [FileData](./vertexai.filedata.md#filedata_interface) Signature: diff --git a/docs-devsite/vertexai.functioncall.md b/docs-devsite/vertexai.functioncall.md index ca7bc015438..299fb7130f4 100644 --- a/docs-devsite/vertexai.functioncall.md +++ b/docs-devsite/vertexai.functioncall.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # FunctionCall interface -A predicted [FunctionCall](./vertexai.functioncall.md#functioncall_interface) returned from the model that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing the parameters and their values. +A predicted [FunctionCall](./vertexai.functioncall.md#functioncall_interface) returned from the model that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing the parameters and their values. Signature: diff --git a/docs-devsite/vertexai.functioncallpart.md b/docs-devsite/vertexai.functioncallpart.md index af8ccf1109a..58fe0f5fa97 100644 --- a/docs-devsite/vertexai.functioncallpart.md +++ b/docs-devsite/vertexai.functioncallpart.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # FunctionCallPart interface -Content part interface if the part represents a [FunctionCall](./vertexai.functioncall.md#functioncall_interface). +Content part interface if the part represents a [FunctionCall](./vertexai.functioncall.md#functioncall_interface). Signature: diff --git a/docs-devsite/vertexai.functiondeclarationstool.md b/docs-devsite/vertexai.functiondeclarationstool.md index 5e728046639..2eff3138d8d 100644 --- a/docs-devsite/vertexai.functiondeclarationstool.md +++ b/docs-devsite/vertexai.functiondeclarationstool.md @@ -22,11 +22,11 @@ export declare interface FunctionDeclarationsTool | Property | Type | Description | | --- | --- | --- | -| [functionDeclarations](./vertexai.functiondeclarationstool.md#functiondeclarationstoolfunctiondeclarations) | [FunctionDeclaration](./vertexai.functiondeclaration.md#functiondeclaration_interface)\[\] | Optional. One or more function declarations to be passed to the model along with the current user query. Model may decide to call a subset of these functions by populating [FunctionCall](./vertexai.functioncall.md#functioncall_interface) in the response. User should provide a [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface) for each function call in the next turn. Based on the function responses, the model will generate the final response back to the user. Maximum 64 function declarations can be provided. | +| [functionDeclarations](./vertexai.functiondeclarationstool.md#functiondeclarationstoolfunctiondeclarations) | [FunctionDeclaration](./vertexai.functiondeclaration.md#functiondeclaration_interface)\[\] | Optional. One or more function declarations to be passed to the model along with the current user query. Model may decide to call a subset of these functions by populating [FunctionCall](./vertexai.functioncall.md#functioncall_interface) in the response. User should provide a [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface) for each function call in the next turn. Based on the function responses, the model will generate the final response back to the user. Maximum 64 function declarations can be provided. | ## FunctionDeclarationsTool.functionDeclarations -Optional. One or more function declarations to be passed to the model along with the current user query. Model may decide to call a subset of these functions by populating [FunctionCall](./vertexai.functioncall.md#functioncall_interface) in the response. User should provide a [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface) for each function call in the next turn. Based on the function responses, the model will generate the final response back to the user. Maximum 64 function declarations can be provided. +Optional. One or more function declarations to be passed to the model along with the current user query. Model may decide to call a subset of these functions by populating [FunctionCall](./vertexai.functioncall.md#functioncall_interface) in the response. User should provide a [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface) for each function call in the next turn. Based on the function responses, the model will generate the final response back to the user. Maximum 64 function declarations can be provided. Signature: diff --git a/docs-devsite/vertexai.functionresponse.md b/docs-devsite/vertexai.functionresponse.md index 0ca553e0e6b..072a08b3486 100644 --- a/docs-devsite/vertexai.functionresponse.md +++ b/docs-devsite/vertexai.functionresponse.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # FunctionResponse interface -The result output from a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing any output from the function is used as context to the model. This should contain the result of a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) made based on model prediction. +The result output from a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing any output from the function is used as context to the model. This should contain the result of a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) made based on model prediction. Signature: diff --git a/docs-devsite/vertexai.functionresponsepart.md b/docs-devsite/vertexai.functionresponsepart.md index 1905c98cdc7..ffbf2ad0517 100644 --- a/docs-devsite/vertexai.functionresponsepart.md +++ b/docs-devsite/vertexai.functionresponsepart.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # FunctionResponsePart interface -Content part interface if the part represents [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface). +Content part interface if the part represents [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface). Signature: diff --git a/docs-devsite/vertexai.generatecontentcandidate.md b/docs-devsite/vertexai.generatecontentcandidate.md index a30eef55485..e5fd9eacbbe 100644 --- a/docs-devsite/vertexai.generatecontentcandidate.md +++ b/docs-devsite/vertexai.generatecontentcandidate.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # GenerateContentCandidate interface -A candidate returned as part of a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). +A candidate returned as part of a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). Signature: diff --git a/docs-devsite/vertexai.generationconfig.md b/docs-devsite/vertexai.generationconfig.md index 3c3d0a14ffa..d3e9879f937 100644 --- a/docs-devsite/vertexai.generationconfig.md +++ b/docs-devsite/vertexai.generationconfig.md @@ -27,7 +27,7 @@ export interface GenerationConfig | [maxOutputTokens](./vertexai.generationconfig.md#generationconfigmaxoutputtokens) | number | | | [presencePenalty](./vertexai.generationconfig.md#generationconfigpresencepenalty) | number | | | [responseMimeType](./vertexai.generationconfig.md#generationconfigresponsemimetype) | string | Output response MIME type of the generated candidate text. Supported MIME types are text/plain (default, text output), application/json (JSON response in the candidates), and text/x.enum. | -| [responseSchema](./vertexai.generationconfig.md#generationconfigresponseschema) | [TypedSchema](./vertexai.md#typedschema) \| [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) | Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./vertexai.schema.md#schema_class) static method like Schema.string() or Schema.object() or it can be a plain JS object matching the [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified responseMIMEType supports a schema; currently this is limited to application/json and text/x.enum. | +| [responseSchema](./vertexai.generationconfig.md#generationconfigresponseschema) | [TypedSchema](./vertexai.md#typedschema) \| [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) | Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./vertexai.schema.md#schema_class) static method like Schema.string() or Schema.object() or it can be a plain JS object matching the [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified responseMIMEType supports a schema; currently this is limited to application/json and text/x.enum. | | [stopSequences](./vertexai.generationconfig.md#generationconfigstopsequences) | string\[\] | | | [temperature](./vertexai.generationconfig.md#generationconfigtemperature) | number | | | [topK](./vertexai.generationconfig.md#generationconfigtopk) | number | | @@ -77,7 +77,7 @@ responseMimeType?: string; ## GenerationConfig.responseSchema -Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./vertexai.schema.md#schema_class) static method like `Schema.string()` or `Schema.object()` or it can be a plain JS object matching the [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified `responseMIMEType` supports a schema; currently this is limited to `application/json` and `text/x.enum`. +Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./vertexai.schema.md#schema_class) static method like `Schema.string()` or `Schema.object()` or it can be a plain JS object matching the [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified `responseMIMEType` supports a schema; currently this is limited to `application/json` and `text/x.enum`. Signature: diff --git a/docs-devsite/vertexai.generativemodel.md b/docs-devsite/vertexai.generativemodel.md index b734e241e78..e4a238b0af5 100644 --- a/docs-devsite/vertexai.generativemodel.md +++ b/docs-devsite/vertexai.generativemodel.md @@ -41,9 +41,9 @@ export declare class GenerativeModel extends VertexAIModel | Method | Modifiers | Description | | --- | --- | --- | | [countTokens(request)](./vertexai.generativemodel.md#generativemodelcounttokens) | | Counts the tokens in the provided request. | -| [generateContent(request)](./vertexai.generativemodel.md#generativemodelgeneratecontent) | | Makes a single non-streaming call to the model and returns an object containing a single [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). | +| [generateContent(request)](./vertexai.generativemodel.md#generativemodelgeneratecontent) | | Makes a single non-streaming call to the model and returns an object containing a single [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). | | [generateContentStream(request)](./vertexai.generativemodel.md#generativemodelgeneratecontentstream) | | Makes a single streaming call to the model and returns an object containing an iterable stream that iterates over all chunks in the streaming response as well as a promise that returns the final aggregated response. | -| [startChat(startChatParams)](./vertexai.generativemodel.md#generativemodelstartchat) | | Gets a new [ChatSession](./vertexai.chatsession.md#chatsession_class) instance which can be used for multi-turn chats. | +| [startChat(startChatParams)](./vertexai.generativemodel.md#generativemodelstartchat) | | Gets a new [ChatSession](./vertexai.chatsession.md#chatsession_class) instance which can be used for multi-turn chats. | ## GenerativeModel.(constructor) @@ -133,7 +133,7 @@ Promise<[CountTokensResponse](./vertexai.counttokensresponse.md#counttokensre ## GenerativeModel.generateContent() -Makes a single non-streaming call to the model and returns an object containing a single [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). +Makes a single non-streaming call to the model and returns an object containing a single [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). Signature: @@ -173,7 +173,7 @@ Promise<[GenerateContentStreamResult](./vertexai.generatecontentstreamresult. ## GenerativeModel.startChat() -Gets a new [ChatSession](./vertexai.chatsession.md#chatsession_class) instance which can be used for multi-turn chats. +Gets a new [ChatSession](./vertexai.chatsession.md#chatsession_class) instance which can be used for multi-turn chats. Signature: diff --git a/docs-devsite/vertexai.imagengcsimage.md b/docs-devsite/vertexai.imagengcsimage.md index b094e63c1d8..23770192b3b 100644 --- a/docs-devsite/vertexai.imagengcsimage.md +++ b/docs-devsite/vertexai.imagengcsimage.md @@ -25,7 +25,7 @@ export interface ImagenGCSImage | Property | Type | Description | | --- | --- | --- | | [gcsURI](./vertexai.imagengcsimage.md#imagengcsimagegcsuri) | string | The URI of the file stored in a Cloud Storage for Firebase bucket. | -| [mimeType](./vertexai.imagengcsimage.md#imagengcsimagemimetype) | string | The MIME type of the image; either "image/png" or "image/jpeg".To request a different format, set the imageFormat property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). | +| [mimeType](./vertexai.imagengcsimage.md#imagengcsimagemimetype) | string | The MIME type of the image; either "image/png" or "image/jpeg".To request a different format, set the imageFormat property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). | ## ImagenGCSImage.gcsURI @@ -45,7 +45,7 @@ gcsURI: string; The MIME type of the image; either `"image/png"` or `"image/jpeg"`. -To request a different format, set the `imageFormat` property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). +To request a different format, set the `imageFormat` property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). Signature: diff --git a/docs-devsite/vertexai.imagengenerationconfig.md b/docs-devsite/vertexai.imagengenerationconfig.md index cee7734f789..b6785b9b2bb 100644 --- a/docs-devsite/vertexai.imagengenerationconfig.md +++ b/docs-devsite/vertexai.imagengenerationconfig.md @@ -27,11 +27,11 @@ export interface ImagenGenerationConfig | Property | Type | Description | | --- | --- | --- | -| [addWatermark](./vertexai.imagengenerationconfig.md#imagengenerationconfigaddwatermark) | boolean | (Public Preview) Whether to add an invisible watermark to generated images.If set to true, an invisible SynthID watermark is embedded in generated images to indicate that they are AI generated. If set to false, watermarking will be disabled.For Imagen 3 models, the default value is true; see the addWatermark documentation for more details. | -| [aspectRatio](./vertexai.imagengenerationconfig.md#imagengenerationconfigaspectratio) | [ImagenAspectRatio](./vertexai.md#imagenaspectratio) | (Public Preview) The aspect ratio of the generated images. The default value is square 1:1. Supported aspect ratios depend on the Imagen model, see [ImagenAspectRatio](./vertexai.md#imagenaspectratio) for more details. | -| [imageFormat](./vertexai.imagengenerationconfig.md#imagengenerationconfigimageformat) | [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) | (Public Preview) The image format of the generated images. The default is PNG.See [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for more details. | +| [addWatermark](./vertexai.imagengenerationconfig.md#imagengenerationconfigaddwatermark) | boolean | (Public Preview) Whether to add an invisible watermark to generated images.If set to true, an invisible SynthID watermark is embedded in generated images to indicate that they are AI generated. If set to false, watermarking will be disabled.For Imagen 3 models, the default value is true; see the addWatermark documentation for more details. | +| [aspectRatio](./vertexai.imagengenerationconfig.md#imagengenerationconfigaspectratio) | [ImagenAspectRatio](./vertexai.md#imagenaspectratio) | (Public Preview) The aspect ratio of the generated images. The default value is square 1:1. Supported aspect ratios depend on the Imagen model, see [ImagenAspectRatio](./vertexai.md#imagenaspectratio) for more details. | +| [imageFormat](./vertexai.imagengenerationconfig.md#imagengenerationconfigimageformat) | [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) | (Public Preview) The image format of the generated images. The default is PNG.See [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for more details. | | [negativePrompt](./vertexai.imagengenerationconfig.md#imagengenerationconfignegativeprompt) | string | (Public Preview) A description of what should be omitted from the generated images.Support for negative prompts depends on the Imagen model.See the [documentation](http://firebase.google.com/docs/vertex-ai/model-parameters#imagen) for more details. | -| [numberOfImages](./vertexai.imagengenerationconfig.md#imagengenerationconfignumberofimages) | number | (Public Preview) The number of images to generate. The default value is 1.The number of sample images that may be generated in each request depends on the model (typically up to 4); see the sampleCount documentation for more details. | +| [numberOfImages](./vertexai.imagengenerationconfig.md#imagengenerationconfignumberofimages) | number | (Public Preview) The number of images to generate. The default value is 1.The number of sample images that may be generated in each request depends on the model (typically up to 4); see the sampleCount documentation for more details. | ## ImagenGenerationConfig.addWatermark @@ -42,7 +42,7 @@ Whether to add an invisible watermark to generated images. If set to `true`, an invisible SynthID watermark is embedded in generated images to indicate that they are AI generated. If set to `false`, watermarking will be disabled. -For Imagen 3 models, the default value is `true`; see the addWatermark documentation for more details. +For Imagen 3 models, the default value is `true`; see the addWatermark documentation for more details. Signature: @@ -55,7 +55,7 @@ addWatermark?: boolean; > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > -The aspect ratio of the generated images. The default value is square 1:1. Supported aspect ratios depend on the Imagen model, see [ImagenAspectRatio](./vertexai.md#imagenaspectratio) for more details. +The aspect ratio of the generated images. The default value is square 1:1. Supported aspect ratios depend on the Imagen model, see [ImagenAspectRatio](./vertexai.md#imagenaspectratio) for more details. Signature: @@ -70,7 +70,7 @@ aspectRatio?: ImagenAspectRatio; The image format of the generated images. The default is PNG. -See [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for more details. +See [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for more details. Signature: @@ -102,7 +102,7 @@ negativePrompt?: string; The number of images to generate. The default value is 1. -The number of sample images that may be generated in each request depends on the model (typically up to 4); see the sampleCount documentation for more details. +The number of sample images that may be generated in each request depends on the model (typically up to 4); see the sampleCount documentation for more details. Signature: diff --git a/docs-devsite/vertexai.imagengenerationresponse.md b/docs-devsite/vertexai.imagengenerationresponse.md index 32ed69718f9..d8de93df3ec 100644 --- a/docs-devsite/vertexai.imagengenerationresponse.md +++ b/docs-devsite/vertexai.imagengenerationresponse.md @@ -25,7 +25,7 @@ export interface ImagenGenerationResponse(Public Preview)
The reason that images were filtered out. This property will only be defined if one or more images were filtered.Images may be filtered out due to the [ImagenSafetyFilterLevel](./vertexai.md#imagensafetyfilterlevel), [ImagenPersonFilterLevel](./vertexai.md#imagenpersonfilterlevel), or filtering included in the model. The filter levels may be adjusted in your [ImagenSafetySettings](./vertexai.imagensafetysettings.md#imagensafetysettings_interface).See the [Responsible AI and usage guidelines for Imagen](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen) for more details. | +| [filteredReason](./vertexai.imagengenerationresponse.md#imagengenerationresponsefilteredreason) | string | (Public Preview) The reason that images were filtered out. This property will only be defined if one or more images were filtered.Images may be filtered out due to the [ImagenSafetyFilterLevel](./vertexai.md#imagensafetyfilterlevel), [ImagenPersonFilterLevel](./vertexai.md#imagenpersonfilterlevel), or filtering included in the model. The filter levels may be adjusted in your [ImagenSafetySettings](./vertexai.imagensafetysettings.md#imagensafetysettings_interface).See the [Responsible AI and usage guidelines for Imagen](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen) for more details. | | [images](./vertexai.imagengenerationresponse.md#imagengenerationresponseimages) | T\[\] | (Public Preview) The images generated by Imagen.The number of images generated may be fewer than the number requested if one or more were filtered out; see filteredReason. | ## ImagenGenerationResponse.filteredReason @@ -35,7 +35,7 @@ export interface ImagenGenerationResponse[ImagenSafetyFilterLevel](./vertexai.md#imagensafetyfilterlevel), [ImagenPersonFilterLevel](./vertexai.md#imagenpersonfilterlevel), or filtering included in the model. The filter levels may be adjusted in your [ImagenSafetySettings](./vertexai.imagensafetysettings.md#imagensafetysettings_interface). +Images may be filtered out due to the [ImagenSafetyFilterLevel](./vertexai.md#imagensafetyfilterlevel), [ImagenPersonFilterLevel](./vertexai.md#imagenpersonfilterlevel), or filtering included in the model. The filter levels may be adjusted in your [ImagenSafetySettings](./vertexai.imagensafetysettings.md#imagensafetysettings_interface). See the [Responsible AI and usage guidelines for Imagen](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen) for more details. diff --git a/docs-devsite/vertexai.imagenimageformat.md b/docs-devsite/vertexai.imagenimageformat.md index 785c7c726fc..68db8bbdae0 100644 --- a/docs-devsite/vertexai.imagenimageformat.md +++ b/docs-devsite/vertexai.imagenimageformat.md @@ -15,7 +15,7 @@ https://github.com/firebase/firebase-js-sdk Defines the image format for images generated by Imagen. -Use this class to specify the desired format (JPEG or PNG) and compression quality for images generated by Imagen. This is typically included as part of [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface). +Use this class to specify the desired format (JPEG or PNG) and compression quality for images generated by Imagen. This is typically included as part of [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface). Signature: @@ -34,8 +34,8 @@ export declare class ImagenImageFormat | Method | Modifiers | Description | | --- | --- | --- | -| [jpeg(compressionQuality)](./vertexai.imagenimageformat.md#imagenimageformatjpeg) | static | (Public Preview) Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a JPEG image. | -| [png()](./vertexai.imagenimageformat.md#imagenimageformatpng) | static | (Public Preview) Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a PNG image. | +| [jpeg(compressionQuality)](./vertexai.imagenimageformat.md#imagenimageformatjpeg) | static | (Public Preview) Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a JPEG image. | +| [png()](./vertexai.imagenimageformat.md#imagenimageformatpng) | static | (Public Preview) Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a PNG image. | ## ImagenImageFormat.compressionQuality @@ -68,7 +68,7 @@ mimeType: string; > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > -Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a JPEG image. +Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a JPEG image. Signature: @@ -86,14 +86,14 @@ static jpeg(compressionQuality?: number): ImagenImageFormat; [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) -An [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) object for a JPEG image. +An [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) object for a JPEG image. ## ImagenImageFormat.png() > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > -Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a PNG image. +Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a PNG image. Signature: @@ -104,7 +104,7 @@ static png(): ImagenImageFormat; [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) -An [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) object for a PNG image. +An [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) object for a PNG image. ### Example diff --git a/docs-devsite/vertexai.imageninlineimage.md b/docs-devsite/vertexai.imageninlineimage.md index 19fe8a67764..a72937b5e5d 100644 --- a/docs-devsite/vertexai.imageninlineimage.md +++ b/docs-devsite/vertexai.imageninlineimage.md @@ -26,7 +26,7 @@ export interface ImagenInlineImage | Property | Type | Description | | --- | --- | --- | | [bytesBase64Encoded](./vertexai.imageninlineimage.md#imageninlineimagebytesbase64encoded) | string | (Public Preview) The base64-encoded image data. | -| [mimeType](./vertexai.imageninlineimage.md#imageninlineimagemimetype) | string | (Public Preview) The MIME type of the image; either "image/png" or "image/jpeg".To request a different format, set the imageFormat property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). | +| [mimeType](./vertexai.imageninlineimage.md#imageninlineimagemimetype) | string | (Public Preview) The MIME type of the image; either "image/png" or "image/jpeg".To request a different format, set the imageFormat property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). | ## ImagenInlineImage.bytesBase64Encoded @@ -48,7 +48,7 @@ bytesBase64Encoded: string; The MIME type of the image; either `"image/png"` or `"image/jpeg"`. -To request a different format, set the `imageFormat` property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). +To request a different format, set the `imageFormat` property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). Signature: diff --git a/docs-devsite/vertexai.imagenmodel.md b/docs-devsite/vertexai.imagenmodel.md index 63e15ff133a..ed40dc8f578 100644 --- a/docs-devsite/vertexai.imagenmodel.md +++ b/docs-devsite/vertexai.imagenmodel.md @@ -28,7 +28,7 @@ export declare class ImagenModel extends VertexAIModel | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(vertexAI, modelParams, requestOptions)](./vertexai.imagenmodel.md#imagenmodelconstructor) | | (Public Preview) Constructs a new instance of the [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class. | +| [(constructor)(vertexAI, modelParams, requestOptions)](./vertexai.imagenmodel.md#imagenmodelconstructor) | | (Public Preview) Constructs a new instance of the [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class. | ## Properties @@ -49,7 +49,7 @@ export declare class ImagenModel extends VertexAIModel > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > -Constructs a new instance of the [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class. +Constructs a new instance of the [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class. Signature: @@ -131,7 +131,7 @@ generateImages(prompt: string): Promise<[ImagenInlineImage](./vertexai.imageninlineimage.md#imageninlineimage_interface)>> -A promise that resolves to an [ImagenGenerationResponse](./vertexai.imagengenerationresponse.md#imagengenerationresponse_interface) object containing the generated images. +A promise that resolves to an [ImagenGenerationResponse](./vertexai.imagengenerationresponse.md#imagengenerationresponse_interface) object containing the generated images. #### Exceptions diff --git a/docs-devsite/vertexai.imagenmodelparams.md b/docs-devsite/vertexai.imagenmodelparams.md index 66c4bb0bfe6..5396a36e4d1 100644 --- a/docs-devsite/vertexai.imagenmodelparams.md +++ b/docs-devsite/vertexai.imagenmodelparams.md @@ -13,7 +13,7 @@ https://github.com/firebase/firebase-js-sdk > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > -Parameters for configuring an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class). +Parameters for configuring an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class). Signature: diff --git a/docs-devsite/vertexai.md b/docs-devsite/vertexai.md index fca51b42f4f..f67254eef20 100644 --- a/docs-devsite/vertexai.md +++ b/docs-devsite/vertexai.md @@ -17,10 +17,10 @@ The Vertex AI in Firebase Web SDK. | Function | Description | | --- | --- | | function(app, ...) | -| [getVertexAI(app, options)](./vertexai.md#getvertexai_04094cf) | Returns a [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance for the given app. | +| [getVertexAI(app, options)](./vertexai.md#getvertexai_04094cf) | Returns a [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance for the given app. | | function(vertexAI, ...) | -| [getGenerativeModel(vertexAI, modelParams, requestOptions)](./vertexai.md#getgenerativemodel_e3037c9) | Returns a [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) class with methods for inference and other functionality. | -| [getImagenModel(vertexAI, modelParams, requestOptions)](./vertexai.md#getimagenmodel_812c375) | (Public Preview) Returns an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class with methods for using Imagen.Only Imagen 3 models (named imagen-3.0-*) are supported. | +| [getGenerativeModel(vertexAI, modelParams, requestOptions)](./vertexai.md#getgenerativemodel_e3037c9) | Returns a [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) class with methods for inference and other functionality. | +| [getImagenModel(vertexAI, modelParams, requestOptions)](./vertexai.md#getimagenmodel_812c375) | (Public Preview) Returns an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class with methods for using Imagen.Only Imagen 3 models (named imagen-3.0-*) are supported. | ## Classes @@ -30,7 +30,7 @@ The Vertex AI in Firebase Web SDK. | [BooleanSchema](./vertexai.booleanschema.md#booleanschema_class) | Schema class for "boolean" types. | | [ChatSession](./vertexai.chatsession.md#chatsession_class) | ChatSession class that enables sending chat messages and stores history of sent and received messages so far. | | [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) | Class for generative model APIs. | -| [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) | (Public Preview) Defines the image format for images generated by Imagen.Use this class to specify the desired format (JPEG or PNG) and compression quality for images generated by Imagen. This is typically included as part of [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface). | +| [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) | (Public Preview) Defines the image format for images generated by Imagen.Use this class to specify the desired format (JPEG or PNG) and compression quality for images generated by Imagen. This is typically included as part of [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface). | | [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) | (Public Preview) Class for Imagen model APIs.This class provides methods for generating images using the Imagen model. | | [IntegerSchema](./vertexai.integerschema.md#integerschema_class) | Schema class for "integer" types. | | [NumberSchema](./vertexai.numberschema.md#numberschema_class) | Schema class for "number" types. | @@ -52,12 +52,12 @@ The Vertex AI in Firebase Web SDK. | [HarmCategory](./vertexai.md#harmcategory) | Harm categories that would cause prompts or candidates to be blocked. | | [HarmProbability](./vertexai.md#harmprobability) | Probability that a prompt or candidate matches a harm category. | | [HarmSeverity](./vertexai.md#harmseverity) | Harm severity levels. | -| [ImagenAspectRatio](./vertexai.md#imagenaspectratio) | (Public Preview) Aspect ratios for Imagen images.To specify an aspect ratio for generated images, set the aspectRatio property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface).See the the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) for more details and examples of the supported aspect ratios. | -| [ImagenPersonFilterLevel](./vertexai.md#imagenpersonfilterlevel) | (Public Preview) A filter level controlling whether generation of images containing people or faces is allowed.See the personGeneration documentation for more details. | +| [ImagenAspectRatio](./vertexai.md#imagenaspectratio) | (Public Preview) Aspect ratios for Imagen images.To specify an aspect ratio for generated images, set the aspectRatio property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface).See the the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) for more details and examples of the supported aspect ratios. | +| [ImagenPersonFilterLevel](./vertexai.md#imagenpersonfilterlevel) | (Public Preview) A filter level controlling whether generation of images containing people or faces is allowed.See the personGeneration documentation for more details. | | [ImagenSafetyFilterLevel](./vertexai.md#imagensafetyfilterlevel) | (Public Preview) A filter level controlling how aggressively to filter sensitive content.Text prompts provided as inputs and images (generated or uploaded) through Imagen on Vertex AI are assessed against a list of safety filters, which include 'harmful categories' (for example, violence, sexual, derogatory, and toxic). This filter level controls how aggressively to filter out potentially harmful content from responses. See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) and the [Responsible AI and usage guidelines](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#safety-filters) for more details. | | [Modality](./vertexai.md#modality) | Content part modality. | | [SchemaType](./vertexai.md#schematype) | Contains the list of OpenAPI data types as defined by the [OpenAPI specification](https://swagger.io/docs/specification/data-models/data-types/) | -| [VertexAIErrorCode](./vertexai.md#vertexaierrorcode) | Standardized error codes that [VertexAIError](./vertexai.vertexaierror.md#vertexaierror_class) can have. | +| [VertexAIErrorCode](./vertexai.md#vertexaierrorcode) | Standardized error codes that [VertexAIError](./vertexai.vertexaierror.md#vertexaierror_class) can have. | ## Interfaces @@ -65,7 +65,7 @@ The Vertex AI in Firebase Web SDK. | --- | --- | | [BaseParams](./vertexai.baseparams.md#baseparams_interface) | Base parameters for a number of methods. | | [Citation](./vertexai.citation.md#citation_interface) | A single citation. | -| [CitationMetadata](./vertexai.citationmetadata.md#citationmetadata_interface) | Citation metadata that may be found on a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface). | +| [CitationMetadata](./vertexai.citationmetadata.md#citationmetadata_interface) | Citation metadata that may be found on a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface). | | [Content](./vertexai.content.md#content_interface) | Content type for both prompts and response candidates. | | [CountTokensRequest](./vertexai.counttokensrequest.md#counttokensrequest_interface) | Params for calling [GenerativeModel.countTokens()](./vertexai.generativemodel.md#generativemodelcounttokens) | | [CountTokensResponse](./vertexai.counttokensresponse.md#counttokensresponse_interface) | Response from calling [GenerativeModel.countTokens()](./vertexai.generativemodel.md#generativemodelcounttokens). | @@ -74,15 +74,15 @@ The Vertex AI in Firebase Web SDK. | [EnhancedGenerateContentResponse](./vertexai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponse_interface) | Response object wrapped with helper methods. | | [ErrorDetails](./vertexai.errordetails.md#errordetails_interface) | Details object that may be included in an error response. | | [FileData](./vertexai.filedata.md#filedata_interface) | Data pointing to a file uploaded on Google Cloud Storage. | -| [FileDataPart](./vertexai.filedatapart.md#filedatapart_interface) | Content part interface if the part represents [FileData](./vertexai.filedata.md#filedata_interface) | -| [FunctionCall](./vertexai.functioncall.md#functioncall_interface) | A predicted [FunctionCall](./vertexai.functioncall.md#functioncall_interface) returned from the model that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing the parameters and their values. | +| [FileDataPart](./vertexai.filedatapart.md#filedatapart_interface) | Content part interface if the part represents [FileData](./vertexai.filedata.md#filedata_interface) | +| [FunctionCall](./vertexai.functioncall.md#functioncall_interface) | A predicted [FunctionCall](./vertexai.functioncall.md#functioncall_interface) returned from the model that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing the parameters and their values. | | [FunctionCallingConfig](./vertexai.functioncallingconfig.md#functioncallingconfig_interface) | | -| [FunctionCallPart](./vertexai.functioncallpart.md#functioncallpart_interface) | Content part interface if the part represents a [FunctionCall](./vertexai.functioncall.md#functioncall_interface). | +| [FunctionCallPart](./vertexai.functioncallpart.md#functioncallpart_interface) | Content part interface if the part represents a [FunctionCall](./vertexai.functioncall.md#functioncall_interface). | | [FunctionDeclaration](./vertexai.functiondeclaration.md#functiondeclaration_interface) | Structured representation of a function declaration as defined by the [OpenAPI 3.0 specification](https://spec.openapis.org/oas/v3.0.3). Included in this declaration are the function name and parameters. This FunctionDeclaration is a representation of a block of code that can be used as a Tool by the model and executed by the client. | | [FunctionDeclarationsTool](./vertexai.functiondeclarationstool.md#functiondeclarationstool_interface) | A FunctionDeclarationsTool is a piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. | -| [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface) | The result output from a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing any output from the function is used as context to the model. This should contain the result of a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) made based on model prediction. | -| [FunctionResponsePart](./vertexai.functionresponsepart.md#functionresponsepart_interface) | Content part interface if the part represents [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface). | -| [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface) | A candidate returned as part of a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). | +| [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface) | The result output from a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing any output from the function is used as context to the model. This should contain the result of a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) made based on model prediction. | +| [FunctionResponsePart](./vertexai.functionresponsepart.md#functionresponsepart_interface) | Content part interface if the part represents [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface). | +| [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface) | A candidate returned as part of a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). | | [GenerateContentRequest](./vertexai.generatecontentrequest.md#generatecontentrequest_interface) | Request sent through [GenerativeModel.generateContent()](./vertexai.generativemodel.md#generativemodelgeneratecontent) | | [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface) | Individual response from [GenerativeModel.generateContent()](./vertexai.generativemodel.md#generativemodelgeneratecontent) and [GenerativeModel.generateContentStream()](./vertexai.generativemodel.md#generativemodelgeneratecontentstream). generateContentStream() will return one in each chunk until the stream is done. | | [GenerateContentResult](./vertexai.generatecontentresult.md#generatecontentresult_interface) | Result object returned from [GenerativeModel.generateContent()](./vertexai.generativemodel.md#generativemodelgeneratecontent) call. | @@ -95,26 +95,26 @@ The Vertex AI in Firebase Web SDK. | [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface) | (Public Preview) Configuration options for generating images with Imagen.See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images-imagen) for more details. | | [ImagenGenerationResponse](./vertexai.imagengenerationresponse.md#imagengenerationresponse_interface) | (Public Preview) The response from a request to generate images with Imagen. | | [ImagenInlineImage](./vertexai.imageninlineimage.md#imageninlineimage_interface) | (Public Preview) An image generated by Imagen, represented as inline data. | -| [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface) | (Public Preview) Parameters for configuring an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class). | +| [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface) | (Public Preview) Parameters for configuring an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class). | | [ImagenSafetySettings](./vertexai.imagensafetysettings.md#imagensafetysettings_interface) | (Public Preview) Settings for controlling the aggressiveness of filtering out sensitive content.See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) for more details. | | [InlineDataPart](./vertexai.inlinedatapart.md#inlinedatapart_interface) | Content part interface if the part represents an image. | | [ModalityTokenCount](./vertexai.modalitytokencount.md#modalitytokencount_interface) | Represents token counting info for a single modality. | -| [ModelParams](./vertexai.modelparams.md#modelparams_interface) | Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). | -| [ObjectSchemaInterface](./vertexai.objectschemainterface.md#objectschemainterface_interface) | Interface for [ObjectSchema](./vertexai.objectschema.md#objectschema_class) class. | +| [ModelParams](./vertexai.modelparams.md#modelparams_interface) | Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). | +| [ObjectSchemaInterface](./vertexai.objectschemainterface.md#objectschemainterface_interface) | Interface for [ObjectSchema](./vertexai.objectschema.md#objectschema_class) class. | | [PromptFeedback](./vertexai.promptfeedback.md#promptfeedback_interface) | If the prompt was blocked, this will be populated with blockReason and the relevant safetyRatings. | -| [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). | +| [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). | | [RetrievedContextAttribution](./vertexai.retrievedcontextattribution.md#retrievedcontextattribution_interface) | | -| [SafetyRating](./vertexai.safetyrating.md#safetyrating_interface) | A safety rating associated with a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface) | +| [SafetyRating](./vertexai.safetyrating.md#safetyrating_interface) | A safety rating associated with a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface) | | [SafetySetting](./vertexai.safetysetting.md#safetysetting_interface) | Safety setting that can be sent as part of request parameters. | -| [SchemaInterface](./vertexai.schemainterface.md#schemainterface_interface) | Interface for [Schema](./vertexai.schema.md#schema_class) class. | -| [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | Params passed to [Schema](./vertexai.schema.md#schema_class) static methods to create specific [Schema](./vertexai.schema.md#schema_class) classes. | -| [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) | Final format for [Schema](./vertexai.schema.md#schema_class) params passed to backend requests. | -| [SchemaShared](./vertexai.schemashared.md#schemashared_interface) | Basic [Schema](./vertexai.schema.md#schema_class) properties shared across several Schema-related types. | +| [SchemaInterface](./vertexai.schemainterface.md#schemainterface_interface) | Interface for [Schema](./vertexai.schema.md#schema_class) class. | +| [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | Params passed to [Schema](./vertexai.schema.md#schema_class) static methods to create specific [Schema](./vertexai.schema.md#schema_class) classes. | +| [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) | Final format for [Schema](./vertexai.schema.md#schema_class) params passed to backend requests. | +| [SchemaShared](./vertexai.schemashared.md#schemashared_interface) | Basic [Schema](./vertexai.schema.md#schema_class) properties shared across several Schema-related types. | | [Segment](./vertexai.segment.md#segment_interface) | | | [StartChatParams](./vertexai.startchatparams.md#startchatparams_interface) | Params for [GenerativeModel.startChat()](./vertexai.generativemodel.md#generativemodelstartchat). | | [TextPart](./vertexai.textpart.md#textpart_interface) | Content part interface if the part represents a text string. | | [ToolConfig](./vertexai.toolconfig.md#toolconfig_interface) | Tool config. This config is shared for all tools provided in the request. | -| [UsageMetadata](./vertexai.usagemetadata.md#usagemetadata_interface) | Usage metadata about a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). | +| [UsageMetadata](./vertexai.usagemetadata.md#usagemetadata_interface) | Usage metadata about a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). | | [VertexAI](./vertexai.vertexai.md#vertexai_interface) | An instance of the Vertex AI in Firebase SDK. | | [VertexAIOptions](./vertexai.vertexaioptions.md#vertexaioptions_interface) | Options when initializing the Vertex AI in Firebase SDK. | | [VideoMetadata](./vertexai.videometadata.md#videometadata_interface) | Describes the input video content. | @@ -139,7 +139,7 @@ The Vertex AI in Firebase Web SDK. ### getVertexAI(app, options) {:#getvertexai_04094cf} -Returns a [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance for the given app. +Returns a [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance for the given app. Signature: @@ -162,7 +162,7 @@ export declare function getVertexAI(app?: FirebaseApp, options?: VertexAIOptions ### getGenerativeModel(vertexAI, modelParams, requestOptions) {:#getgenerativemodel_e3037c9} -Returns a [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) class with methods for inference and other functionality. +Returns a [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) class with methods for inference and other functionality. Signature: @@ -187,7 +187,7 @@ export declare function getGenerativeModel(vertexAI: VertexAI, modelParams: Mode > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > -Returns an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class with methods for using Imagen. +Returns an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class with methods for using Imagen. Only Imagen 3 models (named `imagen-3.0-*`) are supported. @@ -422,7 +422,7 @@ export declare enum HarmSeverity Aspect ratios for Imagen images. -To specify an aspect ratio for generated images, set the `aspectRatio` property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). +To specify an aspect ratio for generated images, set the `aspectRatio` property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). See the the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) for more details and examples of the supported aspect ratios. @@ -449,7 +449,7 @@ export declare enum ImagenAspectRatio A filter level controlling whether generation of images containing people or faces is allowed. -See the personGeneration documentation for more details. +See the personGeneration documentation for more details. Signature: @@ -533,7 +533,7 @@ export declare enum SchemaType ## VertexAIErrorCode -Standardized error codes that [VertexAIError](./vertexai.vertexaierror.md#vertexaierror_class) can have. +Standardized error codes that [VertexAIError](./vertexai.vertexaierror.md#vertexaierror_class) can have. Signature: diff --git a/docs-devsite/vertexai.modelparams.md b/docs-devsite/vertexai.modelparams.md index 590bc14e435..d3963d240eb 100644 --- a/docs-devsite/vertexai.modelparams.md +++ b/docs-devsite/vertexai.modelparams.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # ModelParams interface -Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). +Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). Signature: diff --git a/docs-devsite/vertexai.objectschemainterface.md b/docs-devsite/vertexai.objectschemainterface.md index 6a4e052d183..4eb7a5d80e7 100644 --- a/docs-devsite/vertexai.objectschemainterface.md +++ b/docs-devsite/vertexai.objectschemainterface.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # ObjectSchemaInterface interface -Interface for [ObjectSchema](./vertexai.objectschema.md#objectschema_class) class. +Interface for [ObjectSchema](./vertexai.objectschema.md#objectschema_class) class. Signature: diff --git a/docs-devsite/vertexai.requestoptions.md b/docs-devsite/vertexai.requestoptions.md index 6d074775520..dcd0c552ecb 100644 --- a/docs-devsite/vertexai.requestoptions.md +++ b/docs-devsite/vertexai.requestoptions.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # RequestOptions interface -Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). +Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). Signature: diff --git a/docs-devsite/vertexai.safetyrating.md b/docs-devsite/vertexai.safetyrating.md index b5f204bef2c..28493bafef0 100644 --- a/docs-devsite/vertexai.safetyrating.md +++ b/docs-devsite/vertexai.safetyrating.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # SafetyRating interface -A safety rating associated with a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface) +A safety rating associated with a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface) Signature: diff --git a/docs-devsite/vertexai.schemainterface.md b/docs-devsite/vertexai.schemainterface.md index 3992c9e5116..c14b561193b 100644 --- a/docs-devsite/vertexai.schemainterface.md +++ b/docs-devsite/vertexai.schemainterface.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # SchemaInterface interface -Interface for [Schema](./vertexai.schema.md#schema_class) class. +Interface for [Schema](./vertexai.schema.md#schema_class) class. Signature: diff --git a/docs-devsite/vertexai.schemaparams.md b/docs-devsite/vertexai.schemaparams.md index 3c6d9f385fd..8e4a41f6bdc 100644 --- a/docs-devsite/vertexai.schemaparams.md +++ b/docs-devsite/vertexai.schemaparams.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # SchemaParams interface -Params passed to [Schema](./vertexai.schema.md#schema_class) static methods to create specific [Schema](./vertexai.schema.md#schema_class) classes. +Params passed to [Schema](./vertexai.schema.md#schema_class) static methods to create specific [Schema](./vertexai.schema.md#schema_class) classes. Signature: diff --git a/docs-devsite/vertexai.schemarequest.md b/docs-devsite/vertexai.schemarequest.md index f12259b1608..c382c2a6297 100644 --- a/docs-devsite/vertexai.schemarequest.md +++ b/docs-devsite/vertexai.schemarequest.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # SchemaRequest interface -Final format for [Schema](./vertexai.schema.md#schema_class) params passed to backend requests. +Final format for [Schema](./vertexai.schema.md#schema_class) params passed to backend requests. Signature: diff --git a/docs-devsite/vertexai.schemashared.md b/docs-devsite/vertexai.schemashared.md index 50cc6464ecf..0764a53bdc0 100644 --- a/docs-devsite/vertexai.schemashared.md +++ b/docs-devsite/vertexai.schemashared.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # SchemaShared interface -Basic [Schema](./vertexai.schema.md#schema_class) properties shared across several Schema-related types. +Basic [Schema](./vertexai.schema.md#schema_class) properties shared across several Schema-related types. Signature: diff --git a/docs-devsite/vertexai.usagemetadata.md b/docs-devsite/vertexai.usagemetadata.md index 5f886dd29f2..176878235d5 100644 --- a/docs-devsite/vertexai.usagemetadata.md +++ b/docs-devsite/vertexai.usagemetadata.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # UsageMetadata interface -Usage metadata about a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). +Usage metadata about a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). Signature: diff --git a/docs-devsite/vertexai.vertexai.md b/docs-devsite/vertexai.vertexai.md index 4797bf8bada..d30d0f7113e 100644 --- a/docs-devsite/vertexai.vertexai.md +++ b/docs-devsite/vertexai.vertexai.md @@ -22,12 +22,12 @@ export interface VertexAI | Property | Type | Description | | --- | --- | --- | -| [app](./vertexai.vertexai.md#vertexaiapp) | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance is associated with. | +| [app](./vertexai.vertexai.md#vertexaiapp) | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance is associated with. | | [location](./vertexai.vertexai.md#vertexailocation) | string | | ## VertexAI.app -The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance is associated with. +The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance is associated with. Signature: diff --git a/docs-devsite/vertexai.vertexaierror.md b/docs-devsite/vertexai.vertexaierror.md index 86532ac6018..31f527e59b3 100644 --- a/docs-devsite/vertexai.vertexaierror.md +++ b/docs-devsite/vertexai.vertexaierror.md @@ -46,7 +46,7 @@ constructor(code: VertexAIErrorCode, message: string, customErrorData?: CustomEr | Parameter | Type | Description | | --- | --- | --- | -| code | [VertexAIErrorCode](./vertexai.md#vertexaierrorcode) | The error code from [VertexAIErrorCode](./vertexai.md#vertexaierrorcode). | +| code | [VertexAIErrorCode](./vertexai.md#vertexaierrorcode) | The error code from [VertexAIErrorCode](./vertexai.md#vertexaierrorcode). | | message | string | A human-readable message describing the error. | | customErrorData | [CustomErrorData](./vertexai.customerrordata.md#customerrordata_interface) \| undefined | Optional error data. | diff --git a/packages/vertexai/src/api.ts b/packages/vertexai/src/api.ts index 752e75c7e23..7843a5bdeee 100644 --- a/packages/vertexai/src/api.ts +++ b/packages/vertexai/src/api.ts @@ -43,7 +43,7 @@ declare module '@firebase/component' { } /** - * Returns a {@link VertexAI} instance for the given app. + * Returns a {@link VertexAI} instance for the given app. * * @public * @@ -63,7 +63,7 @@ export function getVertexAI( } /** - * Returns a {@link GenerativeModel} class with methods for inference + * Returns a {@link GenerativeModel} class with methods for inference * and other functionality. * * @public @@ -83,7 +83,7 @@ export function getGenerativeModel( } /** - * Returns an {@link ImagenModel} class with methods for using Imagen. + * Returns an {@link ImagenModel} class with methods for using Imagen. * * Only Imagen 3 models (named `imagen-3.0-*`) are supported. * diff --git a/packages/vertexai/src/errors.ts b/packages/vertexai/src/errors.ts index b643a5f552d..ad3f9b72f5a 100644 --- a/packages/vertexai/src/errors.ts +++ b/packages/vertexai/src/errors.ts @@ -28,7 +28,7 @@ export class VertexAIError extends FirebaseError { /** * Constructs a new instance of the `VertexAIError` class. * - * @param code - The error code from {@link VertexAIErrorCode}. + * @param code - The error code from {@link VertexAIErrorCode}. * @param message - A human-readable message describing the error. * @param customErrorData - Optional error data. */ diff --git a/packages/vertexai/src/methods/chat-session.ts b/packages/vertexai/src/methods/chat-session.ts index dd22b29a7c8..60794001e37 100644 --- a/packages/vertexai/src/methods/chat-session.ts +++ b/packages/vertexai/src/methods/chat-session.ts @@ -72,7 +72,7 @@ export class ChatSession { /** * Sends a chat message and receives a non-streaming - * {@link GenerateContentResult} + * {@link GenerateContentResult} */ async sendMessage( request: string | Array @@ -126,7 +126,7 @@ export class ChatSession { /** * Sends a chat message and receives the response as a - * {@link GenerateContentStreamResult} containing an iterable stream + * {@link GenerateContentStreamResult} containing an iterable stream * and a response promise. */ async sendMessageStream( diff --git a/packages/vertexai/src/models/generative-model.ts b/packages/vertexai/src/models/generative-model.ts index b4cf464f025..983118bf6ff 100644 --- a/packages/vertexai/src/models/generative-model.ts +++ b/packages/vertexai/src/models/generative-model.ts @@ -74,7 +74,7 @@ export class GenerativeModel extends VertexAIModel { /** * Makes a single non-streaming call to the model - * and returns an object containing a single {@link GenerateContentResponse}. + * and returns an object containing a single {@link GenerateContentResponse}. */ async generateContent( request: GenerateContentRequest | string | Array @@ -121,7 +121,7 @@ export class GenerativeModel extends VertexAIModel { } /** - * Gets a new {@link ChatSession} instance which can be used for + * Gets a new {@link ChatSession} instance which can be used for * multi-turn chats. */ startChat(startChatParams?: StartChatParams): ChatSession { diff --git a/packages/vertexai/src/models/imagen-model.ts b/packages/vertexai/src/models/imagen-model.ts index 89c740852a3..04514ef6ffd 100644 --- a/packages/vertexai/src/models/imagen-model.ts +++ b/packages/vertexai/src/models/imagen-model.ts @@ -63,7 +63,7 @@ export class ImagenModel extends VertexAIModel { safetySettings?: ImagenSafetySettings; /** - * Constructs a new instance of the {@link ImagenModel} class. + * Constructs a new instance of the {@link ImagenModel} class. * * @param vertexAI - An instance of the Vertex AI in Firebase SDK. * @param modelParams - Parameters to use when making requests to Imagen. @@ -88,7 +88,7 @@ export class ImagenModel extends VertexAIModel { * base64-encoded strings. * * @param prompt - A text prompt describing the image(s) to generate. - * @returns A promise that resolves to an {@link ImagenGenerationResponse} + * @returns A promise that resolves to an {@link ImagenGenerationResponse} * object containing the generated images. * * @throws If the request to generate images fails. This happens if the @@ -127,7 +127,7 @@ export class ImagenModel extends VertexAIModel { * @param prompt - A text prompt describing the image(s) to generate. * @param gcsURI - The URI of file stored in a Cloud Storage for Firebase bucket. * This should be a directory. For example, `gs://my-bucket/my-directory/`. - * @returns A promise that resolves to an {@link ImagenGenerationResponse} + * @returns A promise that resolves to an {@link ImagenGenerationResponse} * object containing the URLs of the generated images. * * @throws If the request fails to generate images fails. This happens if diff --git a/packages/vertexai/src/public-types.ts b/packages/vertexai/src/public-types.ts index 280fee9d1cd..fbc5d51084d 100644 --- a/packages/vertexai/src/public-types.ts +++ b/packages/vertexai/src/public-types.ts @@ -25,7 +25,7 @@ export * from './types'; */ export interface VertexAI { /** - * The {@link @firebase/app#FirebaseApp} this {@link VertexAI} instance is associated with. + * The {@link @firebase/app#FirebaseApp} this {@link VertexAI} instance is associated with. */ app: FirebaseApp; location: string; diff --git a/packages/vertexai/src/requests/imagen-image-format.ts b/packages/vertexai/src/requests/imagen-image-format.ts index 283dc80bfaf..b9690a7d39b 100644 --- a/packages/vertexai/src/requests/imagen-image-format.ts +++ b/packages/vertexai/src/requests/imagen-image-format.ts @@ -22,7 +22,7 @@ import { logger } from '../logger'; * * Use this class to specify the desired format (JPEG or PNG) and compression quality * for images generated by Imagen. This is typically included as part of - * {@link ImagenModelParams}. + * {@link ImagenModelParams}. * * @example * ```javascript @@ -49,10 +49,10 @@ export class ImagenImageFormat { } /** - * Creates an {@link ImagenImageFormat} for a JPEG image. + * Creates an {@link ImagenImageFormat} for a JPEG image. * * @param compressionQuality - The level of compression (a number between 0 and 100). - * @returns An {@link ImagenImageFormat} object for a JPEG image. + * @returns An {@link ImagenImageFormat} object for a JPEG image. * * @beta */ @@ -69,9 +69,9 @@ export class ImagenImageFormat { } /** - * Creates an {@link ImagenImageFormat} for a PNG image. + * Creates an {@link ImagenImageFormat} for a PNG image. * - * @returns An {@link ImagenImageFormat} object for a PNG image. + * @returns An {@link ImagenImageFormat} object for a PNG image. * * @beta */ diff --git a/packages/vertexai/src/requests/request-helpers.ts b/packages/vertexai/src/requests/request-helpers.ts index f69c88fca92..fd2cd04e0fd 100644 --- a/packages/vertexai/src/requests/request-helpers.ts +++ b/packages/vertexai/src/requests/request-helpers.ts @@ -127,7 +127,7 @@ export function formatGenerateContentInput( } /** - * Convert the user-defined parameters in {@link ImagenGenerationParams} to the format + * Convert the user-defined parameters in {@link ImagenGenerationParams} to the format * that is expected from the REST API. * * @internal diff --git a/packages/vertexai/src/requests/response-helpers.ts b/packages/vertexai/src/requests/response-helpers.ts index 1ba0986f3f9..6d0e3bf2a0a 100644 --- a/packages/vertexai/src/requests/response-helpers.ts +++ b/packages/vertexai/src/requests/response-helpers.ts @@ -144,7 +144,7 @@ export function getText(response: GenerateContentResponse): string { } /** - * Returns {@link FunctionCall}s associated with first candidate. + * Returns {@link FunctionCall}s associated with first candidate. */ export function getFunctionCalls( response: GenerateContentResponse diff --git a/packages/vertexai/src/types/content.ts b/packages/vertexai/src/types/content.ts index abf5d29222a..ad2906671e4 100644 --- a/packages/vertexai/src/types/content.ts +++ b/packages/vertexai/src/types/content.ts @@ -82,7 +82,7 @@ export interface VideoMetadata { } /** - * Content part interface if the part represents a {@link FunctionCall}. + * Content part interface if the part represents a {@link FunctionCall}. * @public */ export interface FunctionCallPart { @@ -93,7 +93,7 @@ export interface FunctionCallPart { } /** - * Content part interface if the part represents {@link FunctionResponse}. + * Content part interface if the part represents {@link FunctionResponse}. * @public */ export interface FunctionResponsePart { @@ -104,7 +104,7 @@ export interface FunctionResponsePart { } /** - * Content part interface if the part represents {@link FileData} + * Content part interface if the part represents {@link FileData} * @public */ export interface FileDataPart { @@ -116,7 +116,7 @@ export interface FileDataPart { } /** - * A predicted {@link FunctionCall} returned from the model + * A predicted {@link FunctionCall} returned from the model * that contains a string representing the {@link FunctionDeclaration.name} * and a structured JSON object containing the parameters and their values. * @public @@ -127,11 +127,11 @@ export interface FunctionCall { } /** - * The result output from a {@link FunctionCall} that contains a string + * The result output from a {@link FunctionCall} that contains a string * representing the {@link FunctionDeclaration.name} * and a structured JSON object containing any output * from the function is used as context to the model. - * This should contain the result of a {@link FunctionCall} + * This should contain the result of a {@link FunctionCall} * made based on model prediction. * @public */ diff --git a/packages/vertexai/src/types/error.ts b/packages/vertexai/src/types/error.ts index c249320a39e..b1f075101a6 100644 --- a/packages/vertexai/src/types/error.ts +++ b/packages/vertexai/src/types/error.ts @@ -50,7 +50,7 @@ export interface CustomErrorData { /** HTTP status text of the error response. */ statusText?: string; - /** Response from a {@link GenerateContentRequest} */ + /** Response from a {@link GenerateContentRequest} */ response?: GenerateContentResponse; /** Optional additional details about the error. */ @@ -58,7 +58,7 @@ export interface CustomErrorData { } /** - * Standardized error codes that {@link VertexAIError} can have. + * Standardized error codes that {@link VertexAIError} can have. * * @public */ diff --git a/packages/vertexai/src/types/imagen/requests.ts b/packages/vertexai/src/types/imagen/requests.ts index ac37488dfb5..70ae182238e 100644 --- a/packages/vertexai/src/types/imagen/requests.ts +++ b/packages/vertexai/src/types/imagen/requests.ts @@ -18,7 +18,7 @@ import { ImagenImageFormat } from '../../requests/imagen-image-format'; /** - * Parameters for configuring an {@link ImagenModel}. + * Parameters for configuring an {@link ImagenModel}. * * @beta */ @@ -64,20 +64,20 @@ export interface ImagenGenerationConfig { * The number of images to generate. The default value is 1. * * The number of sample images that may be generated in each request depends on the model - * (typically up to 4); see the sampleCount + * (typically up to 4); see the sampleCount * documentation for more details. */ numberOfImages?: number; /** * The aspect ratio of the generated images. The default value is square 1:1. - * Supported aspect ratios depend on the Imagen model, see {@link ImagenAspectRatio} + * Supported aspect ratios depend on the Imagen model, see {@link ImagenAspectRatio} * for more details. */ aspectRatio?: ImagenAspectRatio; /** * The image format of the generated images. The default is PNG. * - * See {@link ImagenImageFormat} for more details. + * See {@link ImagenImageFormat} for more details. */ imageFormat?: ImagenImageFormat; /** @@ -86,7 +86,7 @@ export interface ImagenGenerationConfig { * If set to `true`, an invisible SynthID watermark is embedded in generated images to indicate * that they are AI generated. If set to `false`, watermarking will be disabled. * - * For Imagen 3 models, the default value is `true`; see the addWatermark + * For Imagen 3 models, the default value is `true`; see the addWatermark * documentation for more details. */ addWatermark?: boolean; @@ -129,7 +129,7 @@ export enum ImagenSafetyFilterLevel { /** * A filter level controlling whether generation of images containing people or faces is allowed. * - * See the personGeneration + * See the personGeneration * documentation for more details. * * @beta @@ -181,7 +181,7 @@ export interface ImagenSafetySettings { * Aspect ratios for Imagen images. * * To specify an aspect ratio for generated images, set the `aspectRatio` property in your - * {@link ImagenGenerationConfig}. + * {@link ImagenGenerationConfig}. * * See the the {@link http://firebase.google.com/docs/vertex-ai/generate-images | documentation } * for more details and examples of the supported aspect ratios. diff --git a/packages/vertexai/src/types/imagen/responses.ts b/packages/vertexai/src/types/imagen/responses.ts index c5cf5dd9057..4b093fd550f 100644 --- a/packages/vertexai/src/types/imagen/responses.ts +++ b/packages/vertexai/src/types/imagen/responses.ts @@ -24,7 +24,7 @@ export interface ImagenInlineImage { /** * The MIME type of the image; either `"image/png"` or `"image/jpeg"`. * - * To request a different format, set the `imageFormat` property in your {@link ImagenGenerationConfig}. + * To request a different format, set the `imageFormat` property in your {@link ImagenGenerationConfig}. */ mimeType: string; /** @@ -42,7 +42,7 @@ export interface ImagenGCSImage { /** * The MIME type of the image; either `"image/png"` or `"image/jpeg"`. * - * To request a different format, set the `imageFormat` property in your {@link ImagenGenerationConfig}. + * To request a different format, set the `imageFormat` property in your {@link ImagenGenerationConfig}. */ mimeType: string; /** @@ -72,9 +72,9 @@ export interface ImagenGenerationResponse< * The reason that images were filtered out. This property will only be defined if one * or more images were filtered. * - * Images may be filtered out due to the {@link ImagenSafetyFilterLevel}, - * {@link ImagenPersonFilterLevel}, or filtering included in the model. - * The filter levels may be adjusted in your {@link ImagenSafetySettings}. + * Images may be filtered out due to the {@link ImagenSafetyFilterLevel}, + * {@link ImagenPersonFilterLevel}, or filtering included in the model. + * The filter levels may be adjusted in your {@link ImagenSafetySettings}. * * See the {@link https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen | Responsible AI and usage guidelines for Imagen} * for more details. diff --git a/packages/vertexai/src/types/requests.ts b/packages/vertexai/src/types/requests.ts index 5058b457365..c15258b06d0 100644 --- a/packages/vertexai/src/types/requests.ts +++ b/packages/vertexai/src/types/requests.ts @@ -35,7 +35,7 @@ export interface BaseParams { } /** - * Params passed to {@link getGenerativeModel}. + * Params passed to {@link getGenerativeModel}. * @public */ export interface ModelParams extends BaseParams { @@ -88,9 +88,9 @@ export interface GenerationConfig { responseMimeType?: string; /** * Output response schema of the generated candidate text. This - * value can be a class generated with a {@link Schema} static method + * value can be a class generated with a {@link Schema} static method * like `Schema.string()` or `Schema.object()` or it can be a plain - * JS object matching the {@link SchemaRequest} interface. + * JS object matching the {@link SchemaRequest} interface. *
Note: This only applies when the specified `responseMIMEType` supports a schema; currently * this is limited to `application/json` and `text/x.enum`. */ @@ -119,7 +119,7 @@ export interface CountTokensRequest { */ systemInstruction?: string | Part | Content; /** - * {@link Tool} configuration. + * {@link Tool} configuration. */ tools?: Tool[]; /** @@ -129,7 +129,7 @@ export interface CountTokensRequest { } /** - * Params passed to {@link getGenerativeModel}. + * Params passed to {@link getGenerativeModel}. * @public */ export interface RequestOptions { @@ -189,8 +189,8 @@ export declare interface FunctionDeclarationsTool { * Optional. One or more function declarations * to be passed to the model along with the current user query. Model may * decide to call a subset of these functions by populating - * {@link FunctionCall} in the response. User should - * provide a {@link FunctionResponse} for each + * {@link FunctionCall} in the response. User should + * provide a {@link FunctionResponse} for each * function call in the next turn. Based on the function responses, the model will * generate the final response back to the user. Maximum 64 function * declarations can be provided. diff --git a/packages/vertexai/src/types/responses.ts b/packages/vertexai/src/types/responses.ts index 437d33e9a47..7f68df1e679 100644 --- a/packages/vertexai/src/types/responses.ts +++ b/packages/vertexai/src/types/responses.ts @@ -76,7 +76,7 @@ export interface GenerateContentResponse { } /** - * Usage metadata about a {@link GenerateContentResponse}. + * Usage metadata about a {@link GenerateContentResponse}. * * @public */ @@ -112,7 +112,7 @@ export interface PromptFeedback { } /** - * A candidate returned as part of a {@link GenerateContentResponse}. + * A candidate returned as part of a {@link GenerateContentResponse}. * @public */ export interface GenerateContentCandidate { @@ -126,7 +126,7 @@ export interface GenerateContentCandidate { } /** - * Citation metadata that may be found on a {@link GenerateContentCandidate}. + * Citation metadata that may be found on a {@link GenerateContentCandidate}. * @public */ export interface CitationMetadata { @@ -206,7 +206,7 @@ export interface Date { } /** - * A safety rating associated with a {@link GenerateContentCandidate} + * A safety rating associated with a {@link GenerateContentCandidate} * @public */ export interface SafetyRating { diff --git a/packages/vertexai/src/types/schema.ts b/packages/vertexai/src/types/schema.ts index fca213431ad..5c23655be0e 100644 --- a/packages/vertexai/src/types/schema.ts +++ b/packages/vertexai/src/types/schema.ts @@ -37,7 +37,7 @@ export enum SchemaType { } /** - * Basic {@link Schema} properties shared across several Schema-related + * Basic {@link Schema} properties shared across several Schema-related * types. * @public */ @@ -62,14 +62,14 @@ export interface SchemaShared { } /** - * Params passed to {@link Schema} static methods to create specific - * {@link Schema} classes. + * Params passed to {@link Schema} static methods to create specific + * {@link Schema} classes. * @public */ export interface SchemaParams extends SchemaShared {} /** - * Final format for {@link Schema} params passed to backend requests. + * Final format for {@link Schema} params passed to backend requests. * @public */ export interface SchemaRequest extends SchemaShared { @@ -83,7 +83,7 @@ export interface SchemaRequest extends SchemaShared { } /** - * Interface for {@link Schema} class. + * Interface for {@link Schema} class. * @public */ export interface SchemaInterface extends SchemaShared { @@ -95,7 +95,7 @@ export interface SchemaInterface extends SchemaShared { } /** - * Interface for {@link ObjectSchema} class. + * Interface for {@link ObjectSchema} class. * @public */ export interface ObjectSchemaInterface extends SchemaInterface { From 0cbff6bc7a62f77346ce76c9602b3d572a9af6c0 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Tue, 15 Apr 2025 12:12:12 -0400 Subject: [PATCH 11/77] test(vertexai): update developerapi mock response dir to googleai (#8921) --- packages/vertexai/test-utils/convert-mocks.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/vertexai/test-utils/convert-mocks.ts b/packages/vertexai/test-utils/convert-mocks.ts index 851d6017b6d..8690af4ac72 100644 --- a/packages/vertexai/test-utils/convert-mocks.ts +++ b/packages/vertexai/test-utils/convert-mocks.ts @@ -30,8 +30,7 @@ type BackendName = 'vertexAI' | 'googleAI'; const mockDirs: Record = { vertexAI: join(MOCK_RESPONSES_DIR_PATH, 'vertexai'), - // Note: the dirname is developerapi is legacy. It should be updated to googleai. - googleAI: join(MOCK_RESPONSES_DIR_PATH, 'developerapi') + googleAI: join(MOCK_RESPONSES_DIR_PATH, 'googleai') }; /** From 1363ecc533de0ba5bfcae206a831acc33f9020a6 Mon Sep 17 00:00:00 2001 From: Pavan Shankar Date: Wed, 16 Apr 2025 10:06:31 +0530 Subject: [PATCH 12/77] Fix languageCode parameter in action_code_url (#8912) * Fix languageCode parameter in action_code_url * Add changeset --- .changeset/great-cheetahs-invite.md | 5 +++++ packages/auth/src/core/action_code_url.test.ts | 14 +++++++------- packages/auth/src/core/action_code_url.ts | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 .changeset/great-cheetahs-invite.md diff --git a/.changeset/great-cheetahs-invite.md b/.changeset/great-cheetahs-invite.md new file mode 100644 index 00000000000..f6c8dbd182e --- /dev/null +++ b/.changeset/great-cheetahs-invite.md @@ -0,0 +1,5 @@ +--- +'@firebase/auth': patch +--- + +Fixed: `ActionCodeURL` not populating `languageCode` from the url. diff --git a/packages/auth/src/core/action_code_url.test.ts b/packages/auth/src/core/action_code_url.test.ts index 1f85fd94cc4..1432361e221 100644 --- a/packages/auth/src/core/action_code_url.test.ts +++ b/packages/auth/src/core/action_code_url.test.ts @@ -30,7 +30,7 @@ describe('core/action_code_url', () => { 'oobCode=CODE&mode=signIn&apiKey=API_KEY&' + 'continueUrl=' + encodeURIComponent(continueUrl) + - '&languageCode=en&tenantId=TENANT_ID&state=bla'; + '&lang=en&tenantId=TENANT_ID&state=bla'; const actionCodeUrl = ActionCodeURL.parseLink(actionLink); expect(actionCodeUrl!.operation).to.eq(ActionCodeOperation.EMAIL_SIGNIN); expect(actionCodeUrl!.code).to.eq('CODE'); @@ -46,7 +46,7 @@ describe('core/action_code_url', () => { const actionLink = 'https://www.example.com/finishSignIn?' + 'oobCode=CODE&mode=signIn&apiKey=API_KEY&' + - 'languageCode=en'; + 'lang=en'; const actionCodeUrl = ActionCodeURL.parseLink(actionLink); expect(actionCodeUrl!.operation).to.eq( ActionCodeOperation.EMAIL_SIGNIN @@ -57,7 +57,7 @@ describe('core/action_code_url', () => { const actionLink = 'https://www.example.com/finishSignIn?' + 'oobCode=CODE&mode=verifyAndChangeEmail&apiKey=API_KEY&' + - 'languageCode=en'; + 'lang=en'; const actionCodeUrl = ActionCodeURL.parseLink(actionLink); expect(actionCodeUrl!.operation).to.eq( ActionCodeOperation.VERIFY_AND_CHANGE_EMAIL @@ -68,7 +68,7 @@ describe('core/action_code_url', () => { const actionLink = 'https://www.example.com/finishSignIn?' + 'oobCode=CODE&mode=verifyEmail&apiKey=API_KEY&' + - 'languageCode=en'; + 'lang=en'; const actionCodeUrl = ActionCodeURL.parseLink(actionLink); expect(actionCodeUrl!.operation).to.eq( ActionCodeOperation.VERIFY_EMAIL @@ -79,7 +79,7 @@ describe('core/action_code_url', () => { const actionLink = 'https://www.example.com/finishSignIn?' + 'oobCode=CODE&mode=recoverEmail&apiKey=API_KEY&' + - 'languageCode=en'; + 'lang=en'; const actionCodeUrl = ActionCodeURL.parseLink(actionLink); expect(actionCodeUrl!.operation).to.eq( ActionCodeOperation.RECOVER_EMAIL @@ -90,7 +90,7 @@ describe('core/action_code_url', () => { const actionLink = 'https://www.example.com/finishSignIn?' + 'oobCode=CODE&mode=resetPassword&apiKey=API_KEY&' + - 'languageCode=en'; + 'lang=en'; const actionCodeUrl = ActionCodeURL.parseLink(actionLink); expect(actionCodeUrl!.operation).to.eq( ActionCodeOperation.PASSWORD_RESET @@ -101,7 +101,7 @@ describe('core/action_code_url', () => { const actionLink = 'https://www.example.com/finishSignIn?' + 'oobCode=CODE&mode=revertSecondFactorAddition&apiKey=API_KEY&' + - 'languageCode=en'; + 'lang=en'; const actionCodeUrl = ActionCodeURL.parseLink(actionLink); expect(actionCodeUrl!.operation).to.eq( ActionCodeOperation.REVERT_SECOND_FACTOR_ADDITION diff --git a/packages/auth/src/core/action_code_url.ts b/packages/auth/src/core/action_code_url.ts index f3d5c69bc1f..b7778766a9e 100644 --- a/packages/auth/src/core/action_code_url.ts +++ b/packages/auth/src/core/action_code_url.ts @@ -29,7 +29,7 @@ const enum QueryField { API_KEY = 'apiKey', CODE = 'oobCode', CONTINUE_URL = 'continueUrl', - LANGUAGE_CODE = 'languageCode', + LANGUAGE_CODE = 'lang', MODE = 'mode', TENANT_ID = 'tenantId' } From 20b45d3ab12bfae3ac8761fdf32cb613cc2f3641 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Wed, 23 Apr 2025 14:02:07 -0400 Subject: [PATCH 13/77] test(vertexai): update mock responses to v10 (#8959) --- scripts/update_vertexai_responses.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update_vertexai_responses.sh b/scripts/update_vertexai_responses.sh index de55ac176ce..bf55a645a66 100755 --- a/scripts/update_vertexai_responses.sh +++ b/scripts/update_vertexai_responses.sh @@ -17,7 +17,7 @@ # This script replaces mock response files for Vertex AI unit tests with a fresh # clone of the shared repository of Vertex AI test data. -RESPONSES_VERSION='v8.*' # The major version of mock responses to use +RESPONSES_VERSION='v10.*' # The major version of mock responses to use REPO_NAME="vertexai-sdk-test-data" REPO_LINK="https://github.com/FirebaseExtended/$REPO_NAME.git" From b204e7126bf09cdc551993be6427e2e5dd478fc9 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 24 Apr 2025 08:32:02 -0700 Subject: [PATCH 14/77] Version Packages (#8956) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/cyan-frogs-relate.md | 6 ------ .changeset/gentle-rocks-repeat.md | 6 ------ .changeset/great-cheetahs-invite.md | 5 ----- .changeset/hungry-snails-drive.md | 5 ----- .changeset/odd-wolves-sit.md | 5 ----- .changeset/slow-students-fry.md | 6 ------ integration/compat-interop/package.json | 8 ++++---- integration/firestore/package.json | 4 ++-- integration/messaging/package.json | 2 +- packages/analytics-compat/package.json | 2 +- packages/analytics/package.json | 2 +- packages/app-check-compat/package.json | 2 +- packages/app-check/package.json | 2 +- packages/app-compat/CHANGELOG.md | 7 +++++++ packages/app-compat/package.json | 4 ++-- packages/app/CHANGELOG.md | 6 ++++++ packages/app/package.json | 2 +- packages/auth-compat/CHANGELOG.md | 7 +++++++ packages/auth-compat/package.json | 6 +++--- packages/auth/CHANGELOG.md | 6 ++++++ packages/auth/package.json | 4 ++-- packages/data-connect/CHANGELOG.md | 6 ++++++ packages/data-connect/package.json | 4 ++-- packages/database-compat/package.json | 2 +- packages/database/package.json | 2 +- packages/firebase/CHANGELOG.md | 19 +++++++++++++++++++ packages/firebase/package.json | 16 ++++++++-------- packages/firestore-compat/CHANGELOG.md | 7 +++++++ packages/firestore-compat/package.json | 6 +++--- packages/firestore/CHANGELOG.md | 12 ++++++++++++ packages/firestore/package.json | 8 ++++---- packages/functions-compat/package.json | 2 +- packages/functions/package.json | 2 +- packages/installations-compat/package.json | 2 +- packages/installations/package.json | 2 +- packages/messaging-compat/package.json | 2 +- packages/messaging/package.json | 2 +- packages/performance-compat/package.json | 2 +- packages/performance/package.json | 2 +- packages/remote-config-compat/package.json | 2 +- packages/remote-config/package.json | 2 +- packages/storage-compat/package.json | 4 ++-- packages/storage/package.json | 4 ++-- packages/template/package.json | 2 +- packages/vertexai/package.json | 2 +- repo-scripts/size-analysis/package.json | 2 +- 46 files changed, 125 insertions(+), 88 deletions(-) delete mode 100644 .changeset/cyan-frogs-relate.md delete mode 100644 .changeset/gentle-rocks-repeat.md delete mode 100644 .changeset/great-cheetahs-invite.md delete mode 100644 .changeset/hungry-snails-drive.md delete mode 100644 .changeset/odd-wolves-sit.md delete mode 100644 .changeset/slow-students-fry.md diff --git a/.changeset/cyan-frogs-relate.md b/.changeset/cyan-frogs-relate.md deleted file mode 100644 index 08af27593a9..00000000000 --- a/.changeset/cyan-frogs-relate.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@firebase/firestore': patch -'firebase': patch ---- - -Fixed the `null` value handling in `!=` and `not-in` filters. diff --git a/.changeset/gentle-rocks-repeat.md b/.changeset/gentle-rocks-repeat.md deleted file mode 100644 index 462e36659b8..00000000000 --- a/.changeset/gentle-rocks-repeat.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@firebase/firestore': patch -'firebase': patch ---- - -Fix 'window is not defined' error when calling `clearIndexedDbPersistence` from a service worker diff --git a/.changeset/great-cheetahs-invite.md b/.changeset/great-cheetahs-invite.md deleted file mode 100644 index f6c8dbd182e..00000000000 --- a/.changeset/great-cheetahs-invite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@firebase/auth': patch ---- - -Fixed: `ActionCodeURL` not populating `languageCode` from the url. diff --git a/.changeset/hungry-snails-drive.md b/.changeset/hungry-snails-drive.md deleted file mode 100644 index 1a29782a04d..00000000000 --- a/.changeset/hungry-snails-drive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@firebase/data-connect": patch ---- - -Fix DataConnectOperationError. diff --git a/.changeset/odd-wolves-sit.md b/.changeset/odd-wolves-sit.md deleted file mode 100644 index fc63acc005e..00000000000 --- a/.changeset/odd-wolves-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@firebase/firestore": patch ---- - -Add unique IDs and state information into fatal error messages instead of the generic "unexpected state" message. diff --git a/.changeset/slow-students-fry.md b/.changeset/slow-students-fry.md deleted file mode 100644 index 45f3cf7e576..00000000000 --- a/.changeset/slow-students-fry.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@firebase/firestore': patch -'firebase': patch ---- - -Fix issue where Firestore would produce `undefined` for document snapshot data if using IndexedDB persistence and "clear site data" (or equivalent) button was pressed in the web browser. diff --git a/integration/compat-interop/package.json b/integration/compat-interop/package.json index 547862a7b99..4cd59d2626d 100644 --- a/integration/compat-interop/package.json +++ b/integration/compat-interop/package.json @@ -8,12 +8,12 @@ "test:debug": "karma start --browsers Chrome --auto-watch" }, "dependencies": { - "@firebase/app": "0.11.4", - "@firebase/app-compat": "0.2.53", + "@firebase/app": "0.11.5", + "@firebase/app-compat": "0.2.54", "@firebase/analytics": "0.10.12", "@firebase/analytics-compat": "0.2.18", - "@firebase/auth": "1.10.0", - "@firebase/auth-compat": "0.5.20", + "@firebase/auth": "1.10.1", + "@firebase/auth-compat": "0.5.21", "@firebase/functions": "0.12.3", "@firebase/functions-compat": "0.3.20", "@firebase/messaging": "0.12.17", diff --git a/integration/firestore/package.json b/integration/firestore/package.json index 6f0829cc16e..9ca8917ab4c 100644 --- a/integration/firestore/package.json +++ b/integration/firestore/package.json @@ -14,8 +14,8 @@ "test:memory:debug": "yarn build:memory; karma start --auto-watch --browsers Chrome" }, "dependencies": { - "@firebase/app": "0.11.4", - "@firebase/firestore": "4.7.10" + "@firebase/app": "0.11.5", + "@firebase/firestore": "4.7.11" }, "devDependencies": { "@types/mocha": "9.1.1", diff --git a/integration/messaging/package.json b/integration/messaging/package.json index 4ba2bef35b8..a86c4b1f7b5 100644 --- a/integration/messaging/package.json +++ b/integration/messaging/package.json @@ -9,7 +9,7 @@ "test:manual": "mocha --exit" }, "devDependencies": { - "firebase": "11.6.0", + "firebase": "11.6.1", "chai": "4.5.0", "chromedriver": "119.0.1", "express": "4.21.2", diff --git a/packages/analytics-compat/package.json b/packages/analytics-compat/package.json index 0dfbb6e431b..1064125d5f6 100644 --- a/packages/analytics-compat/package.json +++ b/packages/analytics-compat/package.json @@ -22,7 +22,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.2.53", + "@firebase/app-compat": "0.2.54", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 6b73106cd07..d56f6f7c61e 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -47,7 +47,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check-compat/package.json b/packages/app-check-compat/package.json index e0c922a311c..630e7a8d234 100644 --- a/packages/app-check-compat/package.json +++ b/packages/app-check-compat/package.json @@ -43,7 +43,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.2.53", + "@firebase/app-compat": "0.2.54", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check/package.json b/packages/app-check/package.json index 31d2c734de1..ae6555da970 100644 --- a/packages/app-check/package.json +++ b/packages/app-check/package.json @@ -44,7 +44,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-compat/CHANGELOG.md b/packages/app-compat/CHANGELOG.md index 35e8bd7fd36..d505c7b6240 100644 --- a/packages/app-compat/CHANGELOG.md +++ b/packages/app-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/app-compat +## 0.2.54 + +### Patch Changes + +- Updated dependencies []: + - @firebase/app@0.11.5 + ## 0.2.53 ### Patch Changes diff --git a/packages/app-compat/package.json b/packages/app-compat/package.json index 6437d895d4f..e113c708c74 100644 --- a/packages/app-compat/package.json +++ b/packages/app-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-compat", - "version": "0.2.53", + "version": "0.2.54", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,7 +37,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "@firebase/util": "1.11.0", "@firebase/logger": "0.4.4", "@firebase/component": "0.6.13", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 8e1b0766095..3528eee556d 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,11 @@ # @firebase/app +## 0.11.5 + +### Patch Changes + +- Update SDK_VERSION. + ## 0.11.4 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index dce420d3b30..848919067bd 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app", - "version": "0.11.4", + "version": "0.11.5", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", diff --git a/packages/auth-compat/CHANGELOG.md b/packages/auth-compat/CHANGELOG.md index 81cb295aabc..66b1e0d6e28 100644 --- a/packages/auth-compat/CHANGELOG.md +++ b/packages/auth-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/auth-compat +## 0.5.21 + +### Patch Changes + +- Updated dependencies [[`1363ecc`](https://github.com/firebase/firebase-js-sdk/commit/1363ecc533de0ba5bfcae206a831acc33f9020a6)]: + - @firebase/auth@1.10.1 + ## 0.5.20 ### Patch Changes diff --git a/packages/auth-compat/package.json b/packages/auth-compat/package.json index a10dc65173b..fa69e3f3679 100644 --- a/packages/auth-compat/package.json +++ b/packages/auth-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth-compat", - "version": "0.5.20", + "version": "0.5.21", "description": "FirebaseAuth compatibility package that uses API style compatible with Firebase@8 and prior versions", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,7 +49,7 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/auth": "1.10.0", + "@firebase/auth": "1.10.1", "@firebase/auth-types": "0.13.0", "@firebase/component": "0.6.13", "@firebase/util": "1.11.0", @@ -57,7 +57,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.2.53", + "@firebase/app-compat": "0.2.54", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md index 3eb66ffa508..5a52929a128 100644 --- a/packages/auth/CHANGELOG.md +++ b/packages/auth/CHANGELOG.md @@ -1,5 +1,11 @@ # @firebase/auth +## 1.10.1 + +### Patch Changes + +- [`1363ecc`](https://github.com/firebase/firebase-js-sdk/commit/1363ecc533de0ba5bfcae206a831acc33f9020a6) [#8912](https://github.com/firebase/firebase-js-sdk/pull/8912) - Fixed: `ActionCodeURL` not populating `languageCode` from the url. + ## 1.10.0 ### Minor Changes diff --git a/packages/auth/package.json b/packages/auth/package.json index dde545bb198..6a704a4d4b6 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth", - "version": "1.10.0", + "version": "1.10.1", "description": "The Firebase Authenticaton component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/node/index.js", @@ -131,7 +131,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-strip": "2.1.0", "@types/express": "4.17.21", diff --git a/packages/data-connect/CHANGELOG.md b/packages/data-connect/CHANGELOG.md index da401509f5d..03e17644a77 100644 --- a/packages/data-connect/CHANGELOG.md +++ b/packages/data-connect/CHANGELOG.md @@ -1,5 +1,11 @@ ## Unreleased +## 0.3.4 + +### Patch Changes + +- [`1df3d26`](https://github.com/firebase/firebase-js-sdk/commit/1df3d26fbfb4db24b74d5d779825017e9ec40eaa) [#8898](https://github.com/firebase/firebase-js-sdk/pull/8898) - Fix DataConnectOperationError. + ## 0.3.3 ### Patch Changes diff --git a/packages/data-connect/package.json b/packages/data-connect/package.json index 5b792b1bfe1..00f9bb492cc 100644 --- a/packages/data-connect/package.json +++ b/packages/data-connect/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/data-connect", - "version": "0.3.3", + "version": "0.3.4", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -55,7 +55,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/database-compat/package.json b/packages/database-compat/package.json index 4233e735858..65deedb34e2 100644 --- a/packages/database-compat/package.json +++ b/packages/database-compat/package.json @@ -57,7 +57,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.2.53", + "@firebase/app-compat": "0.2.54", "typescript": "5.5.4" }, "repository": { diff --git a/packages/database/package.json b/packages/database/package.json index 2c86f94adbd..d6f5ddc1707 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -57,7 +57,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/firebase/CHANGELOG.md b/packages/firebase/CHANGELOG.md index ca4de9bb639..e71a10d560f 100644 --- a/packages/firebase/CHANGELOG.md +++ b/packages/firebase/CHANGELOG.md @@ -1,5 +1,24 @@ # firebase +## 11.6.1 + +### Patch Changes + +- [`ed0803a`](https://github.com/firebase/firebase-js-sdk/commit/ed0803a29791cc0cecd0153f95e814ddcee7efd8) [#8915](https://github.com/firebase/firebase-js-sdk/pull/8915) - Fixed the `null` value handling in `!=` and `not-in` filters. + +- [`88a8055`](https://github.com/firebase/firebase-js-sdk/commit/88a8055808bdbd1c75011a94d11062460027d931) [#8888](https://github.com/firebase/firebase-js-sdk/pull/8888) (fixes [#6465](https://github.com/firebase/firebase-js-sdk/issues/6465)) - Fix 'window is not defined' error when calling `clearIndexedDbPersistence` from a service worker + +- [`195d943`](https://github.com/firebase/firebase-js-sdk/commit/195d943103795a50bb3fc5c56ef2bb64610006a1) [#8871](https://github.com/firebase/firebase-js-sdk/pull/8871) (fixes [#8593](https://github.com/firebase/firebase-js-sdk/issues/8593)) - Fix issue where Firestore would produce `undefined` for document snapshot data if using IndexedDB persistence and "clear site data" (or equivalent) button was pressed in the web browser. + +- Updated dependencies [[`ed0803a`](https://github.com/firebase/firebase-js-sdk/commit/ed0803a29791cc0cecd0153f95e814ddcee7efd8), [`88a8055`](https://github.com/firebase/firebase-js-sdk/commit/88a8055808bdbd1c75011a94d11062460027d931), [`1363ecc`](https://github.com/firebase/firebase-js-sdk/commit/1363ecc533de0ba5bfcae206a831acc33f9020a6), [`1df3d26`](https://github.com/firebase/firebase-js-sdk/commit/1df3d26fbfb4db24b74d5d779825017e9ec40eaa), [`e055e90`](https://github.com/firebase/firebase-js-sdk/commit/e055e9057caab4d9f73734307fe4e0be2098249b), [`195d943`](https://github.com/firebase/firebase-js-sdk/commit/195d943103795a50bb3fc5c56ef2bb64610006a1)]: + - @firebase/app@0.11.5 + - @firebase/firestore@4.7.11 + - @firebase/auth@1.10.1 + - @firebase/data-connect@0.3.4 + - @firebase/app-compat@0.2.54 + - @firebase/firestore-compat@0.3.46 + - @firebase/auth-compat@0.5.21 + ## 11.6.0 ### Minor Changes diff --git a/packages/firebase/package.json b/packages/firebase/package.json index 0a108875770..3a4bec301e3 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -1,6 +1,6 @@ { "name": "firebase", - "version": "11.6.0", + "version": "11.6.1", "description": "Firebase JavaScript library for web and Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -399,16 +399,16 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { - "@firebase/app": "0.11.4", - "@firebase/app-compat": "0.2.53", + "@firebase/app": "0.11.5", + "@firebase/app-compat": "0.2.54", "@firebase/app-types": "0.9.3", - "@firebase/auth": "1.10.0", - "@firebase/auth-compat": "0.5.20", - "@firebase/data-connect": "0.3.3", + "@firebase/auth": "1.10.1", + "@firebase/auth-compat": "0.5.21", + "@firebase/data-connect": "0.3.4", "@firebase/database": "1.0.14", "@firebase/database-compat": "2.0.5", - "@firebase/firestore": "4.7.10", - "@firebase/firestore-compat": "0.3.45", + "@firebase/firestore": "4.7.11", + "@firebase/firestore-compat": "0.3.46", "@firebase/functions": "0.12.3", "@firebase/functions-compat": "0.3.20", "@firebase/installations": "0.6.13", diff --git a/packages/firestore-compat/CHANGELOG.md b/packages/firestore-compat/CHANGELOG.md index 87b800344b4..aadd8c532b3 100644 --- a/packages/firestore-compat/CHANGELOG.md +++ b/packages/firestore-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/firestore-compat +## 0.3.46 + +### Patch Changes + +- Updated dependencies [[`ed0803a`](https://github.com/firebase/firebase-js-sdk/commit/ed0803a29791cc0cecd0153f95e814ddcee7efd8), [`88a8055`](https://github.com/firebase/firebase-js-sdk/commit/88a8055808bdbd1c75011a94d11062460027d931), [`e055e90`](https://github.com/firebase/firebase-js-sdk/commit/e055e9057caab4d9f73734307fe4e0be2098249b), [`195d943`](https://github.com/firebase/firebase-js-sdk/commit/195d943103795a50bb3fc5c56ef2bb64610006a1)]: + - @firebase/firestore@4.7.11 + ## 0.3.45 ### Patch Changes diff --git a/packages/firestore-compat/package.json b/packages/firestore-compat/package.json index 35415667824..58210252655 100644 --- a/packages/firestore-compat/package.json +++ b/packages/firestore-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore-compat", - "version": "0.3.45", + "version": "0.3.46", "description": "The Cloud Firestore component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -47,13 +47,13 @@ }, "dependencies": { "@firebase/component": "0.6.13", - "@firebase/firestore": "4.7.10", + "@firebase/firestore": "4.7.11", "@firebase/util": "1.11.0", "@firebase/firestore-types": "3.0.3", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.2.53", + "@firebase/app-compat": "0.2.54", "@types/eslint": "7.29.0", "rollup": "2.79.2", "rollup-plugin-sourcemaps": "0.6.3", diff --git a/packages/firestore/CHANGELOG.md b/packages/firestore/CHANGELOG.md index 26128e8d56a..421f33ed1d5 100644 --- a/packages/firestore/CHANGELOG.md +++ b/packages/firestore/CHANGELOG.md @@ -1,5 +1,17 @@ # @firebase/firestore +## 4.7.11 + +### Patch Changes + +- [`ed0803a`](https://github.com/firebase/firebase-js-sdk/commit/ed0803a29791cc0cecd0153f95e814ddcee7efd8) [#8915](https://github.com/firebase/firebase-js-sdk/pull/8915) - Fixed the `null` value handling in `!=` and `not-in` filters. + +- [`88a8055`](https://github.com/firebase/firebase-js-sdk/commit/88a8055808bdbd1c75011a94d11062460027d931) [#8888](https://github.com/firebase/firebase-js-sdk/pull/8888) (fixes [#6465](https://github.com/firebase/firebase-js-sdk/issues/6465)) - Fix 'window is not defined' error when calling `clearIndexedDbPersistence` from a service worker + +- [`e055e90`](https://github.com/firebase/firebase-js-sdk/commit/e055e9057caab4d9f73734307fe4e0be2098249b) [#8313](https://github.com/firebase/firebase-js-sdk/pull/8313) - Add unique IDs and state information into fatal error messages instead of the generic "unexpected state" message. + +- [`195d943`](https://github.com/firebase/firebase-js-sdk/commit/195d943103795a50bb3fc5c56ef2bb64610006a1) [#8871](https://github.com/firebase/firebase-js-sdk/pull/8871) (fixes [#8593](https://github.com/firebase/firebase-js-sdk/issues/8593)) - Fix issue where Firestore would produce `undefined` for document snapshot data if using IndexedDB persistence and "clear site data" (or equivalent) button was pressed in the web browser. + ## 4.7.10 ### Patch Changes diff --git a/packages/firestore/package.json b/packages/firestore/package.json index 0c9ddeee843..8cc7e5e18f5 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore", - "version": "4.7.10", + "version": "4.7.11", "engines": { "node": ">=18.0.0" }, @@ -110,9 +110,9 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.11.4", - "@firebase/app-compat": "0.2.53", - "@firebase/auth": "1.10.0", + "@firebase/app": "0.11.5", + "@firebase/app-compat": "0.2.54", + "@firebase/auth": "1.10.1", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", "@types/eslint": "7.29.0", diff --git a/packages/functions-compat/package.json b/packages/functions-compat/package.json index 5fe4e7e85ce..c2757fcf130 100644 --- a/packages/functions-compat/package.json +++ b/packages/functions-compat/package.json @@ -29,7 +29,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.2.53", + "@firebase/app-compat": "0.2.54", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/functions/package.json b/packages/functions/package.json index 477fd599ac0..4ddf15ac556 100644 --- a/packages/functions/package.json +++ b/packages/functions/package.json @@ -49,7 +49,7 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/installations-compat/package.json b/packages/installations-compat/package.json index 1814656c070..0f5203dd5d1 100644 --- a/packages/installations-compat/package.json +++ b/packages/installations-compat/package.json @@ -44,7 +44,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app-compat": "0.2.53", + "@firebase/app-compat": "0.2.54", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/installations/package.json b/packages/installations/package.json index cf367ff7954..83db977a6b6 100644 --- a/packages/installations/package.json +++ b/packages/installations/package.json @@ -49,7 +49,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/messaging-compat/package.json b/packages/messaging-compat/package.json index 5e02d85a7d4..388670eb5ab 100644 --- a/packages/messaging-compat/package.json +++ b/packages/messaging-compat/package.json @@ -44,7 +44,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.2.53", + "@firebase/app-compat": "0.2.54", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", "ts-essentials": "9.4.2", diff --git a/packages/messaging/package.json b/packages/messaging/package.json index 93300081e57..5e25b2b1ca0 100644 --- a/packages/messaging/package.json +++ b/packages/messaging/package.json @@ -60,7 +60,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/performance-compat/package.json b/packages/performance-compat/package.json index ea04ce4dda3..69c24f13465 100644 --- a/packages/performance-compat/package.json +++ b/packages/performance-compat/package.json @@ -51,7 +51,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.2.53" + "@firebase/app-compat": "0.2.54" }, "repository": { "directory": "packages/performance-compat", diff --git a/packages/performance/package.json b/packages/performance/package.json index 0fca12f70f9..07e8e60d054 100644 --- a/packages/performance/package.json +++ b/packages/performance/package.json @@ -47,7 +47,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/remote-config-compat/package.json b/packages/remote-config-compat/package.json index 2e840c85238..1055c892435 100644 --- a/packages/remote-config-compat/package.json +++ b/packages/remote-config-compat/package.json @@ -50,7 +50,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.2.53" + "@firebase/app-compat": "0.2.54" }, "repository": { "directory": "packages/remote-config-compat", diff --git a/packages/remote-config/package.json b/packages/remote-config/package.json index e0252a59bca..4262488b0fb 100644 --- a/packages/remote-config/package.json +++ b/packages/remote-config/package.json @@ -48,7 +48,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/storage-compat/package.json b/packages/storage-compat/package.json index 1380b70185b..d829a79cbc5 100644 --- a/packages/storage-compat/package.json +++ b/packages/storage-compat/package.json @@ -44,8 +44,8 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.2.53", - "@firebase/auth-compat": "0.5.20", + "@firebase/app-compat": "0.2.54", + "@firebase/auth-compat": "0.5.21", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/storage/package.json b/packages/storage/package.json index 57b58d0dda3..0a7a8af9b93 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -54,8 +54,8 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.11.4", - "@firebase/auth": "1.10.0", + "@firebase/app": "0.11.5", + "@firebase/auth": "1.10.1", "rollup": "2.79.2", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", diff --git a/packages/template/package.json b/packages/template/package.json index e9f19f330e2..80500aa1392 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -48,7 +48,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/vertexai/package.json b/packages/vertexai/package.json index 9faf562a535..e3472e733f8 100644 --- a/packages/vertexai/package.json +++ b/packages/vertexai/package.json @@ -56,7 +56,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/repo-scripts/size-analysis/package.json b/repo-scripts/size-analysis/package.json index 44a870c3905..f2a4a35bd53 100644 --- a/repo-scripts/size-analysis/package.json +++ b/repo-scripts/size-analysis/package.json @@ -20,7 +20,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.4", + "@firebase/app": "0.11.5", "@firebase/logger": "0.4.4", "@firebase/util": "1.11.0", "@rollup/plugin-commonjs": "21.1.0", From fbf72228d6a3fbb9eab9786c0e2fe2605aa9fa35 Mon Sep 17 00:00:00 2001 From: Mila <107142260+milaGGL@users.noreply.github.com> Date: Fri, 25 Apr 2025 14:39:31 -0400 Subject: [PATCH 15/77] Port BSON code updates from other sdks (#341) --- common/api-review/firestore-lite.api.md | 10 +- common/api-review/firestore.api.md | 10 +- packages/firestore/src/api.ts | 2 +- .../src/index/firestore_index_value_writer.ts | 36 +- packages/firestore/src/model/values.ts | 135 ++- .../test/integration/api/database.test.ts | 328 +++++-- .../test/integration/util/helpers.ts | 90 +- .../test/unit/local/index_manager.test.ts | 80 +- .../unit/local/local_store_indexeddb.test.ts | 832 +++++++++++++++++- .../firestore/test/unit/model/values.test.ts | 2 +- 10 files changed, 1226 insertions(+), 299 deletions(-) diff --git a/common/api-review/firestore-lite.api.md b/common/api-review/firestore-lite.api.md index 04faa9c47c6..394fd0b33ce 100644 --- a/common/api-review/firestore-lite.api.md +++ b/common/api-review/firestore-lite.api.md @@ -88,18 +88,18 @@ export class BsonObjectId { export function bsonObjectId(value: string): BsonObjectId; // @public -export function bsonTimestamp(seconds: number, increment: number): BsonTimestampValue; - -// @public -export class BsonTimestampValue { +export class BsonTimestamp { constructor(seconds: number, increment: number); // (undocumented) readonly increment: number; - isEqual(other: BsonTimestampValue): boolean; + isEqual(other: BsonTimestamp): boolean; // (undocumented) readonly seconds: number; } +// @public +export function bsonTimestamp(seconds: number, increment: number): BsonTimestamp; + // @public export class Bytes { static fromBase64String(base64: string): Bytes; diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 5d3c2286859..39dcdb6df59 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -88,18 +88,18 @@ export class BsonObjectId { export function bsonObjectId(value: string): BsonObjectId; // @public -export function bsonTimestamp(seconds: number, increment: number): BsonTimestampValue; - -// @public -export class BsonTimestampValue { +export class BsonTimestamp { constructor(seconds: number, increment: number); // (undocumented) readonly increment: number; - isEqual(other: BsonTimestampValue): boolean; + isEqual(other: BsonTimestamp): boolean; // (undocumented) readonly seconds: number; } +// @public +export function bsonTimestamp(seconds: number, increment: number): BsonTimestamp; + // @public export class Bytes { static fromBase64String(base64: string): Bytes; diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index 46fb1b3bba3..ec6fdd2c4ea 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -193,7 +193,7 @@ export { BsonBinaryData } from './lite-api/bson_binary_data'; export { BsonObjectId } from './lite-api/bson_object_Id'; -export { BsonTimestampValue } from './lite-api/bson_timestamp_value'; +export { BsonTimestamp } from './lite-api/bson_timestamp_value'; export { MinKey } from './lite-api/min_key'; diff --git a/packages/firestore/src/index/firestore_index_value_writer.ts b/packages/firestore/src/index/firestore_index_value_writer.ts index d02a07313fe..6949ad99b31 100644 --- a/packages/firestore/src/index/firestore_index_value_writer.ts +++ b/packages/firestore/src/index/firestore_index_value_writer.ts @@ -23,12 +23,12 @@ import { } from '../model/normalize'; import { VECTOR_MAP_VECTORS_KEY, - detectSpecialMapType, + detectMapRepresentation, RESERVED_BSON_TIMESTAMP_KEY, RESERVED_REGEX_KEY, RESERVED_BSON_OBJECT_ID_KEY, RESERVED_BSON_BINARY_KEY, - SpecialMapValueType, + MapRepresentation, RESERVED_REGEX_PATTERN_KEY, RESERVED_REGEX_OPTIONS_KEY, RESERVED_INT32_KEY @@ -42,6 +42,10 @@ import { DirectionalIndexByteEncoder } from './directional_index_byte_encoder'; // Note: This file is copied from the backend. Code that is not used by // Firestore was removed. Code that has different behavior was modified. +// The client SDK only supports references to documents from the same database. We can skip the +// first five segments. +const DOCUMENT_NAME_OFFSET = 5; + const INDEX_TYPE_NULL = 5; const INDEX_TYPE_MIN_KEY = 7; const INDEX_TYPE_BOOLEAN = 10; @@ -60,7 +64,7 @@ const INDEX_TYPE_ARRAY = 50; const INDEX_TYPE_VECTOR = 53; const INDEX_TYPE_MAP = 55; const INDEX_TYPE_REFERENCE_SEGMENT = 60; -const INDEX_TYPE_MAX_VALUE = 999; +const INDEX_TYPE_MAX_KEY = 999; // A terminator that indicates that a truncatable value was not truncated. // This must be smaller than all other type labels. @@ -137,24 +141,24 @@ export class FirestoreIndexValueWriter { encoder.writeNumber(geoPoint.latitude || 0); encoder.writeNumber(geoPoint.longitude || 0); } else if ('mapValue' in indexValue) { - const type = detectSpecialMapType(indexValue); - if (type === SpecialMapValueType.INTERNAL_MAX) { + const type = detectMapRepresentation(indexValue); + if (type === MapRepresentation.INTERNAL_MAX) { this.writeValueTypeLabel(encoder, Number.MAX_SAFE_INTEGER); - } else if (type === SpecialMapValueType.VECTOR) { + } else if (type === MapRepresentation.VECTOR) { this.writeIndexVector(indexValue.mapValue!, encoder); - } else if (type === SpecialMapValueType.MAX_KEY) { - this.writeValueTypeLabel(encoder, INDEX_TYPE_MAX_VALUE); - } else if (type === SpecialMapValueType.MIN_KEY) { + } else if (type === MapRepresentation.MAX_KEY) { + this.writeValueTypeLabel(encoder, INDEX_TYPE_MAX_KEY); + } else if (type === MapRepresentation.MIN_KEY) { this.writeValueTypeLabel(encoder, INDEX_TYPE_MIN_KEY); - } else if (type === SpecialMapValueType.BSON_BINARY) { + } else if (type === MapRepresentation.BSON_BINARY) { this.writeIndexBsonBinaryData(indexValue.mapValue!, encoder); - } else if (type === SpecialMapValueType.REGEX) { + } else if (type === MapRepresentation.REGEX) { this.writeIndexRegex(indexValue.mapValue!, encoder); - } else if (type === SpecialMapValueType.BSON_TIMESTAMP) { + } else if (type === MapRepresentation.BSON_TIMESTAMP) { this.writeIndexBsonTimestamp(indexValue.mapValue!, encoder); - } else if (type === SpecialMapValueType.BSON_OBJECT_ID) { + } else if (type === MapRepresentation.BSON_OBJECT_ID) { this.writeIndexBsonObjectId(indexValue.mapValue!, encoder); - } else if (type === SpecialMapValueType.INT32) { + } else if (type === MapRepresentation.INT32) { this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER); encoder.writeNumber( normalizeNumber( @@ -237,7 +241,9 @@ export class FirestoreIndexValueWriter { const segments: string[] = referenceValue .split('/') .filter(segment => segment.length > 0); - const path = DocumentKey.fromSegments(segments.slice(5)).path; + const path = DocumentKey.fromSegments( + segments.slice(DOCUMENT_NAME_OFFSET) + ).path; path.forEach(segment => { this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE_SEGMENT); this.writeUnlabeledIndexString(segment, encoder); diff --git a/packages/firestore/src/model/values.ts b/packages/firestore/src/model/values.ts index 02404130a77..15408892725 100644 --- a/packages/firestore/src/model/values.ts +++ b/packages/firestore/src/model/values.ts @@ -160,7 +160,7 @@ export const MIN_BSON_BINARY_VALUE: Value = { } }; -export enum SpecialMapValueType { +export enum MapRepresentation { REGEX = 'regexValue', BSON_OBJECT_ID = 'bsonObjectIdValue', INT32 = 'int32Value', @@ -195,27 +195,27 @@ export function typeOrder(value: Value): TypeOrder { } else if ('arrayValue' in value) { return TypeOrder.ArrayValue; } else if ('mapValue' in value) { - const valueType = detectSpecialMapType(value); + const valueType = detectMapRepresentation(value); switch (valueType) { - case SpecialMapValueType.SERVER_TIMESTAMP: + case MapRepresentation.SERVER_TIMESTAMP: return TypeOrder.ServerTimestampValue; - case SpecialMapValueType.INTERNAL_MAX: + case MapRepresentation.INTERNAL_MAX: return TypeOrder.MaxValue; - case SpecialMapValueType.VECTOR: + case MapRepresentation.VECTOR: return TypeOrder.VectorValue; - case SpecialMapValueType.REGEX: + case MapRepresentation.REGEX: return TypeOrder.RegexValue; - case SpecialMapValueType.BSON_OBJECT_ID: + case MapRepresentation.BSON_OBJECT_ID: return TypeOrder.BsonObjectIdValue; - case SpecialMapValueType.INT32: + case MapRepresentation.INT32: return TypeOrder.NumberValue; - case SpecialMapValueType.BSON_TIMESTAMP: + case MapRepresentation.BSON_TIMESTAMP: return TypeOrder.BsonTimestampValue; - case SpecialMapValueType.BSON_BINARY: + case MapRepresentation.BSON_BINARY: return TypeOrder.BsonBinaryValue; - case SpecialMapValueType.MIN_KEY: + case MapRepresentation.MIN_KEY: return TypeOrder.MinKeyValue; - case SpecialMapValueType.MAX_KEY: + case MapRepresentation.MAX_KEY: return TypeOrder.MaxKeyValue; default: return TypeOrder.ObjectValue; @@ -319,8 +319,8 @@ function blobEquals(left: Value, right: Value): boolean { export function numberEquals(left: Value, right: Value): boolean { if ( ('integerValue' in left && 'integerValue' in right) || - (detectSpecialMapType(left) === SpecialMapValueType.INT32 && - detectSpecialMapType(right) === SpecialMapValueType.INT32) + (detectMapRepresentation(left) === MapRepresentation.INT32 && + detectMapRepresentation(right) === MapRepresentation.INT32) ) { return extractNumber(left) === extractNumber(right); } else if ('doubleValue' in left && 'doubleValue' in right) { @@ -427,7 +427,7 @@ export function valueCompare(left: Value, right: Value): number { export function extractNumber(value: Value): number { let numberValue; - if (detectSpecialMapType(value) === SpecialMapValueType.INT32) { + if (detectMapRepresentation(value) === MapRepresentation.INT32) { numberValue = value.mapValue!.fields![RESERVED_INT32_KEY].integerValue!; } else { numberValue = value.integerValue || value.doubleValue; @@ -686,10 +686,6 @@ function canonifyValue(value: Value): string { } else if ('arrayValue' in value) { return canonifyArray(value.arrayValue!); } else if ('mapValue' in value) { - // BsonBinaryValue contains an array of bytes, and needs to extract `subtype` and `data` from it before canonifying. - if (detectSpecialMapType(value) === SpecialMapValueType.BSON_BINARY) { - return canonifyBsonBinaryData(value.mapValue!); - } return canonifyMap(value.mapValue!); } else { return fail('Invalid value type: ' + JSON.stringify(value)); @@ -713,19 +709,6 @@ function canonifyReference(referenceValue: string): string { return DocumentKey.fromName(referenceValue).toString(); } -function canonifyBsonBinaryData(mapValue: MapValue): string { - const fields = mapValue!.fields?.[RESERVED_BSON_BINARY_KEY]; - const subtypeAndData = fields?.bytesValue; - if (!subtypeAndData) { - throw new Error('Received incorrect bytesValue for BsonBinaryData'); - } - // Normalize the bytesValue to Uint8Array before extracting subtype and data. - const bytes = normalizeByteString(subtypeAndData).toUint8Array(); - return `{__binary__:{subType:${bytes.at(0)},data:${canonifyByteString( - bytes.slice(1) - )}}}`; -} - function canonifyMap(mapValue: MapValue): string { // Iteration order in JavaScript is not guaranteed. To ensure that we generate // matching canonical IDs for identical maps, we need to sort the keys. @@ -886,9 +869,9 @@ export function isMapValue( return !!value && 'mapValue' in value; } -export function detectSpecialMapType(value: Value): SpecialMapValueType { +export function detectMapRepresentation(value: Value): MapRepresentation { if (!value || !value.mapValue || !value.mapValue.fields) { - return SpecialMapValueType.REGULAR_MAP; // Not a special map type + return MapRepresentation.REGULAR_MAP; // Not a special map type } const fields = value.mapValue.fields; @@ -896,25 +879,31 @@ export function detectSpecialMapType(value: Value): SpecialMapValueType { // Check for type-based mappings const type = fields[TYPE_KEY]?.stringValue; if (type) { - const typeMap: Record = { - [RESERVED_VECTOR_KEY]: SpecialMapValueType.VECTOR, - [RESERVED_MAX_KEY]: SpecialMapValueType.INTERNAL_MAX, - [RESERVED_SERVER_TIMESTAMP_KEY]: SpecialMapValueType.SERVER_TIMESTAMP + const typeMap: Record = { + [RESERVED_VECTOR_KEY]: MapRepresentation.VECTOR, + [RESERVED_MAX_KEY]: MapRepresentation.INTERNAL_MAX, + [RESERVED_SERVER_TIMESTAMP_KEY]: MapRepresentation.SERVER_TIMESTAMP }; if (typeMap[type]) { return typeMap[type]; } } + if (objectSize(fields) !== 1) { + // All BSON types have 1 key in the map. To improve performance, we can + // return early if the number of keys in the map is not 1. + return MapRepresentation.REGULAR_MAP; + } + // Check for BSON-related mappings - const bsonMap: Record = { - [RESERVED_REGEX_KEY]: SpecialMapValueType.REGEX, - [RESERVED_BSON_OBJECT_ID_KEY]: SpecialMapValueType.BSON_OBJECT_ID, - [RESERVED_INT32_KEY]: SpecialMapValueType.INT32, - [RESERVED_BSON_TIMESTAMP_KEY]: SpecialMapValueType.BSON_TIMESTAMP, - [RESERVED_BSON_BINARY_KEY]: SpecialMapValueType.BSON_BINARY, - [RESERVED_MIN_KEY]: SpecialMapValueType.MIN_KEY, - [RESERVED_MAX_KEY]: SpecialMapValueType.MAX_KEY + const bsonMap: Record = { + [RESERVED_REGEX_KEY]: MapRepresentation.REGEX, + [RESERVED_BSON_OBJECT_ID_KEY]: MapRepresentation.BSON_OBJECT_ID, + [RESERVED_INT32_KEY]: MapRepresentation.INT32, + [RESERVED_BSON_TIMESTAMP_KEY]: MapRepresentation.BSON_TIMESTAMP, + [RESERVED_BSON_BINARY_KEY]: MapRepresentation.BSON_BINARY, + [RESERVED_MIN_KEY]: MapRepresentation.MIN_KEY, + [RESERVED_MAX_KEY]: MapRepresentation.MAX_KEY }; for (const key in bsonMap) { @@ -923,20 +912,20 @@ export function detectSpecialMapType(value: Value): SpecialMapValueType { } } - return SpecialMapValueType.REGULAR_MAP; + return MapRepresentation.REGULAR_MAP; } export function isBsonType(value: Value): boolean { const bsonTypes = new Set([ - SpecialMapValueType.REGEX, - SpecialMapValueType.BSON_OBJECT_ID, - SpecialMapValueType.INT32, - SpecialMapValueType.BSON_TIMESTAMP, - SpecialMapValueType.BSON_BINARY, - SpecialMapValueType.MIN_KEY, - SpecialMapValueType.MAX_KEY + MapRepresentation.REGEX, + MapRepresentation.BSON_OBJECT_ID, + MapRepresentation.INT32, + MapRepresentation.BSON_TIMESTAMP, + MapRepresentation.BSON_BINARY, + MapRepresentation.MIN_KEY, + MapRepresentation.MAX_KEY ]); - return bsonTypes.has(detectSpecialMapType(value)); + return bsonTypes.has(detectMapRepresentation(value)); } /** Creates a deep copy of `source`. */ @@ -987,23 +976,23 @@ export function valuesGetLowerBound(value: Value): Value { } else if ('arrayValue' in value) { return { arrayValue: {} }; } else if ('mapValue' in value) { - const type = detectSpecialMapType(value); - if (type === SpecialMapValueType.VECTOR) { + const type = detectMapRepresentation(value); + if (type === MapRepresentation.VECTOR) { return MIN_VECTOR_VALUE; - } else if (type === SpecialMapValueType.BSON_OBJECT_ID) { + } else if (type === MapRepresentation.BSON_OBJECT_ID) { return MIN_BSON_OBJECT_ID_VALUE; - } else if (type === SpecialMapValueType.BSON_TIMESTAMP) { + } else if (type === MapRepresentation.BSON_TIMESTAMP) { return MIN_BSON_TIMESTAMP_VALUE; - } else if (type === SpecialMapValueType.BSON_BINARY) { + } else if (type === MapRepresentation.BSON_BINARY) { return MIN_BSON_BINARY_VALUE; - } else if (type === SpecialMapValueType.REGEX) { + } else if (type === MapRepresentation.REGEX) { return MIN_REGEX_VALUE; - } else if (type === SpecialMapValueType.INT32) { + } else if (type === MapRepresentation.INT32) { // int32Value is treated the same as integerValue and doubleValue return { doubleValue: NaN }; - } else if (type === SpecialMapValueType.MIN_KEY) { + } else if (type === MapRepresentation.MIN_KEY) { return MIN_KEY_VALUE; - } else if (type === SpecialMapValueType.MAX_KEY) { + } else if (type === MapRepresentation.MAX_KEY) { return MAX_KEY_VALUE; } return { mapValue: {} }; @@ -1033,23 +1022,23 @@ export function valuesGetUpperBound(value: Value): Value { } else if ('arrayValue' in value) { return MIN_VECTOR_VALUE; } else if ('mapValue' in value) { - const type = detectSpecialMapType(value); - if (type === SpecialMapValueType.VECTOR) { + const type = detectMapRepresentation(value); + if (type === MapRepresentation.VECTOR) { return { mapValue: {} }; - } else if (type === SpecialMapValueType.BSON_OBJECT_ID) { + } else if (type === MapRepresentation.BSON_OBJECT_ID) { return { geoPointValue: { latitude: -90, longitude: -180 } }; - } else if (type === SpecialMapValueType.BSON_TIMESTAMP) { + } else if (type === MapRepresentation.BSON_TIMESTAMP) { return { stringValue: '' }; - } else if (type === SpecialMapValueType.BSON_BINARY) { + } else if (type === MapRepresentation.BSON_BINARY) { return refValue(DatabaseId.empty(), DocumentKey.empty()); - } else if (type === SpecialMapValueType.REGEX) { + } else if (type === MapRepresentation.REGEX) { return { arrayValue: {} }; - } else if (type === SpecialMapValueType.INT32) { + } else if (type === MapRepresentation.INT32) { // int32Value is treated the same as integerValue and doubleValue return { timestampValue: { seconds: Number.MIN_SAFE_INTEGER } }; - } else if (type === SpecialMapValueType.MIN_KEY) { + } else if (type === MapRepresentation.MIN_KEY) { return { booleanValue: false }; - } else if (type === SpecialMapValueType.MAX_KEY) { + } else if (type === MapRepresentation.MAX_KEY) { return INTERNAL_MAX_VALUE; } return MAX_KEY_VALUE; diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index d4091768e7c..2071b786f24 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -20,7 +20,6 @@ import { Deferred } from '@firebase/util'; import { expect, use } from 'chai'; import chaiAsPromised from 'chai-as-promised'; -import { AutoId } from '../../../src/util/misc'; import { EventsAccumulator } from '../util/events_accumulator'; import { addDoc, @@ -93,7 +92,7 @@ import { checkOnlineAndOfflineResultsMatch, toIds, withTestProjectIdAndCollectionSettings, - checkCacheRoundTrip + assertSDKQueryResultsConsistentWithBackend } from '../util/helpers'; import { DEFAULT_SETTINGS, DEFAULT_PROJECT_ID } from '../util/settings'; @@ -2439,7 +2438,8 @@ apiDescribe('Database', persistence => { }); describe('BSON types', () => { - // TODO(Mila/BSON): simplify the test setup once prod support BSON + // TODO(Mila/BSON): simplify the test setup once prod support BSON and + // remove the cache population after the test helper is updated const NIGHTLY_PROJECT_ID = 'firestore-sdk-nightly'; const settings = { ...DEFAULT_SETTINGS, @@ -2548,7 +2548,10 @@ apiDescribe('Database', persistence => { NIGHTLY_PROJECT_ID, settings, testDocs, - async (coll, db) => { + async coll => { + // Populate the cache with all docs first + await getDocs(coll); + let orderedQuery = query( coll, where('key', '>', bsonObjectId('507f191e810c19729de860ea')), @@ -2560,7 +2563,11 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['b'] ]); - await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); orderedQuery = query( coll, @@ -2576,7 +2583,11 @@ apiDescribe('Database', persistence => { testDocs['b'], testDocs['a'] ]); - await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); } ); }); @@ -2592,7 +2603,10 @@ apiDescribe('Database', persistence => { NIGHTLY_PROJECT_ID, settings, testDocs, - async (coll, db) => { + async coll => { + // Populate the cache with all docs first + await getDocs(coll); + let orderedQuery = query( coll, where('key', '>=', int32(1)), @@ -2604,7 +2618,11 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['b'] ]); - await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); orderedQuery = query( coll, @@ -2617,7 +2635,11 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['a'] ]); - await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); } ); }); @@ -2633,7 +2655,10 @@ apiDescribe('Database', persistence => { NIGHTLY_PROJECT_ID, settings, testDocs, - async (coll, db) => { + async coll => { + // Populate the cache with all docs first + await getDocs(coll); + let orderedQuery = query( coll, where('key', '>', bsonTimestamp(1, 1)), @@ -2645,7 +2670,11 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['b'] ]); - await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); orderedQuery = query( coll, @@ -2658,7 +2687,11 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['b'] ]); - await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); } ); }); @@ -2674,7 +2707,10 @@ apiDescribe('Database', persistence => { NIGHTLY_PROJECT_ID, settings, testDocs, - async (coll, db) => { + async coll => { + // Populate the cache with all docs first + await getDocs(coll); + let orderedQuery = query( coll, where('key', '>', bsonBinaryData(1, new Uint8Array([1, 2, 3]))), @@ -2686,7 +2722,11 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['b'] ]); - await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); orderedQuery = query( coll, @@ -2700,7 +2740,11 @@ apiDescribe('Database', persistence => { testDocs['b'], testDocs['a'] ]); - await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); } ); }); @@ -2716,7 +2760,10 @@ apiDescribe('Database', persistence => { NIGHTLY_PROJECT_ID, settings, testDocs, - async (coll, db) => { + async coll => { + // Populate the cache with all docs first + await getDocs(coll); + const orderedQuery = query( coll, or( @@ -2731,7 +2778,11 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['a'] ]); - await checkCacheRoundTrip(orderedQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); } ); }); @@ -2749,22 +2800,34 @@ apiDescribe('Database', persistence => { NIGHTLY_PROJECT_ID, settings, testDocs, - async (coll, db) => { + async coll => { + // Populate the cache with all docs first + await getDocs(coll); + let filteredQuery = query(coll, where('key', '==', minKey())); let snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ testDocs['a'], testDocs['b'] ]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); - filteredQuery = query(coll, where('key', '!=', minKey())); - snapshot = await getDocs(filteredQuery); - expect(toDataArray(snapshot)).to.deep.equal([ - testDocs['d'], - testDocs['e'] - ]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + // TODO(Mila/BSON): uncomment after the null inclusion bug + // filteredQuery = query(coll, where('key', '!=', minKey())); + // snapshot = await getDocs(filteredQuery); + // expect(toDataArray(snapshot)).to.deep.equal([ + // testDocs['d'], + // testDocs['e'] + // ]); + // await assertSDKQueryResultsConsistentWithBackend( + // filteredQuery, + // testDocs, + // toIds(snapshot) + // ); filteredQuery = query(coll, where('key', '>=', minKey())); snapshot = await getDocs(filteredQuery); @@ -2772,7 +2835,11 @@ apiDescribe('Database', persistence => { testDocs['a'], testDocs['b'] ]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); filteredQuery = query(coll, where('key', '<=', minKey())); snapshot = await getDocs(filteredQuery); @@ -2780,22 +2847,38 @@ apiDescribe('Database', persistence => { testDocs['a'], testDocs['b'] ]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); filteredQuery = query(coll, where('key', '>', minKey())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); filteredQuery = query(coll, where('key', '<', minKey())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); filteredQuery = query(coll, where('key', '<', 1)); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); } ); }); @@ -2813,22 +2896,34 @@ apiDescribe('Database', persistence => { NIGHTLY_PROJECT_ID, settings, testDocs, - async (coll, db) => { + async coll => { + // Populate the cache with all docs first + await getDocs(coll); + let filteredQuery = query(coll, where('key', '==', maxKey())); let snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ testDocs['c'], testDocs['d'] ]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); - filteredQuery = query(coll, where('key', '!=', maxKey())); - snapshot = await getDocs(filteredQuery); - expect(toDataArray(snapshot)).to.deep.equal([ - testDocs['a'], - testDocs['b'] - ]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + // TODO(Mila/BSON): uncomment after the null inclusion bug + // filteredQuery = query(coll, where('key', '!=', maxKey())); + // snapshot = await getDocs(filteredQuery); + // expect(toDataArray(snapshot)).to.deep.equal([ + // testDocs['a'], + // testDocs['b'] + // ]); + // await assertSDKQueryResultsConsistentWithBackend( + // filteredQuery, + // testDocs, + // toIds(snapshot) + // ); filteredQuery = query(coll, where('key', '>=', maxKey())); snapshot = await getDocs(filteredQuery); @@ -2836,7 +2931,11 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['d'] ]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); filteredQuery = query(coll, where('key', '<=', maxKey())); snapshot = await getDocs(filteredQuery); @@ -2844,22 +2943,38 @@ apiDescribe('Database', persistence => { testDocs['c'], testDocs['d'] ]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); filteredQuery = query(coll, where('key', '>', maxKey())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); filteredQuery = query(coll, where('key', '<', maxKey())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); filteredQuery = query(coll, where('key', '>', 1)); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); } ); }); @@ -2878,14 +2993,21 @@ apiDescribe('Database', persistence => { NIGHTLY_PROJECT_ID, settings, testDocs, - async (coll, db) => { + async coll => { + // Populate the cache with all docs first + await getDocs(coll); + let filteredQuery = query(coll, where('key', '==', null)); let snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ testDocs['b'], testDocs['c'] ]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); filteredQuery = query(coll, where('key', '!=', null)); snapshot = await getDocs(filteredQuery); @@ -2894,7 +3016,11 @@ apiDescribe('Database', persistence => { testDocs['d'], testDocs['e'] ]); - await checkCacheRoundTrip(filteredQuery, db, toDataArray(snapshot)); + await assertSDKQueryResultsConsistentWithBackend( + filteredQuery, + testDocs, + toIds(snapshot) + ); } ); }); @@ -2913,7 +3039,7 @@ apiDescribe('Database', persistence => { NIGHTLY_PROJECT_ID, settings, testDocs, - async (coll, db) => { + async coll => { const orderedQuery = query(coll, orderBy('key', 'asc')); const storeEvent = new EventsAccumulator(); @@ -2955,18 +3081,17 @@ apiDescribe('Database', persistence => { b: { key: regex('^foo', 'i') }, c: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) } }; - return withTestDbsSettings( + return withTestProjectIdAndCollectionSettings( persistence, NIGHTLY_PROJECT_ID, settings, - 1, - async dbs => { - const coll = collection(dbs[0], AutoId.newId()); + {}, + async (coll, db) => { const docA = await addDoc(coll, testDocs['a']); const docB = await addDoc(coll, { key: 'place holder' }); const docC = await addDoc(coll, testDocs['c']); - await runTransaction(dbs[0], async transaction => { + await runTransaction(db, async transaction => { const docSnapshot = await transaction.get(docA); expect(docSnapshot.data()).to.deep.equal(testDocs['a']); transaction.set(docB, testDocs['b']); @@ -3018,30 +3143,37 @@ apiDescribe('Database', persistence => { // TODO(Mila/BSON): remove after prod supports bson, and use `ref` helper function instead const docRef = doc(coll, 'doc'); await setDoc(doc(coll, 'm'), { key: docRef }); + testDocs['m'] = { key: docRef }; + + // Populate the cache with all docs first + await getDocs(coll); const orderedQuery = query(coll, orderBy('key', 'desc')); - await checkOnlineAndOfflineResultsMatch( + await assertSDKQueryResultsConsistentWithBackend( orderedQuery, - 't', - 's', - 'r', - 'q', - 'p', - 'o', - 'n', - 'm', - 'l', - 'k', - 'j', - 'i', - 'h', - 'g', - 'f', - 'e', - 'd', - 'c', - 'b', - 'a' + testDocs, + [ + 't', + 's', + 'r', + 'q', + 'p', + 'o', + 'n', + 'm', + 'l', + 'k', + 'j', + 'i', + 'h', + 'g', + 'f', + 'e', + 'd', + 'c', + 'b', + 'a' + ] ); } ); @@ -3076,28 +3208,34 @@ apiDescribe('Database', persistence => { settings, testDocs, async coll => { + // Populate the cache with all docs first + await getDocs(coll); + const orderedQuery = query(coll, orderBy('key')); - await checkOnlineAndOfflineResultsMatch( + await assertSDKQueryResultsConsistentWithBackend( orderedQuery, - 'r', - 's', - 'd', - 'e', - 'c', - 'f', - 'h', - 'g', - 'j', - 'i', - 'k', - 'n', - 'm', - 'l', - 'q', - 'o', - 'p', - 'a', - 'b' + testDocs, + [ + 'r', + 's', + 'd', + 'e', + 'c', + 'f', + 'h', + 'g', + 'j', + 'i', + 'k', + 'n', + 'm', + 'l', + 'q', + 'o', + 'p', + 'a', + 'b' + ] ); } ); diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 2c789ec9151..092721e113c 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -18,6 +18,7 @@ import { isIndexedDBAvailable } from '@firebase/util'; import { expect } from 'chai'; +import { EventsAccumulator } from './events_accumulator'; import { clearIndexedDbPersistence, collection, @@ -45,8 +46,7 @@ import { getDocsFromServer, getDocsFromCache, _AutoId, - disableNetwork, - enableNetwork + onSnapshot } from './firebase_export'; import { ALT_PROJECT_ID, @@ -596,32 +596,74 @@ export async function checkOnlineAndOfflineResultsMatch( } /** - * Checks that documents fetched from the server and stored in the cache can be - * successfully retrieved from the cache and matches the expected documents. - * - * This function performs the following steps: - * 1. Fetch documents from the server for provided query and populate the cache. - * 2. Disables the network connection to simulate offline mode. - * 3. Retrieves the documents from the cache using the same query. - * 4. Compares the cached documents with the expected documents. - * - * @param query The query to check. - * @param db The Firestore database instance. - * @param expectedDocs Optional ordered list of document data that are expected to be retrieved from the cache. + * Asserts that the given query produces the expected result for all of the + * following scenarios: + * 1. Performing the given query using source=server, compare with expected result and populate + * cache. + * 2. Performing the given query using source=cache, compare with server result and expected + * result. + * 3. Using a snapshot listener to raise snapshots from cache and server, compare them with + * expected result. + * @param {firebase.firestore.Query} query The query to test. + * @param {Object>} allData A map of document IDs to their data. + * @param {string[]} expectedDocIds An array of expected document IDs in the result. + * @returns {Promise} A Promise that resolves when the assertions are complete. */ -export async function checkCacheRoundTrip( +export async function assertSDKQueryResultsConsistentWithBackend( query: Query, - db: Firestore, - expectedDocs: DocumentData[] + allData: { [key: string]: DocumentData }, + expectedDocIds: string[] ): Promise { - await getDocsFromServer(query); + // Check the cache round trip first to make sure cache is properly populated, otherwise the + // snapshot listener below will return partial results from previous + // "assertSDKQueryResultsConsistentWithBackend" calls if it is called multiple times in one test + await checkOnlineAndOfflineResultsMatch(query, ...expectedDocIds); + + const eventAccumulator = new EventsAccumulator(); + const unsubscribe = onSnapshot( + query, + { includeMetadataChanges: true }, + eventAccumulator.storeEvent + ); + let watchSnapshots; + try { + watchSnapshots = await eventAccumulator.awaitEvents(2); + } finally { + unsubscribe(); + } - await disableNetwork(db); - const docsFromCache = await getDocsFromCache(query); + expect(watchSnapshots[0].metadata.fromCache).to.be.true; + verifySnapshot(watchSnapshots[0], allData, expectedDocIds); + expect(watchSnapshots[1].metadata.fromCache).to.be.false; + verifySnapshot(watchSnapshots[1], allData, expectedDocIds); +} - if (expectedDocs.length !== 0) { - expect(expectedDocs).to.deep.equal(toDataArray(docsFromCache)); +/** + * Verifies that a QuerySnapshot matches the expected data and document IDs. + * @param {firebase.firestore.QuerySnapshot} snapshot The QuerySnapshot to verify. + * @param {Object>} allData A map of document IDs to their data. + * @param {string[]} expectedDocIds An array of expected document IDs in the result. + */ +function verifySnapshot( + snapshot: QuerySnapshot, + allData: { [key: string]: DocumentData }, + expectedDocIds: string[] +): void { + const snapshotDocIds = toIds(snapshot); + expect( + expectedDocIds.length === snapshotDocIds.length, + `Did not get the same document size. Expected doc size: ${expectedDocIds.length}, Actual doc size: ${snapshotDocIds.length} ` + ).to.be.true; + + expect( + expectedDocIds.every((id, index) => id === snapshotDocIds[index]), + `Did not get the expected document IDs. Expected doc IDs: ${expectedDocIds}, Actual doc IDs: ${snapshotDocIds} ` + ).to.be.true; + + const actualDocs = toDataMap(snapshot); + for (const docId of expectedDocIds) { + const expectedDoc = allData[docId]; + const actualDoc = actualDocs[docId]; + expect(expectedDoc).to.deep.equal(actualDoc); } - - await enableNetwork(db); } diff --git a/packages/firestore/test/unit/local/index_manager.test.ts b/packages/firestore/test/unit/local/index_manager.test.ts index 51bef76b31e..1097f5e682e 100644 --- a/packages/firestore/test/unit/local/index_manager.test.ts +++ b/packages/firestore/test/unit/local/index_manager.test.ts @@ -2371,64 +2371,64 @@ describe('IndexedDbIndexManager', async () => { await indexManager.addFieldIndex( fieldIndex('coll', { fields: [['key', IndexKind.DESCENDING]] }) ); - await addDoc('coll/a', { + await addDoc('coll/doc1', { key: null }); - await addDoc('coll/b', { + await addDoc('coll/doc2', { key: minKey() }); - await addDoc('coll/c', { + await addDoc('coll/doc3', { key: true }); - await addDoc('coll/d', { + await addDoc('coll/doc4', { key: NaN }); - await addDoc('coll/e', { + await addDoc('coll/doc5', { key: int32(1) }); - await addDoc('coll/f', { + await addDoc('coll/doc6', { key: 2.0 }); - await addDoc('coll/g', { + await addDoc('coll/doc7', { key: 3 }); - await addDoc('coll/h', { + await addDoc('coll/doc8', { key: new Timestamp(100, 123456000) }); - await addDoc('coll/i', { + await addDoc('coll/doc9', { key: bsonTimestamp(1, 2) }); - await addDoc('coll/j', { + await addDoc('coll/doc10', { key: 'string' }); - await addDoc('coll/k', { + await addDoc('coll/doc11', { key: Bytes.fromUint8Array(new Uint8Array([0, 1, 255])) as Bytes }); - await addDoc('coll/l', { + await addDoc('coll/doc12', { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }); - await addDoc('coll/m', { + await addDoc('coll/doc13', { key: ref('coll/doc') }); - await addDoc('coll/n', { + await addDoc('coll/doc14', { key: bsonObjectId('507f191e810c19729de860ea') }); - await addDoc('coll/o', { + await addDoc('coll/doc15', { key: new GeoPoint(0, 1) }); - await addDoc('coll/p', { + await addDoc('coll/doc16', { key: regex('^foo', 'i') }); - await addDoc('coll/q', { + await addDoc('coll/doc17', { key: [1, 2] }); - await addDoc('coll/r', { + await addDoc('coll/doc18', { key: vector([1, 2]) }); - await addDoc('coll/s', { + await addDoc('coll/doc19', { key: { a: 1 } }); - await addDoc('coll/t', { + await addDoc('coll/doc20', { key: maxKey() }); @@ -2438,26 +2438,26 @@ describe('IndexedDbIndexManager', async () => { const q = queryWithAddedOrderBy(query('coll'), orderBy('key', 'desc')); await verifyResults( q, - 'coll/t', - 'coll/s', - 'coll/r', - 'coll/q', - 'coll/p', - 'coll/o', - 'coll/n', - 'coll/m', - 'coll/l', - 'coll/k', - 'coll/j', - 'coll/i', - 'coll/h', - 'coll/g', - 'coll/f', - 'coll/e', - 'coll/d', - 'coll/c', - 'coll/b', - 'coll/a' + 'coll/doc20', + 'coll/doc19', + 'coll/doc18', + 'coll/doc17', + 'coll/doc16', + 'coll/doc15', + 'coll/doc14', + 'coll/doc13', + 'coll/doc12', + 'coll/doc11', + 'coll/doc10', + 'coll/doc9', + 'coll/doc8', + 'coll/doc7', + 'coll/doc6', + 'coll/doc5', + 'coll/doc4', + 'coll/doc3', + 'coll/doc2', + 'coll/doc1' ); }); diff --git a/packages/firestore/test/unit/local/local_store_indexeddb.test.ts b/packages/firestore/test/unit/local/local_store_indexeddb.test.ts index 6f0275ab4ad..0e5afbc2914 100644 --- a/packages/firestore/test/unit/local/local_store_indexeddb.test.ts +++ b/packages/firestore/test/unit/local/local_store_indexeddb.test.ts @@ -18,17 +18,28 @@ import { isIndexedDBAvailable } from '@firebase/util'; import { expect } from 'chai'; -import { serverTimestamp, Timestamp } from '../../../src'; +import { serverTimestamp, Timestamp, GeoPoint } from '../../../src'; import { User } from '../../../src/auth/user'; import { BundleConverterImpl } from '../../../src/core/bundle_impl'; import { LimitType, + newQueryComparator, Query, queryToTarget, queryWithLimit } from '../../../src/core/query'; import { Target } from '../../../src/core/target'; import { TargetId } from '../../../src/core/types'; +import { + bsonBinaryData, + bsonObjectId, + bsonTimestamp, + int32, + maxKey, + minKey, + regex, + vector +} from '../../../src/lite-api/field_value_impl'; import { IndexBackfiller } from '../../../src/local/index_backfiller'; import { LocalStore } from '../../../src/local/local_store'; import { @@ -44,6 +55,7 @@ import { } from '../../../src/local/local_store_impl'; import { Persistence } from '../../../src/local/persistence'; import { DocumentMap } from '../../../src/model/collections'; +import { Document } from '../../../src/model/document'; import { DocumentKey } from '../../../src/model/document_key'; import { FieldIndex, @@ -53,6 +65,7 @@ import { import { Mutation, MutationType } from '../../../src/model/mutation'; import { MutationBatch } from '../../../src/model/mutation_batch'; import { RemoteEvent } from '../../../src/remote/remote_event'; +import { SortedSet } from '../../../src/util/sorted_set'; import { deletedDoc, deleteMutation, @@ -65,8 +78,10 @@ import { orderBy, orFilter, query, + ref, setMutation, - version + version, + blob } from '../../util/helpers'; import { CountingQueryEngine } from './counting_query_engine'; @@ -208,11 +223,20 @@ class AsyncLocalStoreTester { } } - assertQueryReturned(...keys: string[]): void { + assertQueryReturned(query: Query, ...keys: string[]): void { expect(this.lastChanges).to.exist; - for (const k of keys) { - expect(this.lastChanges?.get(key(k))).to.exist; - } + expect(this.lastChanges?.size === keys.length).to.be.true; + + // lastChanges is a DocumentMap sorted by document keys. Re-sort the documents by the query comparator. + let returnedDocs = new SortedSet(newQueryComparator(query)); + this.lastChanges!.forEach((key, doc) => { + returnedDocs = returnedDocs.add(doc); + }); + + let i = 0; + returnedDocs.forEach(doc => { + expect(keys[i++]).to.equal(doc.key.path.toString()); + }); } async backfillIndexes(config?: { @@ -331,7 +355,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(queryMatches); test.assertRemoteDocumentsRead(1, 0); - test.assertQueryReturned('coll/a'); + test.assertQueryReturned(queryMatches, 'coll/a'); await test.applyRemoteEvent( docUpdateRemoteEvent(deletedDoc('coll/a', 0), [targetId]) @@ -340,7 +364,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { // No backfill needed for deleted document. await test.executeQuery(queryMatches); test.assertRemoteDocumentsRead(0, 0); - test.assertQueryReturned(); + test.assertQueryReturned(queryMatches); }); it('Uses Indexes', async () => { @@ -360,7 +384,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(queryMatches); test.assertRemoteDocumentsRead(1, 0); - test.assertQueryReturned('coll/a'); + test.assertQueryReturned(queryMatches, 'coll/a'); }); it('Uses Partially Indexed Remote Documents When Available', async () => { @@ -384,7 +408,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(queryMatches); test.assertRemoteDocumentsRead(1, 1); - test.assertQueryReturned('coll/a', 'coll/b'); + test.assertQueryReturned(queryMatches, 'coll/a', 'coll/b'); }); it('Uses Partially Indexed Overlays When Available', async () => { @@ -405,7 +429,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { [key('coll/a').toString()]: MutationType.Set, [key('coll/b').toString()]: MutationType.Set }); - test.assertQueryReturned('coll/a', 'coll/b'); + test.assertQueryReturned(queryMatches, 'coll/a', 'coll/b'); }); it('Does Not Use Limit When Index Is Outdated', async () => { @@ -443,7 +467,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { test.assertOverlaysRead(5, 1, { [key('coll/b').toString()]: MutationType.Delete }); - test.assertQueryReturned('coll/a', 'coll/c'); + test.assertQueryReturned(queryCount, 'coll/a', 'coll/c'); }); it('Uses Index For Limit Query When Index Is Updated', async () => { @@ -476,7 +500,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(queryCount); test.assertRemoteDocumentsRead(2, 0); test.assertOverlaysRead(2, 0, {}); - test.assertQueryReturned('coll/a', 'coll/c'); + test.assertQueryReturned(queryCount, 'coll/a', 'coll/c'); }); it('Indexes Server Timestamps', async () => { @@ -496,7 +520,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { test.assertOverlaysRead(1, 0, { [key('coll/a').toString()]: MutationType.Set }); - test.assertQueryReturned('coll/a'); + test.assertQueryReturned(queryTime, 'coll/a'); }); it('can auto-create indexes', async () => { @@ -522,7 +546,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { // Full matched index should be created. await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e'); await test.backfillIndexes(); @@ -532,7 +556,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(query_); test.assertRemoteDocumentsRead(2, 1); - test.assertQueryReturned('coll/a', 'coll/e', 'coll/f'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e', 'coll/f'); }); it('can auto-create indexes works with or query', async () => { @@ -561,7 +585,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { // Full matched index should be created. await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e'); await test.backfillIndexes(); @@ -571,7 +595,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(query_); test.assertRemoteDocumentsRead(2, 1); - test.assertQueryReturned('coll/a', 'coll/e', 'coll/f'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e', 'coll/f'); }); it('does not auto-create indexes for small collections', async () => { @@ -597,7 +621,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { // SDK will not create indexes since collection size is too small. await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/e', 'coll/a'); await test.backfillIndexes(); @@ -607,7 +631,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 3); - test.assertQueryReturned('coll/a', 'coll/e', 'coll/f'); + test.assertQueryReturned(query_, 'coll/e', 'coll/f', 'coll/a'); }); it('does not auto create indexes when index lookup is expensive', async () => { @@ -632,7 +656,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { // SDK will not create indexes since relative read cost is too large. await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e'); await test.backfillIndexes(); @@ -642,7 +666,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 3); - test.assertQueryReturned('coll/a', 'coll/e', 'coll/f'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e', 'coll/f'); }); it('index auto creation works when backfiller runs halfway', async () => { @@ -680,7 +704,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { // Full matched index should be created. await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e'); await test.backfillIndexes({ maxDocumentsToProcess: 2 }); @@ -692,7 +716,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(query_); test.assertRemoteDocumentsRead(1, 2); - test.assertQueryReturned('coll/a', 'coll/e', 'coll/f'); + test.assertQueryReturned(query_, 'coll/a', 'coll/f', 'coll/e'); }); it('index created by index auto creation exists after turn off auto creation', async () => { @@ -718,7 +742,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { // Full matched index should be created. await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/e', 'coll/a'); test.configureIndexAutoCreation({ isEnabled: false }); await test.backfillIndexes(); @@ -729,7 +753,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(query_); test.assertRemoteDocumentsRead(2, 1); - test.assertQueryReturned('coll/a', 'coll/e', 'coll/f'); + test.assertQueryReturned(query_, 'coll/e', 'coll/a', 'coll/f'); }); it('disable index auto creation works', async () => { @@ -757,13 +781,13 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { // Full matched index should be created. await test.executeQuery(query1); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query1, 'coll/a', 'coll/e'); test.configureIndexAutoCreation({ isEnabled: false }); await test.backfillIndexes(); await test.executeQuery(query1); test.assertRemoteDocumentsRead(2, 0); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query1, 'coll/a', 'coll/e'); const targetId2 = await test.allocateQuery(query2); await test.applyRemoteEvents( @@ -776,14 +800,14 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(query2); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('foo/a', 'foo/e'); + test.assertQueryReturned(query2, 'foo/a', 'foo/e'); await test.backfillIndexes(); // Run the query in second time, test index won't be created await test.executeQuery(query2); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('foo/a', 'foo/e'); + test.assertQueryReturned(query2, 'foo/a', 'foo/e'); }); it('index auto creation works with mutation', async () => { @@ -811,7 +835,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e'); await test.writeMutations(deleteMutation('coll/e')); await test.backfillIndexes(); @@ -820,7 +844,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(query_); test.assertRemoteDocumentsRead(1, 0); test.assertOverlaysRead(1, 1); - test.assertQueryReturned('coll/a', 'coll/f'); + test.assertQueryReturned(query_, 'coll/a', 'coll/f'); }); it('delete all indexes works with index auto creation', async () => { @@ -847,24 +871,24 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { // Full matched index should be created. await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e'); await test.backfillIndexes(); await test.executeQuery(query_); test.assertRemoteDocumentsRead(2, 0); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e'); await test.deleteAllFieldIndexes(); await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e'); // Field index is created again. await test.backfillIndexes(); await test.executeQuery(query_); test.assertRemoteDocumentsRead(2, 0); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e'); }); it('delete all indexes works with manual added indexes', async () => { @@ -884,13 +908,13 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.executeQuery(query_); test.assertRemoteDocumentsRead(1, 0); - test.assertQueryReturned('coll/a'); + test.assertQueryReturned(query_, 'coll/a'); await test.deleteAllFieldIndexes(); await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 1); - test.assertQueryReturned('coll/a'); + test.assertQueryReturned(query_, 'coll/a'); }); it('index auto creation does not work with multiple inequality', async () => { @@ -930,11 +954,739 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { // support multiple inequality. await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e'); await test.backfillIndexes(); await test.executeQuery(query_); test.assertRemoteDocumentsRead(0, 2); - test.assertQueryReturned('coll/a', 'coll/e'); + test.assertQueryReturned(query_, 'coll/a', 'coll/e'); + }); + + describe('BSON type indexing', () => { + it('Indexes BSON ObjectId fields', async () => { + const index = fieldIndex('coll', { + id: 1, + fields: [['key', IndexKind.ASCENDING]] + }); + await test.configureFieldsIndexes(index); + + await test.writeMutations( + setMutation('coll/a', { + key: bsonObjectId('507f191e810c19729de860ea') + }), + setMutation('coll/b', { + key: bsonObjectId('507f191e810c19729de860eb') + }), + setMutation('coll/c', { key: bsonObjectId('507f191e810c19729de860ec') }) + ); + await test.backfillIndexes(); + + let query_ = query('coll', orderBy('key', 'asc')); + await test.executeQuery(query_); + test.assertOverlaysRead(3, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b', 'coll/c'); + + query_ = query( + 'coll', + filter('key', '==', bsonObjectId('507f191e810c19729de860ea')) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/a').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a'); + + query_ = query( + 'coll', + filter('key', '!=', bsonObjectId('507f191e810c19729de860ea')) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/b', 'coll/c'); + + query_ = query( + 'coll', + filter('key', '>=', bsonObjectId('507f191e810c19729de860eb')) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/b', 'coll/c'); + + query_ = query( + 'coll', + filter('key', '<', bsonObjectId('507f191e810c19729de860ea')) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0); + test.assertQueryReturned(query_); + + query_ = query( + 'coll', + filter('key', 'in', [ + bsonObjectId('507f191e810c19729de860ea'), + bsonObjectId('507f191e810c19729de860eb') + ]) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query( + 'coll', + filter('key', 'not-in', [ + bsonObjectId('507f191e810c19729de860ea'), + bsonObjectId('507f191e810c19729de860eb') + ]) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/c'); + }); + + it('Indexes BSON Timestamp fields', async () => { + const index = fieldIndex('coll', { + id: 1, + fields: [['key', IndexKind.ASCENDING]] + }); + await test.configureFieldsIndexes(index); + await test.writeMutations( + setMutation('coll/a', { key: bsonTimestamp(1000, 1000) }), + setMutation('coll/b', { key: bsonTimestamp(1001, 1000) }), + setMutation('coll/c', { key: bsonTimestamp(1000, 1001) }) + ); + await test.backfillIndexes(); + + let query_ = query('coll', orderBy('key', 'asc')); + await test.executeQuery(query_); + test.assertOverlaysRead(3, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/c', 'coll/b'); + + query_ = query('coll', filter('key', '==', bsonTimestamp(1000, 1000))); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/a').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a'); + + query_ = query('coll', filter('key', '!=', bsonTimestamp(1000, 1000))); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/c', 'coll/b'); + + query_ = query('coll', filter('key', '>=', bsonTimestamp(1000, 1001))); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/c', 'coll/b'); + + query_ = query('coll', filter('key', '<', bsonTimestamp(1000, 1000))); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0); + test.assertQueryReturned(query_); + + query_ = query( + 'coll', + filter('key', 'in', [ + bsonTimestamp(1000, 1000), + bsonTimestamp(1001, 1000) + ]) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query( + 'coll', + filter('key', 'not-in', [ + bsonTimestamp(1000, 1000), + bsonTimestamp(1001, 1000) + ]) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/c'); + }); + + it('Indexes BSON Binary Data fields', async () => { + const index = fieldIndex('coll', { + id: 1, + fields: [['key', IndexKind.ASCENDING]] + }); + await test.configureFieldsIndexes(index); + await test.writeMutations( + setMutation('coll/a', { + key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + }), + setMutation('coll/b', { + key: bsonBinaryData(1, new Uint8Array([1, 2])) + }), + setMutation('coll/c', { + key: bsonBinaryData(1, new Uint8Array([1, 2, 4])) + }), + setMutation('coll/d', { + key: bsonBinaryData(2, new Uint8Array([1, 2])) + }) + ); + await test.backfillIndexes(); + + let query_ = query('coll', orderBy('key', 'asc')); + await test.executeQuery(query_); + test.assertOverlaysRead(4, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set, + [key('coll/d').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/b', 'coll/a', 'coll/c', 'coll/d'); + + query_ = query( + 'coll', + filter('key', '==', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/a').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a'); + + query_ = query( + 'coll', + filter('key', '!=', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(3, 0, { + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set, + [key('coll/d').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/b', 'coll/c', 'coll/d'); + + query_ = query( + 'coll', + filter('key', '>=', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(3, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set, + [key('coll/d').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/c', 'coll/d'); + + query_ = query( + 'coll', + filter('key', '<', bsonBinaryData(1, new Uint8Array([1, 2]))) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0); + test.assertQueryReturned(query_); + + query_ = query( + 'coll', + filter('key', 'in', [ + bsonBinaryData(1, new Uint8Array([1, 2, 3])), + bsonBinaryData(1, new Uint8Array([1, 2])) + ]) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + // Note that `in` does not add implicit ordering, so the result is ordered by keys + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query( + 'coll', + filter('key', 'not-in', [ + bsonBinaryData(1, new Uint8Array([1, 2, 3])), + bsonBinaryData(1, new Uint8Array([1, 2])) + ]) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/c').toString()]: MutationType.Set, + [key('coll/d').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/c', 'coll/d'); + }); + + it('Indexes BSON Int32 fields', async () => { + const index = fieldIndex('coll', { + id: 1, + fields: [['key', IndexKind.ASCENDING]] + }); + await test.configureFieldsIndexes(index); + await test.writeMutations( + setMutation('coll/a', { key: int32(-1) }), + setMutation('coll/b', { key: int32(0) }), + setMutation('coll/c', { key: int32(1) }) + ); + await test.backfillIndexes(); + + let query_ = query('coll', orderBy('key', 'asc')); + await test.executeQuery(query_); + test.assertOverlaysRead(3, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b', 'coll/c'); + + query_ = query('coll', filter('key', '==', int32(0))); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/b'); + + query_ = query('coll', filter('key', '!=', int32(0))); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/c'); + + query_ = query('coll', filter('key', '>=', int32(0))); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/b', 'coll/c'); + + query_ = query('coll', filter('key', '<', int32(-1))); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0); + test.assertQueryReturned(query_); + + query_ = query('coll', filter('key', 'in', [int32(0), int32(1)])); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/b', 'coll/c'); + + query_ = query('coll', filter('key', 'not-in', [int32(0), int32(1)])); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/a').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a'); + }); + + it('Indexes BSON Regex fields', async () => { + const index = fieldIndex('coll', { + id: 1, + fields: [['key', IndexKind.ASCENDING]] + }); + await test.configureFieldsIndexes(index); + await test.writeMutations( + setMutation('coll/a', { key: regex('a', 'i') }), + setMutation('coll/b', { key: regex('a', 'm') }), + setMutation('coll/c', { key: regex('b', 'i') }) + ); + await test.backfillIndexes(); + + let query_ = query('coll', orderBy('key', 'asc')); + await test.executeQuery(query_); + test.assertOverlaysRead(3, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b', 'coll/c'); + + query_ = query('coll', filter('key', '==', regex('a', 'i'))); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/a').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a'); + + query_ = query('coll', filter('key', '!=', regex('a', 'i'))); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/b', 'coll/c'); + + query_ = query('coll', filter('key', '>=', regex('a', 'm'))); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/b', 'coll/c'); + + query_ = query('coll', filter('key', '<', regex('a', 'i'))); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0); + test.assertQueryReturned(query_); + + query_ = query( + 'coll', + filter('key', 'in', [regex('a', 'i'), regex('a', 'm')]) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query( + 'coll', + filter('key', 'not-in', [regex('a', 'i'), regex('a', 'm')]) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/c'); + }); + + it('Indexes BSON minKey fields', async () => { + const index = fieldIndex('coll', { + id: 1, + fields: [['key', IndexKind.ASCENDING]] + }); + await test.configureFieldsIndexes(index); + await test.writeMutations( + setMutation('coll/a', { key: minKey() }), + setMutation('coll/b', { key: minKey() }), + setMutation('coll/c', { key: null }), + setMutation('coll/d', { key: 1 }), + setMutation('coll/e', { key: maxKey() }) + ); + await test.backfillIndexes(); + + let query_ = query('coll', orderBy('key', 'asc')); + await test.executeQuery(query_); + test.assertOverlaysRead(5, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set, + [key('coll/d').toString()]: MutationType.Set, + [key('coll/e').toString()]: MutationType.Set + }); + test.assertQueryReturned( + query_, + 'coll/c', + 'coll/a', + 'coll/b', + 'coll/d', + 'coll/e' + ); + + query_ = query('coll', filter('key', '==', minKey())); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query('coll', filter('key', '!=', minKey())); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/d').toString()]: MutationType.Set, + [key('coll/e').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/d', 'coll/e'); + + query_ = query('coll', filter('key', '>=', minKey())); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query('coll', filter('key', '<', minKey())); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0, {}); + test.assertQueryReturned(query_); + + query_ = query('coll', filter('key', 'in', [minKey()])); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query('coll', filter('key', 'not-in', [minKey()])); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/d').toString()]: MutationType.Set, + [key('coll/e').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/d', 'coll/e'); + }); + + it('Indexes BSON maxKey fields', async () => { + const index = fieldIndex('coll', { + id: 1, + fields: [['key', IndexKind.ASCENDING]] + }); + await test.configureFieldsIndexes(index); + await test.writeMutations( + setMutation('coll/a', { key: maxKey() }), + setMutation('coll/b', { key: maxKey() }), + setMutation('coll/c', { key: null }), + setMutation('coll/d', { key: 1 }), + setMutation('coll/e', { key: minKey() }) + ); + await test.backfillIndexes(); + + let query_ = query('coll', orderBy('key', 'asc')); + await test.executeQuery(query_); + test.assertOverlaysRead(5, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set, + [key('coll/d').toString()]: MutationType.Set, + [key('coll/e').toString()]: MutationType.Set + }); + test.assertQueryReturned( + query_, + 'coll/c', + 'coll/e', + 'coll/d', + 'coll/a', + 'coll/b' + ); + + query_ = query('coll', filter('key', '==', maxKey())); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query('coll', filter('key', '!=', maxKey())); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/d').toString()]: MutationType.Set, + [key('coll/e').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/e', 'coll/d'); + + query_ = query('coll', filter('key', '<=', maxKey())); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query('coll', filter('key', '>', maxKey())); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0, {}); + test.assertQueryReturned(query_); + + query_ = query('coll', filter('key', '<', maxKey())); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0, {}); + test.assertQueryReturned(query_); + + query_ = query('coll', filter('key', 'in', [maxKey()])); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query('coll', filter('key', 'not-in', [maxKey()])); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/d').toString()]: MutationType.Set, + [key('coll/e').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/e', 'coll/d'); + }); + + it('Indexes multiple BSON types together', async () => { + const index = fieldIndex('coll', { + id: 1, + fields: [['key', IndexKind.DESCENDING]] + }); + await test.configureFieldsIndexes(index); + + await test.writeMutations( + setMutation('coll/a', { key: minKey() }), + setMutation('coll/b', { key: int32(2) }), + setMutation('coll/c', { key: int32(1) }), + setMutation('coll/d', { key: bsonTimestamp(1000, 1001) }), + setMutation('coll/e', { key: bsonTimestamp(1000, 1000) }), + setMutation('coll/f', { + key: bsonBinaryData(1, new Uint8Array([1, 2, 4])) + }), + setMutation('coll/g', { + key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + }), + setMutation('coll/h', { + key: bsonObjectId('507f191e810c19729de860eb') + }), + setMutation('coll/i', { + key: bsonObjectId('507f191e810c19729de860ea') + }), + setMutation('coll/j', { key: regex('^bar', 'm') }), + setMutation('coll/k', { key: regex('^bar', 'i') }), + setMutation('coll/l', { key: maxKey() }) + ); + await test.backfillIndexes(); + + const query_ = query('coll', orderBy('key', 'desc')); + await test.executeQuery(query_); + test.assertOverlaysRead(12, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set, + [key('coll/d').toString()]: MutationType.Set, + [key('coll/e').toString()]: MutationType.Set, + [key('coll/f').toString()]: MutationType.Set, + [key('coll/g').toString()]: MutationType.Set, + [key('coll/h').toString()]: MutationType.Set, + [key('coll/i').toString()]: MutationType.Set, + [key('coll/j').toString()]: MutationType.Set, + [key('coll/k').toString()]: MutationType.Set, + [key('coll/l').toString()]: MutationType.Set + }); + test.assertQueryReturned( + query_, + 'coll/l', + 'coll/j', + 'coll/k', + 'coll/h', + 'coll/i', + 'coll/f', + 'coll/g', + 'coll/d', + 'coll/e', + 'coll/b', + 'coll/c', + 'coll/a' + ); + }); + + it('Indexes all types together', async () => { + const index = fieldIndex('coll', { + id: 1, + fields: [['key', IndexKind.ASCENDING]] + }); + await test.configureFieldsIndexes(index); + + await test.writeMutations( + setMutation('coll/a', { key: null }), + setMutation('coll/b', { key: minKey() }), + setMutation('coll/c', { key: true }), + setMutation('coll/d', { key: NaN }), + setMutation('coll/e', { key: int32(1) }), + setMutation('coll/f', { key: 2.0 }), + setMutation('coll/g', { key: 3 }), + setMutation('coll/h', { key: new Timestamp(100, 123456000) }), + setMutation('coll/i', { key: bsonTimestamp(1, 2) }), + setMutation('coll/j', { key: 'string' }), + setMutation('coll/k', { key: blob(1, 2, 3) }), + setMutation('coll/l', { + key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + }), + setMutation('coll/m', { key: ref('foo/bar') }), + setMutation('coll/n', { + key: bsonObjectId('507f191e810c19729de860ea') + }), + setMutation('coll/o', { key: new GeoPoint(1, 2) }), + setMutation('coll/p', { key: regex('^bar', 'm') }), + setMutation('coll/q', { key: [2, 'foo'] }), + setMutation('coll/r', { key: vector([1, 2, 3]) }), + setMutation('coll/s', { key: { bar: 1, foo: 2 } }), + setMutation('coll/t', { key: maxKey() }) + ); + await test.backfillIndexes(); + + const query_ = query('coll', orderBy('key', 'asc')); + await test.executeQuery(query_); + test.assertOverlaysRead(20, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set, + [key('coll/d').toString()]: MutationType.Set, + [key('coll/e').toString()]: MutationType.Set, + [key('coll/f').toString()]: MutationType.Set, + [key('coll/g').toString()]: MutationType.Set, + [key('coll/h').toString()]: MutationType.Set, + [key('coll/i').toString()]: MutationType.Set, + [key('coll/j').toString()]: MutationType.Set, + [key('coll/k').toString()]: MutationType.Set, + [key('coll/l').toString()]: MutationType.Set, + [key('coll/m').toString()]: MutationType.Set, + [key('coll/n').toString()]: MutationType.Set, + [key('coll/o').toString()]: MutationType.Set, + [key('coll/p').toString()]: MutationType.Set, + [key('coll/q').toString()]: MutationType.Set, + [key('coll/r').toString()]: MutationType.Set, + [key('coll/s').toString()]: MutationType.Set, + [key('coll/t').toString()]: MutationType.Set + }); + test.assertQueryReturned( + query_, + 'coll/a', + 'coll/b', + 'coll/c', + 'coll/d', + 'coll/e', + 'coll/f', + 'coll/g', + 'coll/h', + 'coll/i', + 'coll/j', + 'coll/k', + 'coll/l', + 'coll/m', + 'coll/n', + 'coll/o', + 'coll/p', + 'coll/q', + 'coll/r', + 'coll/s', + 'coll/t' + ); + }); }); }); diff --git a/packages/firestore/test/unit/model/values.test.ts b/packages/firestore/test/unit/model/values.test.ts index 4054dd6481d..0d93d335ded 100644 --- a/packages/firestore/test/unit/model/values.test.ts +++ b/packages/firestore/test/unit/model/values.test.ts @@ -722,7 +722,7 @@ describe('Values', () => { expect(canonicalId(wrap(int32(1)))).to.equal('{__int__:1}'); expect( canonicalId(wrap(bsonBinaryData(1, new Uint8Array([1, 2, 3])))) - ).to.equal('{__binary__:{subType:1,data:AQID}}'); + ).to.equal('{__binary__:AQECAw==}'); expect(canonicalId(wrap(minKey()))).to.equal('{__min__:null}'); expect(canonicalId(wrap(maxKey()))).to.equal('{__max__:null}'); }); From 0e2558a96b1408fa7047b63f3d506939ff1f28ff Mon Sep 17 00:00:00 2001 From: Ehsan Date: Mon, 28 Apr 2025 13:13:42 -0700 Subject: [PATCH 16/77] Improve the integration test coverage for online vs offline comparisons. (#8975) * Improve the integration test coverage for online vs offline comparisons. * prettier. * Add missing cases. --- .../api/composite_index_query.test.ts | 12 ++++++ .../test/integration/api/database.test.ts | 40 ++++++++++++++++--- .../test/integration/api/query.test.ts | 38 +++++++++++++++++- .../util/composite_index_test_helper.ts | 2 + .../test/integration/util/helpers.ts | 29 ++++++++++++-- 5 files changed, 111 insertions(+), 10 deletions(-) diff --git a/packages/firestore/test/integration/api/composite_index_query.test.ts b/packages/firestore/test/integration/api/composite_index_query.test.ts index 04cdafe7169..d08cc77bde9 100644 --- a/packages/firestore/test/integration/api/composite_index_query.test.ts +++ b/packages/firestore/test/integration/api/composite_index_query.test.ts @@ -73,6 +73,7 @@ apiDescribe('Composite Index Queries', persistence => { return testHelper.withTestDocs(persistence, testDocs, async coll => { // a == 1, limit 2, b - desc await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.query( coll, where('a', '==', 1), @@ -97,6 +98,7 @@ apiDescribe('Composite Index Queries', persistence => { return testHelper.withTestDocs(persistence, testDocs, async coll => { // with one inequality: a>2 || b==1. await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.compositeQuery( coll, or(where('a', '>', 2), where('b', '==', 1)) @@ -108,6 +110,7 @@ apiDescribe('Composite Index Queries', persistence => { // Test with limits (implicit order by ASC): (a==1) || (b > 0) LIMIT 2 await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.compositeQuery( coll, or(where('a', '==', 1), where('b', '>', 0)), @@ -120,6 +123,7 @@ apiDescribe('Composite Index Queries', persistence => { // Test with limits (explicit order by): (a==1) || (b > 0) LIMIT_TO_LAST 2 // Note: The public query API does not allow implicit ordering when limitToLast is used. await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.compositeQuery( coll, or(where('a', '==', 1), where('b', '>', 0)), @@ -132,6 +136,7 @@ apiDescribe('Composite Index Queries', persistence => { // Test with limits (explicit order by ASC): (a==2) || (b == 1) ORDER BY a LIMIT 1 await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.compositeQuery( coll, or(where('a', '==', 2), where('b', '==', 1)), @@ -143,6 +148,7 @@ apiDescribe('Composite Index Queries', persistence => { // Test with limits (explicit order by DESC): (a==2) || (b == 1) ORDER BY a LIMIT_TO_LAST 1 await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.compositeQuery( coll, or(where('a', '==', 2), where('b', '==', 1)), @@ -857,12 +863,14 @@ apiDescribe('Composite Index Queries', persistence => { return testHelper.withTestDocs(persistence, testDocs, async coll => { // implicit AND: a != 1 && b < 2 await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.query(coll, where('a', '!=', 1), where('b', '<', 2)), 'doc2' ); // explicit AND: a != 1 && b < 2 await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.compositeQuery( coll, and(where('a', '!=', 1), where('b', '<', 2)) @@ -873,6 +881,7 @@ apiDescribe('Composite Index Queries', persistence => { // explicit AND: a < 3 && b not-in [2, 3] // Implicitly ordered by: a asc, b asc, __name__ asc await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.compositeQuery( coll, and(where('a', '<', 3), where('b', 'not-in', [2, 3])) @@ -884,6 +893,7 @@ apiDescribe('Composite Index Queries', persistence => { // a <3 && b != 0, implicitly ordered by: a asc, b asc, __name__ asc await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.query( coll, where('b', '!=', 0), @@ -896,6 +906,7 @@ apiDescribe('Composite Index Queries', persistence => { // a <3 && b != 0, ordered by: b desc, a desc, __name__ desc await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.query( coll, where('a', '<', 3), @@ -909,6 +920,7 @@ apiDescribe('Composite Index Queries', persistence => { // explicit OR: multiple inequality: a>2 || b<1. await testHelper.assertOnlineAndOfflineResultsMatch( + coll, testHelper.compositeQuery( coll, or(where('a', '>', 2), where('b', '<', 1)) diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index 8cbe99b3cd9..9675e02efeb 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -780,7 +780,11 @@ apiDescribe('Database', persistence => { return withTestCollection(persistence, docs, async randomCol => { const orderedQuery = query(randomCol, orderBy('embedding')); - await checkOnlineAndOfflineResultsMatch(orderedQuery, ...documentIds); + await checkOnlineAndOfflineResultsMatch( + randomCol, + orderedQuery, + ...documentIds + ); const orderedQueryLessThan = query( randomCol, @@ -788,6 +792,7 @@ apiDescribe('Database', persistence => { where('embedding', '<', vector([1, 2, 100, 4, 4])) ); await checkOnlineAndOfflineResultsMatch( + randomCol, orderedQueryLessThan, ...documentIds.slice(2, 11) ); @@ -798,6 +803,7 @@ apiDescribe('Database', persistence => { where('embedding', '>', vector([1, 2, 100, 4, 4])) ); await checkOnlineAndOfflineResultsMatch( + randomCol, orderedQueryGreaterThan, ...documentIds.slice(12, 13) ); @@ -2396,6 +2402,7 @@ apiDescribe('Database', persistence => { 'a' ]; await checkOnlineAndOfflineResultsMatch( + collectionRef, orderedQuery, ...expectedDocs ); @@ -2416,6 +2423,7 @@ apiDescribe('Database', persistence => { 'Aa' ]; await checkOnlineAndOfflineResultsMatch( + collectionRef, filteredQuery, ...expectedDocs ); @@ -2467,7 +2475,11 @@ apiDescribe('Database', persistence => { unsubscribe(); - await checkOnlineAndOfflineResultsMatch(orderedQuery, ...expectedDocs); + await checkOnlineAndOfflineResultsMatch( + collectionRef, + orderedQuery, + ...expectedDocs + ); }); }); @@ -2499,7 +2511,11 @@ apiDescribe('Database', persistence => { unsubscribe(); - await checkOnlineAndOfflineResultsMatch(orderedQuery, ...expectedDocs); + await checkOnlineAndOfflineResultsMatch( + collectionRef, + orderedQuery, + ...expectedDocs + ); }); }); @@ -2531,7 +2547,11 @@ apiDescribe('Database', persistence => { unsubscribe(); - await checkOnlineAndOfflineResultsMatch(orderedQuery, ...expectedDocs); + await checkOnlineAndOfflineResultsMatch( + collectionRef, + orderedQuery, + ...expectedDocs + ); }); }); @@ -2563,7 +2583,11 @@ apiDescribe('Database', persistence => { unsubscribe(); - await checkOnlineAndOfflineResultsMatch(orderedQuery, ...expectedDocs); + await checkOnlineAndOfflineResultsMatch( + collectionRef, + orderedQuery, + ...expectedDocs + ); }); }); @@ -2608,7 +2632,11 @@ apiDescribe('Database', persistence => { unsubscribe(); - await checkOnlineAndOfflineResultsMatch(orderedQuery, ...expectedDocs); + await checkOnlineAndOfflineResultsMatch( + collectionRef, + orderedQuery, + ...expectedDocs + ); }); }); diff --git a/packages/firestore/test/integration/api/query.test.ts b/packages/firestore/test/integration/api/query.test.ts index 5871607eb03..0f3c1c82a2d 100644 --- a/packages/firestore/test/integration/api/query.test.ts +++ b/packages/firestore/test/integration/api/query.test.ts @@ -1357,6 +1357,7 @@ apiDescribe('Queries', persistence => { return withTestCollection(persistence, testDocs, async coll => { // a == 1 await checkOnlineAndOfflineResultsMatch( + coll, query(coll, where('a', '==', 1)), 'doc1', 'doc4', @@ -1365,18 +1366,21 @@ apiDescribe('Queries', persistence => { // Implicit AND: a == 1 && b == 3 await checkOnlineAndOfflineResultsMatch( + coll, query(coll, where('a', '==', 1), where('b', '==', 3)), 'doc4' ); // explicit AND: a == 1 && b == 3 await checkOnlineAndOfflineResultsMatch( + coll, query(coll, and(where('a', '==', 1), where('b', '==', 3))), 'doc4' ); // a == 1, limit 2 await checkOnlineAndOfflineResultsMatch( + coll, query(coll, where('a', '==', 1), limit(2)), 'doc1', 'doc4' @@ -1384,6 +1388,7 @@ apiDescribe('Queries', persistence => { // explicit OR: a == 1 || b == 1 with limit 2 await checkOnlineAndOfflineResultsMatch( + coll, query(coll, or(where('a', '==', 1), where('b', '==', 1)), limit(2)), 'doc1', 'doc2' @@ -1391,6 +1396,7 @@ apiDescribe('Queries', persistence => { // only limit 2 await checkOnlineAndOfflineResultsMatch( + coll, query(coll, limit(2)), 'doc1', 'doc2' @@ -1398,6 +1404,7 @@ apiDescribe('Queries', persistence => { // limit 2 and order by b desc await checkOnlineAndOfflineResultsMatch( + coll, query(coll, limit(2), orderBy('b', 'desc')), 'doc4', 'doc3' @@ -1417,6 +1424,7 @@ apiDescribe('Queries', persistence => { return withTestCollection(persistence, testDocs, async coll => { // Two equalities: a==1 || b==1. await checkOnlineAndOfflineResultsMatch( + coll, query(coll, or(where('a', '==', 1), where('b', '==', 1))), 'doc1', 'doc2', @@ -1426,6 +1434,7 @@ apiDescribe('Queries', persistence => { // (a==1 && b==0) || (a==3 && b==2) await checkOnlineAndOfflineResultsMatch( + coll, query( coll, or( @@ -1439,6 +1448,7 @@ apiDescribe('Queries', persistence => { // a==1 && (b==0 || b==3). await checkOnlineAndOfflineResultsMatch( + coll, query( coll, and( @@ -1452,6 +1462,7 @@ apiDescribe('Queries', persistence => { // (a==2 || b==2) && (a==3 || b==3) await checkOnlineAndOfflineResultsMatch( + coll, query( coll, and( @@ -1464,6 +1475,7 @@ apiDescribe('Queries', persistence => { // Test with limits without orderBy (the __name__ ordering is the tie breaker). await checkOnlineAndOfflineResultsMatch( + coll, query(coll, or(where('a', '==', 2), where('b', '==', 1)), limit(1)), 'doc2' ); @@ -1483,6 +1495,7 @@ apiDescribe('Queries', persistence => { return withTestCollection(persistence, testDocs, async coll => { // a==2 || b in [2,3] await checkOnlineAndOfflineResultsMatch( + coll, query(coll, or(where('a', '==', 2), where('b', 'in', [2, 3]))), 'doc3', 'doc4', @@ -1504,6 +1517,7 @@ apiDescribe('Queries', persistence => { return withTestCollection(persistence, testDocs, async coll => { // a==2 || b array-contains 7 await checkOnlineAndOfflineResultsMatch( + coll, query(coll, or(where('a', '==', 2), where('b', 'array-contains', 7))), 'doc3', 'doc4', @@ -1512,6 +1526,7 @@ apiDescribe('Queries', persistence => { // a==2 || b array-contains-any [0, 3] await checkOnlineAndOfflineResultsMatch( + coll, query( coll, or(where('a', '==', 2), where('b', 'array-contains-any', [0, 3])) @@ -1535,6 +1550,7 @@ apiDescribe('Queries', persistence => { return withTestCollection(persistence, testDocs, async coll => { await checkOnlineAndOfflineResultsMatch( + coll, query( coll, or( @@ -1549,6 +1565,7 @@ apiDescribe('Queries', persistence => { ); await checkOnlineAndOfflineResultsMatch( + coll, query( coll, and( @@ -1560,6 +1577,7 @@ apiDescribe('Queries', persistence => { ); await checkOnlineAndOfflineResultsMatch( + coll, query( coll, or( @@ -1573,6 +1591,7 @@ apiDescribe('Queries', persistence => { ); await checkOnlineAndOfflineResultsMatch( + coll, query( coll, and( @@ -1598,6 +1617,7 @@ apiDescribe('Queries', persistence => { return withTestCollection(persistence, testDocs, async coll => { await checkOnlineAndOfflineResultsMatch( + coll, query( coll, or(where('a', 'in', [2, 3]), where('b', 'array-contains', 3)) @@ -1608,6 +1628,7 @@ apiDescribe('Queries', persistence => { ); await checkOnlineAndOfflineResultsMatch( + coll, query( coll, and(where('a', 'in', [2, 3]), where('b', 'array-contains', 7)) @@ -1616,6 +1637,7 @@ apiDescribe('Queries', persistence => { ); await checkOnlineAndOfflineResultsMatch( + coll, query( coll, or( @@ -1629,6 +1651,7 @@ apiDescribe('Queries', persistence => { ); await checkOnlineAndOfflineResultsMatch( + coll, query( coll, and( @@ -1653,6 +1676,7 @@ apiDescribe('Queries', persistence => { return withTestCollection(persistence, testDocs, async coll => { await checkOnlineAndOfflineResultsMatch( + coll, query(coll, where('a', '==', 1), orderBy('a')), 'doc1', 'doc4', @@ -1660,6 +1684,7 @@ apiDescribe('Queries', persistence => { ); await checkOnlineAndOfflineResultsMatch( + coll, query(coll, where('a', 'in', [2, 3]), orderBy('a')), 'doc6', 'doc3' @@ -1680,6 +1705,7 @@ apiDescribe('Queries', persistence => { return withTestCollection(persistence, testDocs, async coll => { // Two IN operations on different fields with disjunction. await checkOnlineAndOfflineResultsMatch( + coll, query(coll, or(where('a', 'in', [2, 3]), where('b', 'in', [0, 2]))), 'doc1', 'doc3', @@ -1688,6 +1714,7 @@ apiDescribe('Queries', persistence => { // Two IN operations on different fields with conjunction. await checkOnlineAndOfflineResultsMatch( + coll, query(coll, and(where('a', 'in', [2, 3]), where('b', 'in', [0, 2]))), 'doc3' ); @@ -1695,6 +1722,7 @@ apiDescribe('Queries', persistence => { // Two IN operations on the same field. // a IN [1,2,3] && a IN [0,1,4] should result in "a==1". await checkOnlineAndOfflineResultsMatch( + coll, query( coll, and(where('a', 'in', [1, 2, 3]), where('a', 'in', [0, 1, 4])) @@ -1707,6 +1735,7 @@ apiDescribe('Queries', persistence => { // a IN [2,3] && a IN [0,1,4] is never true and so the result should be an // empty set. await checkOnlineAndOfflineResultsMatch( + coll, query( coll, and(where('a', 'in', [2, 3]), where('a', 'in', [0, 1, 4])) @@ -1715,6 +1744,7 @@ apiDescribe('Queries', persistence => { // a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]). await checkOnlineAndOfflineResultsMatch( + coll, query(coll, or(where('a', 'in', [0, 3]), where('a', 'in', [0, 2]))), 'doc3', 'doc6' @@ -1722,6 +1752,7 @@ apiDescribe('Queries', persistence => { // Nested composite filter on the same field. await checkOnlineAndOfflineResultsMatch( + coll, query( coll, and( @@ -1737,6 +1768,7 @@ apiDescribe('Queries', persistence => { // Nested composite filter on the different fields. await checkOnlineAndOfflineResultsMatch( + coll, query( coll, and( @@ -1772,6 +1804,7 @@ apiDescribe('Queries', persistence => { let testQuery = query(coll, where('zip', '!=', 98101)); await checkOnlineAndOfflineResultsMatch( + coll, testQuery, 'a', 'b', @@ -1784,6 +1817,7 @@ apiDescribe('Queries', persistence => { testQuery = query(coll, where('zip', '!=', Number.NaN)); await checkOnlineAndOfflineResultsMatch( + coll, testQuery, 'b', 'c', @@ -1796,6 +1830,7 @@ apiDescribe('Queries', persistence => { testQuery = query(coll, where('zip', '!=', null)); await checkOnlineAndOfflineResultsMatch( + coll, testQuery, 'a', 'b', @@ -1832,6 +1867,7 @@ apiDescribe('Queries', persistence => { where('zip', 'not-in', [98101, 98103, [98101, 98102]]) ); await checkOnlineAndOfflineResultsMatch( + coll, testQuery, 'a', 'b', @@ -1842,7 +1878,7 @@ apiDescribe('Queries', persistence => { ); testQuery = query(coll, where('zip', 'not-in', [null])); - await checkOnlineAndOfflineResultsMatch(testQuery); + await checkOnlineAndOfflineResultsMatch(coll, testQuery); }); }); }); diff --git a/packages/firestore/test/integration/util/composite_index_test_helper.ts b/packages/firestore/test/integration/util/composite_index_test_helper.ts index 5199539768b..a908ed13455 100644 --- a/packages/firestore/test/integration/util/composite_index_test_helper.ts +++ b/packages/firestore/test/integration/util/composite_index_test_helper.ts @@ -162,10 +162,12 @@ export class CompositeIndexTestHelper { // the same as running it while offline. The expected document Ids are hashed to match the // actual document IDs created by the test helper. async assertOnlineAndOfflineResultsMatch( + collection: CollectionReference, query: Query, ...expectedDocs: string[] ): Promise { return checkOnlineAndOfflineResultsMatch( + this.query(collection), query, ...this.toHashedIds(expectedDocs) ); diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 465bc8edd61..b36ed980295 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -541,19 +541,42 @@ export function partitionedTestDocs(partitions: { * documents as running the query while offline. If `expectedDocs` is provided, it also checks * that both online and offline query result is equal to the expected documents. * + * This function first performs a "get" for the entire COLLECTION from the server. + * It then performs the QUERY from CACHE which, results in `executeFullCollectionScan()` + * It then performs the QUERY from SERVER. + * It then performs the QUERY from CACHE again, which results in `performQueryUsingRemoteKeys()`. + * It then ensure that all the above QUERY results are the same. + * + * @param collection The collection on which the query is performed. * @param query The query to check * @param expectedDocs Ordered list of document keys that are expected to match the query */ export async function checkOnlineAndOfflineResultsMatch( + collection: Query, query: Query, ...expectedDocs: string[] ): Promise { + // Note: Order matters. The following has to be done in the specific order: + + // 1- Pre-populate the cache with the entire collection. + await getDocsFromServer(collection); + + // 2- This performs the query against the cache using full collection scan. + const docsFromCacheFullCollectionScan = await getDocsFromCache(query); + + // 3- This goes to the server (backend/emulator). const docsFromServer = await getDocsFromServer(query); + // 4- This performs the query against the cache using remote keys. + const docsFromCacheUsingRemoteKeys = await getDocsFromCache(query); + + expect(toIds(docsFromServer)).to.deep.equal( + toIds(docsFromCacheFullCollectionScan) + ); + expect(toIds(docsFromServer)).to.deep.equal( + toIds(docsFromCacheUsingRemoteKeys) + ); if (expectedDocs.length !== 0) { expect(expectedDocs).to.deep.equal(toIds(docsFromServer)); } - - const docsFromCache = await getDocsFromCache(query); - expect(toIds(docsFromServer)).to.deep.equal(toIds(docsFromCache)); } From dd6a8f076cb06920a2cff3b9adb78a85624ad64d Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Tue, 29 Apr 2025 09:31:41 -0400 Subject: [PATCH 17/77] fix(vertexai): add missing quote to chat role error message (#8979) --- packages/vertexai/src/methods/chat-session-helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vertexai/src/methods/chat-session-helpers.ts b/packages/vertexai/src/methods/chat-session-helpers.ts index 899db4f626a..2106a40b90b 100644 --- a/packages/vertexai/src/methods/chat-session-helpers.ts +++ b/packages/vertexai/src/methods/chat-session-helpers.ts @@ -111,7 +111,7 @@ export function validateChatHistory(history: Content[]): void { if (!validPreviousContentRoles.includes(prevContent.role)) { throw new VertexAIError( VertexAIErrorCode.INVALID_CONTENT, - `Content with role '${role} can't follow '${ + `Content with role '${role}' can't follow '${ prevContent.role }'. Valid previous roles: ${JSON.stringify( VALID_PREVIOUS_CONTENT_ROLES From ea1f9139e6baec0269fbb91233fd3f7f4b0d5875 Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Tue, 29 Apr 2025 11:24:16 -0700 Subject: [PATCH 18/77] Auto Enable SSL for Firebase Studio (#8980) --- .changeset/nice-plants-thank.md | 10 ++++++++ common/api-review/util.api.md | 3 +++ .../database-compat/test/database.test.ts | 11 +++++++++ packages/database/src/api/Database.ts | 9 +++++-- packages/firestore/externs.json | 1 + packages/firestore/src/lite-api/database.ts | 6 +++-- .../firestore/test/unit/api/database.test.ts | 14 +++++++++++ packages/functions/src/service.test.ts | 9 +++++++ packages/functions/src/service.ts | 6 ++++- packages/storage/src/service.ts | 9 +++++-- packages/storage/test/unit/service.test.ts | 22 +++++++++++++++++ packages/util/index.node.ts | 1 + packages/util/index.ts | 1 + packages/util/src/url.ts | 24 +++++++++++++++++++ 14 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 .changeset/nice-plants-thank.md create mode 100644 packages/util/src/url.ts diff --git a/.changeset/nice-plants-thank.md b/.changeset/nice-plants-thank.md new file mode 100644 index 00000000000..05fb520760f --- /dev/null +++ b/.changeset/nice-plants-thank.md @@ -0,0 +1,10 @@ +--- +"@firebase/database-compat": patch +"@firebase/database": patch +"@firebase/firestore": patch +"@firebase/functions": patch +"@firebase/storage": patch +"@firebase/util": patch +--- + +Auto Enable SSL for Firebase Studio diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index 8c62ff229ac..28cc9a160d4 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -269,6 +269,9 @@ export function isBrowserExtension(): boolean; // @public export function isCloudflareWorker(): boolean; +// @public +export function isCloudWorkstation(host: string): boolean; + // Warning: (ae-missing-release-tag) "isElectron" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public diff --git a/packages/database-compat/test/database.test.ts b/packages/database-compat/test/database.test.ts index fa21058591f..19e02943c9c 100644 --- a/packages/database-compat/test/database.test.ts +++ b/packages/database-compat/test/database.test.ts @@ -292,6 +292,17 @@ describe('Database Tests', () => { expect((db as any)._delegate._repo.repoInfo_.isUsingEmulator).to.be.false; }); + it('uses ssl when useEmulator is called with ssl specified', () => { + const db = firebase.database(); + const cloudWorkstation = 'abc.cloudworkstations.dev'; + db.useEmulator(cloudWorkstation, 80); + expect((db as any)._delegate._repo.repoInfo_.isUsingEmulator).to.be.true; + expect((db as any)._delegate._repo.repoInfo_.host).to.equal( + `${cloudWorkstation}:80` + ); + expect((db as any)._delegate._repo.repoInfo_.secure).to.be.true; + }); + it('cannot call useEmulator after use', () => { const db = (firebase as any).database(); diff --git a/packages/database/src/api/Database.ts b/packages/database/src/api/Database.ts index 32fd4674a44..f247fc6288c 100644 --- a/packages/database/src/api/Database.ts +++ b/packages/database/src/api/Database.ts @@ -29,7 +29,8 @@ import { createMockUserToken, deepEqual, EmulatorMockTokenOptions, - getDefaultEmulatorHostnameAndPort + getDefaultEmulatorHostnameAndPort, + isCloudWorkstation } from '@firebase/util'; import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider'; @@ -89,9 +90,12 @@ function repoManagerApplyEmulatorSettings( emulatorOptions: RepoInfoEmulatorOptions, tokenProvider?: AuthTokenProvider ): void { + const portIndex = hostAndPort.lastIndexOf(':'); + const host = hostAndPort.substring(0, portIndex); + const useSsl = isCloudWorkstation(host); repo.repoInfo_ = new RepoInfo( hostAndPort, - /* secure= */ false, + /* secure= */ useSsl, repo.repoInfo_.namespace, repo.repoInfo_.webSocketOnly, repo.repoInfo_.nodeAdmin, @@ -352,6 +356,7 @@ export function connectDatabaseEmulator( ): void { db = getModularInstance(db); db._checkNotDeleted('useEmulator'); + const hostAndPort = `${host}:${port}`; const repo = db._repoInternal; if (db._instanceStarted) { diff --git a/packages/firestore/externs.json b/packages/firestore/externs.json index 03d19ee8e83..d730cfeac0a 100644 --- a/packages/firestore/externs.json +++ b/packages/firestore/externs.json @@ -33,6 +33,7 @@ "packages/util/dist/src/compat.d.ts", "packages/util/dist/src/global.d.ts", "packages/util/dist/src/obj.d.ts", + "packages/util/dist/src/url.d.ts", "packages/firestore/src/protos/firestore_bundle_proto.ts", "packages/firestore/src/protos/firestore_proto_api.ts", "packages/firestore/src/util/error.ts", diff --git a/packages/firestore/src/lite-api/database.ts b/packages/firestore/src/lite-api/database.ts index 9a68e2a86d6..294206e54c2 100644 --- a/packages/firestore/src/lite-api/database.ts +++ b/packages/firestore/src/lite-api/database.ts @@ -26,7 +26,8 @@ import { createMockUserToken, deepEqual, EmulatorMockTokenOptions, - getDefaultEmulatorHostnameAndPort + getDefaultEmulatorHostnameAndPort, + isCloudWorkstation } from '@firebase/util'; import { @@ -325,6 +326,7 @@ export function connectFirestoreEmulator( } = {} ): void { firestore = cast(firestore, Firestore); + const useSsl = isCloudWorkstation(host); const settings = firestore._getSettings(); const existingConfig = { ...settings, @@ -340,7 +342,7 @@ export function connectFirestoreEmulator( const newConfig = { ...settings, host: newHostSetting, - ssl: false, + ssl: useSsl, emulatorOptions: options }; // No-op if the new configuration matches the current configuration. This supports SSR diff --git a/packages/firestore/test/unit/api/database.test.ts b/packages/firestore/test/unit/api/database.test.ts index 1cc1df51063..46e4c65f180 100644 --- a/packages/firestore/test/unit/api/database.test.ts +++ b/packages/firestore/test/unit/api/database.test.ts @@ -564,6 +564,20 @@ describe('Settings', () => { expect(db._getEmulatorOptions()).to.equal(emulatorOptions); }); + it('sets ssl to true if cloud workstation host', () => { + // Use a new instance of Firestore in order to configure settings. + const db = newTestFirestore(); + const emulatorOptions = { mockUserToken: 'test' }; + const workstationHost = 'abc.cloudworkstations.dev'; + connectFirestoreEmulator(db, workstationHost, 9000, emulatorOptions); + + expect(db._getSettings().host).to.exist.and.to.equal( + `${workstationHost}:9000` + ); + expect(db._getSettings().ssl).to.exist.and.to.be.true; + expect(db._getEmulatorOptions()).to.equal(emulatorOptions); + }); + it('prefers host from useEmulator to host from settings', () => { // Use a new instance of Firestore in order to configure settings. const db = newTestFirestore(); diff --git a/packages/functions/src/service.test.ts b/packages/functions/src/service.test.ts index bb29f9025fe..8119fda39d5 100644 --- a/packages/functions/src/service.test.ts +++ b/packages/functions/src/service.test.ts @@ -47,6 +47,15 @@ describe('Firebase Functions > Service', () => { 'http://localhost:5005/my-project/us-central1/foo' ); }); + it('can use emulator with SSL', () => { + service = createTestService(app); + const workstationHost = 'abc.cloudworkstations.dev'; + connectFunctionsEmulator(service, workstationHost, 5005); + assert.equal( + service._url('foo'), + `https://${workstationHost}:5005/my-project/us-central1/foo` + ); + }); it('correctly sets region', () => { service = createTestService(app, 'my-region'); diff --git a/packages/functions/src/service.ts b/packages/functions/src/service.ts index 34cb732bf71..dce52e1f19d 100644 --- a/packages/functions/src/service.ts +++ b/packages/functions/src/service.ts @@ -30,6 +30,7 @@ import { Provider } from '@firebase/component'; import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; import { MessagingInternalComponentName } from '@firebase/messaging-interop-types'; import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types'; +import { isCloudWorkstation } from '@firebase/util'; export const DEFAULT_REGION = 'us-central1'; @@ -174,7 +175,10 @@ export function connectFunctionsEmulator( host: string, port: number ): void { - functionsInstance.emulatorOrigin = `http://${host}:${port}`; + const useSsl = isCloudWorkstation(host); + functionsInstance.emulatorOrigin = `http${ + useSsl ? 's' : '' + }://${host}:${port}`; } /** diff --git a/packages/storage/src/service.ts b/packages/storage/src/service.ts index 422e3e1a188..8a942aac62a 100644 --- a/packages/storage/src/service.ts +++ b/packages/storage/src/service.ts @@ -42,7 +42,11 @@ import { } from './implementation/error'; import { validateNumber } from './implementation/type'; import { FirebaseStorage } from './public-types'; -import { createMockUserToken, EmulatorMockTokenOptions } from '@firebase/util'; +import { + createMockUserToken, + EmulatorMockTokenOptions, + isCloudWorkstation +} from '@firebase/util'; import { Connection, ConnectionType } from './implementation/connection'; export function isUrl(path?: string): boolean { @@ -141,7 +145,8 @@ export function connectStorageEmulator( } = {} ): void { storage.host = `${host}:${port}`; - storage._protocol = 'http'; + const useSsl = isCloudWorkstation(host); + storage._protocol = useSsl ? 'https' : 'http'; const { mockUserToken } = options; if (mockUserToken) { storage._overrideAuthToken = diff --git a/packages/storage/test/unit/service.test.ts b/packages/storage/test/unit/service.test.ts index be42bb8dd6e..bc443c60a03 100644 --- a/packages/storage/test/unit/service.test.ts +++ b/packages/storage/test/unit/service.test.ts @@ -248,6 +248,28 @@ GOOG4-RSA-SHA256` expect(service._protocol).to.equal('http'); void getDownloadURL(ref(service, 'test.png')); }); + it('sets emulator host correctly with ssl', done => { + function newSend(connection: TestingConnection, url: string): void { + // Expect emulator host to be in url of storage operations requests, + // in this case getDownloadURL. + expect(url).to.match(/^https:\/\/test\.cloudworkstations\.dev:1234.+/); + connection.abort(); + injectTestConnection(null); + done(); + } + + injectTestConnection(() => newTestConnection(newSend)); + const service = new FirebaseStorageImpl( + testShared.fakeApp, + testShared.fakeAuthProvider, + testShared.fakeAppCheckTokenProvider + ); + const workstationHost = 'test.cloudworkstations.dev'; + connectStorageEmulator(service, workstationHost, 1234); + expect(service.host).to.equal(`${workstationHost}:1234`); + expect(service._protocol).to.equal('https'); + void getDownloadURL(ref(service, 'test.png')); + }); it('sets mock user token string if specified', done => { const mockUserToken = 'my-mock-user-token'; function newSend( diff --git a/packages/util/index.node.ts b/packages/util/index.node.ts index d839460713c..12fcf8a6de5 100644 --- a/packages/util/index.node.ts +++ b/packages/util/index.node.ts @@ -42,3 +42,4 @@ export * from './src/exponential_backoff'; export * from './src/formatters'; export * from './src/compat'; export * from './src/global'; +export * from './src/url'; diff --git a/packages/util/index.ts b/packages/util/index.ts index 51c27c31099..1829c32a420 100644 --- a/packages/util/index.ts +++ b/packages/util/index.ts @@ -37,3 +37,4 @@ export * from './src/exponential_backoff'; export * from './src/formatters'; export * from './src/compat'; export * from './src/global'; +export * from './src/url'; diff --git a/packages/util/src/url.ts b/packages/util/src/url.ts new file mode 100644 index 00000000000..33cec43bea9 --- /dev/null +++ b/packages/util/src/url.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Checks whether host is a cloud workstation or not. + * @public + */ +export function isCloudWorkstation(host: string): boolean { + return host.endsWith('.cloudworkstations.dev'); +} From 0e127664946ba324c6566a02b393dafd23fc1ddb Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Wed, 30 Apr 2025 12:19:20 -0700 Subject: [PATCH 19/77] Add support for running the emulators in Cloud Workstation (#8968) --- .changeset/nine-pugs-crash.md | 11 ++++ common/api-review/storage.api.md | 4 +- packages/auth/src/api/index.test.ts | 24 +++++++ packages/auth/src/api/index.ts | 11 +++- packages/auth/test/helpers/mock_fetch.ts | 4 +- packages/data-connect/src/network/fetch.ts | 18 ++++-- .../src/network/transport/rest.ts | 8 ++- packages/data-connect/test/unit/fetch.test.ts | 43 +++++++++++-- packages/firestore/externs.json | 1 + packages/firestore/src/core/database_info.ts | 3 +- packages/firestore/src/lite-api/components.ts | 3 +- packages/firestore/src/lite-api/settings.ts | 3 + .../platform/browser/webchannel_connection.ts | 3 +- .../platform/browser_lite/fetch_connection.ts | 11 +++- .../firestore/src/remote/rest_connection.ts | 15 ++++- .../test/integration/util/internal_helpers.ts | 3 +- .../test/unit/remote/fetch_connection.test.ts | 64 +++++++++++++++++++ .../test/unit/remote/rest_connection.test.ts | 3 +- .../test/unit/specs/spec_test_runner.ts | 3 +- .../storage/src/implementation/connection.ts | 1 + .../storage/src/implementation/request.ts | 17 +++-- .../src/platform/browser/connection.ts | 5 ++ .../storage/src/platform/node/connection.ts | 41 +++++++++--- packages/storage/src/service.ts | 7 +- .../storage/test/browser/connection.test.ts | 18 +++++- packages/storage/test/node/connection.test.ts | 21 +++++- packages/storage/test/unit/connection.ts | 1 + packages/storage/test/unit/service.test.ts | 12 +++- 28 files changed, 314 insertions(+), 44 deletions(-) create mode 100644 .changeset/nine-pugs-crash.md create mode 100644 packages/firestore/test/unit/remote/fetch_connection.test.ts diff --git a/.changeset/nine-pugs-crash.md b/.changeset/nine-pugs-crash.md new file mode 100644 index 00000000000..b4a654a484f --- /dev/null +++ b/.changeset/nine-pugs-crash.md @@ -0,0 +1,11 @@ +--- +"@firebase/auth": patch +"@firebase/data-connect": patch +"@firebase/database-compat": patch +"@firebase/database": patch +"@firebase/firestore": patch +"@firebase/storage": patch +"@firebase/util": patch +--- + +Fix Auth Redirects on Firebase Studio diff --git a/common/api-review/storage.api.md b/common/api-review/storage.api.md index 4964aa40af7..f5302d2d5c5 100644 --- a/common/api-review/storage.api.md +++ b/common/api-review/storage.api.md @@ -58,7 +58,7 @@ export class _FirebaseStorageImpl implements FirebaseStorage { constructor( app: FirebaseApp, _authProvider: Provider, _appCheckProvider: Provider, - _url?: string | undefined, _firebaseVersion?: string | undefined); + _url?: string | undefined, _firebaseVersion?: string | undefined, _isUsingEmulator?: boolean); readonly app: FirebaseApp; // (undocumented) readonly _appCheckProvider: Provider; @@ -77,6 +77,8 @@ export class _FirebaseStorageImpl implements FirebaseStorage { _getAuthToken(): Promise; get host(): string; set host(host: string); + // (undocumented) + _isUsingEmulator: boolean; // Warning: (ae-forgotten-export) The symbol "ConnectionType" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RequestInfo" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "Connection" needs to be exported by the entry point index.d.ts diff --git a/packages/auth/src/api/index.test.ts b/packages/auth/src/api/index.test.ts index ea11af59d01..02042fce429 100644 --- a/packages/auth/src/api/index.test.ts +++ b/packages/auth/src/api/index.test.ts @@ -60,6 +60,10 @@ describe('api/_performApiRequest', () => { auth = await testAuth(); }); + afterEach(() => { + sinon.restore(); + }); + context('with regular requests', () => { beforeEach(mockFetch.setUp); afterEach(mockFetch.tearDown); @@ -80,6 +84,26 @@ describe('api/_performApiRequest', () => { expect(mock.calls[0].headers!.get(HttpHeader.X_CLIENT_VERSION)).to.eq( 'testSDK/0.0.0' ); + expect(mock.calls[0].fullRequest?.credentials).to.be.undefined; + }); + + it('should set credentials to "include" when using IDX and emulator', async () => { + const mock = mockEndpoint(Endpoint.SIGN_UP, serverResponse); + auth.emulatorConfig = { + host: 'https://something.cloudworkstations.dev', + protocol: '', + port: 8, + options: { + disableWarnings: false + } + }; + await _performApiRequest( + auth, + HttpMethod.POST, + Endpoint.SIGN_UP, + request + ); + expect(mock.calls[0].fullRequest?.credentials).to.eq('include'); }); it('should set the device language if available', async () => { diff --git a/packages/auth/src/api/index.ts b/packages/auth/src/api/index.ts index 769a1b6accc..af9b3c63bf1 100644 --- a/packages/auth/src/api/index.ts +++ b/packages/auth/src/api/index.ts @@ -15,7 +15,12 @@ * limitations under the License. */ -import { FirebaseError, isCloudflareWorker, querystring } from '@firebase/util'; +import { + FirebaseError, + isCloudflareWorker, + isCloudWorkstation, + querystring +} from '@firebase/util'; import { AuthErrorCode, NamedErrorParams } from '../core/errors'; import { @@ -177,6 +182,10 @@ export async function _performApiRequest( fetchArgs.referrerPolicy = 'no-referrer'; } + if (auth.emulatorConfig && isCloudWorkstation(auth.emulatorConfig.host)) { + fetchArgs.credentials = 'include'; + } + return FetchProvider.fetch()( await _getFinalTarget(auth, auth.config.apiHost, path, query), fetchArgs diff --git a/packages/auth/test/helpers/mock_fetch.ts b/packages/auth/test/helpers/mock_fetch.ts index 2d5794b7327..49fa79966f9 100644 --- a/packages/auth/test/helpers/mock_fetch.ts +++ b/packages/auth/test/helpers/mock_fetch.ts @@ -22,6 +22,7 @@ export interface Call { request?: object | string; method?: string; headers: Headers; + fullRequest?: RequestInit; } export interface Route { @@ -59,7 +60,8 @@ const fakeFetch: typeof fetch = ( calls.push({ request: requestBody, method: request?.method, - headers + headers, + fullRequest: request }); return Promise.resolve( diff --git a/packages/data-connect/src/network/fetch.ts b/packages/data-connect/src/network/fetch.ts index 8353c6b99ab..3e8e2cab476 100644 --- a/packages/data-connect/src/network/fetch.ts +++ b/packages/data-connect/src/network/fetch.ts @@ -15,6 +15,8 @@ * limitations under the License. */ +import { isCloudWorkstation } from '@firebase/util'; + import { Code, DataConnectError, @@ -22,7 +24,7 @@ import { DataConnectOperationFailureResponse } from '../core/error'; import { SDK_VERSION } from '../core/version'; -import { logDebug, logError } from '../logger'; +import { logError } from '../logger'; import { CallerSdkType, CallerSdkTypeEnum } from './transport'; @@ -58,7 +60,8 @@ export function dcFetch( accessToken: string | null, appCheckToken: string | null, _isUsingGen: boolean, - _callerSdkType: CallerSdkType + _callerSdkType: CallerSdkType, + _isUsingEmulator: boolean ): Promise<{ data: T; errors: Error[] }> { if (!connectFetch) { throw new DataConnectError(Code.OTHER, 'No Fetch Implementation detected!'); @@ -77,14 +80,17 @@ export function dcFetch( headers['X-Firebase-AppCheck'] = appCheckToken; } const bodyStr = JSON.stringify(body); - logDebug(`Making request out to ${url} with body: ${bodyStr}`); - - return connectFetch(url, { + const fetchOptions: RequestInit = { body: bodyStr, method: 'POST', headers, signal - }) + }; + if (isCloudWorkstation(url) && _isUsingEmulator) { + fetchOptions.credentials = 'include'; + } + + return connectFetch(url, fetchOptions) .catch(err => { throw new DataConnectError( Code.OTHER, diff --git a/packages/data-connect/src/network/transport/rest.ts b/packages/data-connect/src/network/transport/rest.ts index 0663bc026db..f16154dcb2a 100644 --- a/packages/data-connect/src/network/transport/rest.ts +++ b/packages/data-connect/src/network/transport/rest.ts @@ -36,6 +36,7 @@ export class RESTTransport implements DataConnectTransport { private _accessToken: string | null = null; private _appCheckToken: string | null = null; private _lastToken: string | null = null; + private _isUsingEmulator = false; constructor( options: DataConnectOptions, private apiKey?: string | undefined, @@ -93,6 +94,7 @@ export class RESTTransport implements DataConnectTransport { } useEmulator(host: string, port?: number, isSecure?: boolean): void { this._host = host; + this._isUsingEmulator = true; if (typeof port === 'number') { this._port = port; } @@ -182,7 +184,8 @@ export class RESTTransport implements DataConnectTransport { this._accessToken, this._appCheckToken, this._isUsingGen, - this._callerSdkType + this._callerSdkType, + this._isUsingEmulator ) ); return withAuth; @@ -208,7 +211,8 @@ export class RESTTransport implements DataConnectTransport { this._accessToken, this._appCheckToken, this._isUsingGen, - this._callerSdkType + this._callerSdkType, + this._isUsingEmulator ); }); return taskResult; diff --git a/packages/data-connect/test/unit/fetch.test.ts b/packages/data-connect/test/unit/fetch.test.ts index 6cf2750d50d..be45695190f 100644 --- a/packages/data-connect/test/unit/fetch.test.ts +++ b/packages/data-connect/test/unit/fetch.test.ts @@ -18,10 +18,12 @@ import { expect, use } from 'chai'; import chaiAsPromised from 'chai-as-promised'; import * as sinon from 'sinon'; +import sinonChai from 'sinon-chai'; import { dcFetch, initializeFetch } from '../../src/network/fetch'; import { CallerSdkType, CallerSdkTypeEnum } from '../../src/network/transport'; use(chaiAsPromised); +use(sinonChai); function mockFetch(json: object, reject: boolean): sinon.SinonStub { const fakeFetchImpl = sinon.stub().returns( Promise.resolve({ @@ -57,7 +59,8 @@ describe('fetch', () => { null, null, false, - CallerSdkTypeEnum.Base + CallerSdkTypeEnum.Base, + false ) ).to.eventually.be.rejectedWith(message); }); @@ -81,7 +84,8 @@ describe('fetch', () => { null, null, false, - CallerSdkTypeEnum.Base + CallerSdkTypeEnum.Base, + false ) ).to.eventually.be.rejectedWith(JSON.stringify(json)); }); @@ -112,7 +116,8 @@ describe('fetch', () => { null, null, false, - CallerSdkTypeEnum.Base + CallerSdkTypeEnum.Base, + false ) ).to.eventually.be.rejected.then(error => { expect(error.response.data).to.eq(json.data); @@ -143,7 +148,8 @@ describe('fetch', () => { null, null, false, // _isUsingGen is false - callerSdkType as CallerSdkType + callerSdkType as CallerSdkType, + false ); let expectedHeaderRegex: RegExp; @@ -191,7 +197,8 @@ describe('fetch', () => { null, null, true, // _isUsingGen is true - callerSdkType as CallerSdkType + callerSdkType as CallerSdkType, + false ); let expectedHeaderRegex: RegExp; @@ -215,4 +222,30 @@ describe('fetch', () => { } } }); + it('should call credentials include if using emulator on cloud workstation', async () => { + const json = { + code: 200, + message1: 'success' + }; + const fakeFetchImpl = mockFetch(json, false); + await dcFetch( + 'https://abc.cloudworkstations.dev', + { + name: 'n', + operationName: 'n', + variables: {} + }, + {} as AbortController, + null, + null, + null, + true, // _isUsingGen is true + CallerSdkTypeEnum.Base, + true + ); + expect(fakeFetchImpl).to.have.been.calledWithMatch( + 'https://abc.cloudworkstations.dev', + { credentials: 'include' } + ); + }); }); diff --git a/packages/firestore/externs.json b/packages/firestore/externs.json index d730cfeac0a..c56b078dddf 100644 --- a/packages/firestore/externs.json +++ b/packages/firestore/externs.json @@ -30,6 +30,7 @@ "packages/util/dist/src/defaults.d.ts", "packages/util/dist/src/emulator.d.ts", "packages/util/dist/src/environment.d.ts", + "packages/util/dist/src/url.d.ts", "packages/util/dist/src/compat.d.ts", "packages/util/dist/src/global.d.ts", "packages/util/dist/src/obj.d.ts", diff --git a/packages/firestore/src/core/database_info.ts b/packages/firestore/src/core/database_info.ts index 0325f8166b6..a057516763f 100644 --- a/packages/firestore/src/core/database_info.ts +++ b/packages/firestore/src/core/database_info.ts @@ -48,7 +48,8 @@ export class DatabaseInfo { readonly forceLongPolling: boolean, readonly autoDetectLongPolling: boolean, readonly longPollingOptions: ExperimentalLongPollingOptions, - readonly useFetchStreams: boolean + readonly useFetchStreams: boolean, + readonly isUsingEmulator: boolean ) {} } diff --git a/packages/firestore/src/lite-api/components.ts b/packages/firestore/src/lite-api/components.ts index 436d2b5d4d8..52c3b3729ee 100644 --- a/packages/firestore/src/lite-api/components.ts +++ b/packages/firestore/src/lite-api/components.ts @@ -119,6 +119,7 @@ export function makeDatabaseInfo( settings.experimentalForceLongPolling, settings.experimentalAutoDetectLongPolling, cloneLongPollingOptions(settings.experimentalLongPollingOptions), - settings.useFetchStreams + settings.useFetchStreams, + settings.isUsingEmulator ); } diff --git a/packages/firestore/src/lite-api/settings.ts b/packages/firestore/src/lite-api/settings.ts index a1bba373d13..56c99e7ccea 100644 --- a/packages/firestore/src/lite-api/settings.ts +++ b/packages/firestore/src/lite-api/settings.ts @@ -112,6 +112,8 @@ export class FirestoreSettingsImpl { readonly useFetchStreams: boolean; readonly localCache?: FirestoreLocalCache; + readonly isUsingEmulator: boolean; + // Can be a google-auth-library or gapi client. // eslint-disable-next-line @typescript-eslint/no-explicit-any credentials?: any; @@ -130,6 +132,7 @@ export class FirestoreSettingsImpl { this.host = settings.host; this.ssl = settings.ssl ?? DEFAULT_SSL; } + this.isUsingEmulator = settings.emulatorOptions !== undefined; this.credentials = settings.credentials; this.ignoreUndefinedProperties = !!settings.ignoreUndefinedProperties; diff --git a/packages/firestore/src/platform/browser/webchannel_connection.ts b/packages/firestore/src/platform/browser/webchannel_connection.ts index 5223285c5a4..9a69164457e 100644 --- a/packages/firestore/src/platform/browser/webchannel_connection.ts +++ b/packages/firestore/src/platform/browser/webchannel_connection.ts @@ -71,7 +71,8 @@ export class WebChannelConnection extends RestConnection { rpcName: string, url: string, headers: StringMap, - body: Req + body: Req, + _forwardCredentials: boolean ): Promise { const streamId = generateUniqueDebugId(); return new Promise((resolve: Resolver, reject: Rejecter) => { diff --git a/packages/firestore/src/platform/browser_lite/fetch_connection.ts b/packages/firestore/src/platform/browser_lite/fetch_connection.ts index d11247c8019..227322153e9 100644 --- a/packages/firestore/src/platform/browser_lite/fetch_connection.ts +++ b/packages/firestore/src/platform/browser_lite/fetch_connection.ts @@ -38,17 +38,22 @@ export class FetchConnection extends RestConnection { rpcName: string, url: string, headers: StringMap, - body: Req + body: Req, + forwardCredentials: boolean ): Promise { const requestJson = JSON.stringify(body); let response: Response; try { - response = await fetch(url, { + const fetchArgs: RequestInit = { method: 'POST', headers, body: requestJson - }); + }; + if (forwardCredentials) { + fetchArgs.credentials = 'include'; + } + response = await fetch(url, fetchArgs); } catch (e) { const err = e as { status: number | undefined; statusText: string }; throw new FirestoreError( diff --git a/packages/firestore/src/remote/rest_connection.ts b/packages/firestore/src/remote/rest_connection.ts index 470cb332ce2..2d6889dac3b 100644 --- a/packages/firestore/src/remote/rest_connection.ts +++ b/packages/firestore/src/remote/rest_connection.ts @@ -15,6 +15,8 @@ * limitations under the License. */ +import { isCloudWorkstation } from '@firebase/util'; + import { SDK_VERSION } from '../../src/core/version'; import { Token } from '../api/credentials'; import { @@ -98,7 +100,15 @@ export abstract class RestConnection implements Connection { }; this.modifyHeadersForRequest(headers, authToken, appCheckToken); - return this.performRPCRequest(rpcName, url, headers, req).then( + const { host } = new URL(url); + const forwardCredentials = isCloudWorkstation(host); + return this.performRPCRequest( + rpcName, + url, + headers, + req, + forwardCredentials + ).then( response => { logDebug(LOG_TAG, `Received RPC '${rpcName}' ${streamId}: `, response); return response; @@ -179,7 +189,8 @@ export abstract class RestConnection implements Connection { rpcName: string, url: string, headers: StringMap, - body: Req + body: Req, + _forwardCredentials: boolean ): Promise; private makeUrl(rpcName: string, path: string): string { diff --git a/packages/firestore/test/integration/util/internal_helpers.ts b/packages/firestore/test/integration/util/internal_helpers.ts index 86ded6af3c1..e5e64b5fbf4 100644 --- a/packages/firestore/test/integration/util/internal_helpers.ts +++ b/packages/firestore/test/integration/util/internal_helpers.ts @@ -61,7 +61,8 @@ export function getDefaultDatabaseInfo(): DatabaseInfo { cloneLongPollingOptions( DEFAULT_SETTINGS.experimentalLongPollingOptions ?? {} ), - /*use FetchStreams= */ false + /*use FetchStreams= */ false, + /*isUsingEmulator=*/ false ); } diff --git a/packages/firestore/test/unit/remote/fetch_connection.test.ts b/packages/firestore/test/unit/remote/fetch_connection.test.ts new file mode 100644 index 00000000000..5a9aa67436f --- /dev/null +++ b/packages/firestore/test/unit/remote/fetch_connection.test.ts @@ -0,0 +1,64 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import * as sinon from 'sinon'; +import sinonChai from 'sinon-chai'; + +import { DatabaseId } from '../../../src/core/database_info'; +import { makeDatabaseInfo } from '../../../src/lite-api/components'; +import { FirestoreSettingsImpl } from '../../../src/lite-api/settings'; +import { ResourcePath } from '../../../src/model/path'; +import { FetchConnection } from '../../../src/platform/browser_lite/fetch_connection'; + +use(sinonChai); +use(chaiAsPromised); + +describe('Fetch Connection', () => { + it('should pass in credentials if using emulator and cloud workstation', async () => { + const stub = sinon.stub(globalThis, 'fetch'); + stub.resolves({ + ok: true, + json() { + return Promise.resolve(); + } + } as Response); + const fetchConnection = new FetchConnection( + makeDatabaseInfo( + DatabaseId.empty(), + '', + '', + new FirestoreSettingsImpl({ + host: 'abc.cloudworkstations.dev' + }) + ) + ); + await fetchConnection.invokeRPC( + 'Commit', + new ResourcePath([]), + {}, + null, + null + ); + expect(stub).to.have.been.calledWithMatch( + 'https://abc.cloudworkstations.dev/v1/:commit', + { credentials: 'include' } + ); + stub.restore(); + }); +}); diff --git a/packages/firestore/test/unit/remote/rest_connection.test.ts b/packages/firestore/test/unit/remote/rest_connection.test.ts index d45a75ce67b..100b8b8368e 100644 --- a/packages/firestore/test/unit/remote/rest_connection.test.ts +++ b/packages/firestore/test/unit/remote/rest_connection.test.ts @@ -67,7 +67,8 @@ describe('RestConnection', () => { /*forceLongPolling=*/ false, /*autoDetectLongPolling=*/ false, /*longPollingOptions=*/ {}, - /*useFetchStreams=*/ false + /*useFetchStreams=*/ false, + /*isUsingEmulator=*/ false ); const connection = new TestRestConnection(testDatabaseInfo); diff --git a/packages/firestore/test/unit/specs/spec_test_runner.ts b/packages/firestore/test/unit/specs/spec_test_runner.ts index ee0af0b8bf8..51d2229b8a1 100644 --- a/packages/firestore/test/unit/specs/spec_test_runner.ts +++ b/packages/firestore/test/unit/specs/spec_test_runner.ts @@ -282,7 +282,8 @@ abstract class TestRunner { /*forceLongPolling=*/ false, /*autoDetectLongPolling=*/ false, /*longPollingOptions=*/ {}, - /*useFetchStreams=*/ false + /*useFetchStreams=*/ false, + /*isUsingEmulator=*/ false ); // TODO(mrschmidt): During client startup in `firestore_client`, we block diff --git a/packages/storage/src/implementation/connection.ts b/packages/storage/src/implementation/connection.ts index 80e29c9cd2f..f7630e59708 100644 --- a/packages/storage/src/implementation/connection.ts +++ b/packages/storage/src/implementation/connection.ts @@ -42,6 +42,7 @@ export interface Connection { send( url: string, method: string, + isUsingEmulator: boolean, body?: ArrayBufferView | Blob | string | null, headers?: Headers ): Promise; diff --git a/packages/storage/src/implementation/request.ts b/packages/storage/src/implementation/request.ts index fae46d7a5ab..adfda6e4460 100644 --- a/packages/storage/src/implementation/request.ts +++ b/packages/storage/src/implementation/request.ts @@ -71,7 +71,8 @@ class NetworkRequest implements Request { private timeout_: number, private progressCallback_: ((p1: number, p2: number) => void) | null, private connectionFactory_: () => Connection, - private retry = true + private retry = true, + private isUsingEmulator = false ) { this.promise_ = new Promise((resolve, reject) => { this.resolve_ = resolve as (value?: O | PromiseLike) => void; @@ -111,7 +112,13 @@ class NetworkRequest implements Request { // connection.send() never rejects, so we don't need to have a error handler or use catch on the returned promise. // eslint-disable-next-line @typescript-eslint/no-floating-promises connection - .send(this.url_, this.method_, this.body_, this.headers_) + .send( + this.url_, + this.method_, + this.isUsingEmulator, + this.body_, + this.headers_ + ) .then(() => { if (this.progressCallback_ !== null) { connection.removeUploadProgressListener(progressListener); @@ -261,7 +268,8 @@ export function makeRequest( appCheckToken: string | null, requestFactory: () => Connection, firebaseVersion?: string, - retry = true + retry = true, + isUsingEmulator = false ): Request { const queryPart = makeQueryString(requestInfo.urlParams); const url = requestInfo.url + queryPart; @@ -282,6 +290,7 @@ export function makeRequest( requestInfo.timeout, requestInfo.progressCallback, requestFactory, - retry + retry, + isUsingEmulator ); } diff --git a/packages/storage/src/platform/browser/connection.ts b/packages/storage/src/platform/browser/connection.ts index fdd9b496242..77a2e42809b 100644 --- a/packages/storage/src/platform/browser/connection.ts +++ b/packages/storage/src/platform/browser/connection.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import { isCloudWorkstation } from '@firebase/util'; import { Connection, ConnectionType, @@ -62,12 +63,16 @@ abstract class XhrConnection send( url: string, method: string, + isUsingEmulator: boolean, body?: ArrayBufferView | Blob | string, headers?: Headers ): Promise { if (this.sent_) { throw internalError('cannot .send() more than once'); } + if (isCloudWorkstation(url) && isUsingEmulator) { + this.xhr_.withCredentials = true; + } this.sent_ = true; this.xhr_.open(method, url, true); if (headers !== undefined) { diff --git a/packages/storage/src/platform/node/connection.ts b/packages/storage/src/platform/node/connection.ts index c90f664c3b2..2dd869eb2f0 100644 --- a/packages/storage/src/platform/node/connection.ts +++ b/packages/storage/src/platform/node/connection.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import { isCloudWorkstation } from '@firebase/util'; import { Connection, ConnectionType, @@ -48,6 +49,7 @@ abstract class FetchConnection async send( url: string, method: string, + isUsingEmulator: boolean, body?: NodeJS.ArrayBufferView | Blob | string, headers?: Record ): Promise { @@ -57,11 +59,13 @@ abstract class FetchConnection this.sent_ = true; try { - const response = await fetch(url, { + const response = await newFetch( + url, method, - headers: headers || {}, - body: body as NodeJS.ArrayBufferView | string - }); + isUsingEmulator, + headers, + body + ); this.headers_ = response.headers; this.statusCode_ = response.status; this.errorCode_ = ErrorCode.NO_ERROR; @@ -152,6 +156,7 @@ export class FetchStreamConnection extends FetchConnection< async send( url: string, method: string, + isUsingEmulator: boolean, body?: NodeJS.ArrayBufferView | Blob | string, headers?: Record ): Promise { @@ -161,11 +166,13 @@ export class FetchStreamConnection extends FetchConnection< this.sent_ = true; try { - const response = await fetch(url, { + const response = await newFetch( + url, method, - headers: headers || {}, - body: body as NodeJS.ArrayBufferView | string - }); + isUsingEmulator, + headers, + body + ); this.headers_ = response.headers; this.statusCode_ = response.status; this.errorCode_ = ErrorCode.NO_ERROR; @@ -186,6 +193,24 @@ export class FetchStreamConnection extends FetchConnection< } } +function newFetch( + url: string, + method: string, + isUsingEmulator: boolean, + headers?: Record, + body?: NodeJS.ArrayBufferView | Blob | string +): Promise { + const fetchArgs: RequestInit = { + method, + headers: headers || {}, + body: body as NodeJS.ArrayBufferView | string + }; + if (isCloudWorkstation(url) && isUsingEmulator) { + fetchArgs.credentials = 'include'; + } + return fetch(url, fetchArgs); +} + export function newStreamConnection(): Connection> { return new FetchStreamConnection(); } diff --git a/packages/storage/src/service.ts b/packages/storage/src/service.ts index 8a942aac62a..8ad31ecb13c 100644 --- a/packages/storage/src/service.ts +++ b/packages/storage/src/service.ts @@ -146,6 +146,7 @@ export function connectStorageEmulator( ): void { storage.host = `${host}:${port}`; const useSsl = isCloudWorkstation(host); + storage._isUsingEmulator = true; storage._protocol = useSsl ? 'https' : 'http'; const { mockUserToken } = options; if (mockUserToken) { @@ -192,7 +193,8 @@ export class FirebaseStorageImpl implements FirebaseStorage { * @internal */ readonly _url?: string, - readonly _firebaseVersion?: string + readonly _firebaseVersion?: string, + public _isUsingEmulator = false ) { this._maxOperationRetryTime = DEFAULT_MAX_OPERATION_RETRY_TIME; this._maxUploadRetryTime = DEFAULT_MAX_UPLOAD_RETRY_TIME; @@ -325,7 +327,8 @@ export class FirebaseStorageImpl implements FirebaseStorage { appCheckToken, requestFactory, this._firebaseVersion, - retry + retry, + this._isUsingEmulator ); this._requests.add(request); // Request removes itself from set when complete. diff --git a/packages/storage/test/browser/connection.test.ts b/packages/storage/test/browser/connection.test.ts index b869c9ee31b..2a0320d0c02 100644 --- a/packages/storage/test/browser/connection.test.ts +++ b/packages/storage/test/browser/connection.test.ts @@ -24,11 +24,27 @@ describe('Connections', () => { it('XhrConnection.send() should not reject on network errors', async () => { const fakeXHR = useFakeXMLHttpRequest(); const connection = new XhrBytesConnection(); - const sendPromise = connection.send('testurl', 'GET'); + const sendPromise = connection.send('testurl', 'GET', false); // simulate a network error ((connection as any).xhr_ as SinonFakeXMLHttpRequest).error(); await sendPromise; expect(connection.getErrorCode()).to.equal(ErrorCode.NETWORK_ERROR); fakeXHR.restore(); }); + it('XhrConnection.send() should send credentials when using cloud workstation', async () => { + const fakeXHR = useFakeXMLHttpRequest(); + const connection = new XhrBytesConnection(); + const sendPromise = connection.send( + 'https://abc.cloudworkstations.dev', + 'GET', + true + ); + // simulate a network error + ((connection as any).xhr_ as SinonFakeXMLHttpRequest).error(); + await sendPromise; + expect( + ((connection as any).xhr_ as SinonFakeXMLHttpRequest).withCredentials + ).to.be.true; + fakeXHR.restore(); + }); }); diff --git a/packages/storage/test/node/connection.test.ts b/packages/storage/test/node/connection.test.ts index 925d1f8f7dc..5c9f2efe41d 100644 --- a/packages/storage/test/node/connection.test.ts +++ b/packages/storage/test/node/connection.test.ts @@ -25,8 +25,27 @@ describe('Connections', () => { const connection = new FetchBytesConnection(); const fetchStub = stub(globalThis, 'fetch').rejects(); - await connection.send('testurl', 'GET'); + await connection.send('testurl', 'GET', false); expect(connection.getErrorCode()).to.equal(ErrorCode.NETWORK_ERROR); + + fetchStub.restore(); + }); + it('FetchConnection.send() should send credentials on cloud workstations', async () => { + const connection = new FetchBytesConnection(); + + const fetchStub = stub(globalThis, 'fetch').rejects(); + await connection.send( + 'http://something.cloudworkstations.dev', + 'GET', + true + ); + expect(connection.getErrorCode()).to.equal(ErrorCode.NETWORK_ERROR); + expect(fetchStub).to.have.been.calledWithMatch( + 'http://something.cloudworkstations.dev', + { + credentials: 'include' + } + ); fetchStub.restore(); }); }); diff --git a/packages/storage/test/unit/connection.ts b/packages/storage/test/unit/connection.ts index 6b800a17f91..a2f0ca58750 100644 --- a/packages/storage/test/unit/connection.ts +++ b/packages/storage/test/unit/connection.ts @@ -60,6 +60,7 @@ export class TestingConnection implements Connection { send( url: string, method: string, + _isUsingEmulator: boolean, body?: ArrayBufferView | Blob | string | null, headers?: Headers ): Promise { diff --git a/packages/storage/test/unit/service.test.ts b/packages/storage/test/unit/service.test.ts index bc443c60a03..b37e624e3d1 100644 --- a/packages/storage/test/unit/service.test.ts +++ b/packages/storage/test/unit/service.test.ts @@ -14,7 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { expect } from 'chai'; +import { expect, use } from 'chai'; +import * as sinon from 'sinon'; import { TaskEvent } from '../../src/implementation/taskenums'; import { Headers } from '../../src/implementation/connection'; import { @@ -34,11 +35,13 @@ import { import { Location } from '../../src/implementation/location'; import { newTestConnection, TestingConnection } from './connection'; import { injectTestConnection } from '../../src/platform/connection'; +import sinonChai from 'sinon-chai'; const fakeAppGs = testShared.makeFakeApp('gs://mybucket'); const fakeAppGsEndingSlash = testShared.makeFakeApp('gs://mybucket/'); const fakeAppInvalidGs = testShared.makeFakeApp('gs://mybucket/hello'); const testLocation = new Location('bucket', 'object'); +use(sinonChai); function makeGsUrl(child: string = ''): string { return 'gs://' + testShared.bucket + '/' + child; @@ -227,6 +230,13 @@ GOOG4-RSA-SHA256` }); }); describe('connectStorageEmulator(service, host, port, options)', () => { + let sandbox: sinon.SinonSandbox; + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + afterEach(() => { + sandbox.restore(); + }); it('sets emulator host correctly', done => { function newSend(connection: TestingConnection, url: string): void { // Expect emulator host to be in url of storage operations requests, From 080a90dccbb5a9cc402a3fc8feca4c81fc7e1305 Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Wed, 30 Apr 2025 18:49:33 -0700 Subject: [PATCH 20/77] Add Cookie Support For Firebase Studio (#8986) --- common/api-review/util.api.md | 3 +++ packages/auth/src/core/auth/emulator.ts | 7 ++++++- packages/data-connect/src/api/DataConnect.ts | 5 +++++ packages/database/src/api/Database.ts | 8 +++++++- packages/firestore/src/api/database.ts | 12 +++++++++++- packages/firestore/src/lite-api/database.ts | 6 +++++- packages/functions/src/service.ts | 6 +++++- packages/storage/src/service.ts | 7 ++++++- packages/util/src/url.ts | 12 ++++++++++++ 9 files changed, 60 insertions(+), 6 deletions(-) diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index 28cc9a160d4..0f8fc13cd3a 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -398,6 +398,9 @@ export function ordinal(i: number): string; // @public (undocumented) export type PartialObserver = Partial>; +// @public +export function pingServer(endpoint: string): Promise; + // Warning: (ae-internal-missing-underscore) The name "promiseWithTimeout" should be prefixed with an underscore because the declaration is marked as @internal // // @internal diff --git a/packages/auth/src/core/auth/emulator.ts b/packages/auth/src/core/auth/emulator.ts index 60cc9403d3d..05f2e5e4bd5 100644 --- a/packages/auth/src/core/auth/emulator.ts +++ b/packages/auth/src/core/auth/emulator.ts @@ -18,7 +18,7 @@ import { Auth } from '../../model/public_types'; import { AuthErrorCode } from '../errors'; import { _assert } from '../util/assert'; import { _castAuth } from './auth_impl'; -import { deepEqual } from '@firebase/util'; +import { deepEqual, isCloudWorkstation, pingServer } from '@firebase/util'; /** * Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production @@ -100,6 +100,11 @@ export function connectAuthEmulator( if (!disableWarnings) { emitEmulatorWarning(); } + + // Workaround to get cookies in Firebase Studio + if (isCloudWorkstation(host)) { + void pingServer(`${protocol}//${host}:${port}`); + } } function extractProtocol(url: string): string { diff --git a/packages/data-connect/src/api/DataConnect.ts b/packages/data-connect/src/api/DataConnect.ts index dc170809143..c25a09039ac 100644 --- a/packages/data-connect/src/api/DataConnect.ts +++ b/packages/data-connect/src/api/DataConnect.ts @@ -24,6 +24,7 @@ import { import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types'; import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; import { Provider } from '@firebase/component'; +import { isCloudWorkstation, pingServer } from '@firebase/util'; import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider'; import { Code, DataConnectError } from '../core/error'; @@ -237,6 +238,10 @@ export function connectDataConnectEmulator( port?: number, sslEnabled = false ): void { + // Workaround to get cookies in Firebase Studio + if (isCloudWorkstation(host)) { + void pingServer(`https://${host}${port ? `:${port}` : ''}`); + } dc.enableEmulator({ host, port, sslEnabled }); } diff --git a/packages/database/src/api/Database.ts b/packages/database/src/api/Database.ts index f247fc6288c..515e278b5c5 100644 --- a/packages/database/src/api/Database.ts +++ b/packages/database/src/api/Database.ts @@ -30,7 +30,8 @@ import { deepEqual, EmulatorMockTokenOptions, getDefaultEmulatorHostnameAndPort, - isCloudWorkstation + isCloudWorkstation, + pingServer } from '@firebase/util'; import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider'; @@ -389,6 +390,11 @@ export function connectDatabaseEmulator( tokenProvider = new EmulatorTokenProvider(token); } + // Workaround to get cookies in Firebase Studio + if (isCloudWorkstation(host)) { + void pingServer(host); + } + // Modify the repo to apply emulator settings repoManagerApplyEmulatorSettings(repo, hostAndPort, options, tokenProvider); } diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 0757378a74c..a2feb19507f 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -21,7 +21,12 @@ import { FirebaseApp, getApp } from '@firebase/app'; -import { deepEqual, getDefaultEmulatorHostnameAndPort } from '@firebase/util'; +import { + deepEqual, + getDefaultEmulatorHostnameAndPort, + isCloudWorkstation, + pingServer +} from '@firebase/util'; import { User } from '../auth/user'; import { @@ -194,6 +199,11 @@ export function initializeFirestore( ); } + // Workaround to get cookies in Firebase Studio + if (settings.host && isCloudWorkstation(settings.host)) { + void pingServer(settings.host); + } + return provider.initialize({ options: settings, instanceIdentifier: databaseId diff --git a/packages/firestore/src/lite-api/database.ts b/packages/firestore/src/lite-api/database.ts index 294206e54c2..8e7fdb27e90 100644 --- a/packages/firestore/src/lite-api/database.ts +++ b/packages/firestore/src/lite-api/database.ts @@ -27,7 +27,8 @@ import { deepEqual, EmulatorMockTokenOptions, getDefaultEmulatorHostnameAndPort, - isCloudWorkstation + isCloudWorkstation, + pingServer } from '@firebase/util'; import { @@ -333,6 +334,9 @@ export function connectFirestoreEmulator( emulatorOptions: firestore._getEmulatorOptions() }; const newHostSetting = `${host}:${port}`; + if (useSsl) { + void pingServer(`https://${newHostSetting}`); + } if (settings.host !== DEFAULT_HOST && settings.host !== newHostSetting) { logWarn( 'Host has been set in both settings() and connectFirestoreEmulator(), emulator host ' + diff --git a/packages/functions/src/service.ts b/packages/functions/src/service.ts index dce52e1f19d..af9d8898d2e 100644 --- a/packages/functions/src/service.ts +++ b/packages/functions/src/service.ts @@ -30,7 +30,7 @@ import { Provider } from '@firebase/component'; import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; import { MessagingInternalComponentName } from '@firebase/messaging-interop-types'; import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types'; -import { isCloudWorkstation } from '@firebase/util'; +import { isCloudWorkstation, pingServer } from '@firebase/util'; export const DEFAULT_REGION = 'us-central1'; @@ -179,6 +179,10 @@ export function connectFunctionsEmulator( functionsInstance.emulatorOrigin = `http${ useSsl ? 's' : '' }://${host}:${port}`; + // Workaround to get cookies in Firebase Studio + if (useSsl) { + void pingServer(functionsInstance.emulatorOrigin); + } } /** diff --git a/packages/storage/src/service.ts b/packages/storage/src/service.ts index 8ad31ecb13c..741dd6eaa1a 100644 --- a/packages/storage/src/service.ts +++ b/packages/storage/src/service.ts @@ -45,7 +45,8 @@ import { FirebaseStorage } from './public-types'; import { createMockUserToken, EmulatorMockTokenOptions, - isCloudWorkstation + isCloudWorkstation, + pingServer } from '@firebase/util'; import { Connection, ConnectionType } from './implementation/connection'; @@ -146,6 +147,10 @@ export function connectStorageEmulator( ): void { storage.host = `${host}:${port}`; const useSsl = isCloudWorkstation(host); + // Workaround to get cookies in Firebase Studio + if (useSsl) { + void pingServer(`https://${storage.host}`); + } storage._isUsingEmulator = true; storage._protocol = useSsl ? 'https' : 'http'; const { mockUserToken } = options; diff --git a/packages/util/src/url.ts b/packages/util/src/url.ts index 33cec43bea9..e41d26594c2 100644 --- a/packages/util/src/url.ts +++ b/packages/util/src/url.ts @@ -22,3 +22,15 @@ export function isCloudWorkstation(host: string): boolean { return host.endsWith('.cloudworkstations.dev'); } + +/** + * Makes a fetch request to the given server. + * Mostly used for forwarding cookies in Firebase Studio. + * @public + */ +export async function pingServer(endpoint: string): Promise { + const result = await fetch(endpoint, { + credentials: 'include' + }); + return result.ok; +} From ac59a1c18d5a5b5fc6e3fb2c0e5647c26f8006c2 Mon Sep 17 00:00:00 2001 From: Mila <107142260+milaGGL@users.noreply.github.com> Date: Thu, 1 May 2025 13:02:18 -0400 Subject: [PATCH 21/77] Remove FieldValue factory methods (#342) --- common/api-review/firestore-lite.api.md | 23 +- common/api-review/firestore.api.md | 23 +- packages/firestore/lite/index.ts | 11 +- packages/firestore/src/api.ts | 11 +- .../firestore/src/api/field_value_impl.ts | 9 +- .../src/lite-api/bson_binary_data.ts | 6 +- ...n_timestamp_value.ts => bson_timestamp.ts} | 14 +- .../src/lite-api/field_value_impl.ts | 90 ------ .../src/lite-api/user_data_reader.ts | 2 +- .../src/lite-api/user_data_writer.ts | 9 +- .../test/integration/api/database.test.ts | 258 +++++++++-------- .../test/integration/api/type.test.ts | 92 +++--- .../firestore/test/lite/integration.test.ts | 48 ++-- .../firestore_index_value_writer.test.ts | 39 ++- .../test/unit/local/index_manager.test.ts | 267 +++++++++++------- .../unit/local/local_store_indexeddb.test.ts | 227 ++++++++------- .../test/unit/model/document.test.ts | 44 +-- .../test/unit/model/object_value.test.ts | 130 +++++---- .../firestore/test/unit/model/values.test.ts | 179 ++++++------ .../test/unit/remote/serializer.helper.ts | 32 +-- 20 files changed, 751 insertions(+), 763 deletions(-) rename packages/firestore/src/lite-api/{bson_timestamp_value.ts => bson_timestamp.ts} (72%) diff --git a/common/api-review/firestore-lite.api.md b/common/api-review/firestore-lite.api.md index 394fd0b33ce..f0203c034b3 100644 --- a/common/api-review/firestore-lite.api.md +++ b/common/api-review/firestore-lite.api.md @@ -68,14 +68,13 @@ export function average(field: string | FieldPath): AggregateField> = UnionToIntersection<{ [K in keyof T & string]: ChildUpdateFields; @@ -426,9 +410,6 @@ export class QueryStartAtConstraint extends QueryConstraint { // @public export function refEqual(left: DocumentReference | CollectionReference, right: DocumentReference | CollectionReference): boolean; -// @public -export function regex(pattern: string, options: string): RegexValue; - // @public export class RegexValue { constructor(pattern: string, options: string); diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 39dcdb6df59..90137f78b00 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -68,14 +68,13 @@ export function average(field: string | FieldPath): AggregateField; @@ -686,9 +670,6 @@ export class QueryStartAtConstraint extends QueryConstraint { // @public export function refEqual(left: DocumentReference | CollectionReference, right: DocumentReference | CollectionReference): boolean; -// @public -export function regex(pattern: string, options: string): RegexValue; - // @public export class RegexValue { constructor(pattern: string, options: string); diff --git a/packages/firestore/lite/index.ts b/packages/firestore/lite/index.ts index 48e0bdae068..7eee71a9893 100644 --- a/packages/firestore/lite/index.ts +++ b/packages/firestore/lite/index.ts @@ -128,14 +128,7 @@ export { arrayUnion, serverTimestamp, deleteField, - vector, - int32, - regex, - bsonBinaryData, - bsonObjectId, - bsonTimestamp, - minKey, - maxKey + vector } from '../src/lite-api/field_value_impl'; export { @@ -156,7 +149,7 @@ export { BsonBinaryData } from '../src/lite-api/bson_binary_data'; export { BsonObjectId } from '../src/lite-api/bson_object_Id'; -export { BsonTimestamp } from '../src/lite-api/bson_timestamp_value'; +export { BsonTimestamp } from '../src/lite-api/bson_timestamp'; export { MinKey } from '../src/lite-api/min_key'; diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index ec6fdd2c4ea..b9d14923bcd 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -173,14 +173,7 @@ export { deleteField, increment, serverTimestamp, - vector, - int32, - regex, - bsonBinaryData, - bsonObjectId, - bsonTimestamp, - minKey, - maxKey + vector } from './api/field_value_impl'; export { VectorValue } from './lite-api/vector_value'; @@ -193,7 +186,7 @@ export { BsonBinaryData } from './lite-api/bson_binary_data'; export { BsonObjectId } from './lite-api/bson_object_Id'; -export { BsonTimestamp } from './lite-api/bson_timestamp_value'; +export { BsonTimestamp } from './lite-api/bson_timestamp'; export { MinKey } from './lite-api/min_key'; diff --git a/packages/firestore/src/api/field_value_impl.ts b/packages/firestore/src/api/field_value_impl.ts index 6e65d273259..1b1283a3543 100644 --- a/packages/firestore/src/api/field_value_impl.ts +++ b/packages/firestore/src/api/field_value_impl.ts @@ -21,12 +21,5 @@ export { arrayUnion, serverTimestamp, deleteField, - vector, - int32, - regex, - bsonBinaryData, - bsonObjectId, - bsonTimestamp, - minKey, - maxKey + vector } from '../lite-api/field_value_impl'; diff --git a/packages/firestore/src/lite-api/bson_binary_data.ts b/packages/firestore/src/lite-api/bson_binary_data.ts index 233dd790aec..8b4b1fe0ef0 100644 --- a/packages/firestore/src/lite-api/bson_binary_data.ts +++ b/packages/firestore/src/lite-api/bson_binary_data.ts @@ -24,13 +24,9 @@ import { Code, FirestoreError } from '../util/error'; * @class BsonBinaryData */ export class BsonBinaryData { - /** The subtype for the data */ - readonly subtype: number; - - /** The binary data as a byte array */ readonly data: Uint8Array; - constructor(subtype: number, data: Uint8Array) { + constructor(readonly subtype: number, data: Uint8Array) { if (subtype < 0 || subtype > 255) { throw new FirestoreError( Code.INVALID_ARGUMENT, diff --git a/packages/firestore/src/lite-api/bson_timestamp_value.ts b/packages/firestore/src/lite-api/bson_timestamp.ts similarity index 72% rename from packages/firestore/src/lite-api/bson_timestamp_value.ts rename to packages/firestore/src/lite-api/bson_timestamp.ts index 0b317f9042c..dc18db02bb1 100644 --- a/packages/firestore/src/lite-api/bson_timestamp_value.ts +++ b/packages/firestore/src/lite-api/bson_timestamp.ts @@ -21,7 +21,19 @@ * @class BsonTimestamp */ export class BsonTimestamp { - constructor(readonly seconds: number, readonly increment: number) {} + constructor(readonly seconds: number, readonly increment: number) { + // Make sure 'seconds' and 'increment' are in the range of a 32-bit unsigned integer. + if (seconds < 0 || seconds > 4294967295) { + throw new Error( + "BsonTimestamp 'seconds' must be in the range of a 32-bit unsigned integer." + ); + } + if (increment < 0 || increment > 4294967295) { + throw new Error( + "BsonTimestamp 'increment' must be in the range of a 32-bit unsigned integer." + ); + } + } /** * Returns true if this `BsonTimestamp` is equal to the provided one. diff --git a/packages/firestore/src/lite-api/field_value_impl.ts b/packages/firestore/src/lite-api/field_value_impl.ts index ade0656e0d3..11db1005235 100644 --- a/packages/firestore/src/lite-api/field_value_impl.ts +++ b/packages/firestore/src/lite-api/field_value_impl.ts @@ -14,15 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { BsonBinaryData } from './bson_binary_data'; -import { BsonObjectId } from './bson_object_Id'; -import { BsonTimestamp } from './bson_timestamp_value'; import { FieldValue } from './field_value'; -import { Int32Value } from './int32_value'; -import { MaxKey } from './max_key'; -import { MinKey } from './min_key'; -import { RegexValue } from './regex_value'; import { ArrayRemoveFieldValueImpl, ArrayUnionFieldValueImpl, @@ -116,85 +108,3 @@ export function increment(n: number): FieldValue { export function vector(values?: number[]): VectorValue { return new VectorValue(values); } - -/** - * Creates a new `Int32Value` constructed with the given number. - * - * @param value - The 32-bit number to be used for constructing the Int32Value - * - * @returns A new `Int32Value` constructed with the given number. - */ -export function int32(value: number): Int32Value { - return new Int32Value(value); -} - -/** - * Creates a new `RegexValue` constructed with the given pattern and options. - * - * @param subtype - The subtype of the BSON binary data. - * @param data - The data to use for the BSON binary data. - * - * @returns A new `RegexValue` constructed with the given pattern and options. - */ -export function regex(pattern: string, options: string): RegexValue { - return new RegexValue(pattern, options); -} - -/** - * Creates a new `BsonBinaryData` constructed with the given subtype and data. - * - * @param subtype - Create a `BsonBinaryData` instance with the given subtype. - * @param data - Create a `BsonBinaryData` instance with a copy of this array of numbers. - * - * @returns A new `BsonBinaryData` constructed with the given subtype and data. - */ -export function bsonBinaryData( - subtype: number, - data: Uint8Array -): BsonBinaryData { - return new BsonBinaryData(subtype, data); -} - -/** - * Creates a new `BsonObjectId` constructed with the given string. - * - * @param value - The 24-character hex string representing the ObjectId. - * - * @returns A new `BsonObjectId` constructed with the given string. - */ -export function bsonObjectId(value: string): BsonObjectId { - return new BsonObjectId(value); -} - -/** - * Creates a new `BsonTimestamp` constructed with the given seconds and increment. - * - * @param seconds - The underlying unsigned 32-bit integer for seconds. - * @param seconds - The underlying unsigned 32-bit integer for increment. - * - * @returns A new `BsonTimestamp` constructed with the given seconds and increment. - */ -export function bsonTimestamp( - seconds: number, - increment: number -): BsonTimestamp { - return new BsonTimestamp(seconds, increment); -} - -/** - * Creates or returns a `MinKey` instance. - * - * @returns A `MinKey` instance. - */ -export function minKey(): MinKey { - return MinKey.instance(); -} - -/** - * Creates or returns a `MaxKey` instance. - * - * @returns A `MaxKey` instance. - */ -export function maxKey(): MaxKey { - return MaxKey.instance(); -} diff --git a/packages/firestore/src/lite-api/user_data_reader.ts b/packages/firestore/src/lite-api/user_data_reader.ts index 9d7e6fa79f1..008a5225d9d 100644 --- a/packages/firestore/src/lite-api/user_data_reader.ts +++ b/packages/firestore/src/lite-api/user_data_reader.ts @@ -77,7 +77,7 @@ import { Dict, forEach, isEmpty } from '../util/obj'; import { BsonBinaryData } from './bson_binary_data'; import { BsonObjectId } from './bson_object_Id'; -import { BsonTimestamp } from './bson_timestamp_value'; +import { BsonTimestamp } from './bson_timestamp'; import { Bytes } from './bytes'; import { Firestore } from './database'; import { FieldPath } from './field_path'; diff --git a/packages/firestore/src/lite-api/user_data_writer.ts b/packages/firestore/src/lite-api/user_data_writer.ts index e4719591b4c..94e3f96fe12 100644 --- a/packages/firestore/src/lite-api/user_data_writer.ts +++ b/packages/firestore/src/lite-api/user_data_writer.ts @@ -60,10 +60,11 @@ import { forEach } from '../util/obj'; import { BsonBinaryData } from './bson_binary_data'; import { BsonObjectId } from './bson_object_Id'; -import { BsonTimestamp } from './bson_timestamp_value'; -import { maxKey, minKey } from './field_value_impl'; +import { BsonTimestamp } from './bson_timestamp'; import { GeoPoint } from './geo_point'; import { Int32Value } from './int32_value'; +import { MaxKey } from './max_key'; +import { MinKey } from './min_key'; import { RegexValue } from './regex_value'; import { Timestamp } from './timestamp'; import { VectorValue } from './vector_value'; @@ -118,9 +119,9 @@ export abstract class AbstractUserDataWriter { case TypeOrder.BsonTimestampValue: return this.convertToBsonTimestamp(value.mapValue!); case TypeOrder.MaxKeyValue: - return maxKey(); + return MaxKey.instance(); case TypeOrder.MinKeyValue: - return minKey(); + return MinKey.instance(); default: throw fail('Invalid value type: ' + JSON.stringify(value)); } diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index 2071b786f24..6630384bce1 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -67,17 +67,17 @@ import { QuerySnapshot, vector, getDocsFromServer, - bsonBinaryData, - bsonObjectId, - bsonTimestamp, - int32, - maxKey, - minKey, - regex, or, newTestFirestore, GeoPoint, - Bytes + Bytes, + BsonBinaryData, + BsonObjectId, + Int32Value, + MaxKey, + MinKey, + RegexValue, + BsonTimestamp } from '../util/firebase_export'; import { apiDescribe, @@ -2454,20 +2454,20 @@ apiDescribe('Database', persistence => { {}, async coll => { const docRef = await addDoc(coll, { - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), - objectId: bsonObjectId('507f191e810c19729de860ea'), - int32: int32(1), - min: minKey(), - max: maxKey(), - regex: regex('^foo', 'i') + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + objectId: new BsonObjectId('507f191e810c19729de860ea'), + int32: new Int32Value(1), + min: MinKey.instance(), + max: MaxKey.instance(), + regex: new RegexValue('^foo', 'i') }); await setDoc( docRef, { - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), - timestamp: bsonTimestamp(1, 2), - int32: int32(2) + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: new BsonTimestamp(1, 2), + int32: new Int32Value(2) }, { merge: true } ); @@ -2476,19 +2476,20 @@ apiDescribe('Database', persistence => { expect( snapshot .get('objectId') - .isEqual(bsonObjectId('507f191e810c19729de860ea')) + .isEqual(new BsonObjectId('507f191e810c19729de860ea')) ).to.be.true; - expect(snapshot.get('int32').isEqual(int32(2))).to.be.true; - expect(snapshot.get('min') === minKey()).to.be.true; - expect(snapshot.get('max') === maxKey()).to.be.true; + expect(snapshot.get('int32').isEqual(new Int32Value(2))).to.be.true; + expect(snapshot.get('min') === MinKey.instance()).to.be.true; + expect(snapshot.get('max') === MaxKey.instance()).to.be.true; expect( snapshot .get('binary') - .isEqual(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + .isEqual(new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ).to.be.true; - expect(snapshot.get('timestamp').isEqual(bsonTimestamp(1, 2))).to.be - .true; - expect(snapshot.get('regex').isEqual(regex('^foo', 'i'))).to.be.true; + expect(snapshot.get('timestamp').isEqual(new BsonTimestamp(1, 2))).to + .be.true; + expect(snapshot.get('regex').isEqual(new RegexValue('^foo', 'i'))).to + .be.true; } ); }); @@ -2506,41 +2507,42 @@ apiDescribe('Database', persistence => { // Adding docs to cache, do not wait for promise to resolve. // eslint-disable-next-line @typescript-eslint/no-floating-promises setDoc(docRef, { - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), - objectId: bsonObjectId('507f191e810c19729de860ea'), - int32: int32(1), - regex: regex('^foo', 'i'), - timestamp: bsonTimestamp(1, 2), - min: minKey(), - max: maxKey() + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + objectId: new BsonObjectId('507f191e810c19729de860ea'), + int32: new Int32Value(1), + regex: new RegexValue('^foo', 'i'), + timestamp: new BsonTimestamp(1, 2), + min: MinKey.instance(), + max: MaxKey.instance() }); const snapshot = await getDocFromCache(docRef); expect( snapshot .get('binary') - .isEqual(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + .isEqual(new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ).to.be.true; expect( snapshot .get('objectId') - .isEqual(bsonObjectId('507f191e810c19729de860ea')) + .isEqual(new BsonObjectId('507f191e810c19729de860ea')) ).to.be.true; - expect(snapshot.get('int32').isEqual(int32(1))).to.be.true; - expect(snapshot.get('regex').isEqual(regex('^foo', 'i'))).to.be.true; - expect(snapshot.get('timestamp').isEqual(bsonTimestamp(1, 2))).to.be - .true; - expect(snapshot.get('min') === minKey()).to.be.true; - expect(snapshot.get('max') === maxKey()).to.be.true; + expect(snapshot.get('int32').isEqual(new Int32Value(1))).to.be.true; + expect(snapshot.get('regex').isEqual(new RegexValue('^foo', 'i'))).to + .be.true; + expect(snapshot.get('timestamp').isEqual(new BsonTimestamp(1, 2))).to + .be.true; + expect(snapshot.get('min') === MinKey.instance()).to.be.true; + expect(snapshot.get('max') === MaxKey.instance()).to.be.true; } ); }); it('can filter and order objectIds', async () => { const testDocs = { - a: { key: bsonObjectId('507f191e810c19729de860ea') }, - b: { key: bsonObjectId('507f191e810c19729de860eb') }, - c: { key: bsonObjectId('507f191e810c19729de860ec') } + a: { key: new BsonObjectId('507f191e810c19729de860ea') }, + b: { key: new BsonObjectId('507f191e810c19729de860eb') }, + c: { key: new BsonObjectId('507f191e810c19729de860ec') } }; return withTestProjectIdAndCollectionSettings( @@ -2554,7 +2556,7 @@ apiDescribe('Database', persistence => { let orderedQuery = query( coll, - where('key', '>', bsonObjectId('507f191e810c19729de860ea')), + where('key', '>', new BsonObjectId('507f191e810c19729de860ea')), orderBy('key', 'desc') ); @@ -2572,8 +2574,8 @@ apiDescribe('Database', persistence => { orderedQuery = query( coll, where('key', 'in', [ - bsonObjectId('507f191e810c19729de860ea'), - bsonObjectId('507f191e810c19729de860eb') + new BsonObjectId('507f191e810c19729de860ea'), + new BsonObjectId('507f191e810c19729de860eb') ]), orderBy('key', 'desc') ); @@ -2594,9 +2596,9 @@ apiDescribe('Database', persistence => { it('can filter and order Int32 values', async () => { const testDocs = { - a: { key: int32(-1) }, - b: { key: int32(1) }, - c: { key: int32(2) } + a: { key: new Int32Value(-1) }, + b: { key: new Int32Value(1) }, + c: { key: new Int32Value(2) } }; return withTestProjectIdAndCollectionSettings( persistence, @@ -2609,7 +2611,7 @@ apiDescribe('Database', persistence => { let orderedQuery = query( coll, - where('key', '>=', int32(1)), + where('key', '>=', new Int32Value(1)), orderBy('key', 'desc') ); @@ -2626,7 +2628,7 @@ apiDescribe('Database', persistence => { orderedQuery = query( coll, - where('key', 'not-in', [int32(1)]), + where('key', 'not-in', [new Int32Value(1)]), orderBy('key', 'desc') ); @@ -2646,9 +2648,9 @@ apiDescribe('Database', persistence => { it('can filter and order Timestamp values', async () => { const testDocs = { - a: { key: bsonTimestamp(1, 1) }, - b: { key: bsonTimestamp(1, 2) }, - c: { key: bsonTimestamp(2, 1) } + a: { key: new BsonTimestamp(1, 1) }, + b: { key: new BsonTimestamp(1, 2) }, + c: { key: new BsonTimestamp(2, 1) } }; return withTestProjectIdAndCollectionSettings( persistence, @@ -2661,7 +2663,7 @@ apiDescribe('Database', persistence => { let orderedQuery = query( coll, - where('key', '>', bsonTimestamp(1, 1)), + where('key', '>', new BsonTimestamp(1, 1)), orderBy('key', 'desc') ); @@ -2678,7 +2680,7 @@ apiDescribe('Database', persistence => { orderedQuery = query( coll, - where('key', '!=', bsonTimestamp(1, 1)), + where('key', '!=', new BsonTimestamp(1, 1)), orderBy('key', 'desc') ); @@ -2698,9 +2700,9 @@ apiDescribe('Database', persistence => { it('can filter and order Binary values', async () => { const testDocs = { - a: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }, - b: { key: bsonBinaryData(1, new Uint8Array([1, 2, 4])) }, - c: { key: bsonBinaryData(2, new Uint8Array([1, 2, 3])) } + a: { key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }, + b: { key: new BsonBinaryData(1, new Uint8Array([1, 2, 4])) }, + c: { key: new BsonBinaryData(2, new Uint8Array([1, 2, 3])) } }; return withTestProjectIdAndCollectionSettings( persistence, @@ -2713,7 +2715,7 @@ apiDescribe('Database', persistence => { let orderedQuery = query( coll, - where('key', '>', bsonBinaryData(1, new Uint8Array([1, 2, 3]))), + where('key', '>', new BsonBinaryData(1, new Uint8Array([1, 2, 3]))), orderBy('key', 'desc') ); @@ -2730,8 +2732,12 @@ apiDescribe('Database', persistence => { orderedQuery = query( coll, - where('key', '>=', bsonBinaryData(1, new Uint8Array([1, 2, 3]))), - where('key', '<', bsonBinaryData(2, new Uint8Array([1, 2, 3]))), + where( + 'key', + '>=', + new BsonBinaryData(1, new Uint8Array([1, 2, 3])) + ), + where('key', '<', new BsonBinaryData(2, new Uint8Array([1, 2, 3]))), orderBy('key', 'desc') ); @@ -2751,9 +2757,9 @@ apiDescribe('Database', persistence => { it('can filter and order Regex values', async () => { const testDocs = { - a: { key: regex('^bar', 'i') }, - b: { key: regex('^bar', 'x') }, - c: { key: regex('^baz', 'i') } + a: { key: new RegexValue('^bar', 'i') }, + b: { key: new RegexValue('^bar', 'x') }, + c: { key: new RegexValue('^baz', 'i') } }; return withTestProjectIdAndCollectionSettings( persistence, @@ -2767,8 +2773,8 @@ apiDescribe('Database', persistence => { const orderedQuery = query( coll, or( - where('key', '>', regex('^bar', 'x')), - where('key', '!=', regex('^bar', 'x')) + where('key', '>', new RegexValue('^bar', 'x')), + where('key', '!=', new RegexValue('^bar', 'x')) ), orderBy('key', 'desc') ); @@ -2789,11 +2795,11 @@ apiDescribe('Database', persistence => { it('can filter and order minKey values', async () => { const testDocs = { - a: { key: minKey() }, - b: { key: minKey() }, + a: { key: MinKey.instance() }, + b: { key: MinKey.instance() }, c: { key: null }, d: { key: 1 }, - e: { key: maxKey() } + e: { key: MaxKey.instance() } }; return withTestProjectIdAndCollectionSettings( persistence, @@ -2804,7 +2810,10 @@ apiDescribe('Database', persistence => { // Populate the cache with all docs first await getDocs(coll); - let filteredQuery = query(coll, where('key', '==', minKey())); + let filteredQuery = query( + coll, + where('key', '==', MinKey.instance()) + ); let snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ testDocs['a'], @@ -2817,7 +2826,7 @@ apiDescribe('Database', persistence => { ); // TODO(Mila/BSON): uncomment after the null inclusion bug - // filteredQuery = query(coll, where('key', '!=', minKey())); + // filteredQuery = query(coll, where('key', '!=', MinKey.instance())); // snapshot = await getDocs(filteredQuery); // expect(toDataArray(snapshot)).to.deep.equal([ // testDocs['d'], @@ -2829,7 +2838,7 @@ apiDescribe('Database', persistence => { // toIds(snapshot) // ); - filteredQuery = query(coll, where('key', '>=', minKey())); + filteredQuery = query(coll, where('key', '>=', MinKey.instance())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ testDocs['a'], @@ -2841,7 +2850,7 @@ apiDescribe('Database', persistence => { toIds(snapshot) ); - filteredQuery = query(coll, where('key', '<=', minKey())); + filteredQuery = query(coll, where('key', '<=', MinKey.instance())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ testDocs['a'], @@ -2853,7 +2862,7 @@ apiDescribe('Database', persistence => { toIds(snapshot) ); - filteredQuery = query(coll, where('key', '>', minKey())); + filteredQuery = query(coll, where('key', '>', MinKey.instance())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); await assertSDKQueryResultsConsistentWithBackend( @@ -2862,7 +2871,7 @@ apiDescribe('Database', persistence => { toIds(snapshot) ); - filteredQuery = query(coll, where('key', '<', minKey())); + filteredQuery = query(coll, where('key', '<', MinKey.instance())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); await assertSDKQueryResultsConsistentWithBackend( @@ -2885,10 +2894,10 @@ apiDescribe('Database', persistence => { it('can filter and order maxKey values', async () => { const testDocs = { - a: { key: minKey() }, + a: { key: MinKey.instance() }, b: { key: 1 }, - c: { key: maxKey() }, - d: { key: maxKey() }, + c: { key: MaxKey.instance() }, + d: { key: MaxKey.instance() }, e: { key: null } }; return withTestProjectIdAndCollectionSettings( @@ -2900,7 +2909,10 @@ apiDescribe('Database', persistence => { // Populate the cache with all docs first await getDocs(coll); - let filteredQuery = query(coll, where('key', '==', maxKey())); + let filteredQuery = query( + coll, + where('key', '==', MaxKey.instance()) + ); let snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ testDocs['c'], @@ -2913,7 +2925,7 @@ apiDescribe('Database', persistence => { ); // TODO(Mila/BSON): uncomment after the null inclusion bug - // filteredQuery = query(coll, where('key', '!=', maxKey())); + // filteredQuery = query(coll, where('key', '!=', MaxKey.instance())); // snapshot = await getDocs(filteredQuery); // expect(toDataArray(snapshot)).to.deep.equal([ // testDocs['a'], @@ -2925,7 +2937,7 @@ apiDescribe('Database', persistence => { // toIds(snapshot) // ); - filteredQuery = query(coll, where('key', '>=', maxKey())); + filteredQuery = query(coll, where('key', '>=', MaxKey.instance())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ testDocs['c'], @@ -2937,7 +2949,7 @@ apiDescribe('Database', persistence => { toIds(snapshot) ); - filteredQuery = query(coll, where('key', '<=', maxKey())); + filteredQuery = query(coll, where('key', '<=', MaxKey.instance())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ testDocs['c'], @@ -2949,7 +2961,7 @@ apiDescribe('Database', persistence => { toIds(snapshot) ); - filteredQuery = query(coll, where('key', '>', maxKey())); + filteredQuery = query(coll, where('key', '>', MaxKey.instance())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); await assertSDKQueryResultsConsistentWithBackend( @@ -2958,7 +2970,7 @@ apiDescribe('Database', persistence => { toIds(snapshot) ); - filteredQuery = query(coll, where('key', '<', maxKey())); + filteredQuery = query(coll, where('key', '<', MaxKey.instance())); snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); await assertSDKQueryResultsConsistentWithBackend( @@ -2981,11 +2993,11 @@ apiDescribe('Database', persistence => { it('can handle null with bson values', async () => { const testDocs = { - a: { key: minKey() }, + a: { key: MinKey.instance() }, b: { key: null }, c: { key: null }, d: { key: 1 }, - e: { key: maxKey() } + e: { key: MaxKey.instance() } }; return withTestProjectIdAndCollectionSettings( @@ -3027,12 +3039,12 @@ apiDescribe('Database', persistence => { it('can listen to documents with bson types', async () => { const testDocs = { - a: { key: maxKey() }, - b: { key: minKey() }, - c: { key: bsonTimestamp(1, 2) }, - d: { key: bsonObjectId('507f191e810c19729de860ea') }, - e: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }, - f: { key: regex('^foo', 'i') } + a: { key: MaxKey.instance() }, + b: { key: MinKey.instance() }, + c: { key: new BsonTimestamp(1, 2) }, + d: { key: new BsonObjectId('507f191e810c19729de860ea') }, + e: { key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }, + f: { key: new RegexValue('^foo', 'i') } }; return withTestProjectIdAndCollectionSettings( persistence, @@ -3055,7 +3067,7 @@ apiDescribe('Database', persistence => { testDocs['a'] ]); - const newData = { key: int32(2) }; + const newData = { key: new Int32Value(2) }; await setDoc(doc(coll, 'g'), newData); listenSnapshot = await storeEvent.awaitEvent(); expect(toDataArray(listenSnapshot)).to.deep.equal([ @@ -3077,9 +3089,9 @@ apiDescribe('Database', persistence => { // eslint-disable-next-line no-restricted-properties it.skip('can run transactions on documents with bson types', async () => { const testDocs = { - a: { key: bsonTimestamp(1, 2) }, - b: { key: regex('^foo', 'i') }, - c: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) } + a: { key: new BsonTimestamp(1, 2) }, + b: { key: new RegexValue('^foo', 'i') }, + c: { key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) } }; return withTestProjectIdAndCollectionSettings( persistence, @@ -3114,24 +3126,24 @@ apiDescribe('Database', persistence => { it('SDK orders different value types together the same way online and offline', async () => { const testDocs: { [key: string]: DocumentData } = { a: { key: null }, - b: { key: minKey() }, + b: { key: MinKey.instance() }, c: { key: true }, d: { key: NaN }, - e: { key: int32(1) }, + e: { key: new Int32Value(1) }, f: { key: 2.0 }, g: { key: 3 }, h: { key: new Timestamp(100, 123456000) }, - i: { key: bsonTimestamp(1, 2) }, + i: { key: new BsonTimestamp(1, 2) }, j: { key: 'string' }, k: { key: Bytes.fromUint8Array(new Uint8Array([0, 1, 255])) }, - l: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }, - n: { key: bsonObjectId('507f191e810c19729de860ea') }, + l: { key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }, + n: { key: new BsonObjectId('507f191e810c19729de860ea') }, o: { key: new GeoPoint(0, 0) }, - p: { key: regex('^foo', 'i') }, + p: { key: new RegexValue('^foo', 'i') }, q: { key: [1, 2] }, r: { key: vector([1, 2]) }, s: { key: { a: 1 } }, - t: { key: maxKey() } + t: { key: MaxKey.instance() } }; return withTestProjectIdAndCollectionSettings( @@ -3181,25 +3193,25 @@ apiDescribe('Database', persistence => { it('SDK orders bson types the same way online and offline', async () => { const testDocs: { [key: string]: DocumentData } = { - a: { key: maxKey() }, // maxKeys are all equal - b: { key: maxKey() }, - c: { key: int32(1) }, - d: { key: int32(-1) }, - e: { key: int32(0) }, - f: { key: bsonTimestamp(1, 1) }, - g: { key: bsonTimestamp(2, 1) }, - h: { key: bsonTimestamp(1, 2) }, - i: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }, - j: { key: bsonBinaryData(1, new Uint8Array([1, 1, 4])) }, - k: { key: bsonBinaryData(2, new Uint8Array([1, 0, 0])) }, - l: { key: bsonObjectId('507f191e810c19729de860eb') }, - m: { key: bsonObjectId('507f191e810c19729de860ea') }, - n: { key: bsonObjectId('407f191e810c19729de860ea') }, - o: { key: regex('^foo', 'i') }, - p: { key: regex('^foo', 'm') }, - q: { key: regex('^bar', 'i') }, - r: { key: minKey() }, // minKeys are all equal - s: { key: minKey() } + a: { key: MaxKey.instance() }, // maxKeys are all equal + b: { key: MaxKey.instance() }, + c: { key: new Int32Value(1) }, + d: { key: new Int32Value(-1) }, + e: { key: new Int32Value(0) }, + f: { key: new BsonTimestamp(1, 1) }, + g: { key: new BsonTimestamp(2, 1) }, + h: { key: new BsonTimestamp(1, 2) }, + i: { key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }, + j: { key: new BsonBinaryData(1, new Uint8Array([1, 1, 4])) }, + k: { key: new BsonBinaryData(2, new Uint8Array([1, 0, 0])) }, + l: { key: new BsonObjectId('507f191e810c19729de860eb') }, + m: { key: new BsonObjectId('507f191e810c19729de860ea') }, + n: { key: new BsonObjectId('407f191e810c19729de860ea') }, + o: { key: new RegexValue('^foo', 'i') }, + p: { key: new RegexValue('^foo', 'm') }, + q: { key: new RegexValue('^bar', 'i') }, + r: { key: MinKey.instance() }, // minKeys are all equal + s: { key: MinKey.instance() } }; return withTestProjectIdAndCollectionSettings( diff --git a/packages/firestore/test/integration/api/type.test.ts b/packages/firestore/test/integration/api/type.test.ts index 156eba426f8..2f7cb7f9295 100644 --- a/packages/firestore/test/integration/api/type.test.ts +++ b/packages/firestore/test/integration/api/type.test.ts @@ -20,9 +20,9 @@ import { expect } from 'chai'; import { addEqualityMatcher } from '../../util/equality_matcher'; import { EventsAccumulator } from '../util/events_accumulator'; import { - bsonBinaryData, - bsonObjectId, - bsonTimestamp, + BsonBinaryData, + BsonObjectId, + BsonTimestamp, Bytes, collection, doc, @@ -34,15 +34,15 @@ import { GeoPoint, getDoc, getDocs, - int32, - maxKey, - minKey, + Int32Value, + MaxKey, + MinKey, onSnapshot, orderBy, query, QuerySnapshot, refEqual, - regex, + RegexValue, runTransaction, setDoc, Timestamp, @@ -256,7 +256,9 @@ apiDescribe('Firestore', persistence => { settings, 1, async dbs => { - await expectRoundtripWithoutTransaction(dbs[0], { min: minKey() }); + await expectRoundtripWithoutTransaction(dbs[0], { + min: MinKey.instance() + }); } ); }); @@ -268,7 +270,9 @@ apiDescribe('Firestore', persistence => { settings, 1, async dbs => { - await expectRoundtripWithoutTransaction(dbs[0], { max: maxKey() }); + await expectRoundtripWithoutTransaction(dbs[0], { + max: MaxKey.instance() + }); } ); }); @@ -281,7 +285,7 @@ apiDescribe('Firestore', persistence => { 1, async dbs => { await expectRoundtripWithoutTransaction(dbs[0], { - regex: regex('^foo', 'i') + regex: new RegexValue('^foo', 'i') }); } ); @@ -294,7 +298,9 @@ apiDescribe('Firestore', persistence => { settings, 1, async dbs => { - await expectRoundtripWithoutTransaction(dbs[0], { int32: int32(1) }); + await expectRoundtripWithoutTransaction(dbs[0], { + int32: new Int32Value(1) + }); } ); }); @@ -307,7 +313,7 @@ apiDescribe('Firestore', persistence => { 1, async dbs => { await expectRoundtripWithoutTransaction(dbs[0], { - bsonTimestamp: bsonTimestamp(1, 2) + bsonTimestamp: new BsonTimestamp(1, 2) }); } ); @@ -321,7 +327,7 @@ apiDescribe('Firestore', persistence => { 1, async dbs => { await expectRoundtripWithoutTransaction(dbs[0], { - objectId: bsonObjectId('507f191e810c19729de860ea') + objectId: new BsonObjectId('507f191e810c19729de860ea') }); } ); @@ -335,7 +341,7 @@ apiDescribe('Firestore', persistence => { 1, async dbs => { await expectRoundtripWithoutTransaction(dbs[0], { - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }); } ); @@ -350,12 +356,12 @@ apiDescribe('Firestore', persistence => { async dbs => { await expectRoundtripWithoutTransaction(dbs[0], { array: [ - bsonBinaryData(1, new Uint8Array([1, 2, 3])), - bsonObjectId('507f191e810c19729de860ea'), - int32(1), - minKey(), - maxKey(), - regex('^foo', 'i') + new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + new BsonObjectId('507f191e810c19729de860ea'), + new Int32Value(1), + MinKey.instance(), + MaxKey.instance(), + new RegexValue('^foo', 'i') ] }); } @@ -371,12 +377,12 @@ apiDescribe('Firestore', persistence => { async dbs => { await expectRoundtripWithoutTransaction(dbs[0], { object: { - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), - objectId: bsonObjectId('507f191e810c19729de860ea'), - int32: int32(1), - min: minKey(), - max: maxKey(), - regex: regex('^foo', 'i') + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + objectId: new BsonObjectId('507f191e810c19729de860ea'), + int32: new Int32Value(1), + min: MinKey.instance(), + max: MaxKey.instance(), + regex: new RegexValue('^foo', 'i') } }); } @@ -393,7 +399,7 @@ apiDescribe('Firestore', persistence => { const docRef = doc(coll, 'test-doc'); let errorMessage; try { - await setDoc(docRef, { key: int32(2147483648) }); + await setDoc(docRef, { key: new Int32Value(2147483648) }); } catch (err) { errorMessage = (err as FirestoreError)?.message; } @@ -402,7 +408,7 @@ apiDescribe('Firestore', persistence => { ); try { - await setDoc(docRef, { key: int32(-2147483650) }); + await setDoc(docRef, { key: new Int32Value(-2147483650) }); } catch (err) { errorMessage = (err as FirestoreError)?.message; } @@ -424,22 +430,22 @@ apiDescribe('Firestore', persistence => { let errorMessage; try { // BSON timestamp larger than 32-bit integer gets rejected - await setDoc(docRef, { key: bsonTimestamp(4294967296, 2) }); + await setDoc(docRef, { key: new BsonTimestamp(4294967296, 2) }); } catch (err) { errorMessage = (err as FirestoreError)?.message; } expect(errorMessage).to.contains( - "The field 'seconds' value (4,294,967,296) does not represent an unsigned 32-bit integer." + "BsonTimestamp 'seconds' must be in the range of a 32-bit unsigned integer." ); try { // negative BSON timestamp gets rejected - await setDoc(docRef, { key: bsonTimestamp(-1, 2) }); + await setDoc(docRef, { key: new BsonTimestamp(-1, 2) }); } catch (err) { errorMessage = (err as FirestoreError)?.message; } expect(errorMessage).to.contains( - "The field 'seconds' value (-1) does not represent an unsigned 32-bit integer." + "BsonTimestamp 'seconds' must be in the range of a 32-bit unsigned integer." ); } ); @@ -455,7 +461,7 @@ apiDescribe('Firestore', persistence => { const docRef = doc(coll, 'test-doc'); let errorMessage; try { - await setDoc(docRef, { key: regex('foo', 'a') }); + await setDoc(docRef, { key: new RegexValue('foo', 'a') }); } catch (err) { errorMessage = (err as FirestoreError)?.message; } @@ -478,7 +484,7 @@ apiDescribe('Firestore', persistence => { let errorMessage; try { // bsonObjectId with length not equal to 24 gets rejected - await setDoc(docRef, { key: bsonObjectId('foo') }); + await setDoc(docRef, { key: new BsonObjectId('foo') }); } catch (err) { errorMessage = (err as FirestoreError)?.message; } @@ -500,7 +506,7 @@ apiDescribe('Firestore', persistence => { let errorMessage; try { await setDoc(docRef, { - key: bsonBinaryData(1234, new Uint8Array([1, 2, 3])) + key: new BsonBinaryData(1234, new Uint8Array([1, 2, 3])) }); } catch (err) { errorMessage = (err as FirestoreError)?.message; @@ -515,26 +521,28 @@ apiDescribe('Firestore', persistence => { it('can order values of different TypeOrder together', async () => { const testDocs: { [key: string]: DocumentData } = { nullValue: { key: null }, - minValue: { key: minKey() }, + minValue: { key: MinKey.instance() }, booleanValue: { key: true }, nanValue: { key: NaN }, - int32Value: { key: int32(1) }, + int32Value: { key: new Int32Value(1) }, doubleValue: { key: 2.0 }, integerValue: { key: 3 }, timestampValue: { key: new Timestamp(100, 123456000) }, - bsonTimestampValue: { key: bsonTimestamp(1, 2) }, + bsonTimestampValue: { key: new BsonTimestamp(1, 2) }, stringValue: { key: 'string' }, bytesValue: { key: Bytes.fromUint8Array(new Uint8Array([0, 1, 255])) }, - bsonBinaryValue: { key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) }, + bsonBinaryValue: { + key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) + }, // referenceValue: {key: ref('coll/doc')}, referenceValue: { key: 'placeholder' }, - objectIdValue: { key: bsonObjectId('507f191e810c19729de860ea') }, + objectIdValue: { key: new BsonObjectId('507f191e810c19729de860ea') }, geoPointValue: { key: new GeoPoint(0, 0) }, - regexValue: { key: regex('^foo', 'i') }, + regexValue: { key: new RegexValue('^foo', 'i') }, arrayValue: { key: [1, 2] }, vectorValue: { key: vector([1, 2]) }, objectValue: { key: { a: 1 } }, - maxValue: { key: maxKey() } + maxValue: { key: MaxKey.instance() } }; return withTestProjectIdAndCollectionSettings( diff --git a/packages/firestore/test/lite/integration.test.ts b/packages/firestore/test/lite/integration.test.ts index 9b647587503..25f372bcb95 100644 --- a/packages/firestore/test/lite/integration.test.ts +++ b/packages/firestore/test/lite/integration.test.ts @@ -28,6 +28,9 @@ import { sum, average } from '../../src/lite-api/aggregate'; +import { BsonBinaryData } from '../../src/lite-api/bson_binary_data'; +import { BsonObjectId } from '../../src/lite-api/bson_object_Id'; +import { BsonTimestamp } from '../../src/lite-api/bson_timestamp'; import { Bytes } from '../../src/lite-api/bytes'; import { Firestore, @@ -40,18 +43,14 @@ import { FieldValue } from '../../src/lite-api/field_value'; import { arrayRemove, arrayUnion, - bsonBinaryData, - bsonObjectId, - bsonTimestamp, deleteField, increment, - int32, - maxKey, - minKey, - regex, serverTimestamp, vector } from '../../src/lite-api/field_value_impl'; +import { Int32Value } from '../../src/lite-api/int32_value'; +import { MaxKey } from '../../src/lite-api/max_key'; +import { MinKey } from '../../src/lite-api/min_key'; import { endAt, endBefore, @@ -85,6 +84,7 @@ import { setDoc, updateDoc } from '../../src/lite-api/reference_impl'; +import { RegexValue } from '../../src/lite-api/regex_value'; import { FirestoreDataConverter, snapshotEqual, @@ -2974,37 +2974,41 @@ describe.skip('BSON types', () => { it('can be read and written using the lite SDK', async () => { return withTestCollection(async coll => { const ref = await addDoc(coll, { - objectId: bsonObjectId('507f191e810c19729de860ea'), - int32: int32(1), - min: minKey(), - max: maxKey(), - regex: regex('^foo', 'i') + objectId: new BsonObjectId('507f191e810c19729de860ea'), + int32: new Int32Value(1), + min: MinKey.instance(), + max: MaxKey.instance(), + regex: new RegexValue('^foo', 'i') }); await setDoc( ref, { - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), - timestamp: bsonTimestamp(1, 2), - int32: int32(2) + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: new BsonTimestamp(1, 2), + int32: new Int32Value(2) }, { merge: true } ); const snap1 = await getDoc(ref); expect( - snap1.get('objectId').isEqual(bsonObjectId('507f191e810c19729de860ea')) + snap1 + .get('objectId') + .isEqual(new BsonObjectId('507f191e810c19729de860ea')) ).to.be.true; - expect(snap1.get('int32').isEqual(int32(2))).to.be.true; - expect(snap1.get('min') === minKey()).to.be.true; - expect(snap1.get('max') === maxKey()).to.be.true; + expect(snap1.get('int32').isEqual(new Int32Value(2))).to.be.true; + expect(snap1.get('min') === MinKey.instance()).to.be.true; + expect(snap1.get('max') === MaxKey.instance()).to.be.true; expect( snap1 .get('binary') - .isEqual(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + .isEqual(new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ).to.be.true; - expect(snap1.get('timestamp').isEqual(bsonTimestamp(1, 2))).to.be.true; - expect(snap1.get('regex').isEqual(regex('^foo', 'i'))).to.be.true; + expect(snap1.get('timestamp').isEqual(new BsonTimestamp(1, 2))).to.be + .true; + expect(snap1.get('regex').isEqual(new RegexValue('^foo', 'i'))).to.be + .true; }); }); }); diff --git a/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts index c646726feeb..907881c262c 100644 --- a/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts +++ b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts @@ -16,15 +16,13 @@ */ import { expect } from 'chai'; -import { - bsonBinaryData, - bsonObjectId, - bsonTimestamp, - int32, - regex -} from '../../../lite'; import { FirestoreIndexValueWriter } from '../../../src/index/firestore_index_value_writer'; import { IndexByteEncoder } from '../../../src/index/index_byte_encoder'; +import { BsonBinaryData } from '../../../src/lite-api/bson_binary_data'; +import { BsonObjectId } from '../../../src/lite-api/bson_object_Id'; +import { BsonTimestamp } from '../../../src/lite-api/bson_timestamp'; +import { Int32Value } from '../../../src/lite-api/int32_value'; +import { RegexValue } from '../../../src/lite-api/regex_value'; import { Timestamp } from '../../../src/lite-api/timestamp'; import { parseBsonBinaryData, @@ -287,7 +285,7 @@ describe('Firestore Index Value Writer', () => { } }; const value3 = parseBsonObjectId( - bsonObjectId('507f191e810c19729de860ea') + new BsonObjectId('507f191e810c19729de860ea') ); expect( @@ -340,8 +338,8 @@ describe('Firestore Index Value Writer', () => { } } }; - const value3 = parseBsonTimestamp(bsonTimestamp(1, 2)); - const value4 = parseBsonTimestamp(bsonTimestamp(2, 1)); + const value3 = parseBsonTimestamp(new BsonTimestamp(1, 2)); + const value4 = parseBsonTimestamp(new BsonTimestamp(2, 1)); expect( compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) @@ -400,7 +398,7 @@ describe('Firestore Index Value Writer', () => { ); const value3 = parseBsonBinaryData( serializer, - bsonBinaryData(1, new Uint8Array([1, 2, 3])) + new BsonBinaryData(1, new Uint8Array([1, 2, 3])) ); const jsonSerializer = new JsonProtoSerializer( @@ -410,7 +408,7 @@ describe('Firestore Index Value Writer', () => { const value4 = parseBsonBinaryData( jsonSerializer, - bsonBinaryData(1, new Uint8Array([1, 2, 3])) + new BsonBinaryData(1, new Uint8Array([1, 2, 3])) ); expect( @@ -473,8 +471,8 @@ describe('Firestore Index Value Writer', () => { } } }; - const value3 = parseRegexValue(regex('^foo', 'i')); - const value4 = parseRegexValue(regex('^zoo', 'i')); + const value3 = parseRegexValue(new RegexValue('^foo', 'i')); + const value4 = parseRegexValue(new RegexValue('^zoo', 'i')); expect( compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) @@ -522,7 +520,8 @@ describe('Firestore Index Value Writer', () => { } } }; - const value3 = parseInt32Value(int32(1)); + const value3 = parseInt32Value(new Int32Value(1)); + const value4 = parseInt32Value(new Int32Value(2)); expect( compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) @@ -543,6 +542,16 @@ describe('Firestore Index Value Writer', () => { expect( compareIndexEncodedValues(value3, value1, IndexKind.ASCENDING) ).to.equal(0); + + expect( + compareIndexEncodedValues(value4, value1, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value4, value2, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value4, value3, IndexKind.ASCENDING) + ).to.equal(1); }); it('can compare BSON MinKey', () => { diff --git a/packages/firestore/test/unit/local/index_manager.test.ts b/packages/firestore/test/unit/local/index_manager.test.ts index 1097f5e682e..b6af448b2db 100644 --- a/packages/firestore/test/unit/local/index_manager.test.ts +++ b/packages/firestore/test/unit/local/index_manager.test.ts @@ -17,7 +17,17 @@ import { expect } from 'chai'; -import { Bytes, GeoPoint } from '../../../src/'; +import { + BsonBinaryData, + BsonObjectId, + BsonTimestamp, + Bytes, + GeoPoint, + Int32Value, + MaxKey, + MinKey, + RegexValue +} from '../../../src/'; import { User } from '../../../src/auth/user'; import { FieldFilter } from '../../../src/core/filter'; import { @@ -31,16 +41,7 @@ import { queryWithLimit, queryWithStartAt } from '../../../src/core/query'; -import { - bsonBinaryData, - bsonObjectId, - bsonTimestamp, - int32, - maxKey, - minKey, - regex, - vector -} from '../../../src/lite-api/field_value_impl'; +import { vector } from '../../../src/lite-api/field_value_impl'; import { Timestamp } from '../../../src/lite-api/timestamp'; import { displayNameForIndexType, @@ -1882,13 +1883,13 @@ describe('IndexedDbIndexManager', async () => { ); await addDoc('coll/doc1', { - key: bsonObjectId('507f191e810c19729de860ea') + key: new BsonObjectId('507f191e810c19729de860ea') }); await addDoc('coll/doc2', { - key: bsonObjectId('507f191e810c19729de860eb') + key: new BsonObjectId('507f191e810c19729de860eb') }); await addDoc('coll/doc3', { - key: bsonObjectId('507f191e810c19729de860ec') + key: new BsonObjectId('507f191e810c19729de860ec') }); const fieldIndexes = await indexManager.getFieldIndexes('coll'); @@ -1899,49 +1900,49 @@ describe('IndexedDbIndexManager', async () => { q = queryWithAddedFilter( query('coll'), - filter('key', '==', bsonObjectId('507f191e810c19729de860ea')) + filter('key', '==', new BsonObjectId('507f191e810c19729de860ea')) ); await verifyResults(q, 'coll/doc1'); q = queryWithAddedFilter( query('coll'), - filter('key', '!=', bsonObjectId('507f191e810c19729de860ea')) + filter('key', '!=', new BsonObjectId('507f191e810c19729de860ea')) ); await verifyResults(q, 'coll/doc2', 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '>=', bsonObjectId('507f191e810c19729de860eb')) + filter('key', '>=', new BsonObjectId('507f191e810c19729de860eb')) ); await verifyResults(q, 'coll/doc2', 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '<=', bsonObjectId('507f191e810c19729de860eb')) + filter('key', '<=', new BsonObjectId('507f191e810c19729de860eb')) ); await verifyResults(q, 'coll/doc1', 'coll/doc2'); q = queryWithAddedFilter( query('coll'), - filter('key', '>', bsonObjectId('507f191e810c19729de860eb')) + filter('key', '>', new BsonObjectId('507f191e810c19729de860eb')) ); await verifyResults(q, 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '<', bsonObjectId('507f191e810c19729de860eb')) + filter('key', '<', new BsonObjectId('507f191e810c19729de860eb')) ); await verifyResults(q, 'coll/doc1'); q = queryWithAddedFilter( query('coll'), - filter('key', '>', bsonObjectId('507f191e810c19729de860ec')) + filter('key', '>', new BsonObjectId('507f191e810c19729de860ec')) ); await verifyResults(q); q = queryWithAddedFilter( query('coll'), - filter('key', '<', bsonObjectId('507f191e810c19729de860ea')) + filter('key', '<', new BsonObjectId('507f191e810c19729de860ea')) ); await verifyResults(q); }); @@ -1951,13 +1952,13 @@ describe('IndexedDbIndexManager', async () => { fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) ); await addDoc('coll/doc1', { - key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }); await addDoc('coll/doc2', { - key: bsonBinaryData(1, new Uint8Array([1, 2, 4])) + key: new BsonBinaryData(1, new Uint8Array([1, 2, 4])) }); await addDoc('coll/doc3', { - key: bsonBinaryData(1, new Uint8Array([2, 1, 2])) + key: new BsonBinaryData(1, new Uint8Array([2, 1, 2])) }); const fieldIndexes = await indexManager.getFieldIndexes('coll'); @@ -1968,49 +1969,49 @@ describe('IndexedDbIndexManager', async () => { q = queryWithAddedFilter( query('coll'), - filter('key', '==', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + filter('key', '==', new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ); await verifyResults(q, 'coll/doc1'); q = queryWithAddedFilter( query('coll'), - filter('key', '!=', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + filter('key', '!=', new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ); await verifyResults(q, 'coll/doc2', 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '>=', bsonBinaryData(1, new Uint8Array([1, 2, 4]))) + filter('key', '>=', new BsonBinaryData(1, new Uint8Array([1, 2, 4]))) ); await verifyResults(q, 'coll/doc2', 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '<=', bsonBinaryData(1, new Uint8Array([1, 2, 4]))) + filter('key', '<=', new BsonBinaryData(1, new Uint8Array([1, 2, 4]))) ); await verifyResults(q, 'coll/doc1', 'coll/doc2'); q = queryWithAddedFilter( query('coll'), - filter('key', '>', bsonBinaryData(1, new Uint8Array([1, 2, 4]))) + filter('key', '>', new BsonBinaryData(1, new Uint8Array([1, 2, 4]))) ); await verifyResults(q, 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '<', bsonBinaryData(1, new Uint8Array([1, 2, 4]))) + filter('key', '<', new BsonBinaryData(1, new Uint8Array([1, 2, 4]))) ); await verifyResults(q, 'coll/doc1'); q = queryWithAddedFilter( query('coll'), - filter('key', '>', bsonBinaryData(1, new Uint8Array([2, 1, 2]))) + filter('key', '>', new BsonBinaryData(1, new Uint8Array([2, 1, 2]))) ); await verifyResults(q); q = queryWithAddedFilter( query('coll'), - filter('key', '<', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + filter('key', '<', new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ); await verifyResults(q); }); @@ -2020,13 +2021,13 @@ describe('IndexedDbIndexManager', async () => { fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) ); await addDoc('coll/doc1', { - key: bsonTimestamp(1, 1) + key: new BsonTimestamp(1, 1) }); await addDoc('coll/doc2', { - key: bsonTimestamp(1, 2) + key: new BsonTimestamp(1, 2) }); await addDoc('coll/doc3', { - key: bsonTimestamp(2, 1) + key: new BsonTimestamp(2, 1) }); const fieldIndexes = await indexManager.getFieldIndexes('coll'); @@ -2037,49 +2038,49 @@ describe('IndexedDbIndexManager', async () => { q = queryWithAddedFilter( query('coll'), - filter('key', '==', bsonTimestamp(1, 1)) + filter('key', '==', new BsonTimestamp(1, 1)) ); await verifyResults(q, 'coll/doc1'); q = queryWithAddedFilter( query('coll'), - filter('key', '!=', bsonTimestamp(1, 1)) + filter('key', '!=', new BsonTimestamp(1, 1)) ); await verifyResults(q, 'coll/doc2', 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '>=', bsonTimestamp(1, 2)) + filter('key', '>=', new BsonTimestamp(1, 2)) ); await verifyResults(q, 'coll/doc2', 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '<=', bsonTimestamp(1, 2)) + filter('key', '<=', new BsonTimestamp(1, 2)) ); await verifyResults(q, 'coll/doc1', 'coll/doc2'); q = queryWithAddedFilter( query('coll'), - filter('key', '>', bsonTimestamp(1, 2)) + filter('key', '>', new BsonTimestamp(1, 2)) ); await verifyResults(q, 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '<', bsonTimestamp(1, 2)) + filter('key', '<', new BsonTimestamp(1, 2)) ); await verifyResults(q, 'coll/doc1'); q = queryWithAddedFilter( query('coll'), - filter('key', '>', bsonTimestamp(2, 1)) + filter('key', '>', new BsonTimestamp(2, 1)) ); await verifyResults(q); q = queryWithAddedFilter( query('coll'), - filter('key', '<', bsonTimestamp(1, 1)) + filter('key', '<', new BsonTimestamp(1, 1)) ); await verifyResults(q); }); @@ -2089,13 +2090,13 @@ describe('IndexedDbIndexManager', async () => { fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) ); await addDoc('coll/doc1', { - key: int32(1) + key: new Int32Value(1) }); await addDoc('coll/doc2', { - key: int32(2) + key: new Int32Value(2) }); await addDoc('coll/doc3', { - key: int32(3) + key: new Int32Value(3) }); const fieldIndexes = await indexManager.getFieldIndexes('coll'); expect(fieldIndexes).to.have.length(1); @@ -2103,28 +2104,52 @@ describe('IndexedDbIndexManager', async () => { let q = queryWithAddedOrderBy(query('coll'), orderBy('key')); await verifyResults(q, 'coll/doc1', 'coll/doc2', 'coll/doc3'); - q = queryWithAddedFilter(query('coll'), filter('key', '==', int32(1))); + q = queryWithAddedFilter( + query('coll'), + filter('key', '==', new Int32Value(1)) + ); await verifyResults(q, 'coll/doc1'); - q = queryWithAddedFilter(query('coll'), filter('key', '!=', int32(1))); + q = queryWithAddedFilter( + query('coll'), + filter('key', '!=', new Int32Value(1)) + ); await verifyResults(q, 'coll/doc2', 'coll/doc3'); - q = queryWithAddedFilter(query('coll'), filter('key', '>=', int32(2))); + q = queryWithAddedFilter( + query('coll'), + filter('key', '>=', new Int32Value(2)) + ); await verifyResults(q, 'coll/doc2', 'coll/doc3'); - q = queryWithAddedFilter(query('coll'), filter('key', '<=', int32(2))); + q = queryWithAddedFilter( + query('coll'), + filter('key', '<=', new Int32Value(2)) + ); await verifyResults(q, 'coll/doc1', 'coll/doc2'); - q = queryWithAddedFilter(query('coll'), filter('key', '>', int32(2))); + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', new Int32Value(2)) + ); await verifyResults(q, 'coll/doc3'); - q = queryWithAddedFilter(query('coll'), filter('key', '<', int32(2))); + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', new Int32Value(2)) + ); await verifyResults(q, 'coll/doc1'); - q = queryWithAddedFilter(query('coll'), filter('key', '>', int32(3))); + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', new Int32Value(3)) + ); await verifyResults(q); - q = queryWithAddedFilter(query('coll'), filter('key', '<', int32(1))); + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', new Int32Value(1)) + ); await verifyResults(q); }); @@ -2133,13 +2158,13 @@ describe('IndexedDbIndexManager', async () => { fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) ); await addDoc('coll/doc1', { - key: regex('a', 'i') + key: new RegexValue('a', 'i') }); await addDoc('coll/doc2', { - key: regex('a', 'm') + key: new RegexValue('a', 'm') }); await addDoc('coll/doc3', { - key: regex('b', 'i') + key: new RegexValue('b', 'i') }); const fieldIndexes = await indexManager.getFieldIndexes('coll'); expect(fieldIndexes).to.have.length(1); @@ -2148,49 +2173,49 @@ describe('IndexedDbIndexManager', async () => { q = queryWithAddedFilter( query('coll'), - filter('key', '==', regex('a', 'i')) + filter('key', '==', new RegexValue('a', 'i')) ); await verifyResults(q, 'coll/doc1'); q = queryWithAddedFilter( query('coll'), - filter('key', '!=', regex('a', 'i')) + filter('key', '!=', new RegexValue('a', 'i')) ); await verifyResults(q, 'coll/doc2', 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '>=', regex('a', 'm')) + filter('key', '>=', new RegexValue('a', 'm')) ); await verifyResults(q, 'coll/doc2', 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '<=', regex('a', 'm')) + filter('key', '<=', new RegexValue('a', 'm')) ); await verifyResults(q, 'coll/doc1', 'coll/doc2'); q = queryWithAddedFilter( query('coll'), - filter('key', '>', regex('a', 'm')) + filter('key', '>', new RegexValue('a', 'm')) ); await verifyResults(q, 'coll/doc3'); q = queryWithAddedFilter( query('coll'), - filter('key', '<', regex('a', 'm')) + filter('key', '<', new RegexValue('a', 'm')) ); await verifyResults(q, 'coll/doc1'); q = queryWithAddedFilter( query('coll'), - filter('key', '>', regex('b', 'i')) + filter('key', '>', new RegexValue('b', 'i')) ); await verifyResults(q); q = queryWithAddedFilter( query('coll'), - filter('key', '<', regex('a', 'i')) + filter('key', '<', new RegexValue('a', 'i')) ); await verifyResults(q); }); @@ -2200,10 +2225,10 @@ describe('IndexedDbIndexManager', async () => { fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) ); await addDoc('coll/doc1', { - key: minKey() + key: MinKey.instance() }); await addDoc('coll/doc2', { - key: minKey() + key: MinKey.instance() }); await addDoc('coll/doc3', { key: null @@ -2212,7 +2237,7 @@ describe('IndexedDbIndexManager', async () => { key: 1 }); await addDoc('coll/doc5', { - key: maxKey() + key: MaxKey.instance() }); const fieldIndexes = await indexManager.getFieldIndexes('coll'); @@ -2228,22 +2253,40 @@ describe('IndexedDbIndexManager', async () => { 'coll/doc5' ); - q = queryWithAddedFilter(query('coll'), filter('key', '==', minKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '==', MinKey.instance()) + ); await verifyResults(q, 'coll/doc1', 'coll/doc2'); - q = queryWithAddedFilter(query('coll'), filter('key', '!=', minKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '!=', MinKey.instance()) + ); await verifyResults(q, 'coll/doc4', 'coll/doc5'); - q = queryWithAddedFilter(query('coll'), filter('key', '>=', minKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '>=', MinKey.instance()) + ); await verifyResults(q, 'coll/doc1', 'coll/doc2'); - q = queryWithAddedFilter(query('coll'), filter('key', '<=', minKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '<=', MinKey.instance()) + ); await verifyResults(q, 'coll/doc1', 'coll/doc2'); - q = queryWithAddedFilter(query('coll'), filter('key', '>', minKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', MinKey.instance()) + ); await verifyResults(q); - q = queryWithAddedFilter(query('coll'), filter('key', '<', minKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', MinKey.instance()) + ); await verifyResults(q); }); @@ -2252,16 +2295,16 @@ describe('IndexedDbIndexManager', async () => { fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) ); await addDoc('coll/doc1', { - key: minKey() + key: MinKey.instance() }); await addDoc('coll/doc2', { key: 1 }); await addDoc('coll/doc3', { - key: maxKey() + key: MaxKey.instance() }); await addDoc('coll/doc4', { - key: maxKey() + key: MaxKey.instance() }); await addDoc('coll/doc5', { key: null @@ -2280,22 +2323,40 @@ describe('IndexedDbIndexManager', async () => { 'coll/doc4' ); - q = queryWithAddedFilter(query('coll'), filter('key', '==', maxKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '==', MaxKey.instance()) + ); await verifyResults(q, 'coll/doc3', 'coll/doc4'); - q = queryWithAddedFilter(query('coll'), filter('key', '!=', maxKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '!=', MaxKey.instance()) + ); await verifyResults(q, 'coll/doc1', 'coll/doc2'); - q = queryWithAddedFilter(query('coll'), filter('key', '>=', maxKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '>=', MaxKey.instance()) + ); await verifyResults(q, 'coll/doc3', 'coll/doc4'); - q = queryWithAddedFilter(query('coll'), filter('key', '<=', maxKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '<=', MaxKey.instance()) + ); await verifyResults(q, 'coll/doc3', 'coll/doc4'); - q = queryWithAddedFilter(query('coll'), filter('key', '>', maxKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', MaxKey.instance()) + ); await verifyResults(q); - q = queryWithAddedFilter(query('coll'), filter('key', '<', maxKey())); + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', MaxKey.instance()) + ); await verifyResults(q); }); @@ -2304,45 +2365,45 @@ describe('IndexedDbIndexManager', async () => { fieldIndex('coll', { fields: [['key', IndexKind.DESCENDING]] }) ); await addDoc('coll/doc1', { - key: minKey() + key: MinKey.instance() }); await addDoc('coll/doc2', { - key: int32(2) + key: new Int32Value(2) }); await addDoc('coll/doc3', { - key: int32(1) + key: new Int32Value(1) }); await addDoc('coll/doc4', { - key: bsonTimestamp(1, 2) + key: new BsonTimestamp(1, 2) }); await addDoc('coll/doc5', { - key: bsonTimestamp(1, 1) + key: new BsonTimestamp(1, 1) }); await addDoc('coll/doc6', { - key: bsonBinaryData(1, new Uint8Array([1, 2, 4])) + key: new BsonBinaryData(1, new Uint8Array([1, 2, 4])) }); await addDoc('coll/doc7', { - key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }); await addDoc('coll/doc8', { - key: bsonObjectId('507f191e810c19729de860eb') + key: new BsonObjectId('507f191e810c19729de860eb') }); await addDoc('coll/doc9', { - key: bsonObjectId('507f191e810c19729de860ea') + key: new BsonObjectId('507f191e810c19729de860ea') }); await addDoc('coll/doc10', { - key: regex('a', 'm') + key: new RegexValue('a', 'm') }); await addDoc('coll/doc11', { - key: regex('a', 'i') + key: new RegexValue('a', 'i') }); await addDoc('coll/doc12', { - key: maxKey() + key: MaxKey.instance() }); const fieldIndexes = await indexManager.getFieldIndexes('coll'); @@ -2375,7 +2436,7 @@ describe('IndexedDbIndexManager', async () => { key: null }); await addDoc('coll/doc2', { - key: minKey() + key: MinKey.instance() }); await addDoc('coll/doc3', { key: true @@ -2384,7 +2445,7 @@ describe('IndexedDbIndexManager', async () => { key: NaN }); await addDoc('coll/doc5', { - key: int32(1) + key: new Int32Value(1) }); await addDoc('coll/doc6', { key: 2.0 @@ -2396,7 +2457,7 @@ describe('IndexedDbIndexManager', async () => { key: new Timestamp(100, 123456000) }); await addDoc('coll/doc9', { - key: bsonTimestamp(1, 2) + key: new BsonTimestamp(1, 2) }); await addDoc('coll/doc10', { key: 'string' @@ -2405,19 +2466,19 @@ describe('IndexedDbIndexManager', async () => { key: Bytes.fromUint8Array(new Uint8Array([0, 1, 255])) as Bytes }); await addDoc('coll/doc12', { - key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }); await addDoc('coll/doc13', { key: ref('coll/doc') }); await addDoc('coll/doc14', { - key: bsonObjectId('507f191e810c19729de860ea') + key: new BsonObjectId('507f191e810c19729de860ea') }); await addDoc('coll/doc15', { key: new GeoPoint(0, 1) }); await addDoc('coll/doc16', { - key: regex('^foo', 'i') + key: new RegexValue('^foo', 'i') }); await addDoc('coll/doc17', { key: [1, 2] @@ -2429,7 +2490,7 @@ describe('IndexedDbIndexManager', async () => { key: { a: 1 } }); await addDoc('coll/doc20', { - key: maxKey() + key: MaxKey.instance() }); const fieldIndexes = await indexManager.getFieldIndexes('coll'); diff --git a/packages/firestore/test/unit/local/local_store_indexeddb.test.ts b/packages/firestore/test/unit/local/local_store_indexeddb.test.ts index 0e5afbc2914..5f68684d193 100644 --- a/packages/firestore/test/unit/local/local_store_indexeddb.test.ts +++ b/packages/firestore/test/unit/local/local_store_indexeddb.test.ts @@ -18,7 +18,18 @@ import { isIndexedDBAvailable } from '@firebase/util'; import { expect } from 'chai'; -import { serverTimestamp, Timestamp, GeoPoint } from '../../../src'; +import { + serverTimestamp, + Timestamp, + GeoPoint, + BsonObjectId, + BsonBinaryData, + BsonTimestamp, + Int32Value, + RegexValue, + MaxKey, + MinKey +} from '../../../src'; import { User } from '../../../src/auth/user'; import { BundleConverterImpl } from '../../../src/core/bundle_impl'; import { @@ -30,16 +41,7 @@ import { } from '../../../src/core/query'; import { Target } from '../../../src/core/target'; import { TargetId } from '../../../src/core/types'; -import { - bsonBinaryData, - bsonObjectId, - bsonTimestamp, - int32, - maxKey, - minKey, - regex, - vector -} from '../../../src/lite-api/field_value_impl'; +import { vector } from '../../../src/lite-api/field_value_impl'; import { IndexBackfiller } from '../../../src/local/index_backfiller'; import { LocalStore } from '../../../src/local/local_store'; import { @@ -972,12 +974,14 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.writeMutations( setMutation('coll/a', { - key: bsonObjectId('507f191e810c19729de860ea') + key: new BsonObjectId('507f191e810c19729de860ea') }), setMutation('coll/b', { - key: bsonObjectId('507f191e810c19729de860eb') + key: new BsonObjectId('507f191e810c19729de860eb') }), - setMutation('coll/c', { key: bsonObjectId('507f191e810c19729de860ec') }) + setMutation('coll/c', { + key: new BsonObjectId('507f191e810c19729de860ec') + }) ); await test.backfillIndexes(); @@ -992,7 +996,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', - filter('key', '==', bsonObjectId('507f191e810c19729de860ea')) + filter('key', '==', new BsonObjectId('507f191e810c19729de860ea')) ); await test.executeQuery(query_); test.assertOverlaysRead(1, 0, { @@ -1002,7 +1006,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', - filter('key', '!=', bsonObjectId('507f191e810c19729de860ea')) + filter('key', '!=', new BsonObjectId('507f191e810c19729de860ea')) ); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { @@ -1013,7 +1017,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', - filter('key', '>=', bsonObjectId('507f191e810c19729de860eb')) + filter('key', '>=', new BsonObjectId('507f191e810c19729de860eb')) ); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { @@ -1024,7 +1028,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', - filter('key', '<', bsonObjectId('507f191e810c19729de860ea')) + filter('key', '<', new BsonObjectId('507f191e810c19729de860ea')) ); await test.executeQuery(query_); test.assertOverlaysRead(0, 0); @@ -1033,8 +1037,8 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', filter('key', 'in', [ - bsonObjectId('507f191e810c19729de860ea'), - bsonObjectId('507f191e810c19729de860eb') + new BsonObjectId('507f191e810c19729de860ea'), + new BsonObjectId('507f191e810c19729de860eb') ]) ); await test.executeQuery(query_); @@ -1047,8 +1051,8 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', filter('key', 'not-in', [ - bsonObjectId('507f191e810c19729de860ea'), - bsonObjectId('507f191e810c19729de860eb') + new BsonObjectId('507f191e810c19729de860ea'), + new BsonObjectId('507f191e810c19729de860eb') ]) ); await test.executeQuery(query_); @@ -1065,9 +1069,9 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); await test.configureFieldsIndexes(index); await test.writeMutations( - setMutation('coll/a', { key: bsonTimestamp(1000, 1000) }), - setMutation('coll/b', { key: bsonTimestamp(1001, 1000) }), - setMutation('coll/c', { key: bsonTimestamp(1000, 1001) }) + setMutation('coll/a', { key: new BsonTimestamp(1000, 1000) }), + setMutation('coll/b', { key: new BsonTimestamp(1001, 1000) }), + setMutation('coll/c', { key: new BsonTimestamp(1000, 1001) }) ); await test.backfillIndexes(); @@ -1080,14 +1084,20 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/a', 'coll/c', 'coll/b'); - query_ = query('coll', filter('key', '==', bsonTimestamp(1000, 1000))); + query_ = query( + 'coll', + filter('key', '==', new BsonTimestamp(1000, 1000)) + ); await test.executeQuery(query_); test.assertOverlaysRead(1, 0, { [key('coll/a').toString()]: MutationType.Set }); test.assertQueryReturned(query_, 'coll/a'); - query_ = query('coll', filter('key', '!=', bsonTimestamp(1000, 1000))); + query_ = query( + 'coll', + filter('key', '!=', new BsonTimestamp(1000, 1000)) + ); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/b').toString()]: MutationType.Set, @@ -1095,7 +1105,10 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/c', 'coll/b'); - query_ = query('coll', filter('key', '>=', bsonTimestamp(1000, 1001))); + query_ = query( + 'coll', + filter('key', '>=', new BsonTimestamp(1000, 1001)) + ); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/b').toString()]: MutationType.Set, @@ -1103,7 +1116,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/c', 'coll/b'); - query_ = query('coll', filter('key', '<', bsonTimestamp(1000, 1000))); + query_ = query('coll', filter('key', '<', new BsonTimestamp(1000, 1000))); await test.executeQuery(query_); test.assertOverlaysRead(0, 0); test.assertQueryReturned(query_); @@ -1111,8 +1124,8 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', filter('key', 'in', [ - bsonTimestamp(1000, 1000), - bsonTimestamp(1001, 1000) + new BsonTimestamp(1000, 1000), + new BsonTimestamp(1001, 1000) ]) ); await test.executeQuery(query_); @@ -1125,8 +1138,8 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', filter('key', 'not-in', [ - bsonTimestamp(1000, 1000), - bsonTimestamp(1001, 1000) + new BsonTimestamp(1000, 1000), + new BsonTimestamp(1001, 1000) ]) ); await test.executeQuery(query_); @@ -1144,16 +1157,16 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.configureFieldsIndexes(index); await test.writeMutations( setMutation('coll/a', { - key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }), setMutation('coll/b', { - key: bsonBinaryData(1, new Uint8Array([1, 2])) + key: new BsonBinaryData(1, new Uint8Array([1, 2])) }), setMutation('coll/c', { - key: bsonBinaryData(1, new Uint8Array([1, 2, 4])) + key: new BsonBinaryData(1, new Uint8Array([1, 2, 4])) }), setMutation('coll/d', { - key: bsonBinaryData(2, new Uint8Array([1, 2])) + key: new BsonBinaryData(2, new Uint8Array([1, 2])) }) ); await test.backfillIndexes(); @@ -1170,7 +1183,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', - filter('key', '==', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + filter('key', '==', new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ); await test.executeQuery(query_); test.assertOverlaysRead(1, 0, { @@ -1180,7 +1193,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', - filter('key', '!=', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + filter('key', '!=', new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ); await test.executeQuery(query_); test.assertOverlaysRead(3, 0, { @@ -1192,7 +1205,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', - filter('key', '>=', bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + filter('key', '>=', new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ); await test.executeQuery(query_); test.assertOverlaysRead(3, 0, { @@ -1204,7 +1217,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', - filter('key', '<', bsonBinaryData(1, new Uint8Array([1, 2]))) + filter('key', '<', new BsonBinaryData(1, new Uint8Array([1, 2]))) ); await test.executeQuery(query_); test.assertOverlaysRead(0, 0); @@ -1213,8 +1226,8 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', filter('key', 'in', [ - bsonBinaryData(1, new Uint8Array([1, 2, 3])), - bsonBinaryData(1, new Uint8Array([1, 2])) + new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + new BsonBinaryData(1, new Uint8Array([1, 2])) ]) ); await test.executeQuery(query_); @@ -1228,8 +1241,8 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', filter('key', 'not-in', [ - bsonBinaryData(1, new Uint8Array([1, 2, 3])), - bsonBinaryData(1, new Uint8Array([1, 2])) + new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + new BsonBinaryData(1, new Uint8Array([1, 2])) ]) ); await test.executeQuery(query_); @@ -1247,9 +1260,9 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); await test.configureFieldsIndexes(index); await test.writeMutations( - setMutation('coll/a', { key: int32(-1) }), - setMutation('coll/b', { key: int32(0) }), - setMutation('coll/c', { key: int32(1) }) + setMutation('coll/a', { key: new Int32Value(-1) }), + setMutation('coll/b', { key: new Int32Value(0) }), + setMutation('coll/c', { key: new Int32Value(1) }) ); await test.backfillIndexes(); @@ -1262,14 +1275,14 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/a', 'coll/b', 'coll/c'); - query_ = query('coll', filter('key', '==', int32(0))); + query_ = query('coll', filter('key', '==', new Int32Value(0))); await test.executeQuery(query_); test.assertOverlaysRead(1, 0, { [key('coll/b').toString()]: MutationType.Set }); test.assertQueryReturned(query_, 'coll/b'); - query_ = query('coll', filter('key', '!=', int32(0))); + query_ = query('coll', filter('key', '!=', new Int32Value(0))); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/a').toString()]: MutationType.Set, @@ -1277,7 +1290,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/a', 'coll/c'); - query_ = query('coll', filter('key', '>=', int32(0))); + query_ = query('coll', filter('key', '>=', new Int32Value(0))); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/b').toString()]: MutationType.Set, @@ -1285,12 +1298,15 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/b', 'coll/c'); - query_ = query('coll', filter('key', '<', int32(-1))); + query_ = query('coll', filter('key', '<', new Int32Value(-1))); await test.executeQuery(query_); test.assertOverlaysRead(0, 0); test.assertQueryReturned(query_); - query_ = query('coll', filter('key', 'in', [int32(0), int32(1)])); + query_ = query( + 'coll', + filter('key', 'in', [new Int32Value(0), new Int32Value(1)]) + ); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/b').toString()]: MutationType.Set, @@ -1298,7 +1314,10 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/b', 'coll/c'); - query_ = query('coll', filter('key', 'not-in', [int32(0), int32(1)])); + query_ = query( + 'coll', + filter('key', 'not-in', [new Int32Value(0), new Int32Value(1)]) + ); await test.executeQuery(query_); test.assertOverlaysRead(1, 0, { [key('coll/a').toString()]: MutationType.Set @@ -1313,9 +1332,9 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); await test.configureFieldsIndexes(index); await test.writeMutations( - setMutation('coll/a', { key: regex('a', 'i') }), - setMutation('coll/b', { key: regex('a', 'm') }), - setMutation('coll/c', { key: regex('b', 'i') }) + setMutation('coll/a', { key: new RegexValue('a', 'i') }), + setMutation('coll/b', { key: new RegexValue('a', 'm') }), + setMutation('coll/c', { key: new RegexValue('b', 'i') }) ); await test.backfillIndexes(); @@ -1328,14 +1347,14 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/a', 'coll/b', 'coll/c'); - query_ = query('coll', filter('key', '==', regex('a', 'i'))); + query_ = query('coll', filter('key', '==', new RegexValue('a', 'i'))); await test.executeQuery(query_); test.assertOverlaysRead(1, 0, { [key('coll/a').toString()]: MutationType.Set }); test.assertQueryReturned(query_, 'coll/a'); - query_ = query('coll', filter('key', '!=', regex('a', 'i'))); + query_ = query('coll', filter('key', '!=', new RegexValue('a', 'i'))); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/b').toString()]: MutationType.Set, @@ -1343,7 +1362,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/b', 'coll/c'); - query_ = query('coll', filter('key', '>=', regex('a', 'm'))); + query_ = query('coll', filter('key', '>=', new RegexValue('a', 'm'))); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/b').toString()]: MutationType.Set, @@ -1351,14 +1370,17 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/b', 'coll/c'); - query_ = query('coll', filter('key', '<', regex('a', 'i'))); + query_ = query('coll', filter('key', '<', new RegexValue('a', 'i'))); await test.executeQuery(query_); test.assertOverlaysRead(0, 0); test.assertQueryReturned(query_); query_ = query( 'coll', - filter('key', 'in', [regex('a', 'i'), regex('a', 'm')]) + filter('key', 'in', [ + new RegexValue('a', 'i'), + new RegexValue('a', 'm') + ]) ); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { @@ -1369,7 +1391,10 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { query_ = query( 'coll', - filter('key', 'not-in', [regex('a', 'i'), regex('a', 'm')]) + filter('key', 'not-in', [ + new RegexValue('a', 'i'), + new RegexValue('a', 'm') + ]) ); await test.executeQuery(query_); test.assertOverlaysRead(1, 0, { @@ -1385,11 +1410,11 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); await test.configureFieldsIndexes(index); await test.writeMutations( - setMutation('coll/a', { key: minKey() }), - setMutation('coll/b', { key: minKey() }), + setMutation('coll/a', { key: MinKey.instance() }), + setMutation('coll/b', { key: MinKey.instance() }), setMutation('coll/c', { key: null }), setMutation('coll/d', { key: 1 }), - setMutation('coll/e', { key: maxKey() }) + setMutation('coll/e', { key: MaxKey.instance() }) ); await test.backfillIndexes(); @@ -1411,7 +1436,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { 'coll/e' ); - query_ = query('coll', filter('key', '==', minKey())); + query_ = query('coll', filter('key', '==', MinKey.instance())); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/a').toString()]: MutationType.Set, @@ -1419,7 +1444,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/a', 'coll/b'); - query_ = query('coll', filter('key', '!=', minKey())); + query_ = query('coll', filter('key', '!=', MinKey.instance())); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/d').toString()]: MutationType.Set, @@ -1427,7 +1452,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/d', 'coll/e'); - query_ = query('coll', filter('key', '>=', minKey())); + query_ = query('coll', filter('key', '>=', MinKey.instance())); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/a').toString()]: MutationType.Set, @@ -1435,12 +1460,12 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/a', 'coll/b'); - query_ = query('coll', filter('key', '<', minKey())); + query_ = query('coll', filter('key', '<', MinKey.instance())); await test.executeQuery(query_); test.assertOverlaysRead(0, 0, {}); test.assertQueryReturned(query_); - query_ = query('coll', filter('key', 'in', [minKey()])); + query_ = query('coll', filter('key', 'in', [MinKey.instance()])); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/a').toString()]: MutationType.Set, @@ -1448,7 +1473,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/a', 'coll/b'); - query_ = query('coll', filter('key', 'not-in', [minKey()])); + query_ = query('coll', filter('key', 'not-in', [MinKey.instance()])); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/d').toString()]: MutationType.Set, @@ -1464,11 +1489,11 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); await test.configureFieldsIndexes(index); await test.writeMutations( - setMutation('coll/a', { key: maxKey() }), - setMutation('coll/b', { key: maxKey() }), + setMutation('coll/a', { key: MaxKey.instance() }), + setMutation('coll/b', { key: MaxKey.instance() }), setMutation('coll/c', { key: null }), setMutation('coll/d', { key: 1 }), - setMutation('coll/e', { key: minKey() }) + setMutation('coll/e', { key: MinKey.instance() }) ); await test.backfillIndexes(); @@ -1490,7 +1515,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { 'coll/b' ); - query_ = query('coll', filter('key', '==', maxKey())); + query_ = query('coll', filter('key', '==', MaxKey.instance())); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/a').toString()]: MutationType.Set, @@ -1498,7 +1523,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/a', 'coll/b'); - query_ = query('coll', filter('key', '!=', maxKey())); + query_ = query('coll', filter('key', '!=', MaxKey.instance())); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/d').toString()]: MutationType.Set, @@ -1506,7 +1531,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/e', 'coll/d'); - query_ = query('coll', filter('key', '<=', maxKey())); + query_ = query('coll', filter('key', '<=', MaxKey.instance())); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/a').toString()]: MutationType.Set, @@ -1514,17 +1539,17 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/a', 'coll/b'); - query_ = query('coll', filter('key', '>', maxKey())); + query_ = query('coll', filter('key', '>', MaxKey.instance())); await test.executeQuery(query_); test.assertOverlaysRead(0, 0, {}); test.assertQueryReturned(query_); - query_ = query('coll', filter('key', '<', maxKey())); + query_ = query('coll', filter('key', '<', MaxKey.instance())); await test.executeQuery(query_); test.assertOverlaysRead(0, 0, {}); test.assertQueryReturned(query_); - query_ = query('coll', filter('key', 'in', [maxKey()])); + query_ = query('coll', filter('key', 'in', [MaxKey.instance()])); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/a').toString()]: MutationType.Set, @@ -1532,7 +1557,7 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { }); test.assertQueryReturned(query_, 'coll/a', 'coll/b'); - query_ = query('coll', filter('key', 'not-in', [maxKey()])); + query_ = query('coll', filter('key', 'not-in', [MaxKey.instance()])); await test.executeQuery(query_); test.assertOverlaysRead(2, 0, { [key('coll/d').toString()]: MutationType.Set, @@ -1549,26 +1574,26 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.configureFieldsIndexes(index); await test.writeMutations( - setMutation('coll/a', { key: minKey() }), - setMutation('coll/b', { key: int32(2) }), - setMutation('coll/c', { key: int32(1) }), - setMutation('coll/d', { key: bsonTimestamp(1000, 1001) }), - setMutation('coll/e', { key: bsonTimestamp(1000, 1000) }), + setMutation('coll/a', { key: MinKey.instance() }), + setMutation('coll/b', { key: new Int32Value(2) }), + setMutation('coll/c', { key: new Int32Value(1) }), + setMutation('coll/d', { key: new BsonTimestamp(1000, 1001) }), + setMutation('coll/e', { key: new BsonTimestamp(1000, 1000) }), setMutation('coll/f', { - key: bsonBinaryData(1, new Uint8Array([1, 2, 4])) + key: new BsonBinaryData(1, new Uint8Array([1, 2, 4])) }), setMutation('coll/g', { - key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }), setMutation('coll/h', { - key: bsonObjectId('507f191e810c19729de860eb') + key: new BsonObjectId('507f191e810c19729de860eb') }), setMutation('coll/i', { - key: bsonObjectId('507f191e810c19729de860ea') + key: new BsonObjectId('507f191e810c19729de860ea') }), - setMutation('coll/j', { key: regex('^bar', 'm') }), - setMutation('coll/k', { key: regex('^bar', 'i') }), - setMutation('coll/l', { key: maxKey() }) + setMutation('coll/j', { key: new RegexValue('^bar', 'm') }), + setMutation('coll/k', { key: new RegexValue('^bar', 'i') }), + setMutation('coll/l', { key: MaxKey.instance() }) ); await test.backfillIndexes(); @@ -1614,29 +1639,29 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.writeMutations( setMutation('coll/a', { key: null }), - setMutation('coll/b', { key: minKey() }), + setMutation('coll/b', { key: MinKey.instance() }), setMutation('coll/c', { key: true }), setMutation('coll/d', { key: NaN }), - setMutation('coll/e', { key: int32(1) }), + setMutation('coll/e', { key: new Int32Value(1) }), setMutation('coll/f', { key: 2.0 }), setMutation('coll/g', { key: 3 }), setMutation('coll/h', { key: new Timestamp(100, 123456000) }), - setMutation('coll/i', { key: bsonTimestamp(1, 2) }), + setMutation('coll/i', { key: new BsonTimestamp(1, 2) }), setMutation('coll/j', { key: 'string' }), setMutation('coll/k', { key: blob(1, 2, 3) }), setMutation('coll/l', { - key: bsonBinaryData(1, new Uint8Array([1, 2, 3])) + key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }), setMutation('coll/m', { key: ref('foo/bar') }), setMutation('coll/n', { - key: bsonObjectId('507f191e810c19729de860ea') + key: new BsonObjectId('507f191e810c19729de860ea') }), setMutation('coll/o', { key: new GeoPoint(1, 2) }), - setMutation('coll/p', { key: regex('^bar', 'm') }), + setMutation('coll/p', { key: new RegexValue('^bar', 'm') }), setMutation('coll/q', { key: [2, 'foo'] }), setMutation('coll/r', { key: vector([1, 2, 3]) }), setMutation('coll/s', { key: { bar: 1, foo: 2 } }), - setMutation('coll/t', { key: maxKey() }) + setMutation('coll/t', { key: MaxKey.instance() }) ); await test.backfillIndexes(); diff --git a/packages/firestore/test/unit/model/document.test.ts b/packages/firestore/test/unit/model/document.test.ts index 2c2387cca63..f67e9d971a0 100644 --- a/packages/firestore/test/unit/model/document.test.ts +++ b/packages/firestore/test/unit/model/document.test.ts @@ -18,14 +18,14 @@ import { expect } from 'chai'; import { - bsonBinaryData, - bsonObjectId, - bsonTimestamp, - int32, - maxKey, - minKey, - regex -} from '../../../src/lite-api/field_value_impl'; + BsonBinaryData, + BsonObjectId, + BsonTimestamp, + Int32Value, + MaxKey, + MinKey, + RegexValue +} from '../../../src'; import { doc, expectEqual, @@ -55,26 +55,26 @@ describe('Document', () => { it('can be constructed with bson types', () => { const data = { - objectId: bsonObjectId('foo'), - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), - timestamp: bsonTimestamp(1, 2), - min: minKey(), - max: maxKey(), - regex: regex('a', 'b'), - int32: int32(1) + objectId: new BsonObjectId('foo'), + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: new BsonTimestamp(1, 2), + min: MinKey.instance(), + max: MaxKey.instance(), + regex: new RegexValue('a', 'b'), + int32: new Int32Value(1) }; const document = doc('rooms/Eros', 1, data); const value = document.data; expect(value.value).to.deep.equal( wrap({ - objectId: bsonObjectId('foo'), - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), - timestamp: bsonTimestamp(1, 2), - min: minKey(), - max: maxKey(), - regex: regex('a', 'b'), - int32: int32(1) + objectId: new BsonObjectId('foo'), + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: new BsonTimestamp(1, 2), + min: MinKey.instance(), + max: MaxKey.instance(), + regex: new RegexValue('a', 'b'), + int32: new Int32Value(1) }) ); expect(value).not.to.equal(data); diff --git a/packages/firestore/test/unit/model/object_value.test.ts b/packages/firestore/test/unit/model/object_value.test.ts index 13cfa02131b..40b18893e68 100644 --- a/packages/firestore/test/unit/model/object_value.test.ts +++ b/packages/firestore/test/unit/model/object_value.test.ts @@ -18,15 +18,15 @@ import { expect } from 'chai'; import { - vector, - bsonObjectId, - bsonBinaryData, - bsonTimestamp, - int32, - regex, - minKey, - maxKey -} from '../../../src/lite-api/field_value_impl'; + BsonObjectId, + BsonBinaryData, + BsonTimestamp, + RegexValue, + Int32Value, + MaxKey, + MinKey +} from '../../../src'; +import { vector } from '../../../src/lite-api/field_value_impl'; import { extractFieldMask, ObjectValue } from '../../../src/model/object_value'; import { TypeOrder } from '../../../src/model/type_order'; import { typeOrder } from '../../../src/model/values'; @@ -38,13 +38,13 @@ describe('ObjectValue', () => { foo: { a: 1, b: true, c: 'string' }, embedding: vector([1]), bson: { - objectId: bsonObjectId('foo'), - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), - timestamp: bsonTimestamp(1, 2), - min: minKey(), - max: maxKey(), - regex: regex('a', 'b'), - int32: int32(1) + objectId: new BsonObjectId('foo'), + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: new BsonTimestamp(1, 2), + min: MinKey.instance(), + max: MaxKey.instance(), + regex: new RegexValue('a', 'b'), + int32: new Int32Value(1) } }); @@ -102,30 +102,36 @@ describe('ObjectValue', () => { expect(objValue.field(field('bson'))!).to.deep.equal( wrap({ - objectId: bsonObjectId('foo'), - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), - timestamp: bsonTimestamp(1, 2), - min: minKey(), - max: maxKey(), - regex: regex('a', 'b'), - int32: int32(1) + objectId: new BsonObjectId('foo'), + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: new BsonTimestamp(1, 2), + min: MinKey.instance(), + max: MaxKey.instance(), + regex: new RegexValue('a', 'b'), + int32: new Int32Value(1) }) ); expect(objValue.field(field('bson.objectId'))!).to.deep.equal( - wrap(bsonObjectId('foo')) + wrap(new BsonObjectId('foo')) ); expect(objValue.field(field('bson.binary'))!).to.deep.equal( - wrap(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + wrap(new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ); expect(objValue.field(field('bson.timestamp'))!).to.deep.equal( - wrap(bsonTimestamp(1, 2)) + wrap(new BsonTimestamp(1, 2)) + ); + expect(objValue.field(field('bson.min'))!).to.deep.equal( + wrap(MinKey.instance()) + ); + expect(objValue.field(field('bson.max'))!).to.deep.equal( + wrap(MaxKey.instance()) ); - expect(objValue.field(field('bson.min'))!).to.deep.equal(wrap(minKey())); - expect(objValue.field(field('bson.max'))!).to.deep.equal(wrap(maxKey())); expect(objValue.field(field('bson.regex'))!).to.deep.equal( - wrap(regex('a', 'b')) + wrap(new RegexValue('a', 'b')) + ); + expect(objValue.field(field('bson.int32'))!).to.deep.equal( + wrap(new Int32Value(1)) ); - expect(objValue.field(field('bson.int32'))!).to.deep.equal(wrap(int32(1))); }); it('can overwrite existing fields', () => { @@ -234,56 +240,56 @@ describe('ObjectValue', () => { it('can handle bson types in ObjectValue', () => { const objValue = ObjectValue.empty(); // Add new fields - objValue.set(field('objectId'), wrap(bsonObjectId('foo-value'))); + objValue.set(field('objectId'), wrap(new BsonObjectId('foo-value'))); objValue.set( field('binary'), - wrap(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + wrap(new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ); - objValue.set(field('timestamp'), wrap(bsonTimestamp(1, 2))); - objValue.set(field('regex'), wrap(regex('a', 'b'))); - objValue.set(field('int32'), wrap(int32(1))); - objValue.set(field('min'), wrap(minKey())); - objValue.set(field('max'), wrap(maxKey())); + objValue.set(field('timestamp'), wrap(new BsonTimestamp(1, 2))); + objValue.set(field('regex'), wrap(new RegexValue('a', 'b'))); + objValue.set(field('int32'), wrap(new Int32Value(1))); + objValue.set(field('min'), wrap(MinKey.instance())); + objValue.set(field('max'), wrap(MaxKey.instance())); assertObjectEquals(objValue, { - objectId: bsonObjectId('foo-value'), - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), - timestamp: bsonTimestamp(1, 2), - regex: regex('a', 'b'), - int32: int32(1), - min: minKey(), - max: maxKey() + objectId: new BsonObjectId('foo-value'), + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: new BsonTimestamp(1, 2), + regex: new RegexValue('a', 'b'), + int32: new Int32Value(1), + min: MinKey.instance(), + max: MaxKey.instance() }); // Overwrite existing fields - objValue.set(field('objectId'), wrap(bsonObjectId('new-foo-value'))); + objValue.set(field('objectId'), wrap(new BsonObjectId('new-foo-value'))); // Create nested objects objValue.set( field('foo.binary'), - wrap(bsonBinaryData(2, new Uint8Array([1, 2, 3]))) + wrap(new BsonBinaryData(2, new Uint8Array([1, 2, 3]))) ); - objValue.set(field('foo.timestamp'), wrap(bsonTimestamp(1, 2))); + objValue.set(field('foo.timestamp'), wrap(new BsonTimestamp(1, 2))); // Delete fields objValue.delete(field('binary')); // overwrite nested objects - objValue.set(field('foo.timestamp'), wrap(bsonTimestamp(2, 1))); + objValue.set(field('foo.timestamp'), wrap(new BsonTimestamp(2, 1))); // Overwrite primitive values to create objects objValue.set(field('min'), wrap(null)); assertObjectEquals(objValue, { - objectId: bsonObjectId('new-foo-value'), - timestamp: bsonTimestamp(1, 2), - regex: regex('a', 'b'), - int32: int32(1), + objectId: new BsonObjectId('new-foo-value'), + timestamp: new BsonTimestamp(1, 2), + regex: new RegexValue('a', 'b'), + int32: new Int32Value(1), min: null, - max: maxKey(), + max: MaxKey.instance(), foo: { - binary: bsonBinaryData(2, new Uint8Array([1, 2, 3])), - timestamp: bsonTimestamp(2, 1) + binary: new BsonBinaryData(2, new Uint8Array([1, 2, 3])), + timestamp: new BsonTimestamp(2, 1) } }); }); @@ -294,13 +300,13 @@ describe('ObjectValue', () => { map: { a: 1, b: true, c: 'string', nested: { d: 'e' } }, emptymap: {}, bar: { - objectId: bsonObjectId('foo'), - binary: bsonBinaryData(1, new Uint8Array([1, 2, 3])), - timestamp: bsonTimestamp(1, 2), - min: minKey(), - max: maxKey(), - regex: regex('a', 'b'), - int32: int32(1) + objectId: new BsonObjectId('foo'), + binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), + timestamp: new BsonTimestamp(1, 2), + min: MinKey.instance(), + max: MaxKey.instance(), + regex: new RegexValue('a', 'b'), + int32: new Int32Value(1) } }); const expectedMask = mask( diff --git a/packages/firestore/test/unit/model/values.test.ts b/packages/firestore/test/unit/model/values.test.ts index 0d93d335ded..048ae762a98 100644 --- a/packages/firestore/test/unit/model/values.test.ts +++ b/packages/firestore/test/unit/model/values.test.ts @@ -17,25 +17,19 @@ import { expect } from 'chai'; -import { GeoPoint, Timestamp } from '../../../src'; -import { DatabaseId } from '../../../src/core/database_info'; -import { BsonBinaryData } from '../../../src/lite-api/bson_binary_data'; -import { BsonObjectId } from '../../../src/lite-api/bson_object_Id'; -import { BsonTimestamp } from '../../../src/lite-api/bson_timestamp_value'; import { - vector, - regex, - bsonTimestamp, - int32, - bsonBinaryData, - bsonObjectId, - minKey, - maxKey -} from '../../../src/lite-api/field_value_impl'; -import { Int32Value } from '../../../src/lite-api/int32_value'; -import { MaxKey } from '../../../src/lite-api/max_key'; -import { MinKey } from '../../../src/lite-api/min_key'; -import { RegexValue } from '../../../src/lite-api/regex_value'; + GeoPoint, + Timestamp, + BsonBinaryData, + BsonTimestamp, + BsonObjectId, + RegexValue, + Int32Value, + MaxKey, + MinKey +} from '../../../src'; +import { DatabaseId } from '../../../src/core/database_info'; +import { vector } from '../../../src/lite-api/field_value_impl'; import { serverTimestamp } from '../../../src/model/server_timestamps'; import { canonicalId, @@ -79,7 +73,7 @@ describe('Values', () => { [wrap(true), wrap(true)], [wrap(false), wrap(false)], [wrap(null), wrap(null)], - [wrap(minKey()), wrap(minKey()), wrap(MinKey.instance())], + [wrap(MinKey.instance()), wrap(MinKey.instance())], [wrap(0 / 0), wrap(Number.NaN), wrap(NaN)], // -0.0 and 0.0 order the same but are not considered equal. [wrap(-0.0)], @@ -117,20 +111,20 @@ describe('Values', () => { [wrap({ foo: 1 })], [wrap(vector([]))], [wrap(vector([1, 2.3, -4.0]))], - [wrap(regex('^foo', 'i')), wrap(new RegexValue('^foo', 'i'))], - [wrap(bsonTimestamp(57, 4)), wrap(new BsonTimestamp(57, 4))], + [wrap(new RegexValue('^foo', 'i')), wrap(new RegexValue('^foo', 'i'))], + [wrap(new BsonTimestamp(57, 4)), wrap(new BsonTimestamp(57, 4))], [ - wrap(bsonBinaryData(128, Uint8Array.from([7, 8, 9]))), wrap(new BsonBinaryData(128, Uint8Array.from([7, 8, 9]))), - wrap(bsonBinaryData(128, Buffer.from([7, 8, 9]))), + wrap(new BsonBinaryData(128, Uint8Array.from([7, 8, 9]))), + wrap(new BsonBinaryData(128, Buffer.from([7, 8, 9]))), wrap(new BsonBinaryData(128, Buffer.from([7, 8, 9]))) ], [ - wrap(bsonObjectId('123456789012')), + wrap(new BsonObjectId('123456789012')), wrap(new BsonObjectId('123456789012')) ], - [wrap(int32(255)), wrap(new Int32Value(255))], - [wrap(maxKey()), wrap(maxKey()), wrap(MaxKey.instance())] + [wrap(new Int32Value(255)), wrap(new Int32Value(255))], + [wrap(MaxKey.instance()), wrap(MaxKey.instance())] ]; expectEqualitySets(values, (v1, v2) => valueEquals(v1, v2)); }); @@ -170,7 +164,7 @@ describe('Values', () => { [wrap(null)], // MinKey is after null - [wrap(minKey())], + [wrap(MinKey.instance())], // booleans [wrap(false)], @@ -183,23 +177,23 @@ describe('Values', () => { [wrap(Number.MIN_SAFE_INTEGER - 1)], [wrap(Number.MIN_SAFE_INTEGER)], // 64-bit and 32-bit integers order together numerically. - [{ integerValue: -2147483648 }, wrap(int32(-2147483648))], + [{ integerValue: -2147483648 }, wrap(new Int32Value(-2147483648))], [wrap(-1.1)], // Integers, Int32Values and Doubles order the same. - [{ integerValue: -1 }, { doubleValue: -1 }, wrap(int32(-1))], + [{ integerValue: -1 }, { doubleValue: -1 }, wrap(new Int32Value(-1))], [wrap(-Number.MIN_VALUE)], // zeros all compare the same. [ { integerValue: 0 }, { doubleValue: 0 }, { doubleValue: -0 }, - wrap(int32(0)) + wrap(new Int32Value(0)) ], [wrap(Number.MIN_VALUE)], - [{ integerValue: 1 }, { doubleValue: 1.0 }, wrap(int32(1))], + [{ integerValue: 1 }, { doubleValue: 1.0 }, wrap(new Int32Value(1))], [wrap(1.1)], - [wrap(int32(2))], - [wrap(int32(2147483647))], + [wrap(new Int32Value(2))], + [wrap(new Int32Value(2147483647))], [wrap(Number.MAX_SAFE_INTEGER)], [wrap(Number.MAX_SAFE_INTEGER + 1)], [wrap(Infinity)], @@ -215,9 +209,9 @@ describe('Values', () => { ], // request timestamp - [wrap(bsonTimestamp(123, 4))], - [wrap(bsonTimestamp(123, 5))], - [wrap(bsonTimestamp(124, 0))], + [wrap(new BsonTimestamp(123, 4))], + [wrap(new BsonTimestamp(123, 5))], + [wrap(new BsonTimestamp(124, 0))], // server timestamps come after all concrete timestamps. [serverTimestamp(Timestamp.fromDate(date1), null)], @@ -243,11 +237,11 @@ describe('Values', () => { [wrap(blob(255))], [ - wrap(bsonBinaryData(5, Buffer.from([1, 2, 3]))), - wrap(bsonBinaryData(5, new Uint8Array([1, 2, 3]))) + wrap(new BsonBinaryData(5, Buffer.from([1, 2, 3]))), + wrap(new BsonBinaryData(5, new Uint8Array([1, 2, 3]))) ], - [wrap(bsonBinaryData(7, Buffer.from([1])))], - [wrap(bsonBinaryData(7, new Uint8Array([2])))], + [wrap(new BsonBinaryData(7, Buffer.from([1])))], + [wrap(new BsonBinaryData(7, new Uint8Array([2])))], // reference values [refValue(dbId('p1', 'd1'), key('c1/doc1'))], @@ -258,11 +252,11 @@ describe('Values', () => { [refValue(dbId('p2', 'd1'), key('c1/doc1'))], // ObjectId - [wrap(bsonObjectId('foo')), wrap(bsonObjectId('foo'))], + [wrap(new BsonObjectId('foo')), wrap(new BsonObjectId('foo'))], // TODO(Mila/BSON): uncomment after string sort bug is fixed - // [wrap(bsonObjectId('Ḟoo'))], // with latin capital letter f with dot above - // [wrap(bsonObjectId('foo\u0301'))], // with combining acute accent - [wrap(bsonObjectId('xyz'))], + // [wrap(new BsonObjectId('Ḟoo'))], // with latin capital letter f with dot above + // [wrap(new BsonObjectId('foo\u0301'))], // with combining acute accent + [wrap(new BsonObjectId('xyz'))], // geo points [wrap(new GeoPoint(-90, -180))], @@ -279,10 +273,10 @@ describe('Values', () => { [wrap(new GeoPoint(90, 180))], // regular expressions - [wrap(regex('a', 'bar1'))], - [wrap(regex('foo', 'bar1'))], - [wrap(regex('foo', 'bar2'))], - [wrap(regex('go', 'bar1'))], + [wrap(new RegexValue('a', 'bar1'))], + [wrap(new RegexValue('foo', 'bar1'))], + [wrap(new RegexValue('foo', 'bar2'))], + [wrap(new RegexValue('go', 'bar1'))], // arrays [wrap([])], @@ -305,7 +299,7 @@ describe('Values', () => { [wrap({ foo: '0' })], // MaxKey - [wrap(maxKey())] + [wrap(MaxKey.instance())] ]; expectCorrectComparisonGroups( @@ -412,28 +406,34 @@ describe('Values', () => { }, { expectedByteSize: 27, - elements: [wrap(regex('a', 'b')), wrap(regex('c', 'd'))] + elements: [ + wrap(new RegexValue('a', 'b')), + wrap(new RegexValue('c', 'd')) + ] }, { expectedByteSize: 13, - elements: [wrap(bsonObjectId('foo')), wrap(bsonObjectId('bar'))] + elements: [wrap(new BsonObjectId('foo')), wrap(new BsonObjectId('bar'))] }, { expectedByteSize: 53, - elements: [wrap(bsonTimestamp(1, 2)), wrap(bsonTimestamp(3, 4))] + elements: [wrap(new BsonTimestamp(1, 2)), wrap(new BsonTimestamp(3, 4))] }, { expectedByteSize: 8, - elements: [wrap(int32(1)), wrap(int32(2147483647))] + elements: [wrap(new Int32Value(1)), wrap(new Int32Value(2147483647))] }, { expectedByteSize: 16, elements: [ - wrap(bsonBinaryData(1, new Uint8Array([127, 128]))), - wrap(bsonBinaryData(128, new Uint8Array([1, 2]))) + wrap(new BsonBinaryData(1, new Uint8Array([127, 128]))), + wrap(new BsonBinaryData(128, new Uint8Array([1, 2]))) ] }, - { expectedByteSize: 11, elements: [wrap(minKey()), wrap(maxKey())] } + { + expectedByteSize: 11, + elements: [wrap(MinKey.instance()), wrap(MaxKey.instance())] + } ]; for (const group of equalityGroups) { @@ -464,11 +464,11 @@ describe('Values', () => { [wrap({ a: 'a', b: 'b' }), wrap({ a: 'a', b: 'b', c: 'c' })], [wrap({ a: 'a', b: 'b' }), wrap({ a: 'a', b: 'b', c: 'c' })], [wrap(vector([2, 3])), wrap(vector([1, 2, 3]))], - [wrap(regex('a', 'b')), wrap(regex('cc', 'dd'))], - [wrap(bsonObjectId('foo')), wrap(bsonObjectId('foobar'))], + [wrap(new RegexValue('a', 'b')), wrap(new RegexValue('cc', 'dd'))], + [wrap(new BsonObjectId('foo')), wrap(new BsonObjectId('foobar'))], [ - wrap(bsonBinaryData(128, new Uint8Array([127, 128]))), - wrap(bsonBinaryData(1, new Uint8Array([1, 2, 3]))) + wrap(new BsonBinaryData(128, new Uint8Array([127, 128]))), + wrap(new BsonBinaryData(1, new Uint8Array([1, 2, 3]))) ] ]; @@ -489,7 +489,7 @@ describe('Values', () => { [valuesGetLowerBound({ nullValue: 'NULL_VALUE' }), wrap(null)], // lower bound of MinKey is MinKey - [valuesGetLowerBound(MIN_KEY_VALUE), wrap(minKey())], + [valuesGetLowerBound(MIN_KEY_VALUE), wrap(MinKey.instance())], // booleans [valuesGetLowerBound({ booleanValue: true }), wrap(false)], @@ -512,11 +512,11 @@ describe('Values', () => { // bson timestamps [ - valuesGetLowerBound(wrap(bsonTimestamp(4294967295, 4294967295))), + valuesGetLowerBound(wrap(new BsonTimestamp(4294967295, 4294967295))), MIN_BSON_TIMESTAMP_VALUE, - wrap(bsonTimestamp(0, 0)) + wrap(new BsonTimestamp(0, 0)) ], - [wrap(bsonTimestamp(1, 1))], + [wrap(new BsonTimestamp(1, 1))], // strings [valuesGetLowerBound({ stringValue: 'Z' }), wrap('')], @@ -529,11 +529,11 @@ describe('Values', () => { // bson binary data [ valuesGetLowerBound( - wrap(bsonBinaryData(128, new Uint8Array([128, 128]))) + wrap(new BsonBinaryData(128, new Uint8Array([128, 128]))) ), MIN_BSON_BINARY_VALUE ], - [wrap(bsonBinaryData(0, new Uint8Array([0])))], + [wrap(new BsonBinaryData(0, new Uint8Array([0])))], // resource names [ @@ -544,11 +544,11 @@ describe('Values', () => { // bson object ids [ - valuesGetLowerBound(wrap(bsonObjectId('ZZZ'))), - wrap(bsonObjectId('')), + valuesGetLowerBound(wrap(new BsonObjectId('ZZZ'))), + wrap(new BsonObjectId('')), MIN_BSON_OBJECT_ID_VALUE ], - [wrap(bsonObjectId('a'))], + [wrap(new BsonObjectId('a'))], // geo points [ @@ -559,11 +559,11 @@ describe('Values', () => { // regular expressions [ - valuesGetLowerBound(wrap(regex('ZZZ', 'i'))), - wrap(regex('', '')), + valuesGetLowerBound(wrap(new RegexValue('ZZZ', 'i'))), + wrap(new RegexValue('', '')), MIN_REGEX_VALUE ], - [wrap(regex('a', 'i'))], + [wrap(new RegexValue('a', 'i'))], // arrays [valuesGetLowerBound({ arrayValue: {} }), wrap([])], @@ -590,7 +590,7 @@ describe('Values', () => { [valuesGetLowerBound({ mapValue: {} }), wrap({})], // MaxKey - [wrap(maxKey())] + [wrap(MaxKey.instance())] ]; expectCorrectComparisonGroups( @@ -607,7 +607,10 @@ describe('Values', () => { [wrap(null)], // upper value of null is MinKey - [valuesGetUpperBound({ nullValue: 'NULL_VALUE' }), wrap(minKey())], + [ + valuesGetUpperBound({ nullValue: 'NULL_VALUE' }), + wrap(MinKey.instance()) + ], // upper value of MinKey is boolean `false` [valuesGetUpperBound(MIN_KEY_VALUE), wrap(false)], @@ -617,7 +620,7 @@ describe('Values', () => { [valuesGetUpperBound({ booleanValue: false })], // numbers - [wrap(int32(2147483647))], //largest int32 value + [wrap(new Int32Value(2147483647))], //largest int32 value [wrap(Number.MAX_SAFE_INTEGER)], [wrap(Number.POSITIVE_INFINITY)], [valuesGetUpperBound({ doubleValue: NaN })], @@ -627,7 +630,7 @@ describe('Values', () => { [valuesGetUpperBound({ timestampValue: {} })], // bson timestamps - [wrap(bsonTimestamp(4294967295, 4294967295))], // largest bson timestamp value + [wrap(new BsonTimestamp(4294967295, 4294967295))], // largest bson timestamp value [valuesGetUpperBound(MIN_BSON_TIMESTAMP_VALUE)], // strings @@ -639,7 +642,7 @@ describe('Values', () => { [valuesGetUpperBound({ bytesValue: '' })], // bson binary data - [wrap(bsonBinaryData(128, new Uint8Array([255, 255, 255])))], + [wrap(new BsonBinaryData(128, new Uint8Array([255, 255, 255])))], [valuesGetUpperBound(MIN_BSON_BINARY_VALUE)], // resource names @@ -647,7 +650,7 @@ describe('Values', () => { [valuesGetUpperBound({ referenceValue: '' })], // bson object ids - [wrap(bsonObjectId('foo'))], + [wrap(new BsonObjectId('foo'))], [valuesGetUpperBound(MIN_BSON_OBJECT_ID_VALUE)], // geo points @@ -655,7 +658,7 @@ describe('Values', () => { [valuesGetUpperBound({ geoPointValue: {} })], // regular expressions - [wrap(regex('a', 'i'))], + [wrap(new RegexValue('a', 'i'))], [valuesGetUpperBound(MIN_REGEX_VALUE)], // arrays @@ -670,7 +673,7 @@ describe('Values', () => { [wrap({ 'a': 'b' })], // MaxKey - [wrap(maxKey())] + [wrap(MaxKey.instance())] ]; expectCorrectComparisonGroups( @@ -712,19 +715,21 @@ describe('Values', () => { expect( canonicalId(wrap({ 'a': ['b', { 'c': new GeoPoint(30, 60) }] })) ).to.equal('{a:[b,{c:geo(30,60)}]}'); - expect(canonicalId(wrap(regex('a', 'b')))).to.equal( + expect(canonicalId(wrap(new RegexValue('a', 'b')))).to.equal( '{__regex__:{options:b,pattern:a}}' ); - expect(canonicalId(wrap(bsonObjectId('foo')))).to.equal('{__oid__:foo}'); - expect(canonicalId(wrap(bsonTimestamp(1, 2)))).to.equal( + expect(canonicalId(wrap(new BsonObjectId('foo')))).to.equal( + '{__oid__:foo}' + ); + expect(canonicalId(wrap(new BsonTimestamp(1, 2)))).to.equal( '{__request_timestamp__:{increment:2,seconds:1}}' ); - expect(canonicalId(wrap(int32(1)))).to.equal('{__int__:1}'); + expect(canonicalId(wrap(new Int32Value(1)))).to.equal('{__int__:1}'); expect( - canonicalId(wrap(bsonBinaryData(1, new Uint8Array([1, 2, 3])))) + canonicalId(wrap(new BsonBinaryData(1, new Uint8Array([1, 2, 3])))) ).to.equal('{__binary__:AQECAw==}'); - expect(canonicalId(wrap(minKey()))).to.equal('{__min__:null}'); - expect(canonicalId(wrap(maxKey()))).to.equal('{__max__:null}'); + expect(canonicalId(wrap(MinKey.instance()))).to.equal('{__min__:null}'); + expect(canonicalId(wrap(MaxKey.instance()))).to.equal('{__max__:null}'); }); it('canonical IDs ignore sort order', () => { diff --git a/packages/firestore/test/unit/remote/serializer.helper.ts b/packages/firestore/test/unit/remote/serializer.helper.ts index 9c116549928..24d7b039d0c 100644 --- a/packages/firestore/test/unit/remote/serializer.helper.ts +++ b/packages/firestore/test/unit/remote/serializer.helper.ts @@ -20,11 +20,18 @@ import { expect } from 'chai'; import { arrayRemove, arrayUnion, + BsonBinaryData, + BsonObjectId, + BsonTimestamp, Bytes, DocumentReference, GeoPoint, increment, + Int32Value, + MaxKey, + MinKey, refEqual, + RegexValue, serverTimestamp, Timestamp } from '../../../src'; @@ -52,16 +59,7 @@ import { } from '../../../src/core/query'; import { SnapshotVersion } from '../../../src/core/snapshot_version'; import { Target, targetEquals, TargetImpl } from '../../../src/core/target'; -import { - bsonBinaryData, - bsonObjectId, - bsonTimestamp, - int32, - maxKey, - minKey, - regex, - vector -} from '../../../src/lite-api/field_value_impl'; +import { vector } from '../../../src/lite-api/field_value_impl'; import { parseQueryValue } from '../../../src/lite-api/user_data_reader'; import { TargetData, TargetPurpose } from '../../../src/local/target_data'; import { FieldMask } from '../../../src/model/field_mask'; @@ -577,12 +575,12 @@ export function serializerTest( it('converts BSON types in mapValue', () => { const examples = [ - bsonObjectId('foo'), - bsonTimestamp(1, 2), - minKey(), - maxKey(), - regex('a', 'b'), - int32(1) + new BsonObjectId('foo'), + new BsonTimestamp(1, 2), + MinKey.instance(), + MaxKey.instance(), + new RegexValue('a', 'b'), + new Int32Value(1) ]; for (const example of examples) { @@ -598,7 +596,7 @@ export function serializerTest( } // BsonBinaryData will be serialized differently Proto3Json VS. regular Protobuf format - const bsonBinary = bsonBinaryData(1, new Uint8Array([1, 2, 3])); + const bsonBinary = new BsonBinaryData(1, new Uint8Array([1, 2, 3])); const expectedJson: api.Value = { mapValue: { fields: { From 4600a34360e0edff345068ad7a44403d9cd1b5fd Mon Sep 17 00:00:00 2001 From: Ehsan Date: Fri, 2 May 2025 15:07:37 -0700 Subject: [PATCH 22/77] Create seven-actors-kneel.md --- .changeset/seven-actors-kneel.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/seven-actors-kneel.md diff --git a/.changeset/seven-actors-kneel.md b/.changeset/seven-actors-kneel.md new file mode 100644 index 00000000000..6bc97d0e3ed --- /dev/null +++ b/.changeset/seven-actors-kneel.md @@ -0,0 +1,5 @@ +--- +"@firebase/firestore": minor +--- + +feat: Adds support for MinKey, MaxKey, RegexValue, Int32Value, BsonObjectId, BsonTimestamp, and BsonBinaryData. From cbba951db7ced310ff052d88241fa682ad2e971a Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 5 May 2025 12:40:20 -0400 Subject: [PATCH 23/77] resolve TODOs --- packages/firestore/src/model/values.ts | 5 +- .../test/integration/api/database.test.ts | 103 +++++++++--------- .../test/integration/util/helpers.ts | 3 +- .../firestore/test/unit/model/values.test.ts | 12 +- 4 files changed, 60 insertions(+), 63 deletions(-) diff --git a/packages/firestore/src/model/values.ts b/packages/firestore/src/model/values.ts index 848325c3521..fca9c34b9ea 100644 --- a/packages/firestore/src/model/values.ts +++ b/packages/firestore/src/model/values.ts @@ -644,7 +644,7 @@ function compareRegex(left: Value, right: Value): number { ]?.stringValue ?? ''; // First order by patterns, and then options. - const patternDiff = primitiveComparator(leftPattern, rightPattern); + const patternDiff = compareUtf8Strings(leftPattern, rightPattern); return patternDiff !== 0 ? patternDiff : primitiveComparator(leftOptions, rightOptions); @@ -656,8 +656,7 @@ function compareBsonObjectIds(left: Value, right: Value): number { const rightOid = right.mapValue!.fields?.[RESERVED_BSON_OBJECT_ID_KEY]?.stringValue ?? ''; - // TODO(Mila/BSON): use compareUtf8Strings once the bug fix is merged. - return primitiveComparator(leftOid, rightOid); + return compareUtf8Strings(leftOid, rightOid); } /** diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index 8d9167cee9c..ce5a3d34eae 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -2818,9 +2818,6 @@ apiDescribe('Database', persistence => { settings, testDocs, async coll => { - // Populate the cache with all docs first - await getDocs(coll); - let orderedQuery = query( coll, where('key', '>', new BsonObjectId('507f191e810c19729de860ea')), @@ -2833,6 +2830,7 @@ apiDescribe('Database', persistence => { testDocs['b'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -2853,6 +2851,7 @@ apiDescribe('Database', persistence => { testDocs['a'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -2873,9 +2872,6 @@ apiDescribe('Database', persistence => { settings, testDocs, async coll => { - // Populate the cache with all docs first - await getDocs(coll); - let orderedQuery = query( coll, where('key', '>=', new Int32Value(1)), @@ -2888,6 +2884,7 @@ apiDescribe('Database', persistence => { testDocs['b'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -2905,6 +2902,7 @@ apiDescribe('Database', persistence => { testDocs['a'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -2925,9 +2923,6 @@ apiDescribe('Database', persistence => { settings, testDocs, async coll => { - // Populate the cache with all docs first - await getDocs(coll); - let orderedQuery = query( coll, where('key', '>', new BsonTimestamp(1, 1)), @@ -2940,6 +2935,7 @@ apiDescribe('Database', persistence => { testDocs['b'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -2957,6 +2953,7 @@ apiDescribe('Database', persistence => { testDocs['b'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -2977,9 +2974,6 @@ apiDescribe('Database', persistence => { settings, testDocs, async coll => { - // Populate the cache with all docs first - await getDocs(coll); - let orderedQuery = query( coll, where('key', '>', new BsonBinaryData(1, new Uint8Array([1, 2, 3]))), @@ -2992,6 +2986,7 @@ apiDescribe('Database', persistence => { testDocs['b'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3014,6 +3009,7 @@ apiDescribe('Database', persistence => { testDocs['a'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3034,9 +3030,6 @@ apiDescribe('Database', persistence => { settings, testDocs, async coll => { - // Populate the cache with all docs first - await getDocs(coll); - const orderedQuery = query( coll, or( @@ -3052,6 +3045,7 @@ apiDescribe('Database', persistence => { testDocs['a'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3074,9 +3068,6 @@ apiDescribe('Database', persistence => { settings, testDocs, async coll => { - // Populate the cache with all docs first - await getDocs(coll); - let filteredQuery = query( coll, where('key', '==', MinKey.instance()) @@ -3087,23 +3078,24 @@ apiDescribe('Database', persistence => { testDocs['b'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) ); - // TODO(Mila/BSON): uncomment after the null inclusion bug - // filteredQuery = query(coll, where('key', '!=', MinKey.instance())); - // snapshot = await getDocs(filteredQuery); - // expect(toDataArray(snapshot)).to.deep.equal([ - // testDocs['d'], - // testDocs['e'] - // ]); - // await assertSDKQueryResultsConsistentWithBackend( - // filteredQuery, - // testDocs, - // toIds(snapshot) - // ); + filteredQuery = query(coll, where('key', '!=', MinKey.instance())); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['d'], + testDocs['e'] + ]); + await assertSDKQueryResultsConsistentWithBackend( + coll, + filteredQuery, + testDocs, + toIds(snapshot) + ); filteredQuery = query(coll, where('key', '>=', MinKey.instance())); snapshot = await getDocs(filteredQuery); @@ -3112,6 +3104,7 @@ apiDescribe('Database', persistence => { testDocs['b'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3124,6 +3117,7 @@ apiDescribe('Database', persistence => { testDocs['b'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3133,6 +3127,7 @@ apiDescribe('Database', persistence => { snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3142,6 +3137,7 @@ apiDescribe('Database', persistence => { snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3151,6 +3147,7 @@ apiDescribe('Database', persistence => { snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3173,9 +3170,6 @@ apiDescribe('Database', persistence => { settings, testDocs, async coll => { - // Populate the cache with all docs first - await getDocs(coll); - let filteredQuery = query( coll, where('key', '==', MaxKey.instance()) @@ -3186,23 +3180,24 @@ apiDescribe('Database', persistence => { testDocs['d'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) ); - // TODO(Mila/BSON): uncomment after the null inclusion bug - // filteredQuery = query(coll, where('key', '!=', MaxKey.instance())); - // snapshot = await getDocs(filteredQuery); - // expect(toDataArray(snapshot)).to.deep.equal([ - // testDocs['a'], - // testDocs['b'] - // ]); - // await assertSDKQueryResultsConsistentWithBackend( - // filteredQuery, - // testDocs, - // toIds(snapshot) - // ); + filteredQuery = query(coll, where('key', '!=', MaxKey.instance())); + snapshot = await getDocs(filteredQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['a'], + testDocs['b'] + ]); + await assertSDKQueryResultsConsistentWithBackend( + coll, + filteredQuery, + testDocs, + toIds(snapshot) + ); filteredQuery = query(coll, where('key', '>=', MaxKey.instance())); snapshot = await getDocs(filteredQuery); @@ -3211,6 +3206,7 @@ apiDescribe('Database', persistence => { testDocs['d'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3223,6 +3219,7 @@ apiDescribe('Database', persistence => { testDocs['d'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3232,6 +3229,7 @@ apiDescribe('Database', persistence => { snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3241,6 +3239,7 @@ apiDescribe('Database', persistence => { snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3250,6 +3249,7 @@ apiDescribe('Database', persistence => { snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3273,9 +3273,6 @@ apiDescribe('Database', persistence => { settings, testDocs, async coll => { - // Populate the cache with all docs first - await getDocs(coll); - let filteredQuery = query(coll, where('key', '==', null)); let snapshot = await getDocs(filteredQuery); expect(toDataArray(snapshot)).to.deep.equal([ @@ -3283,6 +3280,7 @@ apiDescribe('Database', persistence => { testDocs['c'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3296,6 +3294,7 @@ apiDescribe('Database', persistence => { testDocs['e'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, filteredQuery, testDocs, toIds(snapshot) @@ -3424,11 +3423,9 @@ apiDescribe('Database', persistence => { await setDoc(doc(coll, 'm'), { key: docRef }); testDocs['m'] = { key: docRef }; - // Populate the cache with all docs first - await getDocs(coll); - const orderedQuery = query(coll, orderBy('key', 'desc')); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, [ @@ -3487,11 +3484,9 @@ apiDescribe('Database', persistence => { settings, testDocs, async coll => { - // Populate the cache with all docs first - await getDocs(coll); - const orderedQuery = query(coll, orderBy('key')); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, [ diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index cdedf2a3e02..7cfe7d3a7a4 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -633,6 +633,7 @@ export async function checkOnlineAndOfflineResultsMatch( * @returns {Promise} A Promise that resolves when the assertions are complete. */ export async function assertSDKQueryResultsConsistentWithBackend( + collection: CollectionReference, query: Query, allData: { [key: string]: DocumentData }, expectedDocIds: string[] @@ -640,7 +641,7 @@ export async function assertSDKQueryResultsConsistentWithBackend( // Check the cache round trip first to make sure cache is properly populated, otherwise the // snapshot listener below will return partial results from previous // "assertSDKQueryResultsConsistentWithBackend" calls if it is called multiple times in one test - await checkOnlineAndOfflineResultsMatch(query, ...expectedDocIds); + await checkOnlineAndOfflineResultsMatch(collection, query, ...expectedDocIds); const eventAccumulator = new EventsAccumulator(); const unsubscribe = onSnapshot( diff --git a/packages/firestore/test/unit/model/values.test.ts b/packages/firestore/test/unit/model/values.test.ts index 048ae762a98..dce8f1e123c 100644 --- a/packages/firestore/test/unit/model/values.test.ts +++ b/packages/firestore/test/unit/model/values.test.ts @@ -252,11 +252,13 @@ describe('Values', () => { [refValue(dbId('p2', 'd1'), key('c1/doc1'))], // ObjectId - [wrap(new BsonObjectId('foo')), wrap(new BsonObjectId('foo'))], - // TODO(Mila/BSON): uncomment after string sort bug is fixed - // [wrap(new BsonObjectId('Ḟoo'))], // with latin capital letter f with dot above - // [wrap(new BsonObjectId('foo\u0301'))], // with combining acute accent - [wrap(new BsonObjectId('xyz'))], + [wrap(new BsonObjectId('507f191e810c19729de860ea'))], + [wrap(new BsonObjectId('507f191e810c19729de860eb'))], + // latin small letter e + combining acute accent + latin small letter b + [wrap(new BsonObjectId('e\u0301b'))], + [wrap(new BsonObjectId('æ'))], + // latin small letter e with acute accent + latin small letter a + [wrap(new BsonObjectId('\u00e9a'))], // geo points [wrap(new GeoPoint(-90, -180))], From 6a02778e3d12af683e710b53dc6dfb64329e8229 Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Mon, 5 May 2025 13:09:02 -0700 Subject: [PATCH 24/77] Fix Auth Port for Firebase Studio (#8998) --- .changeset/lemon-tomatoes-breathe.md | 5 +++++ packages/auth/src/core/auth/emulator.test.ts | 16 ++++++++++++++++ packages/auth/src/core/auth/emulator.ts | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 .changeset/lemon-tomatoes-breathe.md diff --git a/.changeset/lemon-tomatoes-breathe.md b/.changeset/lemon-tomatoes-breathe.md new file mode 100644 index 00000000000..0df04153d61 --- /dev/null +++ b/.changeset/lemon-tomatoes-breathe.md @@ -0,0 +1,5 @@ +--- +"@firebase/auth": patch +--- + +Fix issue where auth port wasn't properly set when setting up cookies in Firebase Studio. diff --git a/packages/auth/src/core/auth/emulator.test.ts b/packages/auth/src/core/auth/emulator.test.ts index 47c5d927c44..5b3ba360c11 100644 --- a/packages/auth/src/core/auth/emulator.test.ts +++ b/packages/auth/src/core/auth/emulator.test.ts @@ -29,6 +29,7 @@ import { Endpoint } from '../../api'; import { UserInternal } from '../../model/user'; import { _castAuth } from './auth_impl'; import { connectAuthEmulator } from './emulator'; +import * as Util from '@firebase/util'; use(sinonChai); use(chaiAsPromised); @@ -38,8 +39,10 @@ describe('core/auth/emulator', () => { let user: UserInternal; let normalEndpoint: fetch.Route; let emulatorEndpoint: fetch.Route; + let utilStub: sinon.SinonStub; beforeEach(async () => { + utilStub = sinon.stub(Util, 'pingServer'); auth = await testAuth(); user = testUser(_castAuth(auth), 'uid', 'email', true); fetch.setUp(); @@ -154,6 +157,19 @@ describe('core/auth/emulator', () => { ); } }); + it('calls pingServer with port if specified', () => { + connectAuthEmulator(auth, 'https://abc.cloudworkstations.dev:2020'); + expect(utilStub).to.have.been.calledWith( + 'https://abc.cloudworkstations.dev:2020' + ); + }); + + it('calls pingServer with no port if none specified', () => { + connectAuthEmulator(auth, 'https://abc.cloudworkstations.dev'); + expect(utilStub).to.have.been.calledWith( + 'https://abc.cloudworkstations.dev' + ); + }); it('logs out a warning to the console', () => { sinon.stub(console, 'info'); diff --git a/packages/auth/src/core/auth/emulator.ts b/packages/auth/src/core/auth/emulator.ts index 05f2e5e4bd5..8547f7bad6c 100644 --- a/packages/auth/src/core/auth/emulator.ts +++ b/packages/auth/src/core/auth/emulator.ts @@ -103,7 +103,7 @@ export function connectAuthEmulator( // Workaround to get cookies in Firebase Studio if (isCloudWorkstation(host)) { - void pingServer(`${protocol}//${host}:${port}`); + void pingServer(`${protocol}//${host}${portStr}`); } } From 3789b5ad16ffd462fce1d0b9c2e9ffae373bc6eb Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Wed, 7 May 2025 07:57:52 -0700 Subject: [PATCH 25/77] Change automaticDataCollectionEnabled to default to true. (#8999) --- .changeset/afraid-baboons-develop.md | 8 ++ docs-devsite/app.firebaseappsettings.md | 4 +- packages/app-check/src/api.test.ts | 73 ++++++++++++++++++- packages/app-check/src/api.ts | 20 +++-- .../app-compat/test/firebaseAppCompat.test.ts | 8 +- packages/app/src/api.test.ts | 19 +++-- packages/app/src/api.ts | 4 +- packages/app/src/firebaseApp.test.ts | 4 +- packages/app/src/firebaseServerApp.ts | 2 +- packages/app/src/public-types.ts | 2 +- 10 files changed, 116 insertions(+), 28 deletions(-) create mode 100644 .changeset/afraid-baboons-develop.md diff --git a/.changeset/afraid-baboons-develop.md b/.changeset/afraid-baboons-develop.md new file mode 100644 index 00000000000..84e4e923f43 --- /dev/null +++ b/.changeset/afraid-baboons-develop.md @@ -0,0 +1,8 @@ +--- +'@firebase/app-check': minor +'@firebase/app': minor +'@firebase/app-compat': minor +'firebase': minor +--- + +Default automaticDataCollectionEnabled to true without changing App Check's default behavior. diff --git a/docs-devsite/app.firebaseappsettings.md b/docs-devsite/app.firebaseappsettings.md index 1515e21ac93..e7838ab4185 100644 --- a/docs-devsite/app.firebaseappsettings.md +++ b/docs-devsite/app.firebaseappsettings.md @@ -22,12 +22,12 @@ export interface FirebaseAppSettings | Property | Type | Description | | --- | --- | --- | -| [automaticDataCollectionEnabled](./app.firebaseappsettings.md#firebaseappsettingsautomaticdatacollectionenabled) | boolean | The settable config flag for GDPR opt-in/opt-out | +| [automaticDataCollectionEnabled](./app.firebaseappsettings.md#firebaseappsettingsautomaticdatacollectionenabled) | boolean | The settable config flag for GDPR opt-in/opt-out. Defaults to true. | | [name](./app.firebaseappsettings.md#firebaseappsettingsname) | string | custom name for the Firebase App. The default value is "[DEFAULT]". | ## FirebaseAppSettings.automaticDataCollectionEnabled -The settable config flag for GDPR opt-in/opt-out +The settable config flag for GDPR opt-in/opt-out. Defaults to true. Signature: diff --git a/packages/app-check/src/api.test.ts b/packages/app-check/src/api.test.ts index a6805d1b0b3..b71971e9d70 100644 --- a/packages/app-check/src/api.test.ts +++ b/packages/app-check/src/api.test.ts @@ -239,7 +239,7 @@ describe('api', () => { expect(getStateReference(app).activated).to.equal(true); }); - it('isTokenAutoRefreshEnabled value defaults to global setting', () => { + it('global false + local unset = false', () => { app.automaticDataCollectionEnabled = false; initializeAppCheck(app, { provider: new ReCaptchaV3Provider(FAKE_SITE_KEY) @@ -247,8 +247,77 @@ describe('api', () => { expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); }); - it('sets isTokenAutoRefreshEnabled correctly, overriding global setting', () => { + it('global false + local true = false', () => { app.automaticDataCollectionEnabled = false; + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: true + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global false + local false = false', () => { + app.automaticDataCollectionEnabled = false; + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: false + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global unset + local unset = false', () => { + // Global unset should default to true. + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY) + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global unset + local false = false', () => { + // Global unset should default to true. + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: false + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global unset + local true = true', () => { + // Global unset should default to true. + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: true + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(true); + }); + + it('global true + local unset = false', () => { + app.automaticDataCollectionEnabled = true; + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY) + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global true + local false = false', () => { + app.automaticDataCollectionEnabled = true; + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: false + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global true + local true = true', () => { + app.automaticDataCollectionEnabled = true; + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: true + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(true); + }); + + it('sets isTokenAutoRefreshEnabled correctly, overriding global setting', () => { initializeAppCheck(app, { provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), isTokenAutoRefreshEnabled: true diff --git a/packages/app-check/src/api.ts b/packages/app-check/src/api.ts index a4dd87a4e77..e7c9f8cfcf9 100644 --- a/packages/app-check/src/api.ts +++ b/packages/app-check/src/api.ts @@ -43,6 +43,7 @@ import { } from './internal-api'; import { readTokenFromStorage } from './storage'; import { getDebugToken, initializeDebugMode, isDebugMode } from './debug'; +import { logger } from './logger'; declare module '@firebase/component' { interface NameServiceMapping { @@ -132,7 +133,7 @@ export function initializeAppCheck( function _activate( app: FirebaseApp, provider: AppCheckProvider, - isTokenAutoRefreshEnabled?: boolean + isTokenAutoRefreshEnabled: boolean = false ): void { // Create an entry in the APP_CHECK_STATES map. Further changes should // directly mutate this object. @@ -149,13 +150,18 @@ function _activate( return cachedToken; }); - // Use value of global `automaticDataCollectionEnabled` (which - // itself defaults to false if not specified in config) if - // `isTokenAutoRefreshEnabled` param was not provided by user. + // Global `automaticDataCollectionEnabled` (defaults to true) and + // `isTokenAutoRefreshEnabled` must both be true. state.isTokenAutoRefreshEnabled = - isTokenAutoRefreshEnabled === undefined - ? app.automaticDataCollectionEnabled - : isTokenAutoRefreshEnabled; + isTokenAutoRefreshEnabled && app.automaticDataCollectionEnabled; + + if (!app.automaticDataCollectionEnabled && isTokenAutoRefreshEnabled) { + logger.warn( + '`isTokenAutoRefreshEnabled` is true but ' + + '`automaticDataCollectionEnabled` was set to false during' + + ' `initializeApp()`. This blocks automatic token refresh.' + ); + } state.provider.initialize(app); } diff --git a/packages/app-compat/test/firebaseAppCompat.test.ts b/packages/app-compat/test/firebaseAppCompat.test.ts index f12a73e61a8..61bbed848d8 100644 --- a/packages/app-compat/test/firebaseAppCompat.test.ts +++ b/packages/app-compat/test/firebaseAppCompat.test.ts @@ -403,17 +403,17 @@ function firebaseAppTests( ).throws(/'abc'.*exists/i); }); - it('automaticDataCollectionEnabled is `false` by default', () => { + it('automaticDataCollectionEnabled is `true` by default', () => { const app = firebase.initializeApp({}, 'my-app'); - expect(app.automaticDataCollectionEnabled).to.eq(false); + expect(app.automaticDataCollectionEnabled).to.eq(true); }); it('automaticDataCollectionEnabled can be set via the config object', () => { const app = firebase.initializeApp( {}, - { automaticDataCollectionEnabled: true } + { automaticDataCollectionEnabled: false } ); - expect(app.automaticDataCollectionEnabled).to.eq(true); + expect(app.automaticDataCollectionEnabled).to.eq(false); }); it('Modifying options object does not change options.', () => { diff --git a/packages/app/src/api.test.ts b/packages/app/src/api.test.ts index f6cf922ba05..4e79ad58d82 100644 --- a/packages/app/src/api.test.ts +++ b/packages/app/src/api.test.ts @@ -128,14 +128,14 @@ describe('API tests', () => { { apiKey: 'test1' }, - { automaticDataCollectionEnabled: true } + { automaticDataCollectionEnabled: false } ); expect(() => initializeApp( { apiKey: 'test1' }, - { automaticDataCollectionEnabled: false } + { automaticDataCollectionEnabled: true } ) ).throws(/\[DEFAULT\].*exists/i); }); @@ -146,14 +146,14 @@ describe('API tests', () => { { apiKey: 'test1' }, - { name: appName, automaticDataCollectionEnabled: true } + { name: appName, automaticDataCollectionEnabled: false } ); expect(() => initializeApp( { apiKey: 'test1' }, - { name: appName, automaticDataCollectionEnabled: false } + { name: appName, automaticDataCollectionEnabled: true } ) ).throws(/'MyApp'.*exists/i); }); @@ -164,11 +164,16 @@ describe('API tests', () => { expect(app.name).to.equal(appName); }); - it('sets automaticDataCollectionEnabled', () => { - const app = initializeApp({}, { automaticDataCollectionEnabled: true }); + it('sets automaticDataCollectionEnabled to true by default', () => { + const app = initializeApp({}); expect(app.automaticDataCollectionEnabled).to.be.true; }); + it('sets a new automaticDataCollectionEnabled value if provided', () => { + const app = initializeApp({}, { automaticDataCollectionEnabled: false }); + expect(app.automaticDataCollectionEnabled).to.be.false; + }); + it('adds registered components to App', () => { _clearComponents(); const comp1 = createTestComponent('test1'); @@ -211,7 +216,7 @@ describe('API tests', () => { const app = initializeServerApp(options, serverAppSettings); expect(app).to.not.equal(null); - expect(app.automaticDataCollectionEnabled).to.be.false; + expect(app.automaticDataCollectionEnabled).to.be.true; await deleteApp(app); expect((app as FirebaseServerAppImpl).isDeleted).to.be.true; }); diff --git a/packages/app/src/api.ts b/packages/app/src/api.ts index b8ec25fc509..9cba8ec6f50 100644 --- a/packages/app/src/api.ts +++ b/packages/app/src/api.ts @@ -143,7 +143,7 @@ export function initializeApp( const config: Required = { name: DEFAULT_ENTRY_NAME, - automaticDataCollectionEnabled: false, + automaticDataCollectionEnabled: true, ...rawConfig }; const name = config.name; @@ -241,7 +241,7 @@ export function initializeServerApp( } if (_serverAppConfig.automaticDataCollectionEnabled === undefined) { - _serverAppConfig.automaticDataCollectionEnabled = false; + _serverAppConfig.automaticDataCollectionEnabled = true; } let appOptions: FirebaseOptions; diff --git a/packages/app/src/firebaseApp.test.ts b/packages/app/src/firebaseApp.test.ts index 419eeef4f1f..3acbb4a2869 100644 --- a/packages/app/src/firebaseApp.test.ts +++ b/packages/app/src/firebaseApp.test.ts @@ -27,11 +27,11 @@ describe('FirebaseAppNext', () => { }; const app = new FirebaseAppImpl( options, - { name: 'test', automaticDataCollectionEnabled: false }, + { name: 'test', automaticDataCollectionEnabled: true }, new ComponentContainer('test') ); - expect(app.automaticDataCollectionEnabled).to.be.false; + expect(app.automaticDataCollectionEnabled).to.be.true; expect(app.name).to.equal('test'); expect(app.options).to.deep.equal(options); }); diff --git a/packages/app/src/firebaseServerApp.ts b/packages/app/src/firebaseServerApp.ts index 21232869c3c..2bb8efc7a63 100644 --- a/packages/app/src/firebaseServerApp.ts +++ b/packages/app/src/firebaseServerApp.ts @@ -74,7 +74,7 @@ export class FirebaseServerAppImpl const automaticDataCollectionEnabled = serverConfig.automaticDataCollectionEnabled !== undefined ? serverConfig.automaticDataCollectionEnabled - : false; + : true; // Create the FirebaseAppSettings object for the FirebaseAppImp constructor. const config: Required = { diff --git a/packages/app/src/public-types.ts b/packages/app/src/public-types.ts index ea68579a7e9..4f8373f2250 100644 --- a/packages/app/src/public-types.ts +++ b/packages/app/src/public-types.ts @@ -165,7 +165,7 @@ export interface FirebaseAppSettings { */ name?: string; /** - * The settable config flag for GDPR opt-in/opt-out + * The settable config flag for GDPR opt-in/opt-out. Defaults to true. */ automaticDataCollectionEnabled?: boolean; } From d5428f3d23e8e483feb42d98bbcf6fa7f3e6df8f Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 7 May 2025 10:32:54 -0700 Subject: [PATCH 26/77] Version Packages (#8997) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/afraid-baboons-develop.md | 8 ---- .changeset/lemon-tomatoes-breathe.md | 5 -- .changeset/nice-plants-thank.md | 10 ---- .changeset/nine-pugs-crash.md | 11 ----- integration/compat-interop/package.json | 28 +++++------ integration/firestore/package.json | 4 +- integration/messaging/package.json | 2 +- packages/analytics-compat/CHANGELOG.md | 9 ++++ packages/analytics-compat/package.json | 10 ++-- packages/analytics/CHANGELOG.md | 9 ++++ packages/analytics/package.json | 10 ++-- packages/app-check-compat/CHANGELOG.md | 9 ++++ packages/app-check-compat/package.json | 10 ++-- packages/app-check/CHANGELOG.md | 12 +++++ packages/app-check/package.json | 8 ++-- packages/app-compat/CHANGELOG.md | 13 +++++ packages/app-compat/package.json | 8 ++-- packages/app/CHANGELOG.md | 12 +++++ packages/app/package.json | 6 +-- packages/auth-compat/CHANGELOG.md | 9 ++++ packages/auth-compat/package.json | 10 ++-- packages/auth/CHANGELOG.md | 12 +++++ packages/auth/package.json | 8 ++-- packages/component/CHANGELOG.md | 7 +++ packages/component/package.json | 4 +- packages/data-connect/CHANGELOG.md | 10 ++++ packages/data-connect/package.json | 8 ++-- packages/database-compat/CHANGELOG.md | 14 ++++++ packages/database-compat/package.json | 12 ++--- packages/database-types/CHANGELOG.md | 7 +++ packages/database-types/package.json | 4 +- packages/database/CHANGELOG.md | 12 +++++ packages/database/package.json | 8 ++-- packages/firebase/CHANGELOG.md | 37 ++++++++++++++ packages/firebase/package.json | 56 +++++++++++----------- packages/firestore-compat/CHANGELOG.md | 9 ++++ packages/firestore-compat/package.json | 10 ++-- packages/firestore/CHANGELOG.md | 12 +++++ packages/firestore/package.json | 12 ++--- packages/functions-compat/CHANGELOG.md | 9 ++++ packages/functions-compat/package.json | 10 ++-- packages/functions/CHANGELOG.md | 10 ++++ packages/functions/package.json | 8 ++-- packages/installations-compat/CHANGELOG.md | 9 ++++ packages/installations-compat/package.json | 10 ++-- packages/installations/CHANGELOG.md | 8 ++++ packages/installations/package.json | 8 ++-- packages/messaging-compat/CHANGELOG.md | 9 ++++ packages/messaging-compat/package.json | 10 ++-- packages/messaging/CHANGELOG.md | 9 ++++ packages/messaging/package.json | 10 ++-- packages/performance-compat/CHANGELOG.md | 9 ++++ packages/performance-compat/package.json | 10 ++-- packages/performance/CHANGELOG.md | 9 ++++ packages/performance/package.json | 10 ++-- packages/remote-config-compat/CHANGELOG.md | 9 ++++ packages/remote-config-compat/package.json | 10 ++-- packages/remote-config/CHANGELOG.md | 9 ++++ packages/remote-config/package.json | 10 ++-- packages/storage-compat/CHANGELOG.md | 9 ++++ packages/storage-compat/package.json | 12 ++--- packages/storage/CHANGELOG.md | 12 +++++ packages/storage/package.json | 10 ++-- packages/template/package.json | 2 +- packages/util/CHANGELOG.md | 8 ++++ packages/util/package.json | 2 +- packages/vertexai/CHANGELOG.md | 8 ++++ packages/vertexai/package.json | 8 ++-- repo-scripts/size-analysis/package.json | 4 +- 69 files changed, 496 insertions(+), 210 deletions(-) delete mode 100644 .changeset/afraid-baboons-develop.md delete mode 100644 .changeset/lemon-tomatoes-breathe.md delete mode 100644 .changeset/nice-plants-thank.md delete mode 100644 .changeset/nine-pugs-crash.md diff --git a/.changeset/afraid-baboons-develop.md b/.changeset/afraid-baboons-develop.md deleted file mode 100644 index 84e4e923f43..00000000000 --- a/.changeset/afraid-baboons-develop.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@firebase/app-check': minor -'@firebase/app': minor -'@firebase/app-compat': minor -'firebase': minor ---- - -Default automaticDataCollectionEnabled to true without changing App Check's default behavior. diff --git a/.changeset/lemon-tomatoes-breathe.md b/.changeset/lemon-tomatoes-breathe.md deleted file mode 100644 index 0df04153d61..00000000000 --- a/.changeset/lemon-tomatoes-breathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@firebase/auth": patch ---- - -Fix issue where auth port wasn't properly set when setting up cookies in Firebase Studio. diff --git a/.changeset/nice-plants-thank.md b/.changeset/nice-plants-thank.md deleted file mode 100644 index 05fb520760f..00000000000 --- a/.changeset/nice-plants-thank.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@firebase/database-compat": patch -"@firebase/database": patch -"@firebase/firestore": patch -"@firebase/functions": patch -"@firebase/storage": patch -"@firebase/util": patch ---- - -Auto Enable SSL for Firebase Studio diff --git a/.changeset/nine-pugs-crash.md b/.changeset/nine-pugs-crash.md deleted file mode 100644 index b4a654a484f..00000000000 --- a/.changeset/nine-pugs-crash.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@firebase/auth": patch -"@firebase/data-connect": patch -"@firebase/database-compat": patch -"@firebase/database": patch -"@firebase/firestore": patch -"@firebase/storage": patch -"@firebase/util": patch ---- - -Fix Auth Redirects on Firebase Studio diff --git a/integration/compat-interop/package.json b/integration/compat-interop/package.json index 4cd59d2626d..dce91e6ce6f 100644 --- a/integration/compat-interop/package.json +++ b/integration/compat-interop/package.json @@ -8,20 +8,20 @@ "test:debug": "karma start --browsers Chrome --auto-watch" }, "dependencies": { - "@firebase/app": "0.11.5", - "@firebase/app-compat": "0.2.54", - "@firebase/analytics": "0.10.12", - "@firebase/analytics-compat": "0.2.18", - "@firebase/auth": "1.10.1", - "@firebase/auth-compat": "0.5.21", - "@firebase/functions": "0.12.3", - "@firebase/functions-compat": "0.3.20", - "@firebase/messaging": "0.12.17", - "@firebase/messaging-compat": "0.2.17", - "@firebase/performance": "0.7.2", - "@firebase/performance-compat": "0.2.15", - "@firebase/remote-config": "0.6.0", - "@firebase/remote-config-compat": "0.2.13" + "@firebase/app": "0.12.0", + "@firebase/app-compat": "0.3.0", + "@firebase/analytics": "0.10.13", + "@firebase/analytics-compat": "0.2.19", + "@firebase/auth": "1.10.2", + "@firebase/auth-compat": "0.5.22", + "@firebase/functions": "0.12.4", + "@firebase/functions-compat": "0.3.21", + "@firebase/messaging": "0.12.18", + "@firebase/messaging-compat": "0.2.18", + "@firebase/performance": "0.7.3", + "@firebase/performance-compat": "0.2.16", + "@firebase/remote-config": "0.6.1", + "@firebase/remote-config-compat": "0.2.14" }, "devDependencies": { "typescript": "5.5.4" diff --git a/integration/firestore/package.json b/integration/firestore/package.json index 9ca8917ab4c..5d4aa90895e 100644 --- a/integration/firestore/package.json +++ b/integration/firestore/package.json @@ -14,8 +14,8 @@ "test:memory:debug": "yarn build:memory; karma start --auto-watch --browsers Chrome" }, "dependencies": { - "@firebase/app": "0.11.5", - "@firebase/firestore": "4.7.11" + "@firebase/app": "0.12.0", + "@firebase/firestore": "4.7.12" }, "devDependencies": { "@types/mocha": "9.1.1", diff --git a/integration/messaging/package.json b/integration/messaging/package.json index a86c4b1f7b5..1f27302d673 100644 --- a/integration/messaging/package.json +++ b/integration/messaging/package.json @@ -9,7 +9,7 @@ "test:manual": "mocha --exit" }, "devDependencies": { - "firebase": "11.6.1", + "firebase": "11.7.0", "chai": "4.5.0", "chromedriver": "119.0.1", "express": "4.21.2", diff --git a/packages/analytics-compat/CHANGELOG.md b/packages/analytics-compat/CHANGELOG.md index 8afe6a643d0..0a349c8d806 100644 --- a/packages/analytics-compat/CHANGELOG.md +++ b/packages/analytics-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/analytics-compat +## 0.2.19 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/analytics@0.10.13 + - @firebase/component@0.6.14 + ## 0.2.18 ### Patch Changes diff --git a/packages/analytics-compat/package.json b/packages/analytics-compat/package.json index 1064125d5f6..b3b633a1572 100644 --- a/packages/analytics-compat/package.json +++ b/packages/analytics-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/analytics-compat", - "version": "0.2.18", + "version": "0.2.19", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -22,7 +22,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.2.54", + "@firebase/app-compat": "0.3.0", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -52,10 +52,10 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.13", - "@firebase/analytics": "0.10.12", + "@firebase/component": "0.6.14", + "@firebase/analytics": "0.10.13", "@firebase/analytics-types": "0.8.3", - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/analytics/CHANGELOG.md b/packages/analytics/CHANGELOG.md index 6b7df8ce4e9..964d404dc9c 100644 --- a/packages/analytics/CHANGELOG.md +++ b/packages/analytics/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/analytics +## 0.10.13 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/installations@0.6.14 + - @firebase/component@0.6.14 + ## 0.10.12 ### Patch Changes diff --git a/packages/analytics/package.json b/packages/analytics/package.json index d56f6f7c61e..76765839c2f 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/analytics", - "version": "0.10.12", + "version": "0.10.13", "description": "A analytics package for new firebase packages", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -39,15 +39,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.13", + "@firebase/installations": "0.6.14", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check-compat/CHANGELOG.md b/packages/app-check-compat/CHANGELOG.md index a5ca70adbc6..3cec9412293 100644 --- a/packages/app-check-compat/CHANGELOG.md +++ b/packages/app-check-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/app-check-compat +## 0.3.21 + +### Patch Changes + +- Updated dependencies [[`3789b5a`](https://github.com/firebase/firebase-js-sdk/commit/3789b5ad16ffd462fce1d0b9c2e9ffae373bc6eb), [`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/app-check@0.9.0 + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.3.20 ### Patch Changes diff --git a/packages/app-check-compat/package.json b/packages/app-check-compat/package.json index 630e7a8d234..44eb56be76a 100644 --- a/packages/app-check-compat/package.json +++ b/packages/app-check-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-check-compat", - "version": "0.3.20", + "version": "0.3.21", "description": "A compat App Check package for new firebase packages", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -34,16 +34,16 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/app-check": "0.8.13", + "@firebase/app-check": "0.9.0", "@firebase/app-check-types": "0.5.3", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.2.54", + "@firebase/app-compat": "0.3.0", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check/CHANGELOG.md b/packages/app-check/CHANGELOG.md index a2afaa1b23a..539e9541659 100644 --- a/packages/app-check/CHANGELOG.md +++ b/packages/app-check/CHANGELOG.md @@ -1,5 +1,17 @@ # @firebase/app-check +## 0.9.0 + +### Minor Changes + +- [`3789b5a`](https://github.com/firebase/firebase-js-sdk/commit/3789b5ad16ffd462fce1d0b9c2e9ffae373bc6eb) [#8999](https://github.com/firebase/firebase-js-sdk/pull/8999) - Default automaticDataCollectionEnabled to true without changing App Check's default behavior. + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.8.13 ### Patch Changes diff --git a/packages/app-check/package.json b/packages/app-check/package.json index ae6555da970..22df124e1c9 100644 --- a/packages/app-check/package.json +++ b/packages/app-check/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-check", - "version": "0.8.13", + "version": "0.9.0", "description": "The App Check component of the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,14 +37,14 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "@firebase/logger": "0.4.4", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-compat/CHANGELOG.md b/packages/app-compat/CHANGELOG.md index d505c7b6240..64790ad39d5 100644 --- a/packages/app-compat/CHANGELOG.md +++ b/packages/app-compat/CHANGELOG.md @@ -1,5 +1,18 @@ # @firebase/app-compat +## 0.3.0 + +### Minor Changes + +- [`3789b5a`](https://github.com/firebase/firebase-js-sdk/commit/3789b5ad16ffd462fce1d0b9c2e9ffae373bc6eb) [#8999](https://github.com/firebase/firebase-js-sdk/pull/8999) - Default automaticDataCollectionEnabled to true without changing App Check's default behavior. + +### Patch Changes + +- Updated dependencies [[`3789b5a`](https://github.com/firebase/firebase-js-sdk/commit/3789b5ad16ffd462fce1d0b9c2e9ffae373bc6eb), [`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/app@0.12.0 + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.2.54 ### Patch Changes diff --git a/packages/app-compat/package.json b/packages/app-compat/package.json index e113c708c74..fbb28d22510 100644 --- a/packages/app-compat/package.json +++ b/packages/app-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-compat", - "version": "0.2.54", + "version": "0.3.0", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,10 +37,10 @@ }, "license": "Apache-2.0", "dependencies": { - "@firebase/app": "0.11.5", - "@firebase/util": "1.11.0", + "@firebase/app": "0.12.0", + "@firebase/util": "1.11.1", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.13", + "@firebase/component": "0.6.14", "tslib": "^2.1.0" }, "devDependencies": { diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 3528eee556d..24b71ffa7ba 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,17 @@ # @firebase/app +## 0.12.0 + +### Minor Changes + +- [`3789b5a`](https://github.com/firebase/firebase-js-sdk/commit/3789b5ad16ffd462fce1d0b9c2e9ffae373bc6eb) [#8999](https://github.com/firebase/firebase-js-sdk/pull/8999) - Default automaticDataCollectionEnabled to true without changing App Check's default behavior. + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.11.5 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 848919067bd..0995e0223f7 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app", - "version": "0.11.5", + "version": "0.12.0", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,9 +37,9 @@ "typings:internal": "node ../../scripts/build/use_typings.js ./dist/app.d.ts" }, "dependencies": { - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.13", + "@firebase/component": "0.6.14", "idb": "7.1.1", "tslib": "^2.1.0" }, diff --git a/packages/auth-compat/CHANGELOG.md b/packages/auth-compat/CHANGELOG.md index 66b1e0d6e28..c3d3e3e3983 100644 --- a/packages/auth-compat/CHANGELOG.md +++ b/packages/auth-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/auth-compat +## 0.5.22 + +### Patch Changes + +- Updated dependencies [[`6a02778`](https://github.com/firebase/firebase-js-sdk/commit/6a02778e3d12af683e710b53dc6dfb64329e8229), [`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/auth@1.10.2 + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.5.21 ### Patch Changes diff --git a/packages/auth-compat/package.json b/packages/auth-compat/package.json index fa69e3f3679..ab1d2548cc0 100644 --- a/packages/auth-compat/package.json +++ b/packages/auth-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth-compat", - "version": "0.5.21", + "version": "0.5.22", "description": "FirebaseAuth compatibility package that uses API style compatible with Firebase@8 and prior versions", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,15 +49,15 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/auth": "1.10.1", + "@firebase/auth": "1.10.2", "@firebase/auth-types": "0.13.0", - "@firebase/component": "0.6.13", - "@firebase/util": "1.11.0", + "@firebase/component": "0.6.14", + "@firebase/util": "1.11.1", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.2.54", + "@firebase/app-compat": "0.3.0", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md index 5a52929a128..74d290fead1 100644 --- a/packages/auth/CHANGELOG.md +++ b/packages/auth/CHANGELOG.md @@ -1,5 +1,17 @@ # @firebase/auth +## 1.10.2 + +### Patch Changes + +- [`6a02778`](https://github.com/firebase/firebase-js-sdk/commit/6a02778e3d12af683e710b53dc6dfb64329e8229) [#8998](https://github.com/firebase/firebase-js-sdk/pull/8998) - Fix issue where auth port wasn't properly set when setting up cookies in Firebase Studio. + +- [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb) [#8968](https://github.com/firebase/firebase-js-sdk/pull/8968) - Fix Auth Redirects on Firebase Studio + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 1.10.1 ### Patch Changes diff --git a/packages/auth/package.json b/packages/auth/package.json index 6a704a4d4b6..6acc99393c8 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth", - "version": "1.10.1", + "version": "1.10.2", "description": "The Firebase Authenticaton component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/node/index.js", @@ -124,14 +124,14 @@ } }, "dependencies": { - "@firebase/component": "0.6.13", + "@firebase/component": "0.6.14", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-strip": "2.1.0", "@types/express": "4.17.21", diff --git a/packages/component/CHANGELOG.md b/packages/component/CHANGELOG.md index df1e6c026ca..6213c4204ae 100644 --- a/packages/component/CHANGELOG.md +++ b/packages/component/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/component +## 0.6.14 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + ## 0.6.13 ### Patch Changes diff --git a/packages/component/package.json b/packages/component/package.json index 3e2e6d11838..f3338c83fdc 100644 --- a/packages/component/package.json +++ b/packages/component/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/component", - "version": "0.6.13", + "version": "0.6.14", "description": "Firebase Component Platform", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -31,7 +31,7 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "tslib": "^2.1.0" }, "license": "Apache-2.0", diff --git a/packages/data-connect/CHANGELOG.md b/packages/data-connect/CHANGELOG.md index 03e17644a77..c222e97117a 100644 --- a/packages/data-connect/CHANGELOG.md +++ b/packages/data-connect/CHANGELOG.md @@ -1,5 +1,15 @@ ## Unreleased +## 0.3.5 + +### Patch Changes + +- [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb) [#8968](https://github.com/firebase/firebase-js-sdk/pull/8968) - Fix Auth Redirects on Firebase Studio + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.3.4 ### Patch Changes diff --git a/packages/data-connect/package.json b/packages/data-connect/package.json index 00f9bb492cc..3a180cabcbc 100644 --- a/packages/data-connect/package.json +++ b/packages/data-connect/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/data-connect", - "version": "0.3.4", + "version": "0.3.5", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,13 +49,13 @@ }, "dependencies": { "@firebase/auth-interop-types": "0.2.4", - "@firebase/component": "0.6.13", + "@firebase/component": "0.6.14", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/database-compat/CHANGELOG.md b/packages/database-compat/CHANGELOG.md index 8cd7d96b5a4..9fde0eac95c 100644 --- a/packages/database-compat/CHANGELOG.md +++ b/packages/database-compat/CHANGELOG.md @@ -1,5 +1,19 @@ # @firebase/database-compat +## 2.0.6 + +### Patch Changes + +- [`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875) [#8980](https://github.com/firebase/firebase-js-sdk/pull/8980) - Auto Enable SSL for Firebase Studio + +- [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb) [#8968](https://github.com/firebase/firebase-js-sdk/pull/8968) - Fix Auth Redirects on Firebase Studio + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/database@1.0.15 + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + - @firebase/database-types@1.0.11 + ## 2.0.5 ### Patch Changes diff --git a/packages/database-compat/package.json b/packages/database-compat/package.json index 65deedb34e2..d85ba6991b2 100644 --- a/packages/database-compat/package.json +++ b/packages/database-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database-compat", - "version": "2.0.5", + "version": "2.0.6", "description": "The Realtime Database component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.js", @@ -49,15 +49,15 @@ "add-compat-overloads": "ts-node-script ../../scripts/build/create-overloads.ts -i ../database/dist/public.d.ts -o dist/database-compat/src/index.d.ts -a -r Database:types.FirebaseDatabase -r Query:types.Query -r DatabaseReference:types.Reference -r FirebaseApp:FirebaseAppCompat --moduleToEnhance @firebase/database" }, "dependencies": { - "@firebase/database": "1.0.14", - "@firebase/database-types": "1.0.10", + "@firebase/database": "1.0.15", + "@firebase/database-types": "1.0.11", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.2.54", + "@firebase/app-compat": "0.3.0", "typescript": "5.5.4" }, "repository": { diff --git a/packages/database-types/CHANGELOG.md b/packages/database-types/CHANGELOG.md index 6292941e87e..e24ab9364a6 100644 --- a/packages/database-types/CHANGELOG.md +++ b/packages/database-types/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/database-types +## 1.0.11 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + ## 1.0.10 ### Patch Changes diff --git a/packages/database-types/package.json b/packages/database-types/package.json index 4a4995377fe..20565d5c28a 100644 --- a/packages/database-types/package.json +++ b/packages/database-types/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database-types", - "version": "1.0.10", + "version": "1.0.11", "description": "@firebase/database Types", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -13,7 +13,7 @@ ], "dependencies": { "@firebase/app-types": "0.9.3", - "@firebase/util": "1.11.0" + "@firebase/util": "1.11.1" }, "repository": { "directory": "packages/database-types", diff --git a/packages/database/CHANGELOG.md b/packages/database/CHANGELOG.md index 451201ee462..1cfa56fd78e 100644 --- a/packages/database/CHANGELOG.md +++ b/packages/database/CHANGELOG.md @@ -1,5 +1,17 @@ # Unreleased +## 1.0.15 + +### Patch Changes + +- [`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875) [#8980](https://github.com/firebase/firebase-js-sdk/pull/8980) - Auto Enable SSL for Firebase Studio + +- [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb) [#8968](https://github.com/firebase/firebase-js-sdk/pull/8968) - Fix Auth Redirects on Firebase Studio + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 1.0.14 ### Patch Changes diff --git a/packages/database/package.json b/packages/database/package.json index d6f5ddc1707..aef6416d018 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database", - "version": "1.0.14", + "version": "1.0.15", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,15 +49,15 @@ "peerDependencies": {}, "dependencies": { "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "@firebase/app-check-interop-types": "0.3.3", "@firebase/auth-interop-types": "0.2.4", "faye-websocket": "0.11.4", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/firebase/CHANGELOG.md b/packages/firebase/CHANGELOG.md index e71a10d560f..8e2d196db27 100644 --- a/packages/firebase/CHANGELOG.md +++ b/packages/firebase/CHANGELOG.md @@ -1,5 +1,42 @@ # firebase +## 11.7.0 + +### Minor Changes + +- [`3789b5a`](https://github.com/firebase/firebase-js-sdk/commit/3789b5ad16ffd462fce1d0b9c2e9ffae373bc6eb) [#8999](https://github.com/firebase/firebase-js-sdk/pull/8999) - Default automaticDataCollectionEnabled to true without changing App Check's default behavior. + +### Patch Changes + +- Updated dependencies [[`3789b5a`](https://github.com/firebase/firebase-js-sdk/commit/3789b5ad16ffd462fce1d0b9c2e9ffae373bc6eb), [`6a02778`](https://github.com/firebase/firebase-js-sdk/commit/6a02778e3d12af683e710b53dc6dfb64329e8229), [`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/app-check@0.9.0 + - @firebase/app@0.12.0 + - @firebase/app-compat@0.3.0 + - @firebase/auth@1.10.2 + - @firebase/database-compat@2.0.6 + - @firebase/database@1.0.15 + - @firebase/firestore@4.7.12 + - @firebase/functions@0.12.4 + - @firebase/storage@0.13.8 + - @firebase/util@1.11.1 + - @firebase/data-connect@0.3.5 + - @firebase/app-check-compat@0.3.21 + - @firebase/analytics@0.10.13 + - @firebase/installations@0.6.14 + - @firebase/messaging@0.12.18 + - @firebase/performance@0.7.3 + - @firebase/remote-config@0.6.1 + - @firebase/vertexai@1.2.2 + - @firebase/analytics-compat@0.2.19 + - @firebase/auth-compat@0.5.22 + - @firebase/firestore-compat@0.3.47 + - @firebase/functions-compat@0.3.21 + - @firebase/installations-compat@0.2.14 + - @firebase/messaging-compat@0.2.18 + - @firebase/performance-compat@0.2.16 + - @firebase/remote-config-compat@0.2.14 + - @firebase/storage-compat@0.3.18 + ## 11.6.1 ### Patch Changes diff --git a/packages/firebase/package.json b/packages/firebase/package.json index 3a4bec301e3..2335648ddc7 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -1,6 +1,6 @@ { "name": "firebase", - "version": "11.6.1", + "version": "11.7.0", "description": "Firebase JavaScript library for web and Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -399,34 +399,34 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { - "@firebase/app": "0.11.5", - "@firebase/app-compat": "0.2.54", + "@firebase/app": "0.12.0", + "@firebase/app-compat": "0.3.0", "@firebase/app-types": "0.9.3", - "@firebase/auth": "1.10.1", - "@firebase/auth-compat": "0.5.21", - "@firebase/data-connect": "0.3.4", - "@firebase/database": "1.0.14", - "@firebase/database-compat": "2.0.5", - "@firebase/firestore": "4.7.11", - "@firebase/firestore-compat": "0.3.46", - "@firebase/functions": "0.12.3", - "@firebase/functions-compat": "0.3.20", - "@firebase/installations": "0.6.13", - "@firebase/installations-compat": "0.2.13", - "@firebase/messaging": "0.12.17", - "@firebase/messaging-compat": "0.2.17", - "@firebase/storage": "0.13.7", - "@firebase/storage-compat": "0.3.17", - "@firebase/performance": "0.7.2", - "@firebase/performance-compat": "0.2.15", - "@firebase/remote-config": "0.6.0", - "@firebase/remote-config-compat": "0.2.13", - "@firebase/analytics": "0.10.12", - "@firebase/analytics-compat": "0.2.18", - "@firebase/app-check": "0.8.13", - "@firebase/app-check-compat": "0.3.20", - "@firebase/util": "1.11.0", - "@firebase/vertexai": "1.2.1" + "@firebase/auth": "1.10.2", + "@firebase/auth-compat": "0.5.22", + "@firebase/data-connect": "0.3.5", + "@firebase/database": "1.0.15", + "@firebase/database-compat": "2.0.6", + "@firebase/firestore": "4.7.12", + "@firebase/firestore-compat": "0.3.47", + "@firebase/functions": "0.12.4", + "@firebase/functions-compat": "0.3.21", + "@firebase/installations": "0.6.14", + "@firebase/installations-compat": "0.2.14", + "@firebase/messaging": "0.12.18", + "@firebase/messaging-compat": "0.2.18", + "@firebase/storage": "0.13.8", + "@firebase/storage-compat": "0.3.18", + "@firebase/performance": "0.7.3", + "@firebase/performance-compat": "0.2.16", + "@firebase/remote-config": "0.6.1", + "@firebase/remote-config-compat": "0.2.14", + "@firebase/analytics": "0.10.13", + "@firebase/analytics-compat": "0.2.19", + "@firebase/app-check": "0.9.0", + "@firebase/app-check-compat": "0.3.21", + "@firebase/util": "1.11.1", + "@firebase/vertexai": "1.2.2" }, "devDependencies": { "rollup": "2.79.2", diff --git a/packages/firestore-compat/CHANGELOG.md b/packages/firestore-compat/CHANGELOG.md index aadd8c532b3..c721b0b7b20 100644 --- a/packages/firestore-compat/CHANGELOG.md +++ b/packages/firestore-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/firestore-compat +## 0.3.47 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/firestore@4.7.12 + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.3.46 ### Patch Changes diff --git a/packages/firestore-compat/package.json b/packages/firestore-compat/package.json index 58210252655..bfc0f71c03a 100644 --- a/packages/firestore-compat/package.json +++ b/packages/firestore-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore-compat", - "version": "0.3.46", + "version": "0.3.47", "description": "The Cloud Firestore component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -46,14 +46,14 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/component": "0.6.13", - "@firebase/firestore": "4.7.11", - "@firebase/util": "1.11.0", + "@firebase/component": "0.6.14", + "@firebase/firestore": "4.7.12", + "@firebase/util": "1.11.1", "@firebase/firestore-types": "3.0.3", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.2.54", + "@firebase/app-compat": "0.3.0", "@types/eslint": "7.29.0", "rollup": "2.79.2", "rollup-plugin-sourcemaps": "0.6.3", diff --git a/packages/firestore/CHANGELOG.md b/packages/firestore/CHANGELOG.md index 421f33ed1d5..0fd288b01c8 100644 --- a/packages/firestore/CHANGELOG.md +++ b/packages/firestore/CHANGELOG.md @@ -1,5 +1,17 @@ # @firebase/firestore +## 4.7.12 + +### Patch Changes + +- [`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875) [#8980](https://github.com/firebase/firebase-js-sdk/pull/8980) - Auto Enable SSL for Firebase Studio + +- [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb) [#8968](https://github.com/firebase/firebase-js-sdk/pull/8968) - Fix Auth Redirects on Firebase Studio + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 4.7.11 ### Patch Changes diff --git a/packages/firestore/package.json b/packages/firestore/package.json index 8cc7e5e18f5..c6075767a85 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore", - "version": "4.7.11", + "version": "4.7.12", "engines": { "node": ">=18.0.0" }, @@ -98,9 +98,9 @@ "lite/package.json" ], "dependencies": { - "@firebase/component": "0.6.13", + "@firebase/component": "0.6.14", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "@firebase/webchannel-wrapper": "1.0.3", "@grpc/grpc-js": "~1.9.0", "@grpc/proto-loader": "^0.7.8", @@ -110,9 +110,9 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.11.5", - "@firebase/app-compat": "0.2.54", - "@firebase/auth": "1.10.1", + "@firebase/app": "0.12.0", + "@firebase/app-compat": "0.3.0", + "@firebase/auth": "1.10.2", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", "@types/eslint": "7.29.0", diff --git a/packages/functions-compat/CHANGELOG.md b/packages/functions-compat/CHANGELOG.md index 6187c248d91..28a5efc295d 100644 --- a/packages/functions-compat/CHANGELOG.md +++ b/packages/functions-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/functions-compat +## 0.3.21 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/functions@0.12.4 + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.3.20 ### Patch Changes diff --git a/packages/functions-compat/package.json b/packages/functions-compat/package.json index c2757fcf130..72f3643a99b 100644 --- a/packages/functions-compat/package.json +++ b/packages/functions-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/functions-compat", - "version": "0.3.20", + "version": "0.3.21", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -29,7 +29,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.2.54", + "@firebase/app-compat": "0.3.0", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -62,10 +62,10 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.13", - "@firebase/functions": "0.12.3", + "@firebase/component": "0.6.14", + "@firebase/functions": "0.12.4", "@firebase/functions-types": "0.6.3", - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/functions/CHANGELOG.md b/packages/functions/CHANGELOG.md index c9657c72276..c0a54ac71b0 100644 --- a/packages/functions/CHANGELOG.md +++ b/packages/functions/CHANGELOG.md @@ -1,5 +1,15 @@ # @firebase/functions +## 0.12.4 + +### Patch Changes + +- [`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875) [#8980](https://github.com/firebase/firebase-js-sdk/pull/8980) - Auto Enable SSL for Firebase Studio + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.12.3 ### Patch Changes diff --git a/packages/functions/package.json b/packages/functions/package.json index 4ddf15ac556..8ec8ee12ed3 100644 --- a/packages/functions/package.json +++ b/packages/functions/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/functions", - "version": "0.12.3", + "version": "0.12.4", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -49,7 +49,7 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -65,11 +65,11 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.13", + "@firebase/component": "0.6.14", "@firebase/messaging-interop-types": "0.2.3", "@firebase/auth-interop-types": "0.2.4", "@firebase/app-check-interop-types": "0.3.3", - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/installations-compat/CHANGELOG.md b/packages/installations-compat/CHANGELOG.md index c3283b451a4..e34cf0add2a 100644 --- a/packages/installations-compat/CHANGELOG.md +++ b/packages/installations-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/installations-compat +## 0.2.14 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/installations@0.6.14 + - @firebase/component@0.6.14 + ## 0.2.13 ### Patch Changes diff --git a/packages/installations-compat/package.json b/packages/installations-compat/package.json index 0f5203dd5d1..28f0827fdff 100644 --- a/packages/installations-compat/package.json +++ b/packages/installations-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/installations-compat", - "version": "0.2.13", + "version": "0.2.14", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", "module": "dist/esm/index.esm2017.js", @@ -44,7 +44,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app-compat": "0.2.54", + "@firebase/app-compat": "0.3.0", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", @@ -57,10 +57,10 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.13", + "@firebase/installations": "0.6.14", "@firebase/installations-types": "0.5.3", - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "tslib": "^2.1.0" } } diff --git a/packages/installations/CHANGELOG.md b/packages/installations/CHANGELOG.md index da975f33726..71a9e9757cd 100644 --- a/packages/installations/CHANGELOG.md +++ b/packages/installations/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/installations +## 0.6.14 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.6.13 ### Patch Changes diff --git a/packages/installations/package.json b/packages/installations/package.json index 83db977a6b6..08c7803e9b1 100644 --- a/packages/installations/package.json +++ b/packages/installations/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/installations", - "version": "0.6.13", + "version": "0.6.14", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", "module": "dist/esm/index.esm2017.js", @@ -49,7 +49,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", @@ -62,8 +62,8 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "idb": "7.1.1", "tslib": "^2.1.0" } diff --git a/packages/messaging-compat/CHANGELOG.md b/packages/messaging-compat/CHANGELOG.md index b99cdf6a91d..61da0c87ed3 100644 --- a/packages/messaging-compat/CHANGELOG.md +++ b/packages/messaging-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/messaging-compat +## 0.2.18 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/messaging@0.12.18 + - @firebase/component@0.6.14 + ## 0.2.17 ### Patch Changes diff --git a/packages/messaging-compat/package.json b/packages/messaging-compat/package.json index 388670eb5ab..23de8651813 100644 --- a/packages/messaging-compat/package.json +++ b/packages/messaging-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/messaging-compat", - "version": "0.2.17", + "version": "0.2.18", "license": "Apache-2.0", "description": "", "author": "Firebase (https://firebase.google.com/)", @@ -38,13 +38,13 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/messaging": "0.12.17", - "@firebase/component": "0.6.13", - "@firebase/util": "1.11.0", + "@firebase/messaging": "0.12.18", + "@firebase/component": "0.6.14", + "@firebase/util": "1.11.1", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.2.54", + "@firebase/app-compat": "0.3.0", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", "ts-essentials": "9.4.2", diff --git a/packages/messaging/CHANGELOG.md b/packages/messaging/CHANGELOG.md index 87191b38a3b..d0a684dcb8e 100644 --- a/packages/messaging/CHANGELOG.md +++ b/packages/messaging/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/messaging +## 0.12.18 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/installations@0.6.14 + - @firebase/component@0.6.14 + ## 0.12.17 ### Patch Changes diff --git a/packages/messaging/package.json b/packages/messaging/package.json index 5e25b2b1ca0..d18343d169f 100644 --- a/packages/messaging/package.json +++ b/packages/messaging/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/messaging", - "version": "0.12.17", + "version": "0.12.18", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -52,15 +52,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.13", + "@firebase/installations": "0.6.14", "@firebase/messaging-interop-types": "0.2.3", - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "idb": "7.1.1", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/performance-compat/CHANGELOG.md b/packages/performance-compat/CHANGELOG.md index 0091e5fbb04..56d5b805eef 100644 --- a/packages/performance-compat/CHANGELOG.md +++ b/packages/performance-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/performance-compat +## 0.2.16 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/performance@0.7.3 + - @firebase/component@0.6.14 + ## 0.2.15 ### Patch Changes diff --git a/packages/performance-compat/package.json b/packages/performance-compat/package.json index 69c24f13465..4034920cb27 100644 --- a/packages/performance-compat/package.json +++ b/packages/performance-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/performance-compat", - "version": "0.2.15", + "version": "0.2.16", "description": "The compatibility package of Firebase Performance", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -38,11 +38,11 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/performance": "0.7.2", + "@firebase/performance": "0.7.3", "@firebase/performance-types": "0.2.3", - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.13", + "@firebase/component": "0.6.14", "tslib": "^2.1.0" }, "devDependencies": { @@ -51,7 +51,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.2.54" + "@firebase/app-compat": "0.3.0" }, "repository": { "directory": "packages/performance-compat", diff --git a/packages/performance/CHANGELOG.md b/packages/performance/CHANGELOG.md index d58028ad104..b03ece7ed0c 100644 --- a/packages/performance/CHANGELOG.md +++ b/packages/performance/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/performance +## 0.7.3 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/installations@0.6.14 + - @firebase/component@0.6.14 + ## 0.7.2 ### Patch Changes diff --git a/packages/performance/package.json b/packages/performance/package.json index 07e8e60d054..ed9f28e04b9 100644 --- a/packages/performance/package.json +++ b/packages/performance/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/performance", - "version": "0.7.2", + "version": "0.7.3", "description": "Firebase performance for web", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -39,15 +39,15 @@ }, "dependencies": { "@firebase/logger": "0.4.4", - "@firebase/installations": "0.6.13", - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/installations": "0.6.14", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "tslib": "^2.1.0", "web-vitals": "^4.2.4" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/remote-config-compat/CHANGELOG.md b/packages/remote-config-compat/CHANGELOG.md index abd4ab9ae01..01ed6bc0cef 100644 --- a/packages/remote-config-compat/CHANGELOG.md +++ b/packages/remote-config-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/remote-config-compat +## 0.2.14 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/remote-config@0.6.1 + - @firebase/component@0.6.14 + ## 0.2.13 ### Patch Changes diff --git a/packages/remote-config-compat/package.json b/packages/remote-config-compat/package.json index 1055c892435..0bbeb8c33e5 100644 --- a/packages/remote-config-compat/package.json +++ b/packages/remote-config-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/remote-config-compat", - "version": "0.2.13", + "version": "0.2.14", "description": "The compatibility package of Remote Config", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,11 +37,11 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/remote-config": "0.6.0", + "@firebase/remote-config": "0.6.1", "@firebase/remote-config-types": "0.4.0", - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.13", + "@firebase/component": "0.6.14", "tslib": "^2.1.0" }, "devDependencies": { @@ -50,7 +50,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.2.54" + "@firebase/app-compat": "0.3.0" }, "repository": { "directory": "packages/remote-config-compat", diff --git a/packages/remote-config/CHANGELOG.md b/packages/remote-config/CHANGELOG.md index 055cdae474f..4a209ee5e38 100644 --- a/packages/remote-config/CHANGELOG.md +++ b/packages/remote-config/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/remote-config +## 0.6.1 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/installations@0.6.14 + - @firebase/component@0.6.14 + ## 0.6.0 ### Minor Changes diff --git a/packages/remote-config/package.json b/packages/remote-config/package.json index 4262488b0fb..666f5d4b141 100644 --- a/packages/remote-config/package.json +++ b/packages/remote-config/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/remote-config", - "version": "0.6.0", + "version": "0.6.1", "description": "The Remote Config package of the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -40,15 +40,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.13", + "@firebase/installations": "0.6.14", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/storage-compat/CHANGELOG.md b/packages/storage-compat/CHANGELOG.md index 1ddc092ba0b..309cd864964 100644 --- a/packages/storage-compat/CHANGELOG.md +++ b/packages/storage-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/storage-compat +## 0.3.18 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/storage@0.13.8 + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.3.17 ### Patch Changes diff --git a/packages/storage-compat/package.json b/packages/storage-compat/package.json index d829a79cbc5..8b7fca14dff 100644 --- a/packages/storage-compat/package.json +++ b/packages/storage-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage-compat", - "version": "0.3.17", + "version": "0.3.18", "description": "The Firebase Firestore compatibility package", "author": "Firebase (https://firebase.google.com/)", "main": "./dist/index.cjs.js", @@ -37,15 +37,15 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/storage": "0.13.7", + "@firebase/storage": "0.13.8", "@firebase/storage-types": "0.8.3", - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.2.54", - "@firebase/auth-compat": "0.5.21", + "@firebase/app-compat": "0.3.0", + "@firebase/auth-compat": "0.5.22", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/storage/CHANGELOG.md b/packages/storage/CHANGELOG.md index 4bfd1ded8da..7636c13260d 100644 --- a/packages/storage/CHANGELOG.md +++ b/packages/storage/CHANGELOG.md @@ -1,5 +1,17 @@ #Unreleased +## 0.13.8 + +### Patch Changes + +- [`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875) [#8980](https://github.com/firebase/firebase-js-sdk/pull/8980) - Auto Enable SSL for Firebase Studio + +- [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb) [#8968](https://github.com/firebase/firebase-js-sdk/pull/8968) - Fix Auth Redirects on Firebase Studio + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 0.13.7 ### Patch Changes diff --git a/packages/storage/package.json b/packages/storage/package.json index 0a7a8af9b93..b7cf6228960 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage", - "version": "0.13.7", + "version": "0.13.8", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -46,16 +46,16 @@ }, "license": "Apache-2.0", "dependencies": { - "@firebase/util": "1.11.0", - "@firebase/component": "0.6.13", + "@firebase/util": "1.11.1", + "@firebase/component": "0.6.14", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.11.5", - "@firebase/auth": "1.10.1", + "@firebase/app": "0.12.0", + "@firebase/auth": "1.10.2", "rollup": "2.79.2", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", diff --git a/packages/template/package.json b/packages/template/package.json index 80500aa1392..a67a5813982 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -48,7 +48,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/util/CHANGELOG.md b/packages/util/CHANGELOG.md index e42a02b2220..5a33cbad42b 100644 --- a/packages/util/CHANGELOG.md +++ b/packages/util/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/util +## 1.11.1 + +### Patch Changes + +- [`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875) [#8980](https://github.com/firebase/firebase-js-sdk/pull/8980) - Auto Enable SSL for Firebase Studio + +- [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb) [#8968](https://github.com/firebase/firebase-js-sdk/pull/8968) - Fix Auth Redirects on Firebase Studio + ## 1.11.0 ### Minor Changes diff --git a/packages/util/package.json b/packages/util/package.json index 3200d18c487..8d8c85b3f2e 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/util", - "version": "1.11.0", + "version": "1.11.1", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", diff --git a/packages/vertexai/CHANGELOG.md b/packages/vertexai/CHANGELOG.md index 3e1a1f3c326..8fa9e9d4b4a 100644 --- a/packages/vertexai/CHANGELOG.md +++ b/packages/vertexai/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/vertexai +## 1.2.2 + +### Patch Changes + +- Updated dependencies [[`ea1f913`](https://github.com/firebase/firebase-js-sdk/commit/ea1f9139e6baec0269fbb91233fd3f7f4b0d5875), [`0e12766`](https://github.com/firebase/firebase-js-sdk/commit/0e127664946ba324c6566a02b393dafd23fc1ddb)]: + - @firebase/util@1.11.1 + - @firebase/component@0.6.14 + ## 1.2.1 ### Patch Changes diff --git a/packages/vertexai/package.json b/packages/vertexai/package.json index e3472e733f8..9d71ac73b91 100644 --- a/packages/vertexai/package.json +++ b/packages/vertexai/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/vertexai", - "version": "1.2.1", + "version": "1.2.2", "description": "A Firebase SDK for VertexAI", "author": "Firebase (https://firebase.google.com/)", "engines": { @@ -49,14 +49,14 @@ }, "dependencies": { "@firebase/app-check-interop-types": "0.3.3", - "@firebase/component": "0.6.13", + "@firebase/component": "0.6.14", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/repo-scripts/size-analysis/package.json b/repo-scripts/size-analysis/package.json index f2a4a35bd53..a723d4a52df 100644 --- a/repo-scripts/size-analysis/package.json +++ b/repo-scripts/size-analysis/package.json @@ -20,9 +20,9 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.11.5", + "@firebase/app": "0.12.0", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.0", + "@firebase/util": "1.11.1", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-node-resolve": "16.0.0", From 51e7b489d8aadd531453f882421903da8727b19d Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Wed, 7 May 2025 15:51:17 -0700 Subject: [PATCH 27/77] Roll back #8999 (#9007) * Revert "Change automaticDataCollectionEnabled to default to true. (#8999)" This reverts commit 3789b5ad16ffd462fce1d0b9c2e9ffae373bc6eb. * Revert #8999 - backend is not ready --- .changeset/blue-spies-visit.md | 7 ++ docs-devsite/app.firebaseappsettings.md | 4 +- packages/app-check/src/api.test.ts | 73 +------------------ packages/app-check/src/api.ts | 20 ++--- .../app-compat/test/firebaseAppCompat.test.ts | 8 +- packages/app/src/api.test.ts | 19 ++--- packages/app/src/api.ts | 4 +- packages/app/src/firebaseApp.test.ts | 4 +- packages/app/src/firebaseServerApp.ts | 2 +- packages/app/src/public-types.ts | 2 +- 10 files changed, 35 insertions(+), 108 deletions(-) create mode 100644 .changeset/blue-spies-visit.md diff --git a/.changeset/blue-spies-visit.md b/.changeset/blue-spies-visit.md new file mode 100644 index 00000000000..2f1ebf01827 --- /dev/null +++ b/.changeset/blue-spies-visit.md @@ -0,0 +1,7 @@ +--- +'@firebase/app-compat': patch +'@firebase/app-check': patch +'@firebase/app': patch +--- + +Revert https://github.com/firebase/firebase-js-sdk/pull/8999 diff --git a/docs-devsite/app.firebaseappsettings.md b/docs-devsite/app.firebaseappsettings.md index e7838ab4185..1515e21ac93 100644 --- a/docs-devsite/app.firebaseappsettings.md +++ b/docs-devsite/app.firebaseappsettings.md @@ -22,12 +22,12 @@ export interface FirebaseAppSettings | Property | Type | Description | | --- | --- | --- | -| [automaticDataCollectionEnabled](./app.firebaseappsettings.md#firebaseappsettingsautomaticdatacollectionenabled) | boolean | The settable config flag for GDPR opt-in/opt-out. Defaults to true. | +| [automaticDataCollectionEnabled](./app.firebaseappsettings.md#firebaseappsettingsautomaticdatacollectionenabled) | boolean | The settable config flag for GDPR opt-in/opt-out | | [name](./app.firebaseappsettings.md#firebaseappsettingsname) | string | custom name for the Firebase App. The default value is "[DEFAULT]". | ## FirebaseAppSettings.automaticDataCollectionEnabled -The settable config flag for GDPR opt-in/opt-out. Defaults to true. +The settable config flag for GDPR opt-in/opt-out Signature: diff --git a/packages/app-check/src/api.test.ts b/packages/app-check/src/api.test.ts index b71971e9d70..a6805d1b0b3 100644 --- a/packages/app-check/src/api.test.ts +++ b/packages/app-check/src/api.test.ts @@ -239,7 +239,7 @@ describe('api', () => { expect(getStateReference(app).activated).to.equal(true); }); - it('global false + local unset = false', () => { + it('isTokenAutoRefreshEnabled value defaults to global setting', () => { app.automaticDataCollectionEnabled = false; initializeAppCheck(app, { provider: new ReCaptchaV3Provider(FAKE_SITE_KEY) @@ -247,77 +247,8 @@ describe('api', () => { expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); }); - it('global false + local true = false', () => { - app.automaticDataCollectionEnabled = false; - initializeAppCheck(app, { - provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), - isTokenAutoRefreshEnabled: true - }); - expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); - }); - - it('global false + local false = false', () => { - app.automaticDataCollectionEnabled = false; - initializeAppCheck(app, { - provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), - isTokenAutoRefreshEnabled: false - }); - expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); - }); - - it('global unset + local unset = false', () => { - // Global unset should default to true. - initializeAppCheck(app, { - provider: new ReCaptchaV3Provider(FAKE_SITE_KEY) - }); - expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); - }); - - it('global unset + local false = false', () => { - // Global unset should default to true. - initializeAppCheck(app, { - provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), - isTokenAutoRefreshEnabled: false - }); - expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); - }); - - it('global unset + local true = true', () => { - // Global unset should default to true. - initializeAppCheck(app, { - provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), - isTokenAutoRefreshEnabled: true - }); - expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(true); - }); - - it('global true + local unset = false', () => { - app.automaticDataCollectionEnabled = true; - initializeAppCheck(app, { - provider: new ReCaptchaV3Provider(FAKE_SITE_KEY) - }); - expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); - }); - - it('global true + local false = false', () => { - app.automaticDataCollectionEnabled = true; - initializeAppCheck(app, { - provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), - isTokenAutoRefreshEnabled: false - }); - expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); - }); - - it('global true + local true = true', () => { - app.automaticDataCollectionEnabled = true; - initializeAppCheck(app, { - provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), - isTokenAutoRefreshEnabled: true - }); - expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(true); - }); - it('sets isTokenAutoRefreshEnabled correctly, overriding global setting', () => { + app.automaticDataCollectionEnabled = false; initializeAppCheck(app, { provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), isTokenAutoRefreshEnabled: true diff --git a/packages/app-check/src/api.ts b/packages/app-check/src/api.ts index e7c9f8cfcf9..a4dd87a4e77 100644 --- a/packages/app-check/src/api.ts +++ b/packages/app-check/src/api.ts @@ -43,7 +43,6 @@ import { } from './internal-api'; import { readTokenFromStorage } from './storage'; import { getDebugToken, initializeDebugMode, isDebugMode } from './debug'; -import { logger } from './logger'; declare module '@firebase/component' { interface NameServiceMapping { @@ -133,7 +132,7 @@ export function initializeAppCheck( function _activate( app: FirebaseApp, provider: AppCheckProvider, - isTokenAutoRefreshEnabled: boolean = false + isTokenAutoRefreshEnabled?: boolean ): void { // Create an entry in the APP_CHECK_STATES map. Further changes should // directly mutate this object. @@ -150,18 +149,13 @@ function _activate( return cachedToken; }); - // Global `automaticDataCollectionEnabled` (defaults to true) and - // `isTokenAutoRefreshEnabled` must both be true. + // Use value of global `automaticDataCollectionEnabled` (which + // itself defaults to false if not specified in config) if + // `isTokenAutoRefreshEnabled` param was not provided by user. state.isTokenAutoRefreshEnabled = - isTokenAutoRefreshEnabled && app.automaticDataCollectionEnabled; - - if (!app.automaticDataCollectionEnabled && isTokenAutoRefreshEnabled) { - logger.warn( - '`isTokenAutoRefreshEnabled` is true but ' + - '`automaticDataCollectionEnabled` was set to false during' + - ' `initializeApp()`. This blocks automatic token refresh.' - ); - } + isTokenAutoRefreshEnabled === undefined + ? app.automaticDataCollectionEnabled + : isTokenAutoRefreshEnabled; state.provider.initialize(app); } diff --git a/packages/app-compat/test/firebaseAppCompat.test.ts b/packages/app-compat/test/firebaseAppCompat.test.ts index 61bbed848d8..f12a73e61a8 100644 --- a/packages/app-compat/test/firebaseAppCompat.test.ts +++ b/packages/app-compat/test/firebaseAppCompat.test.ts @@ -403,17 +403,17 @@ function firebaseAppTests( ).throws(/'abc'.*exists/i); }); - it('automaticDataCollectionEnabled is `true` by default', () => { + it('automaticDataCollectionEnabled is `false` by default', () => { const app = firebase.initializeApp({}, 'my-app'); - expect(app.automaticDataCollectionEnabled).to.eq(true); + expect(app.automaticDataCollectionEnabled).to.eq(false); }); it('automaticDataCollectionEnabled can be set via the config object', () => { const app = firebase.initializeApp( {}, - { automaticDataCollectionEnabled: false } + { automaticDataCollectionEnabled: true } ); - expect(app.automaticDataCollectionEnabled).to.eq(false); + expect(app.automaticDataCollectionEnabled).to.eq(true); }); it('Modifying options object does not change options.', () => { diff --git a/packages/app/src/api.test.ts b/packages/app/src/api.test.ts index 4e79ad58d82..f6cf922ba05 100644 --- a/packages/app/src/api.test.ts +++ b/packages/app/src/api.test.ts @@ -128,14 +128,14 @@ describe('API tests', () => { { apiKey: 'test1' }, - { automaticDataCollectionEnabled: false } + { automaticDataCollectionEnabled: true } ); expect(() => initializeApp( { apiKey: 'test1' }, - { automaticDataCollectionEnabled: true } + { automaticDataCollectionEnabled: false } ) ).throws(/\[DEFAULT\].*exists/i); }); @@ -146,14 +146,14 @@ describe('API tests', () => { { apiKey: 'test1' }, - { name: appName, automaticDataCollectionEnabled: false } + { name: appName, automaticDataCollectionEnabled: true } ); expect(() => initializeApp( { apiKey: 'test1' }, - { name: appName, automaticDataCollectionEnabled: true } + { name: appName, automaticDataCollectionEnabled: false } ) ).throws(/'MyApp'.*exists/i); }); @@ -164,16 +164,11 @@ describe('API tests', () => { expect(app.name).to.equal(appName); }); - it('sets automaticDataCollectionEnabled to true by default', () => { - const app = initializeApp({}); + it('sets automaticDataCollectionEnabled', () => { + const app = initializeApp({}, { automaticDataCollectionEnabled: true }); expect(app.automaticDataCollectionEnabled).to.be.true; }); - it('sets a new automaticDataCollectionEnabled value if provided', () => { - const app = initializeApp({}, { automaticDataCollectionEnabled: false }); - expect(app.automaticDataCollectionEnabled).to.be.false; - }); - it('adds registered components to App', () => { _clearComponents(); const comp1 = createTestComponent('test1'); @@ -216,7 +211,7 @@ describe('API tests', () => { const app = initializeServerApp(options, serverAppSettings); expect(app).to.not.equal(null); - expect(app.automaticDataCollectionEnabled).to.be.true; + expect(app.automaticDataCollectionEnabled).to.be.false; await deleteApp(app); expect((app as FirebaseServerAppImpl).isDeleted).to.be.true; }); diff --git a/packages/app/src/api.ts b/packages/app/src/api.ts index 9cba8ec6f50..b8ec25fc509 100644 --- a/packages/app/src/api.ts +++ b/packages/app/src/api.ts @@ -143,7 +143,7 @@ export function initializeApp( const config: Required = { name: DEFAULT_ENTRY_NAME, - automaticDataCollectionEnabled: true, + automaticDataCollectionEnabled: false, ...rawConfig }; const name = config.name; @@ -241,7 +241,7 @@ export function initializeServerApp( } if (_serverAppConfig.automaticDataCollectionEnabled === undefined) { - _serverAppConfig.automaticDataCollectionEnabled = true; + _serverAppConfig.automaticDataCollectionEnabled = false; } let appOptions: FirebaseOptions; diff --git a/packages/app/src/firebaseApp.test.ts b/packages/app/src/firebaseApp.test.ts index 3acbb4a2869..419eeef4f1f 100644 --- a/packages/app/src/firebaseApp.test.ts +++ b/packages/app/src/firebaseApp.test.ts @@ -27,11 +27,11 @@ describe('FirebaseAppNext', () => { }; const app = new FirebaseAppImpl( options, - { name: 'test', automaticDataCollectionEnabled: true }, + { name: 'test', automaticDataCollectionEnabled: false }, new ComponentContainer('test') ); - expect(app.automaticDataCollectionEnabled).to.be.true; + expect(app.automaticDataCollectionEnabled).to.be.false; expect(app.name).to.equal('test'); expect(app.options).to.deep.equal(options); }); diff --git a/packages/app/src/firebaseServerApp.ts b/packages/app/src/firebaseServerApp.ts index 2bb8efc7a63..21232869c3c 100644 --- a/packages/app/src/firebaseServerApp.ts +++ b/packages/app/src/firebaseServerApp.ts @@ -74,7 +74,7 @@ export class FirebaseServerAppImpl const automaticDataCollectionEnabled = serverConfig.automaticDataCollectionEnabled !== undefined ? serverConfig.automaticDataCollectionEnabled - : true; + : false; // Create the FirebaseAppSettings object for the FirebaseAppImp constructor. const config: Required = { diff --git a/packages/app/src/public-types.ts b/packages/app/src/public-types.ts index 4f8373f2250..ea68579a7e9 100644 --- a/packages/app/src/public-types.ts +++ b/packages/app/src/public-types.ts @@ -165,7 +165,7 @@ export interface FirebaseAppSettings { */ name?: string; /** - * The settable config flag for GDPR opt-in/opt-out. Defaults to true. + * The settable config flag for GDPR opt-in/opt-out */ automaticDataCollectionEnabled?: boolean; } From f8334eade721db4ee3b60f748a5c9ff338bf1168 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 7 May 2025 16:11:55 -0700 Subject: [PATCH 28/77] Version Packages (#9008) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/blue-spies-visit.md | 7 ------- integration/compat-interop/package.json | 4 ++-- integration/firestore/package.json | 2 +- integration/messaging/package.json | 2 +- packages/analytics-compat/package.json | 2 +- packages/analytics/package.json | 2 +- packages/app-check-compat/CHANGELOG.md | 7 +++++++ packages/app-check-compat/package.json | 6 +++--- packages/app-check/CHANGELOG.md | 6 ++++++ packages/app-check/package.json | 4 ++-- packages/app-compat/CHANGELOG.md | 9 +++++++++ packages/app-compat/package.json | 4 ++-- packages/app/CHANGELOG.md | 6 ++++++ packages/app/package.json | 2 +- packages/auth-compat/package.json | 2 +- packages/auth/package.json | 2 +- packages/data-connect/package.json | 2 +- packages/database-compat/package.json | 2 +- packages/database/package.json | 2 +- packages/firebase/CHANGELOG.md | 10 ++++++++++ packages/firebase/package.json | 10 +++++----- packages/firestore-compat/package.json | 2 +- packages/firestore/package.json | 4 ++-- packages/functions-compat/package.json | 2 +- packages/functions/package.json | 2 +- packages/installations-compat/package.json | 2 +- packages/installations/package.json | 2 +- packages/messaging-compat/package.json | 2 +- packages/messaging/package.json | 2 +- packages/performance-compat/package.json | 2 +- packages/performance/package.json | 2 +- packages/remote-config-compat/package.json | 2 +- packages/remote-config/package.json | 2 +- packages/storage-compat/package.json | 2 +- packages/storage/package.json | 2 +- packages/template/package.json | 2 +- packages/vertexai/package.json | 2 +- repo-scripts/size-analysis/package.json | 2 +- 38 files changed, 80 insertions(+), 49 deletions(-) delete mode 100644 .changeset/blue-spies-visit.md diff --git a/.changeset/blue-spies-visit.md b/.changeset/blue-spies-visit.md deleted file mode 100644 index 2f1ebf01827..00000000000 --- a/.changeset/blue-spies-visit.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@firebase/app-compat': patch -'@firebase/app-check': patch -'@firebase/app': patch ---- - -Revert https://github.com/firebase/firebase-js-sdk/pull/8999 diff --git a/integration/compat-interop/package.json b/integration/compat-interop/package.json index dce91e6ce6f..c28006dd7d3 100644 --- a/integration/compat-interop/package.json +++ b/integration/compat-interop/package.json @@ -8,8 +8,8 @@ "test:debug": "karma start --browsers Chrome --auto-watch" }, "dependencies": { - "@firebase/app": "0.12.0", - "@firebase/app-compat": "0.3.0", + "@firebase/app": "0.12.1", + "@firebase/app-compat": "0.3.1", "@firebase/analytics": "0.10.13", "@firebase/analytics-compat": "0.2.19", "@firebase/auth": "1.10.2", diff --git a/integration/firestore/package.json b/integration/firestore/package.json index 5d4aa90895e..ee49ae9734f 100644 --- a/integration/firestore/package.json +++ b/integration/firestore/package.json @@ -14,7 +14,7 @@ "test:memory:debug": "yarn build:memory; karma start --auto-watch --browsers Chrome" }, "dependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "@firebase/firestore": "4.7.12" }, "devDependencies": { diff --git a/integration/messaging/package.json b/integration/messaging/package.json index 1f27302d673..a8f0fd1cf7a 100644 --- a/integration/messaging/package.json +++ b/integration/messaging/package.json @@ -9,7 +9,7 @@ "test:manual": "mocha --exit" }, "devDependencies": { - "firebase": "11.7.0", + "firebase": "11.7.1", "chai": "4.5.0", "chromedriver": "119.0.1", "express": "4.21.2", diff --git a/packages/analytics-compat/package.json b/packages/analytics-compat/package.json index b3b633a1572..dc614771cda 100644 --- a/packages/analytics-compat/package.json +++ b/packages/analytics-compat/package.json @@ -22,7 +22,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.3.0", + "@firebase/app-compat": "0.3.1", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 76765839c2f..3878c34658e 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -47,7 +47,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check-compat/CHANGELOG.md b/packages/app-check-compat/CHANGELOG.md index 3cec9412293..db793dea557 100644 --- a/packages/app-check-compat/CHANGELOG.md +++ b/packages/app-check-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/app-check-compat +## 0.3.22 + +### Patch Changes + +- Updated dependencies [[`51e7b48`](https://github.com/firebase/firebase-js-sdk/commit/51e7b489d8aadd531453f882421903da8727b19d)]: + - @firebase/app-check@0.9.1 + ## 0.3.21 ### Patch Changes diff --git a/packages/app-check-compat/package.json b/packages/app-check-compat/package.json index 44eb56be76a..05be510bdf8 100644 --- a/packages/app-check-compat/package.json +++ b/packages/app-check-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-check-compat", - "version": "0.3.21", + "version": "0.3.22", "description": "A compat App Check package for new firebase packages", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -34,7 +34,7 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/app-check": "0.9.0", + "@firebase/app-check": "0.9.1", "@firebase/app-check-types": "0.5.3", "@firebase/logger": "0.4.4", "@firebase/util": "1.11.1", @@ -43,7 +43,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.3.0", + "@firebase/app-compat": "0.3.1", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check/CHANGELOG.md b/packages/app-check/CHANGELOG.md index 539e9541659..a3afb0864cb 100644 --- a/packages/app-check/CHANGELOG.md +++ b/packages/app-check/CHANGELOG.md @@ -1,5 +1,11 @@ # @firebase/app-check +## 0.9.1 + +### Patch Changes + +- [`51e7b48`](https://github.com/firebase/firebase-js-sdk/commit/51e7b489d8aadd531453f882421903da8727b19d) [#9007](https://github.com/firebase/firebase-js-sdk/pull/9007) - Revert https://github.com/firebase/firebase-js-sdk/pull/8999 + ## 0.9.0 ### Minor Changes diff --git a/packages/app-check/package.json b/packages/app-check/package.json index 22df124e1c9..7dc2a2bb139 100644 --- a/packages/app-check/package.json +++ b/packages/app-check/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-check", - "version": "0.9.0", + "version": "0.9.1", "description": "The App Check component of the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -44,7 +44,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-compat/CHANGELOG.md b/packages/app-compat/CHANGELOG.md index 64790ad39d5..be47b8e2e3b 100644 --- a/packages/app-compat/CHANGELOG.md +++ b/packages/app-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/app-compat +## 0.3.1 + +### Patch Changes + +- [`51e7b48`](https://github.com/firebase/firebase-js-sdk/commit/51e7b489d8aadd531453f882421903da8727b19d) [#9007](https://github.com/firebase/firebase-js-sdk/pull/9007) - Revert https://github.com/firebase/firebase-js-sdk/pull/8999 + +- Updated dependencies [[`51e7b48`](https://github.com/firebase/firebase-js-sdk/commit/51e7b489d8aadd531453f882421903da8727b19d)]: + - @firebase/app@0.12.1 + ## 0.3.0 ### Minor Changes diff --git a/packages/app-compat/package.json b/packages/app-compat/package.json index fbb28d22510..725c2fa99b1 100644 --- a/packages/app-compat/package.json +++ b/packages/app-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-compat", - "version": "0.3.0", + "version": "0.3.1", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,7 +37,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "@firebase/util": "1.11.1", "@firebase/logger": "0.4.4", "@firebase/component": "0.6.14", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 24b71ffa7ba..c8950255f24 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,11 @@ # @firebase/app +## 0.12.1 + +### Patch Changes + +- [`51e7b48`](https://github.com/firebase/firebase-js-sdk/commit/51e7b489d8aadd531453f882421903da8727b19d) [#9007](https://github.com/firebase/firebase-js-sdk/pull/9007) - Revert https://github.com/firebase/firebase-js-sdk/pull/8999 + ## 0.12.0 ### Minor Changes diff --git a/packages/app/package.json b/packages/app/package.json index 0995e0223f7..24d32c0a686 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app", - "version": "0.12.0", + "version": "0.12.1", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", diff --git a/packages/auth-compat/package.json b/packages/auth-compat/package.json index ab1d2548cc0..d061a5f17a0 100644 --- a/packages/auth-compat/package.json +++ b/packages/auth-compat/package.json @@ -57,7 +57,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.3.0", + "@firebase/app-compat": "0.3.1", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/packages/auth/package.json b/packages/auth/package.json index 6acc99393c8..a58608538db 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -131,7 +131,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-strip": "2.1.0", "@types/express": "4.17.21", diff --git a/packages/data-connect/package.json b/packages/data-connect/package.json index 3a180cabcbc..2c0c454ff55 100644 --- a/packages/data-connect/package.json +++ b/packages/data-connect/package.json @@ -55,7 +55,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/database-compat/package.json b/packages/database-compat/package.json index d85ba6991b2..ec0e6994468 100644 --- a/packages/database-compat/package.json +++ b/packages/database-compat/package.json @@ -57,7 +57,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.0", + "@firebase/app-compat": "0.3.1", "typescript": "5.5.4" }, "repository": { diff --git a/packages/database/package.json b/packages/database/package.json index aef6416d018..e68132857d4 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -57,7 +57,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/firebase/CHANGELOG.md b/packages/firebase/CHANGELOG.md index 8e2d196db27..3ecab3e54f6 100644 --- a/packages/firebase/CHANGELOG.md +++ b/packages/firebase/CHANGELOG.md @@ -1,5 +1,15 @@ # firebase +## 11.7.1 + +### Patch Changes + +- Updated dependencies [[`51e7b48`](https://github.com/firebase/firebase-js-sdk/commit/51e7b489d8aadd531453f882421903da8727b19d)]: + - @firebase/app-compat@0.3.1 + - @firebase/app-check@0.9.1 + - @firebase/app@0.12.1 + - @firebase/app-check-compat@0.3.22 + ## 11.7.0 ### Minor Changes diff --git a/packages/firebase/package.json b/packages/firebase/package.json index 2335648ddc7..f4fe3bb2d50 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -1,6 +1,6 @@ { "name": "firebase", - "version": "11.7.0", + "version": "11.7.1", "description": "Firebase JavaScript library for web and Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -399,8 +399,8 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { - "@firebase/app": "0.12.0", - "@firebase/app-compat": "0.3.0", + "@firebase/app": "0.12.1", + "@firebase/app-compat": "0.3.1", "@firebase/app-types": "0.9.3", "@firebase/auth": "1.10.2", "@firebase/auth-compat": "0.5.22", @@ -423,8 +423,8 @@ "@firebase/remote-config-compat": "0.2.14", "@firebase/analytics": "0.10.13", "@firebase/analytics-compat": "0.2.19", - "@firebase/app-check": "0.9.0", - "@firebase/app-check-compat": "0.3.21", + "@firebase/app-check": "0.9.1", + "@firebase/app-check-compat": "0.3.22", "@firebase/util": "1.11.1", "@firebase/vertexai": "1.2.2" }, diff --git a/packages/firestore-compat/package.json b/packages/firestore-compat/package.json index bfc0f71c03a..648cf8ee019 100644 --- a/packages/firestore-compat/package.json +++ b/packages/firestore-compat/package.json @@ -53,7 +53,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.0", + "@firebase/app-compat": "0.3.1", "@types/eslint": "7.29.0", "rollup": "2.79.2", "rollup-plugin-sourcemaps": "0.6.3", diff --git a/packages/firestore/package.json b/packages/firestore/package.json index c6075767a85..38eea5b1505 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -110,8 +110,8 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.0", - "@firebase/app-compat": "0.3.0", + "@firebase/app": "0.12.1", + "@firebase/app-compat": "0.3.1", "@firebase/auth": "1.10.2", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", diff --git a/packages/functions-compat/package.json b/packages/functions-compat/package.json index 72f3643a99b..02725731af4 100644 --- a/packages/functions-compat/package.json +++ b/packages/functions-compat/package.json @@ -29,7 +29,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.3.0", + "@firebase/app-compat": "0.3.1", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/functions/package.json b/packages/functions/package.json index 8ec8ee12ed3..e2e6d9f99fc 100644 --- a/packages/functions/package.json +++ b/packages/functions/package.json @@ -49,7 +49,7 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/installations-compat/package.json b/packages/installations-compat/package.json index 28f0827fdff..ce75757d337 100644 --- a/packages/installations-compat/package.json +++ b/packages/installations-compat/package.json @@ -44,7 +44,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app-compat": "0.3.0", + "@firebase/app-compat": "0.3.1", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/installations/package.json b/packages/installations/package.json index 08c7803e9b1..32285b7fb1c 100644 --- a/packages/installations/package.json +++ b/packages/installations/package.json @@ -49,7 +49,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/messaging-compat/package.json b/packages/messaging-compat/package.json index 23de8651813..1021d98c515 100644 --- a/packages/messaging-compat/package.json +++ b/packages/messaging-compat/package.json @@ -44,7 +44,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.0", + "@firebase/app-compat": "0.3.1", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", "ts-essentials": "9.4.2", diff --git a/packages/messaging/package.json b/packages/messaging/package.json index d18343d169f..3d2e70ebfb4 100644 --- a/packages/messaging/package.json +++ b/packages/messaging/package.json @@ -60,7 +60,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/performance-compat/package.json b/packages/performance-compat/package.json index 4034920cb27..37b49ad2455 100644 --- a/packages/performance-compat/package.json +++ b/packages/performance-compat/package.json @@ -51,7 +51,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.3.0" + "@firebase/app-compat": "0.3.1" }, "repository": { "directory": "packages/performance-compat", diff --git a/packages/performance/package.json b/packages/performance/package.json index ed9f28e04b9..a6256c12f39 100644 --- a/packages/performance/package.json +++ b/packages/performance/package.json @@ -47,7 +47,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/remote-config-compat/package.json b/packages/remote-config-compat/package.json index 0bbeb8c33e5..9839182086c 100644 --- a/packages/remote-config-compat/package.json +++ b/packages/remote-config-compat/package.json @@ -50,7 +50,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.3.0" + "@firebase/app-compat": "0.3.1" }, "repository": { "directory": "packages/remote-config-compat", diff --git a/packages/remote-config/package.json b/packages/remote-config/package.json index 666f5d4b141..1b068256178 100644 --- a/packages/remote-config/package.json +++ b/packages/remote-config/package.json @@ -48,7 +48,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/storage-compat/package.json b/packages/storage-compat/package.json index 8b7fca14dff..fe7b9772ec0 100644 --- a/packages/storage-compat/package.json +++ b/packages/storage-compat/package.json @@ -44,7 +44,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.0", + "@firebase/app-compat": "0.3.1", "@firebase/auth-compat": "0.5.22", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", diff --git a/packages/storage/package.json b/packages/storage/package.json index b7cf6228960..963ad3e86d6 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -54,7 +54,7 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "@firebase/auth": "1.10.2", "rollup": "2.79.2", "@rollup/plugin-alias": "5.1.1", diff --git a/packages/template/package.json b/packages/template/package.json index a67a5813982..7f5bf9cb1bd 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -48,7 +48,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/vertexai/package.json b/packages/vertexai/package.json index 9d71ac73b91..0c6b24fc495 100644 --- a/packages/vertexai/package.json +++ b/packages/vertexai/package.json @@ -56,7 +56,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/repo-scripts/size-analysis/package.json b/repo-scripts/size-analysis/package.json index a723d4a52df..85590c3c77d 100644 --- a/repo-scripts/size-analysis/package.json +++ b/repo-scripts/size-analysis/package.json @@ -20,7 +20,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.0", + "@firebase/app": "0.12.1", "@firebase/logger": "0.4.4", "@firebase/util": "1.11.1", "@rollup/plugin-commonjs": "21.1.0", From 050c1b6a099b87be1488b9207e4fad4da9f8f64b Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Thu, 8 May 2025 13:15:03 -0400 Subject: [PATCH 29/77] fix(vertexai): pass `GenerativeModel`'s `BaseParams` to `ChatSession` (#8972) startChat wasn't passing GenerativeModel's BaseParams to ChatSession. So, if a model had a generationConfig, it would never be passed to ChatSession. --- .changeset/fast-mangos-chew.md | 5 +++++ .../src/models/generative-model.test.ts | 22 ++++++++++++++----- .../vertexai/src/models/generative-model.ts | 7 ++++++ 3 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 .changeset/fast-mangos-chew.md diff --git a/.changeset/fast-mangos-chew.md b/.changeset/fast-mangos-chew.md new file mode 100644 index 00000000000..c5d2e4b4d1f --- /dev/null +++ b/.changeset/fast-mangos-chew.md @@ -0,0 +1,5 @@ +--- +'@firebase/vertexai': patch +--- + +Pass `GenerativeModel`'s `BaseParams` to created chat sessions. This fixes an issue where `GenerationConfig` would not be inherited from `ChatSession`. diff --git a/packages/vertexai/src/models/generative-model.test.ts b/packages/vertexai/src/models/generative-model.test.ts index 987f9b115e2..51ea8aafead 100644 --- a/packages/vertexai/src/models/generative-model.test.ts +++ b/packages/vertexai/src/models/generative-model.test.ts @@ -172,7 +172,10 @@ describe('GenerativeModel', () => { { functionDeclarations: [{ name: 'myfunc', description: 'mydesc' }] } ], toolConfig: { functionCallingConfig: { mode: FunctionCallingMode.NONE } }, - systemInstruction: { role: 'system', parts: [{ text: 'be friendly' }] } + systemInstruction: { role: 'system', parts: [{ text: 'be friendly' }] }, + generationConfig: { + topK: 1 + } }); expect(genModel.tools?.length).to.equal(1); expect(genModel.toolConfig?.functionCallingConfig?.mode).to.equal( @@ -196,7 +199,8 @@ describe('GenerativeModel', () => { return ( value.includes('myfunc') && value.includes(FunctionCallingMode.NONE) && - value.includes('be friendly') + value.includes('be friendly') && + value.includes('topK') ); }), {} @@ -236,7 +240,10 @@ describe('GenerativeModel', () => { { functionDeclarations: [{ name: 'myfunc', description: 'mydesc' }] } ], toolConfig: { functionCallingConfig: { mode: FunctionCallingMode.NONE } }, - systemInstruction: { role: 'system', parts: [{ text: 'be friendly' }] } + systemInstruction: { role: 'system', parts: [{ text: 'be friendly' }] }, + generationConfig: { + responseMimeType: 'image/jpeg' + } }); expect(genModel.tools?.length).to.equal(1); expect(genModel.toolConfig?.functionCallingConfig?.mode).to.equal( @@ -262,7 +269,10 @@ describe('GenerativeModel', () => { toolConfig: { functionCallingConfig: { mode: FunctionCallingMode.AUTO } }, - systemInstruction: { role: 'system', parts: [{ text: 'be formal' }] } + systemInstruction: { role: 'system', parts: [{ text: 'be formal' }] }, + generationConfig: { + responseMimeType: 'image/png' + } }) .sendMessage('hello'); expect(makeRequestStub).to.be.calledWith( @@ -274,7 +284,9 @@ describe('GenerativeModel', () => { return ( value.includes('otherfunc') && value.includes(FunctionCallingMode.AUTO) && - value.includes('be formal') + value.includes('be formal') && + value.includes('image/png') && + !value.includes('image/jpeg') ); }), {} diff --git a/packages/vertexai/src/models/generative-model.ts b/packages/vertexai/src/models/generative-model.ts index 983118bf6ff..1af1ee700d5 100644 --- a/packages/vertexai/src/models/generative-model.ts +++ b/packages/vertexai/src/models/generative-model.ts @@ -132,6 +132,13 @@ export class GenerativeModel extends VertexAIModel { tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction, + generationConfig: this.generationConfig, + safetySettings: this.safetySettings, + /** + * Overrides params inherited from GenerativeModel with those explicitly set in the + * StartChatParams. For example, if startChatParams.generationConfig is set, it'll override + * this.generationConfig. + */ ...startChatParams }, this.requestOptions From e99683b17cf75c581bd362a1d7cb85f0b9c110ba Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Thu, 8 May 2025 13:39:24 -0400 Subject: [PATCH 30/77] feat(vertexai): Gemini multimodal output (#8922) Adds new ResponseModality enum that allows users to specify which modalities should be included in a response. Since we provide a text() accessor, a similar inlineDataParts() accessor was added to return all InlineDataPart[] in the first candidate. --- .changeset/perfect-camels-try.md | 6 ++ common/api-review/vertexai.api.md | 12 ++++ ...ertexai.enhancedgeneratecontentresponse.md | 11 ++++ docs-devsite/vertexai.generationconfig.md | 16 +++++ docs-devsite/vertexai.md | 31 +++++++++ .../src/requests/response-helpers.test.ts | 64 +++++++++++++++++++ .../vertexai/src/requests/response-helpers.ts | 60 +++++++++++++++++ packages/vertexai/src/types/enums.ts | 26 ++++++++ packages/vertexai/src/types/requests.ts | 13 +++- packages/vertexai/src/types/responses.ts | 11 +++- 10 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 .changeset/perfect-camels-try.md diff --git a/.changeset/perfect-camels-try.md b/.changeset/perfect-camels-try.md new file mode 100644 index 00000000000..409a598531d --- /dev/null +++ b/.changeset/perfect-camels-try.md @@ -0,0 +1,6 @@ +--- +'firebase': minor +'@firebase/vertexai': minor +--- + +Add support for Gemini multimodal output diff --git a/common/api-review/vertexai.api.md b/common/api-review/vertexai.api.md index e7f00c2f4e0..f9cf3dac5bd 100644 --- a/common/api-review/vertexai.api.md +++ b/common/api-review/vertexai.api.md @@ -124,6 +124,7 @@ export { Date_2 as Date } export interface EnhancedGenerateContentResponse extends GenerateContentResponse { // (undocumented) functionCalls: () => FunctionCall[] | undefined; + inlineDataParts: () => InlineDataPart[] | undefined; text: () => string; } @@ -304,6 +305,8 @@ export interface GenerationConfig { // (undocumented) presencePenalty?: number; responseMimeType?: string; + // @beta + responseModalities?: ResponseModality[]; responseSchema?: TypedSchema | SchemaRequest; // (undocumented) stopSequences?: string[]; @@ -596,6 +599,15 @@ export interface RequestOptions { timeout?: number; } +// @beta +export const ResponseModality: { + readonly TEXT: "TEXT"; + readonly IMAGE: "IMAGE"; +}; + +// @beta +export type ResponseModality = (typeof ResponseModality)[keyof typeof ResponseModality]; + // @public (undocumented) export interface RetrievedContextAttribution { // (undocumented) diff --git a/docs-devsite/vertexai.enhancedgeneratecontentresponse.md b/docs-devsite/vertexai.enhancedgeneratecontentresponse.md index 535fb9def8f..b557219bf84 100644 --- a/docs-devsite/vertexai.enhancedgeneratecontentresponse.md +++ b/docs-devsite/vertexai.enhancedgeneratecontentresponse.md @@ -24,6 +24,7 @@ export interface EnhancedGenerateContentResponse extends GenerateContentResponse | Property | Type | Description | | --- | --- | --- | | [functionCalls](./vertexai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponsefunctioncalls) | () => [FunctionCall](./vertexai.functioncall.md#functioncall_interface)\[\] \| undefined | | +| [inlineDataParts](./vertexai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponseinlinedataparts) | () => [InlineDataPart](./vertexai.inlinedatapart.md#inlinedatapart_interface)\[\] \| undefined | Aggregates and returns all [InlineDataPart](./vertexai.inlinedatapart.md#inlinedatapart_interface)s from the [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface)'s first candidate. | | [text](./vertexai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponsetext) | () => string | Returns the text string from the response, if available. Throws if the prompt or candidate was blocked. | ## EnhancedGenerateContentResponse.functionCalls @@ -34,6 +35,16 @@ export interface EnhancedGenerateContentResponse extends GenerateContentResponse functionCalls: () => FunctionCall[] | undefined; ``` +## EnhancedGenerateContentResponse.inlineDataParts + +Aggregates and returns all [InlineDataPart](./vertexai.inlinedatapart.md#inlinedatapart_interface)s from the [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface)'s first candidate. + +Signature: + +```typescript +inlineDataParts: () => InlineDataPart[] | undefined; +``` + ## EnhancedGenerateContentResponse.text Returns the text string from the response, if available. Throws if the prompt or candidate was blocked. diff --git a/docs-devsite/vertexai.generationconfig.md b/docs-devsite/vertexai.generationconfig.md index d3e9879f937..360ef709419 100644 --- a/docs-devsite/vertexai.generationconfig.md +++ b/docs-devsite/vertexai.generationconfig.md @@ -27,6 +27,7 @@ export interface GenerationConfig | [maxOutputTokens](./vertexai.generationconfig.md#generationconfigmaxoutputtokens) | number | | | [presencePenalty](./vertexai.generationconfig.md#generationconfigpresencepenalty) | number | | | [responseMimeType](./vertexai.generationconfig.md#generationconfigresponsemimetype) | string | Output response MIME type of the generated candidate text. Supported MIME types are text/plain (default, text output), application/json (JSON response in the candidates), and text/x.enum. | +| [responseModalities](./vertexai.generationconfig.md#generationconfigresponsemodalities) | [ResponseModality](./vertexai.md#responsemodality)\[\] | (Public Preview) Generation modalities to be returned in generation responses. | | [responseSchema](./vertexai.generationconfig.md#generationconfigresponseschema) | [TypedSchema](./vertexai.md#typedschema) \| [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) | Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./vertexai.schema.md#schema_class) static method like Schema.string() or Schema.object() or it can be a plain JS object matching the [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified responseMIMEType supports a schema; currently this is limited to application/json and text/x.enum. | | [stopSequences](./vertexai.generationconfig.md#generationconfigstopsequences) | string\[\] | | | [temperature](./vertexai.generationconfig.md#generationconfigtemperature) | number | | @@ -75,6 +76,21 @@ Output response MIME type of the generated candidate text. Supported MIME types responseMimeType?: string; ``` +## GenerationConfig.responseModalities + +> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> + +Generation modalities to be returned in generation responses. + +- Multimodal response generation is only supported by some Gemini models and versions; see [model versions](https://firebase.google.com/docs/vertex-ai/models). - Only image generation (`ResponseModality.IMAGE`) is supported. + +Signature: + +```typescript +responseModalities?: ResponseModality[]; +``` + ## GenerationConfig.responseSchema Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./vertexai.schema.md#schema_class) static method like `Schema.string()` or `Schema.object()` or it can be a plain JS object matching the [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified `responseMIMEType` supports a schema; currently this is limited to `application/json` and `text/x.enum`. diff --git a/docs-devsite/vertexai.md b/docs-devsite/vertexai.md index f67254eef20..47d45a492ec 100644 --- a/docs-devsite/vertexai.md +++ b/docs-devsite/vertexai.md @@ -125,12 +125,14 @@ The Vertex AI in Firebase Web SDK. | Variable | Description | | --- | --- | | [POSSIBLE\_ROLES](./vertexai.md#possible_roles) | Possible roles. | +| [ResponseModality](./vertexai.md#responsemodality) | (Public Preview) Generation modalities to be returned in generation responses. | ## Type Aliases | Type Alias | Description | | --- | --- | | [Part](./vertexai.md#part) | Content part - includes text, image/video, or function call/response part types. | +| [ResponseModality](./vertexai.md#responsemodality) | (Public Preview) Generation modalities to be returned in generation responses. | | [Role](./vertexai.md#role) | Role is the producer of the content. | | [Tool](./vertexai.md#tool) | Defines a tool that model can call to access external knowledge. | | [TypedSchema](./vertexai.md#typedschema) | A type that includes all specific Schema types. | @@ -223,6 +225,22 @@ Possible roles. POSSIBLE_ROLES: readonly ["user", "model", "function", "system"] ``` +## ResponseModality + +> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> + +Generation modalities to be returned in generation responses. + +Signature: + +```typescript +ResponseModality: { + readonly TEXT: "TEXT"; + readonly IMAGE: "IMAGE"; +} +``` + ## Part Content part - includes text, image/video, or function call/response part types. @@ -233,6 +251,19 @@ Content part - includes text, image/video, or function call/response part types. export type Part = TextPart | InlineDataPart | FunctionCallPart | FunctionResponsePart | FileDataPart; ``` +## ResponseModality + +> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> + +Generation modalities to be returned in generation responses. + +Signature: + +```typescript +export type ResponseModality = (typeof ResponseModality)[keyof typeof ResponseModality]; +``` + ## Role Role is the producer of the content. diff --git a/packages/vertexai/src/requests/response-helpers.test.ts b/packages/vertexai/src/requests/response-helpers.test.ts index 5371d040253..97dd2f9fe30 100644 --- a/packages/vertexai/src/requests/response-helpers.test.ts +++ b/packages/vertexai/src/requests/response-helpers.test.ts @@ -29,6 +29,7 @@ import { FinishReason, GenerateContentResponse, ImagenGCSImage, + InlineDataPart, ImagenInlineImage } from '../types'; import { getMockResponse } from '../../test-utils/mock-response'; @@ -132,6 +133,44 @@ const fakeResponseMixed3: GenerateContentResponse = { ] }; +const inlineDataPart1: InlineDataPart = { + inlineData: { + mimeType: 'image/png', + data: 'base64encoded...' + } +}; + +const inlineDataPart2: InlineDataPart = { + inlineData: { + mimeType: 'image/jpeg', + data: 'anotherbase64...' + } +}; + +const fakeResponseInlineData: GenerateContentResponse = { + candidates: [ + { + index: 0, + content: { + role: 'model', + parts: [inlineDataPart1, inlineDataPart2] + } + } + ] +}; + +const fakeResponseTextAndInlineData: GenerateContentResponse = { + candidates: [ + { + index: 0, + content: { + role: 'model', + parts: [{ text: 'Describe this:' }, inlineDataPart1] + } + } + ] +}; + const badFakeResponse: GenerateContentResponse = { promptFeedback: { blockReason: BlockReason.SAFETY, @@ -148,6 +187,7 @@ describe('response-helpers methods', () => { const enhancedResponse = addHelpers(fakeResponseText); expect(enhancedResponse.text()).to.equal('Some text and some more text'); expect(enhancedResponse.functionCalls()).to.be.undefined; + expect(enhancedResponse.inlineDataParts()).to.be.undefined; }); it('good response functionCall', async () => { const enhancedResponse = addHelpers(fakeResponseFunctionCall); @@ -155,6 +195,7 @@ describe('response-helpers methods', () => { expect(enhancedResponse.functionCalls()).to.deep.equal([ functionCallPart1.functionCall ]); + expect(enhancedResponse.inlineDataParts()).to.be.undefined; }); it('good response functionCalls', async () => { const enhancedResponse = addHelpers(fakeResponseFunctionCalls); @@ -163,6 +204,7 @@ describe('response-helpers methods', () => { functionCallPart1.functionCall, functionCallPart2.functionCall ]); + expect(enhancedResponse.inlineDataParts()).to.be.undefined; }); it('good response text/functionCall', async () => { const enhancedResponse = addHelpers(fakeResponseMixed1); @@ -170,6 +212,7 @@ describe('response-helpers methods', () => { functionCallPart2.functionCall ]); expect(enhancedResponse.text()).to.equal('some text'); + expect(enhancedResponse.inlineDataParts()).to.be.undefined; }); it('good response functionCall/text', async () => { const enhancedResponse = addHelpers(fakeResponseMixed2); @@ -177,6 +220,7 @@ describe('response-helpers methods', () => { functionCallPart1.functionCall ]); expect(enhancedResponse.text()).to.equal('some text'); + expect(enhancedResponse.inlineDataParts()).to.be.undefined; }); it('good response text/functionCall/text', async () => { const enhancedResponse = addHelpers(fakeResponseMixed3); @@ -184,10 +228,30 @@ describe('response-helpers methods', () => { functionCallPart1.functionCall ]); expect(enhancedResponse.text()).to.equal('some text and more text'); + expect(enhancedResponse.inlineDataParts()).to.be.undefined; }); it('bad response safety', async () => { const enhancedResponse = addHelpers(badFakeResponse); expect(enhancedResponse.text).to.throw('SAFETY'); + expect(enhancedResponse.functionCalls).to.throw('SAFETY'); + expect(enhancedResponse.inlineDataParts).to.throw('SAFETY'); + }); + it('good response inlineData', async () => { + const enhancedResponse = addHelpers(fakeResponseInlineData); + expect(enhancedResponse.text()).to.equal(''); + expect(enhancedResponse.functionCalls()).to.be.undefined; + expect(enhancedResponse.inlineDataParts()).to.deep.equal([ + inlineDataPart1, + inlineDataPart2 + ]); + }); + it('good response text/inlineData', async () => { + const enhancedResponse = addHelpers(fakeResponseTextAndInlineData); + expect(enhancedResponse.text()).to.equal('Describe this:'); + expect(enhancedResponse.functionCalls()).to.be.undefined; + expect(enhancedResponse.inlineDataParts()).to.deep.equal([ + inlineDataPart1 + ]); }); }); describe('getBlockString', () => { diff --git a/packages/vertexai/src/requests/response-helpers.ts b/packages/vertexai/src/requests/response-helpers.ts index 6d0e3bf2a0a..d820f100a50 100644 --- a/packages/vertexai/src/requests/response-helpers.ts +++ b/packages/vertexai/src/requests/response-helpers.ts @@ -23,6 +23,7 @@ import { GenerateContentResponse, ImagenGCSImage, ImagenInlineImage, + InlineDataPart, VertexAIErrorCode } from '../types'; import { VertexAIError } from '../errors'; @@ -89,6 +90,40 @@ export function addHelpers( } return ''; }; + (response as EnhancedGenerateContentResponse).inlineDataParts = (): + | InlineDataPart[] + | undefined => { + if (response.candidates && response.candidates.length > 0) { + if (response.candidates.length > 1) { + logger.warn( + `This response had ${response.candidates.length} ` + + `candidates. Returning data from the first candidate only. ` + + `Access response.candidates directly to use the other candidates.` + ); + } + if (hadBadFinishReason(response.candidates[0])) { + throw new VertexAIError( + VertexAIErrorCode.RESPONSE_ERROR, + `Response error: ${formatBlockErrorMessage( + response + )}. Response body stored in error.response`, + { + response + } + ); + } + return getInlineDataParts(response); + } else if (response.promptFeedback) { + throw new VertexAIError( + VertexAIErrorCode.RESPONSE_ERROR, + `Data not available. ${formatBlockErrorMessage(response)}`, + { + response + } + ); + } + return undefined; + }; (response as EnhancedGenerateContentResponse).functionCalls = () => { if (response.candidates && response.candidates.length > 0) { if (response.candidates.length > 1) { @@ -164,6 +199,31 @@ export function getFunctionCalls( } } +/** + * Returns {@link InlineDataPart}s in the first candidate if present. + * + * @internal + */ +export function getInlineDataParts( + response: GenerateContentResponse +): InlineDataPart[] | undefined { + const data: InlineDataPart[] = []; + + if (response.candidates?.[0].content?.parts) { + for (const part of response.candidates?.[0].content?.parts) { + if (part.inlineData) { + data.push(part); + } + } + } + + if (data.length > 0) { + return data; + } else { + return undefined; + } +} + const badFinishReasons = [FinishReason.RECITATION, FinishReason.SAFETY]; function hadBadFinishReason(candidate: GenerateContentCandidate): boolean { diff --git a/packages/vertexai/src/types/enums.ts b/packages/vertexai/src/types/enums.ts index a9481d40f5f..d6702a0f1a8 100644 --- a/packages/vertexai/src/types/enums.ts +++ b/packages/vertexai/src/types/enums.ts @@ -240,3 +240,29 @@ export enum Modality { */ DOCUMENT = 'DOCUMENT' } + +/** + * Generation modalities to be returned in generation responses. + * + * @beta + */ +export const ResponseModality = { + /** + * Text. + * @beta + */ + TEXT: 'TEXT', + /** + * Image. + * @beta + */ + IMAGE: 'IMAGE' +} as const; + +/** + * Generation modalities to be returned in generation responses. + * + * @beta + */ +export type ResponseModality = + (typeof ResponseModality)[keyof typeof ResponseModality]; diff --git a/packages/vertexai/src/types/requests.ts b/packages/vertexai/src/types/requests.ts index c15258b06d0..ee45b636673 100644 --- a/packages/vertexai/src/types/requests.ts +++ b/packages/vertexai/src/types/requests.ts @@ -21,7 +21,8 @@ import { FunctionCallingMode, HarmBlockMethod, HarmBlockThreshold, - HarmCategory + HarmCategory, + ResponseModality } from './enums'; import { ObjectSchemaInterface, SchemaRequest } from './schema'; @@ -95,6 +96,16 @@ export interface GenerationConfig { * this is limited to `application/json` and `text/x.enum`. */ responseSchema?: TypedSchema | SchemaRequest; + /** + * Generation modalities to be returned in generation responses. + * + * @remarks + * - Multimodal response generation is only supported by some Gemini models and versions; see {@link https://firebase.google.com/docs/vertex-ai/models | model versions}. + * - Only image generation (`ResponseModality.IMAGE`) is supported. + * + * @beta + */ + responseModalities?: ResponseModality[]; } /** diff --git a/packages/vertexai/src/types/responses.ts b/packages/vertexai/src/types/responses.ts index 7f68df1e679..e4a247bec49 100644 --- a/packages/vertexai/src/types/responses.ts +++ b/packages/vertexai/src/types/responses.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Content, FunctionCall } from './content'; +import { Content, FunctionCall, InlineDataPart } from './content'; import { BlockReason, FinishReason, @@ -59,6 +59,15 @@ export interface EnhancedGenerateContentResponse * Throws if the prompt or candidate was blocked. */ text: () => string; + /** + * Aggregates and returns all {@link InlineDataPart}s from the {@link GenerateContentResponse}'s + * first candidate. + * + * @returns An array of {@link InlineDataPart}s containing data from the response, if available. + * + * @throws If the prompt or candidate was blocked. + */ + inlineDataParts: () => InlineDataPart[] | undefined; functionCalls: () => FunctionCall[] | undefined; } From 88584fdeb32d33994f0212c82d460b0a6eb43fb0 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Thu, 8 May 2025 14:09:43 -0400 Subject: [PATCH 31/77] test: Clean up vscode launch file and add vertexai debug config (#9000) --- .vscode/launch.json | 109 ++++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 44 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index e0a16340123..df14c96daa6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,26 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "AI Unit Tests (node)", + "type": "node", + "request": "launch", + "program": "${workspaceFolder}/node_modules/.bin/_mocha", + "cwd": "${workspaceRoot}/packages/vertexai", + "args": [ + "--require", + "ts-node/register", + "--require", + "src/index.node.ts", + "--timeout", + "5000", + "src/**/*.test.ts" + ], + "env": { + "TS_NODE_COMPILER_OPTIONS": "{\"module\":\"commonjs\"}" + }, + "sourceMaps": true + }, { "type": "node", "request": "launch", @@ -18,9 +38,9 @@ "${workspaceFolder}/repo-scripts/prune-dts/*.test.ts" ], "env": { - "TS_NODE_COMPILER_OPTIONS" : "{\"module\":\"commonjs\"}" + "TS_NODE_COMPILER_OPTIONS": "{\"module\":\"commonjs\"}" }, - "sourceMaps": true, + "sourceMaps": true }, { "type": "node", @@ -30,16 +50,17 @@ "cwd": "${workspaceRoot}/packages/database", "args": [ "test/{,!(browser)/**/}*.test.ts", - "--file", "src/index.node.ts", - "--config", "../../config/mocharc.node.js", + "--file", + "src/index.node.ts", + "--config", + "../../config/mocharc.node.js" ], "env": { - "TS_NODE_FILES":true, + "TS_NODE_FILES": true, "TS_NODE_CACHE": "NO", - "TS_NODE_COMPILER_OPTIONS" : "{\"module\":\"commonjs\"}" + "TS_NODE_COMPILER_OPTIONS": "{\"module\":\"commonjs\"}" }, - "sourceMaps": true, - "protocol": "inspector" + "sourceMaps": true }, { "type": "node", @@ -48,14 +69,16 @@ "program": "${workspaceRoot}/node_modules/.bin/_mocha", "cwd": "${workspaceRoot}/packages/firestore", "args": [ - "--require", "babel-register.js", - "--require", "src/index.node.ts", - "--timeout", "5000", + "--require", + "babel-register.js", + "--require", + "src/index.node.ts", + "--timeout", + "5000", "test/{,!(browser|integration)/**/}*.test.ts", "--exit" ], - "sourceMaps": true, - "protocol": "inspector" + "sourceMaps": true }, { "type": "node", @@ -64,18 +87,21 @@ "program": "${workspaceRoot}/node_modules/.bin/_mocha", "cwd": "${workspaceRoot}/packages/firestore", "args": [ - "--require", "babel-register.js", - "--require", "index.node.ts", - "--require", "test/util/node_persistence.ts", - "--timeout", "5000", + "--require", + "babel-register.js", + "--require", + "index.node.ts", + "--require", + "test/util/node_persistence.ts", + "--timeout", + "5000", "test/{,!(browser|integration)/**/}*.test.ts", "--exit" ], "env": { "USE_MOCK_PERSISTENCE": "YES" }, - "sourceMaps": true, - "protocol": "inspector" + "sourceMaps": true }, { "type": "node", @@ -84,17 +110,19 @@ "program": "${workspaceRoot}/node_modules/.bin/_mocha", "cwd": "${workspaceRoot}/packages/firestore", "args": [ - "--require", "babel-register.js", - "--require", "index.node.ts", - "--timeout", "5000", + "--require", + "babel-register.js", + "--require", + "index.node.ts", + "--timeout", + "5000", "test/{,!(browser|unit)/**/}*.test.ts", "--exit" ], "env": { - "FIRESTORE_TARGET_BACKEND" : "emulator", + "FIRESTORE_TARGET_BACKEND": "emulator" }, - "sourceMaps": true, - "protocol": "inspector" + "sourceMaps": true }, { "type": "node", @@ -103,19 +131,22 @@ "program": "${workspaceRoot}/node_modules/.bin/_mocha", "cwd": "${workspaceRoot}/packages/firestore", "args": [ - "--require", "babel-register.js", - "--require", "index.node.ts", - "--require", "test/util/node_persistence.ts", - "--timeout", "5000", + "--require", + "babel-register.js", + "--require", + "index.node.ts", + "--require", + "test/util/node_persistence.ts", + "--timeout", + "5000", "test/{,!(browser|unit)/**/}*.test.ts", "--exit" ], "env": { "USE_MOCK_PERSISTENCE": "YES", - "FIRESTORE_TARGET_BACKEND" : "emulator", + "FIRESTORE_TARGET_BACKEND": "emulator" }, - "sourceMaps": true, - "protocol": "inspector" + "sourceMaps": true }, { "type": "node", @@ -123,12 +154,7 @@ "name": "Firestore Unit Tests (Browser)", "program": "${workspaceRoot}/node_modules/.bin/karma", "cwd": "${workspaceRoot}/packages/firestore", - "args": [ - "start", - "--auto-watch", - "--unit", - "--browsers", "Chrome" - ] + "args": ["start", "--auto-watch", "--unit", "--browsers", "Chrome"] }, { "type": "node", @@ -136,12 +162,7 @@ "name": "Firestore Integration Tests (Browser)", "program": "${workspaceRoot}/node_modules/.bin/karma", "cwd": "${workspaceRoot}/packages/firestore", - "args": [ - "start", - "--auto-watch", - "--integration", - "--browsers", "Chrome" - ] + "args": ["start", "--auto-watch", "--integration", "--browsers", "Chrome"] } ] } From 8a03143b9217effdd86d68bdf195493c0979aa27 Mon Sep 17 00:00:00 2001 From: Mark Duckworth <1124037+MarkDuckworth@users.noreply.github.com> Date: Fri, 9 May 2025 14:45:08 -0600 Subject: [PATCH 32/77] Fix encoding of CSI keys in Safari (#8993) --- .changeset/eighty-starfishes-listen.md | 6 + common/api-review/util.api.md | 5 + config/karma.base.js | 1 + packages/firestore/package.json | 2 + packages/firestore/src/index/index_entry.ts | 121 +++++++++++++++--- .../src/local/indexeddb_index_manager.ts | 65 +++++----- .../firestore/src/local/indexeddb_schema.ts | 12 +- .../src/local/indexeddb_schema_converter.ts | 18 +++ .../src/local/indexeddb_sentinels.ts | 22 +++- packages/util/src/environment.ts | 11 ++ 10 files changed, 204 insertions(+), 59 deletions(-) create mode 100644 .changeset/eighty-starfishes-listen.md diff --git a/.changeset/eighty-starfishes-listen.md b/.changeset/eighty-starfishes-listen.md new file mode 100644 index 00000000000..4f53b0fb786 --- /dev/null +++ b/.changeset/eighty-starfishes-listen.md @@ -0,0 +1,6 @@ +--- +"@firebase/firestore": patch +"@firebase/util": minor +--- + +Fix Safari/WebKit cache issues when client-side indexing is used. diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index 0f8fc13cd3a..427cb5e174a 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -317,6 +317,11 @@ export function isReactNative(): boolean; // @public export function isSafari(): boolean; +// Warning: (ae-missing-release-tag) "isSafariOrWebkit" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function isSafariOrWebkit(): boolean; + // Warning: (ae-missing-release-tag) "issuedAtTime" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public diff --git a/config/karma.base.js b/config/karma.base.js index c49b1246ed6..9d92053115f 100644 --- a/config/karma.base.js +++ b/config/karma.base.js @@ -53,6 +53,7 @@ const config = { // Doing 65 seconds to allow for the 20 second firestore tests browserNoActivityTimeout: 65000, + browserDisconnectTimeout: 65000, // Preprocess matching files before serving them to the browser. // Available preprocessors: diff --git a/packages/firestore/package.json b/packages/firestore/package.json index 38eea5b1505..fdf849b996e 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -36,6 +36,8 @@ "test:browser:emulator": "karma start --targetBackend=emulator", "test:browser:nightly": "karma start --targetBackend=nightly", "test:browser:prod": "karma start --targetBackend=prod", + "test:webkit:prod": "BROWSERS=WebkitHeadless karma start --targetBackend=prod", + "test:webkit:unit": "BROWSERS=WebkitHeadless karma start --unit --targetBackend=prod", "test:browser:prod:nameddb": "karma start --targetBackend=prod --databaseId=test-db", "test:browser:unit": "karma start --unit", "test:browser:debug": "karma start --browsers=Chrome --auto-watch", diff --git a/packages/firestore/src/index/index_entry.ts b/packages/firestore/src/index/index_entry.ts index ee80276f325..c9f3218c65e 100644 --- a/packages/firestore/src/index/index_entry.ts +++ b/packages/firestore/src/index/index_entry.ts @@ -15,15 +15,19 @@ * limitations under the License. */ +import { isSafariOrWebkit } from '@firebase/util'; + +import { DbIndexEntry } from '../local/indexeddb_schema'; +import { DbIndexEntryKey, KeySafeBytes } from '../local/indexeddb_sentinels'; import { DocumentKey } from '../model/document_key'; /** Represents an index entry saved by the SDK in persisted storage. */ export class IndexEntry { constructor( - readonly indexId: number, - readonly documentKey: DocumentKey, - readonly arrayValue: Uint8Array, - readonly directionalValue: Uint8Array + readonly _indexId: number, + readonly _documentKey: DocumentKey, + readonly _arrayValue: Uint8Array, + readonly _directionalValue: Uint8Array ) {} /** @@ -31,49 +35,82 @@ export class IndexEntry { * directional value. */ successor(): IndexEntry { - const currentLength = this.directionalValue.length; + const currentLength = this._directionalValue.length; const newLength = - currentLength === 0 || this.directionalValue[currentLength - 1] === 255 + currentLength === 0 || this._directionalValue[currentLength - 1] === 255 ? currentLength + 1 : currentLength; const successor = new Uint8Array(newLength); - successor.set(this.directionalValue, 0); + successor.set(this._directionalValue, 0); if (newLength !== currentLength) { - successor.set([0], this.directionalValue.length); + successor.set([0], this._directionalValue.length); } else { ++successor[successor.length - 1]; } return new IndexEntry( - this.indexId, - this.documentKey, - this.arrayValue, + this._indexId, + this._documentKey, + this._arrayValue, successor ); } + + // Create a representation of the Index Entry as a DbIndexEntry + dbIndexEntry( + uid: string, + orderedDocumentKey: Uint8Array, + documentKey: DocumentKey + ): DbIndexEntry { + return { + indexId: this._indexId, + uid, + arrayValue: encodeKeySafeBytes(this._arrayValue), + directionalValue: encodeKeySafeBytes(this._directionalValue), + orderedDocumentKey: encodeKeySafeBytes(orderedDocumentKey), + documentKey: documentKey.path.toArray() + }; + } + + // Create a representation of the Index Entry as a DbIndexEntryKey + dbIndexEntryKey( + uid: string, + orderedDocumentKey: Uint8Array, + documentKey: DocumentKey + ): DbIndexEntryKey { + const entry = this.dbIndexEntry(uid, orderedDocumentKey, documentKey); + return [ + entry.indexId, + entry.uid, + entry.arrayValue, + entry.directionalValue, + entry.orderedDocumentKey, + entry.documentKey + ]; + } } export function indexEntryComparator( left: IndexEntry, right: IndexEntry ): number { - let cmp = left.indexId - right.indexId; + let cmp = left._indexId - right._indexId; if (cmp !== 0) { return cmp; } - cmp = compareByteArrays(left.arrayValue, right.arrayValue); + cmp = compareByteArrays(left._arrayValue, right._arrayValue); if (cmp !== 0) { return cmp; } - cmp = compareByteArrays(left.directionalValue, right.directionalValue); + cmp = compareByteArrays(left._directionalValue, right._directionalValue); if (cmp !== 0) { return cmp; } - return DocumentKey.comparator(left.documentKey, right.documentKey); + return DocumentKey.comparator(left._documentKey, right._documentKey); } export function compareByteArrays(left: Uint8Array, right: Uint8Array): number { @@ -85,3 +122,57 @@ export function compareByteArrays(left: Uint8Array, right: Uint8Array): number { } return left.length - right.length; } + +/** + * Workaround for WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=292721 + * Create a key safe representation of Uint8Array values. + * If the browser is detected as Safari or WebKit, then + * the input array will be converted to "sortable byte string". + * Otherwise, the input array will be returned in its original type. + */ +export function encodeKeySafeBytes(array: Uint8Array): KeySafeBytes { + if (isSafariOrWebkit()) { + return encodeUint8ArrayToSortableString(array); + } + return array; +} + +/** + * Reverts the key safe representation of Uint8Array (created by + * encodeKeySafeBytes) to a normal Uint8Array. + */ +export function decodeKeySafeBytes(input: KeySafeBytes): Uint8Array { + if (typeof input !== 'string') { + return input; + } + return decodeSortableStringToUint8Array(input); +} + +/** + * Encodes a Uint8Array into a "sortable byte string". + * A "sortable byte string" sorts in the same order as the Uint8Array. + * This works because JS string comparison sorts strings based on code points. + */ +function encodeUint8ArrayToSortableString(array: Uint8Array): string { + let byteString = ''; + for (let i = 0; i < array.length; i++) { + byteString += String.fromCharCode(array[i]); + } + + return byteString; +} + +/** + * Decodes a "sortable byte string" back into a Uint8Array. + * A "sortable byte string" is assumed to be created where each character's + * Unicode code point directly corresponds to a single byte value (0-255). + */ +function decodeSortableStringToUint8Array(byteString: string): Uint8Array { + const uint8array = new Uint8Array(byteString.length); + + for (let i = 0; i < byteString.length; i++) { + uint8array[i] = byteString.charCodeAt(i); + } + + return uint8array; +} diff --git a/packages/firestore/src/local/indexeddb_index_manager.ts b/packages/firestore/src/local/indexeddb_index_manager.ts index d2b8bc47163..2d707470b2f 100644 --- a/packages/firestore/src/local/indexeddb_index_manager.ts +++ b/packages/firestore/src/local/indexeddb_index_manager.ts @@ -39,7 +39,12 @@ import { } from '../core/target'; import { FirestoreIndexValueWriter } from '../index/firestore_index_value_writer'; import { IndexByteEncoder } from '../index/index_byte_encoder'; -import { IndexEntry, indexEntryComparator } from '../index/index_entry'; +import { + IndexEntry, + indexEntryComparator, + encodeKeySafeBytes, + decodeKeySafeBytes +} from '../index/index_entry'; import { documentKeySet, DocumentMap } from '../model/collections'; import { Document } from '../model/document'; import { DocumentKey } from '../model/document_key'; @@ -817,14 +822,13 @@ export class IndexedDbIndexManager implements IndexManager { indexEntry: IndexEntry ): PersistencePromise { const indexEntries = indexEntriesStore(transaction); - return indexEntries.put({ - indexId: indexEntry.indexId, - uid: this.uid, - arrayValue: indexEntry.arrayValue, - directionalValue: indexEntry.directionalValue, - orderedDocumentKey: this.encodeDirectionalKey(fieldIndex, document.key), - documentKey: document.key.path.toArray() - }); + return indexEntries.put( + indexEntry.dbIndexEntry( + this.uid, + this.encodeDirectionalKey(fieldIndex, document.key), + document.key + ) + ); } private deleteIndexEntry( @@ -834,14 +838,13 @@ export class IndexedDbIndexManager implements IndexManager { indexEntry: IndexEntry ): PersistencePromise { const indexEntries = indexEntriesStore(transaction); - return indexEntries.delete([ - indexEntry.indexId, - this.uid, - indexEntry.arrayValue, - indexEntry.directionalValue, - this.encodeDirectionalKey(fieldIndex, document.key), - document.key.path.toArray() - ]); + return indexEntries.delete( + indexEntry.dbIndexEntryKey( + this.uid, + this.encodeDirectionalKey(fieldIndex, document.key), + document.key + ) + ); } private getExistingIndexEntries( @@ -858,7 +861,9 @@ export class IndexedDbIndexManager implements IndexManager { range: IDBKeyRange.only([ fieldIndex.indexId, this.uid, - this.encodeDirectionalKey(fieldIndex, documentKey) + encodeKeySafeBytes( + this.encodeDirectionalKey(fieldIndex, documentKey) + ) ]) }, (_, entry) => { @@ -866,8 +871,8 @@ export class IndexedDbIndexManager implements IndexManager { new IndexEntry( fieldIndex.indexId, documentKey, - entry.arrayValue, - entry.directionalValue + decodeKeySafeBytes(entry.arrayValue), + decodeKeySafeBytes(entry.directionalValue) ) ); } @@ -1020,24 +1025,16 @@ export class IndexedDbIndexManager implements IndexManager { return []; } - const lowerBound = [ - bounds[i].indexId, + const lowerBound = bounds[i].dbIndexEntryKey( this.uid, - bounds[i].arrayValue, - bounds[i].directionalValue, EMPTY_VALUE, - [] - ] as DbIndexEntryKey; - - const upperBound = [ - bounds[i + 1].indexId, + DocumentKey.empty() + ); + const upperBound = bounds[i + 1].dbIndexEntryKey( this.uid, - bounds[i + 1].arrayValue, - bounds[i + 1].directionalValue, EMPTY_VALUE, - [] - ] as DbIndexEntryKey; - + DocumentKey.empty() + ); ranges.push(IDBKeyRange.bound(lowerBound, upperBound)); } return ranges; diff --git a/packages/firestore/src/local/indexeddb_schema.ts b/packages/firestore/src/local/indexeddb_schema.ts index 0395756ab96..b8b6c1111d8 100644 --- a/packages/firestore/src/local/indexeddb_schema.ts +++ b/packages/firestore/src/local/indexeddb_schema.ts @@ -26,7 +26,7 @@ import { } from '../protos/firestore_proto_api'; import { EncodedResourcePath } from './encoded_resource_path'; -import { DbTimestampKey } from './indexeddb_sentinels'; +import { DbTimestampKey, KeySafeBytes } from './indexeddb_sentinels'; /** * Schema Version for the Web client: @@ -52,9 +52,11 @@ import { DbTimestampKey } from './indexeddb_sentinels'; * 14. Add overlays. * 15. Add indexing support. * 16. Parse timestamp strings before creating index entries. + * 17. TODO(tomandersen) + * 18. Encode key safe representations of IndexEntry in DbIndexEntryStore. */ -export const SCHEMA_VERSION = 17; +export const SCHEMA_VERSION = 18; /** * Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects. @@ -507,14 +509,14 @@ export interface DbIndexEntry { /** The user id for this entry. */ uid: string; /** The encoded array index value for this entry. */ - arrayValue: Uint8Array; + arrayValue: KeySafeBytes; /** The encoded directional value for equality and inequality filters. */ - directionalValue: Uint8Array; + directionalValue: KeySafeBytes; /** * The document key this entry points to. This entry is encoded by an ordered * encoder to match the key order of the index. */ - orderedDocumentKey: Uint8Array; + orderedDocumentKey: KeySafeBytes; /** The segments of the document key this entry points to. */ documentKey: string[]; } diff --git a/packages/firestore/src/local/indexeddb_schema_converter.ts b/packages/firestore/src/local/indexeddb_schema_converter.ts index 7446ae7ae20..1b84fa4f2c1 100644 --- a/packages/firestore/src/local/indexeddb_schema_converter.ts +++ b/packages/firestore/src/local/indexeddb_schema_converter.ts @@ -15,6 +15,8 @@ * limitations under the License. */ +import { isSafariOrWebkit } from '@firebase/util'; + import { User } from '../auth/user'; import { ListenSequence } from '../core/listen_sequence'; import { SnapshotVersion } from '../core/snapshot_version'; @@ -277,6 +279,22 @@ export class SchemaConverter implements SimpleDbSchemaConverter { }); } + if (fromVersion < 18 && toVersion >= 18) { + // Clear the IndexEntryStores on WebKit and Safari to remove possibly + // corrupted index entries + if (isSafariOrWebkit()) { + p = p + .next(() => { + const indexStateStore = txn.objectStore(DbIndexStateStore); + indexStateStore.clear(); + }) + .next(() => { + const indexEntryStore = txn.objectStore(DbIndexEntryStore); + indexEntryStore.clear(); + }); + } + } + return p; } diff --git a/packages/firestore/src/local/indexeddb_sentinels.ts b/packages/firestore/src/local/indexeddb_sentinels.ts index cb6ebcb664a..0b4f5ed8918 100644 --- a/packages/firestore/src/local/indexeddb_sentinels.ts +++ b/packages/firestore/src/local/indexeddb_sentinels.ts @@ -305,6 +305,15 @@ export const DbIndexStateSequenceNumberIndex = 'sequenceNumberIndex'; export const DbIndexStateSequenceNumberIndexPath = ['uid', 'sequenceNumber']; +/** + * Representation of a byte array that is safe for + * use in an IndexedDb key. The value is either + * a "sortable byte string", which is key safe in + * Safari/WebKit, or the value is a Uint8Array, + * which is key safe in other browsers. + */ +export type KeySafeBytes = Uint8Array | string; + /** * The key for each index entry consists of the index id and its user id, * the encoded array and directional value for the indexed fields as well as @@ -313,9 +322,9 @@ export const DbIndexStateSequenceNumberIndexPath = ['uid', 'sequenceNumber']; export type DbIndexEntryKey = [ number, string, - Uint8Array, - Uint8Array, - Uint8Array, + KeySafeBytes, + KeySafeBytes, + KeySafeBytes, string[] ]; @@ -425,6 +434,7 @@ export const V15_STORES = [ ]; export const V16_STORES = V15_STORES; export const V17_STORES = [...V15_STORES, DbGlobalsStore]; +export const V18_STORES = V17_STORES; /** * The list of all default IndexedDB stores used throughout the SDK. This is @@ -435,7 +445,9 @@ export const ALL_STORES = V12_STORES; /** Returns the object stores for the provided schema. */ export function getObjectStores(schemaVersion: number): string[] { - if (schemaVersion === 17) { + if (schemaVersion === 18) { + return V18_STORES; + } else if (schemaVersion === 17) { return V17_STORES; } else if (schemaVersion === 16) { return V16_STORES; @@ -450,6 +462,6 @@ export function getObjectStores(schemaVersion: number): string[] { } else if (schemaVersion === 11) { return V11_STORES; } else { - fail(0xeb55, 'Only schema version 11 and 12 and 13 are supported'); + fail(0xeb55, 'Only schema versions >11 are supported'); } } diff --git a/packages/util/src/environment.ts b/packages/util/src/environment.ts index a0467b08c59..85410494241 100644 --- a/packages/util/src/environment.ts +++ b/packages/util/src/environment.ts @@ -173,6 +173,17 @@ export function isSafari(): boolean { ); } +/** Returns true if we are running in Safari or WebKit */ +export function isSafariOrWebkit(): boolean { + return ( + !isNode() && + !!navigator.userAgent && + (navigator.userAgent.includes('Safari') || + navigator.userAgent.includes('WebKit')) && + !navigator.userAgent.includes('Chrome') + ); +} + /** * This method checks if indexedDB is supported by current browser/service worker context * @return true if indexedDB is supported by current browser/service worker context From 6be75f74dec92d1b84f77f79ccb770a3e23280b7 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Mon, 12 May 2025 09:42:19 -0700 Subject: [PATCH 33/77] Restore #8999 (#9010) * Revert "Roll back #8999 (#9007)" This reverts commit 51e7b489d8aadd531453f882421903da8727b19d. * Changeset --- .changeset/ninety-ways-dress.md | 8 ++ docs-devsite/app.firebaseappsettings.md | 4 +- packages/app-check/src/api.test.ts | 73 ++++++++++++++++++- packages/app-check/src/api.ts | 20 +++-- .../app-compat/test/firebaseAppCompat.test.ts | 8 +- packages/app/src/api.test.ts | 19 +++-- packages/app/src/api.ts | 4 +- packages/app/src/firebaseApp.test.ts | 4 +- packages/app/src/firebaseServerApp.ts | 2 +- packages/app/src/public-types.ts | 2 +- 10 files changed, 116 insertions(+), 28 deletions(-) create mode 100644 .changeset/ninety-ways-dress.md diff --git a/.changeset/ninety-ways-dress.md b/.changeset/ninety-ways-dress.md new file mode 100644 index 00000000000..82c9e15cf4d --- /dev/null +++ b/.changeset/ninety-ways-dress.md @@ -0,0 +1,8 @@ +--- +'@firebase/app-compat': minor +'@firebase/app-check': minor +'@firebase/app': minor +'firebase': minor +--- + +Default `automaticDataCollectionEnabled` to true without changing App Check's default behavior. diff --git a/docs-devsite/app.firebaseappsettings.md b/docs-devsite/app.firebaseappsettings.md index 1515e21ac93..e7838ab4185 100644 --- a/docs-devsite/app.firebaseappsettings.md +++ b/docs-devsite/app.firebaseappsettings.md @@ -22,12 +22,12 @@ export interface FirebaseAppSettings | Property | Type | Description | | --- | --- | --- | -| [automaticDataCollectionEnabled](./app.firebaseappsettings.md#firebaseappsettingsautomaticdatacollectionenabled) | boolean | The settable config flag for GDPR opt-in/opt-out | +| [automaticDataCollectionEnabled](./app.firebaseappsettings.md#firebaseappsettingsautomaticdatacollectionenabled) | boolean | The settable config flag for GDPR opt-in/opt-out. Defaults to true. | | [name](./app.firebaseappsettings.md#firebaseappsettingsname) | string | custom name for the Firebase App. The default value is "[DEFAULT]". | ## FirebaseAppSettings.automaticDataCollectionEnabled -The settable config flag for GDPR opt-in/opt-out +The settable config flag for GDPR opt-in/opt-out. Defaults to true. Signature: diff --git a/packages/app-check/src/api.test.ts b/packages/app-check/src/api.test.ts index a6805d1b0b3..b71971e9d70 100644 --- a/packages/app-check/src/api.test.ts +++ b/packages/app-check/src/api.test.ts @@ -239,7 +239,7 @@ describe('api', () => { expect(getStateReference(app).activated).to.equal(true); }); - it('isTokenAutoRefreshEnabled value defaults to global setting', () => { + it('global false + local unset = false', () => { app.automaticDataCollectionEnabled = false; initializeAppCheck(app, { provider: new ReCaptchaV3Provider(FAKE_SITE_KEY) @@ -247,8 +247,77 @@ describe('api', () => { expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); }); - it('sets isTokenAutoRefreshEnabled correctly, overriding global setting', () => { + it('global false + local true = false', () => { app.automaticDataCollectionEnabled = false; + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: true + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global false + local false = false', () => { + app.automaticDataCollectionEnabled = false; + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: false + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global unset + local unset = false', () => { + // Global unset should default to true. + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY) + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global unset + local false = false', () => { + // Global unset should default to true. + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: false + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global unset + local true = true', () => { + // Global unset should default to true. + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: true + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(true); + }); + + it('global true + local unset = false', () => { + app.automaticDataCollectionEnabled = true; + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY) + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global true + local false = false', () => { + app.automaticDataCollectionEnabled = true; + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: false + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false); + }); + + it('global true + local true = true', () => { + app.automaticDataCollectionEnabled = true; + initializeAppCheck(app, { + provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), + isTokenAutoRefreshEnabled: true + }); + expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(true); + }); + + it('sets isTokenAutoRefreshEnabled correctly, overriding global setting', () => { initializeAppCheck(app, { provider: new ReCaptchaV3Provider(FAKE_SITE_KEY), isTokenAutoRefreshEnabled: true diff --git a/packages/app-check/src/api.ts b/packages/app-check/src/api.ts index a4dd87a4e77..e7c9f8cfcf9 100644 --- a/packages/app-check/src/api.ts +++ b/packages/app-check/src/api.ts @@ -43,6 +43,7 @@ import { } from './internal-api'; import { readTokenFromStorage } from './storage'; import { getDebugToken, initializeDebugMode, isDebugMode } from './debug'; +import { logger } from './logger'; declare module '@firebase/component' { interface NameServiceMapping { @@ -132,7 +133,7 @@ export function initializeAppCheck( function _activate( app: FirebaseApp, provider: AppCheckProvider, - isTokenAutoRefreshEnabled?: boolean + isTokenAutoRefreshEnabled: boolean = false ): void { // Create an entry in the APP_CHECK_STATES map. Further changes should // directly mutate this object. @@ -149,13 +150,18 @@ function _activate( return cachedToken; }); - // Use value of global `automaticDataCollectionEnabled` (which - // itself defaults to false if not specified in config) if - // `isTokenAutoRefreshEnabled` param was not provided by user. + // Global `automaticDataCollectionEnabled` (defaults to true) and + // `isTokenAutoRefreshEnabled` must both be true. state.isTokenAutoRefreshEnabled = - isTokenAutoRefreshEnabled === undefined - ? app.automaticDataCollectionEnabled - : isTokenAutoRefreshEnabled; + isTokenAutoRefreshEnabled && app.automaticDataCollectionEnabled; + + if (!app.automaticDataCollectionEnabled && isTokenAutoRefreshEnabled) { + logger.warn( + '`isTokenAutoRefreshEnabled` is true but ' + + '`automaticDataCollectionEnabled` was set to false during' + + ' `initializeApp()`. This blocks automatic token refresh.' + ); + } state.provider.initialize(app); } diff --git a/packages/app-compat/test/firebaseAppCompat.test.ts b/packages/app-compat/test/firebaseAppCompat.test.ts index f12a73e61a8..61bbed848d8 100644 --- a/packages/app-compat/test/firebaseAppCompat.test.ts +++ b/packages/app-compat/test/firebaseAppCompat.test.ts @@ -403,17 +403,17 @@ function firebaseAppTests( ).throws(/'abc'.*exists/i); }); - it('automaticDataCollectionEnabled is `false` by default', () => { + it('automaticDataCollectionEnabled is `true` by default', () => { const app = firebase.initializeApp({}, 'my-app'); - expect(app.automaticDataCollectionEnabled).to.eq(false); + expect(app.automaticDataCollectionEnabled).to.eq(true); }); it('automaticDataCollectionEnabled can be set via the config object', () => { const app = firebase.initializeApp( {}, - { automaticDataCollectionEnabled: true } + { automaticDataCollectionEnabled: false } ); - expect(app.automaticDataCollectionEnabled).to.eq(true); + expect(app.automaticDataCollectionEnabled).to.eq(false); }); it('Modifying options object does not change options.', () => { diff --git a/packages/app/src/api.test.ts b/packages/app/src/api.test.ts index f6cf922ba05..4e79ad58d82 100644 --- a/packages/app/src/api.test.ts +++ b/packages/app/src/api.test.ts @@ -128,14 +128,14 @@ describe('API tests', () => { { apiKey: 'test1' }, - { automaticDataCollectionEnabled: true } + { automaticDataCollectionEnabled: false } ); expect(() => initializeApp( { apiKey: 'test1' }, - { automaticDataCollectionEnabled: false } + { automaticDataCollectionEnabled: true } ) ).throws(/\[DEFAULT\].*exists/i); }); @@ -146,14 +146,14 @@ describe('API tests', () => { { apiKey: 'test1' }, - { name: appName, automaticDataCollectionEnabled: true } + { name: appName, automaticDataCollectionEnabled: false } ); expect(() => initializeApp( { apiKey: 'test1' }, - { name: appName, automaticDataCollectionEnabled: false } + { name: appName, automaticDataCollectionEnabled: true } ) ).throws(/'MyApp'.*exists/i); }); @@ -164,11 +164,16 @@ describe('API tests', () => { expect(app.name).to.equal(appName); }); - it('sets automaticDataCollectionEnabled', () => { - const app = initializeApp({}, { automaticDataCollectionEnabled: true }); + it('sets automaticDataCollectionEnabled to true by default', () => { + const app = initializeApp({}); expect(app.automaticDataCollectionEnabled).to.be.true; }); + it('sets a new automaticDataCollectionEnabled value if provided', () => { + const app = initializeApp({}, { automaticDataCollectionEnabled: false }); + expect(app.automaticDataCollectionEnabled).to.be.false; + }); + it('adds registered components to App', () => { _clearComponents(); const comp1 = createTestComponent('test1'); @@ -211,7 +216,7 @@ describe('API tests', () => { const app = initializeServerApp(options, serverAppSettings); expect(app).to.not.equal(null); - expect(app.automaticDataCollectionEnabled).to.be.false; + expect(app.automaticDataCollectionEnabled).to.be.true; await deleteApp(app); expect((app as FirebaseServerAppImpl).isDeleted).to.be.true; }); diff --git a/packages/app/src/api.ts b/packages/app/src/api.ts index b8ec25fc509..9cba8ec6f50 100644 --- a/packages/app/src/api.ts +++ b/packages/app/src/api.ts @@ -143,7 +143,7 @@ export function initializeApp( const config: Required = { name: DEFAULT_ENTRY_NAME, - automaticDataCollectionEnabled: false, + automaticDataCollectionEnabled: true, ...rawConfig }; const name = config.name; @@ -241,7 +241,7 @@ export function initializeServerApp( } if (_serverAppConfig.automaticDataCollectionEnabled === undefined) { - _serverAppConfig.automaticDataCollectionEnabled = false; + _serverAppConfig.automaticDataCollectionEnabled = true; } let appOptions: FirebaseOptions; diff --git a/packages/app/src/firebaseApp.test.ts b/packages/app/src/firebaseApp.test.ts index 419eeef4f1f..3acbb4a2869 100644 --- a/packages/app/src/firebaseApp.test.ts +++ b/packages/app/src/firebaseApp.test.ts @@ -27,11 +27,11 @@ describe('FirebaseAppNext', () => { }; const app = new FirebaseAppImpl( options, - { name: 'test', automaticDataCollectionEnabled: false }, + { name: 'test', automaticDataCollectionEnabled: true }, new ComponentContainer('test') ); - expect(app.automaticDataCollectionEnabled).to.be.false; + expect(app.automaticDataCollectionEnabled).to.be.true; expect(app.name).to.equal('test'); expect(app.options).to.deep.equal(options); }); diff --git a/packages/app/src/firebaseServerApp.ts b/packages/app/src/firebaseServerApp.ts index 21232869c3c..2bb8efc7a63 100644 --- a/packages/app/src/firebaseServerApp.ts +++ b/packages/app/src/firebaseServerApp.ts @@ -74,7 +74,7 @@ export class FirebaseServerAppImpl const automaticDataCollectionEnabled = serverConfig.automaticDataCollectionEnabled !== undefined ? serverConfig.automaticDataCollectionEnabled - : false; + : true; // Create the FirebaseAppSettings object for the FirebaseAppImp constructor. const config: Required = { diff --git a/packages/app/src/public-types.ts b/packages/app/src/public-types.ts index ea68579a7e9..4f8373f2250 100644 --- a/packages/app/src/public-types.ts +++ b/packages/app/src/public-types.ts @@ -165,7 +165,7 @@ export interface FirebaseAppSettings { */ name?: string; /** - * The settable config flag for GDPR opt-in/opt-out + * The settable config flag for GDPR opt-in/opt-out. Defaults to true. */ automaticDataCollectionEnabled?: boolean; } From d5082f9f2fc4de98a6bfd1c6a5af4571af4d0bc6 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Mon, 12 May 2025 15:43:44 -0400 Subject: [PATCH 34/77] feat(ai): Migrate to `AI` and add GoogleAI support (#8931) --- .changeset/tall-zoos-stare.md | 6 + common/api-review/vertexai.api.md | 197 ++++++--- docs-devsite/_toc.yaml | 20 +- docs-devsite/index.md | 2 +- docs-devsite/vertexai.ai.md | 64 +++ ...i.vertexaierror.md => vertexai.aierror.md} | 26 +- docs-devsite/vertexai.aimodel.md | 39 ++ docs-devsite/vertexai.aioptions.md | 35 ++ docs-devsite/vertexai.backend.md | 57 +++ docs-devsite/vertexai.citation.md | 12 +- docs-devsite/vertexai.counttokensresponse.md | 4 +- docs-devsite/vertexai.generativemodel.md | 10 +- docs-devsite/vertexai.googleaibackend.md | 38 ++ .../vertexai.imagengenerationconfig.md | 8 +- docs-devsite/vertexai.imagenmodel.md | 12 +- docs-devsite/vertexai.md | 225 +++++++--- docs-devsite/vertexai.modelparams.md | 2 +- docs-devsite/vertexai.promptfeedback.md | 6 +- docs-devsite/vertexai.requestoptions.md | 2 +- docs-devsite/vertexai.safetyrating.md | 18 +- docs-devsite/vertexai.safetysetting.md | 6 +- docs-devsite/vertexai.schemashared.md | 4 +- docs-devsite/vertexai.vertexai.md | 44 -- docs-devsite/vertexai.vertexaibackend.md | 60 +++ docs-devsite/vertexai.vertexaimodel.md | 66 --- docs-devsite/vertexai.vertexaioptions.md | 2 +- packages/firebase/ai/index.ts | 18 + packages/firebase/ai/package.json | 7 + packages/firebase/package.json | 13 + packages/vertexai/src/api.test.ts | 123 +++--- packages/vertexai/src/api.ts | 117 ++++-- packages/vertexai/src/backend.test.ts | 52 +++ packages/vertexai/src/backend.ts | 92 +++++ .../src/backwards-compatbility.test.ts | 85 ++++ packages/vertexai/src/constants.ts | 3 + packages/vertexai/src/errors.ts | 25 +- .../vertexai/src/googleai-mappers.test.ts | 391 ++++++++++++++++++ packages/vertexai/src/googleai-mappers.ts | 227 ++++++++++ packages/vertexai/src/helpers.test.ts | 121 ++++++ packages/vertexai/src/helpers.ts | 74 ++++ packages/vertexai/src/index.node.ts | 28 +- packages/vertexai/src/index.ts | 30 +- .../src/methods/chat-session-helpers.ts | 34 +- .../vertexai/src/methods/chat-session.test.ts | 4 +- .../vertexai/src/methods/count-tokens.test.ts | 41 +- packages/vertexai/src/methods/count-tokens.ts | 11 +- .../src/methods/generate-content.test.ts | 96 ++++- .../vertexai/src/methods/generate-content.ts | 31 +- ...ertexai-model.test.ts => ai-model.test.ts} | 51 +-- .../models/{vertexai-model.ts => ai-model.ts} | 102 +++-- .../src/models/generative-model.test.ts | 49 ++- .../vertexai/src/models/generative-model.ts | 10 +- .../vertexai/src/models/imagen-model.test.ts | 22 +- packages/vertexai/src/models/imagen-model.ts | 14 +- packages/vertexai/src/models/index.ts | 2 +- packages/vertexai/src/public-types.ts | 85 +++- .../vertexai/src/requests/request-helpers.ts | 17 +- .../vertexai/src/requests/request.test.ts | 74 ++-- packages/vertexai/src/requests/request.ts | 74 ++-- .../vertexai/src/requests/response-helpers.ts | 38 +- .../src/requests/schema-builder.test.ts | 4 +- .../vertexai/src/requests/schema-builder.ts | 8 +- .../src/requests/stream-reader.test.ts | 44 +- .../vertexai/src/requests/stream-reader.ts | 59 ++- packages/vertexai/src/service.test.ts | 18 +- packages/vertexai/src/service.ts | 19 +- packages/vertexai/src/types/enums.ts | 13 +- packages/vertexai/src/types/error.ts | 9 +- packages/vertexai/src/types/googleai.ts | 70 ++++ .../vertexai/src/types/imagen/requests.ts | 6 + packages/vertexai/src/types/index.ts | 1 + packages/vertexai/src/types/internal.ts | 7 +- packages/vertexai/src/types/requests.ts | 7 + packages/vertexai/src/types/responses.ts | 36 ++ packages/vertexai/src/types/schema.ts | 5 +- scripts/update_vertexai_responses.sh | 2 +- 76 files changed, 2765 insertions(+), 669 deletions(-) create mode 100644 .changeset/tall-zoos-stare.md create mode 100644 docs-devsite/vertexai.ai.md rename docs-devsite/{vertexai.vertexaierror.md => vertexai.aierror.md} (50%) create mode 100644 docs-devsite/vertexai.aimodel.md create mode 100644 docs-devsite/vertexai.aioptions.md create mode 100644 docs-devsite/vertexai.backend.md create mode 100644 docs-devsite/vertexai.googleaibackend.md delete mode 100644 docs-devsite/vertexai.vertexai.md create mode 100644 docs-devsite/vertexai.vertexaibackend.md delete mode 100644 docs-devsite/vertexai.vertexaimodel.md create mode 100644 packages/firebase/ai/index.ts create mode 100644 packages/firebase/ai/package.json create mode 100644 packages/vertexai/src/backend.test.ts create mode 100644 packages/vertexai/src/backend.ts create mode 100644 packages/vertexai/src/backwards-compatbility.test.ts create mode 100644 packages/vertexai/src/googleai-mappers.test.ts create mode 100644 packages/vertexai/src/googleai-mappers.ts create mode 100644 packages/vertexai/src/helpers.test.ts create mode 100644 packages/vertexai/src/helpers.ts rename packages/vertexai/src/models/{vertexai-model.test.ts => ai-model.test.ts} (66%) rename packages/vertexai/src/models/{vertexai-model.ts => ai-model.ts} (55%) create mode 100644 packages/vertexai/src/types/googleai.ts diff --git a/.changeset/tall-zoos-stare.md b/.changeset/tall-zoos-stare.md new file mode 100644 index 00000000000..2711107986c --- /dev/null +++ b/.changeset/tall-zoos-stare.md @@ -0,0 +1,6 @@ +--- +'firebase': minor +'@firebase/vertexai': minor +--- + +Add support for the Gemini Developer API, enabling usage in a free tier, and add new `AI` API to accomodate new product naming. diff --git a/common/api-review/vertexai.api.md b/common/api-review/vertexai.api.md index f9cf3dac5bd..42da114f9e9 100644 --- a/common/api-review/vertexai.api.md +++ b/common/api-review/vertexai.api.md @@ -9,6 +9,62 @@ import { FirebaseApp } from '@firebase/app'; import { FirebaseAuthTokenData } from '@firebase/auth-interop-types'; import { FirebaseError } from '@firebase/util'; +// @public +export interface AI { + app: FirebaseApp; + backend: Backend; + // @deprecated + location: string; +} + +// @public +export class AIError extends FirebaseError { + constructor(code: AIErrorCode, message: string, customErrorData?: CustomErrorData | undefined); + // (undocumented) + readonly code: AIErrorCode; + // (undocumented) + readonly customErrorData?: CustomErrorData | undefined; +} + +// @public +const enum AIErrorCode { + API_NOT_ENABLED = "api-not-enabled", + ERROR = "error", + FETCH_ERROR = "fetch-error", + INVALID_CONTENT = "invalid-content", + INVALID_SCHEMA = "invalid-schema", + NO_API_KEY = "no-api-key", + NO_APP_ID = "no-app-id", + NO_MODEL = "no-model", + NO_PROJECT_ID = "no-project-id", + PARSE_FAILED = "parse-failed", + REQUEST_ERROR = "request-error", + RESPONSE_ERROR = "response-error", + UNSUPPORTED = "unsupported" +} + +export { AIErrorCode } + +export { AIErrorCode as VertexAIErrorCode } + +// @public +export abstract class AIModel { + // @internal + protected constructor(ai: AI, modelName: string); + // Warning: (ae-forgotten-export) The symbol "ApiSettings" needs to be exported by the entry point index.d.ts + // + // @internal (undocumented) + protected _apiSettings: ApiSettings; + readonly model: string; + // @internal + static normalizeModelName(modelName: string, backendType: BackendType): string; + } + +// @public +export interface AIOptions { + backend: Backend; +} + // @public export class ArraySchema extends Schema { constructor(schemaParams: SchemaParams, items: TypedSchema); @@ -18,6 +74,21 @@ export class ArraySchema extends Schema { toJSON(): SchemaRequest; } +// @public +export abstract class Backend { + protected constructor(type: BackendType); + readonly backendType: BackendType; +} + +// @public +export const BackendType: { + readonly VERTEX_AI: "VERTEX_AI"; + readonly GOOGLE_AI: "GOOGLE_AI"; +}; + +// @public +export type BackendType = (typeof BackendType)[keyof typeof BackendType]; + // @public export interface BaseParams { // (undocumented) @@ -41,7 +112,6 @@ export class BooleanSchema extends Schema { // @public export class ChatSession { - // Warning: (ae-forgotten-export) The symbol "ApiSettings" needs to be exported by the entry point index.d.ts constructor(apiSettings: ApiSettings, model: string, params?: StartChatParams | undefined, requestOptions?: RequestOptions | undefined); getHistory(): Promise; // (undocumented) @@ -60,11 +130,9 @@ export interface Citation { endIndex?: number; // (undocumented) license?: string; - // (undocumented) publicationDate?: Date_2; // (undocumented) startIndex?: number; - // (undocumented) title?: string; // (undocumented) uri?: string; @@ -326,8 +394,8 @@ export interface GenerativeContentBlob { } // @public -export class GenerativeModel extends VertexAIModel { - constructor(vertexAI: VertexAI, modelParams: ModelParams, requestOptions?: RequestOptions); +export class GenerativeModel extends AIModel { + constructor(ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions); countTokens(request: CountTokensRequest | string | Array): Promise; generateContent(request: GenerateContentRequest | string | Array): Promise; generateContentStream(request: GenerateContentRequest | string | Array): Promise; @@ -347,14 +415,76 @@ export class GenerativeModel extends VertexAIModel { } // @public -export function getGenerativeModel(vertexAI: VertexAI, modelParams: ModelParams, requestOptions?: RequestOptions): GenerativeModel; +export function getAI(app?: FirebaseApp, options?: AIOptions): AI; + +// @public +export function getGenerativeModel(ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions): GenerativeModel; // @beta -export function getImagenModel(vertexAI: VertexAI, modelParams: ImagenModelParams, requestOptions?: RequestOptions): ImagenModel; +export function getImagenModel(ai: AI, modelParams: ImagenModelParams, requestOptions?: RequestOptions): ImagenModel; // @public export function getVertexAI(app?: FirebaseApp, options?: VertexAIOptions): VertexAI; +// @public +export class GoogleAIBackend extends Backend { + constructor(); +} + +// Warning: (ae-internal-missing-underscore) The name "GoogleAICitationMetadata" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export interface GoogleAICitationMetadata { + // (undocumented) + citationSources: Citation[]; +} + +// Warning: (ae-internal-missing-underscore) The name "GoogleAICountTokensRequest" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export interface GoogleAICountTokensRequest { + // (undocumented) + generateContentRequest: { + model: string; + contents: Content[]; + systemInstruction?: string | Part | Content; + tools?: Tool[]; + generationConfig?: GenerationConfig; + }; +} + +// Warning: (ae-internal-missing-underscore) The name "GoogleAIGenerateContentCandidate" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export interface GoogleAIGenerateContentCandidate { + // (undocumented) + citationMetadata?: GoogleAICitationMetadata; + // (undocumented) + content: Content; + // (undocumented) + finishMessage?: string; + // (undocumented) + finishReason?: FinishReason; + // (undocumented) + groundingMetadata?: GroundingMetadata; + // (undocumented) + index: number; + // (undocumented) + safetyRatings?: SafetyRating[]; +} + +// Warning: (ae-internal-missing-underscore) The name "GoogleAIGenerateContentResponse" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export interface GoogleAIGenerateContentResponse { + // (undocumented) + candidates?: GoogleAIGenerateContentCandidate[]; + // (undocumented) + promptFeedback?: PromptFeedback; + // (undocumented) + usageMetadata?: UsageMetadata; +} + // @public @deprecated (undocumented) export interface GroundingAttribution { // (undocumented) @@ -377,7 +507,7 @@ export interface GroundingMetadata { webSearchQueries?: string[]; } -// @public (undocumented) +// @public export enum HarmBlockMethod { PROBABILITY = "PROBABILITY", SEVERITY = "SEVERITY" @@ -416,7 +546,8 @@ export enum HarmSeverity { HARM_SEVERITY_HIGH = "HARM_SEVERITY_HIGH", HARM_SEVERITY_LOW = "HARM_SEVERITY_LOW", HARM_SEVERITY_MEDIUM = "HARM_SEVERITY_MEDIUM", - HARM_SEVERITY_NEGLIGIBLE = "HARM_SEVERITY_NEGLIGIBLE" + HARM_SEVERITY_NEGLIGIBLE = "HARM_SEVERITY_NEGLIGIBLE", + HARM_SEVERITY_UNSUPPORTED = "HARM_SEVERITY_UNSUPPORTED" } // @beta @@ -464,8 +595,8 @@ export interface ImagenInlineImage { } // @beta -export class ImagenModel extends VertexAIModel { - constructor(vertexAI: VertexAI, modelParams: ImagenModelParams, requestOptions?: RequestOptions | undefined); +export class ImagenModel extends AIModel { + constructor(ai: AI, modelParams: ImagenModelParams, requestOptions?: RequestOptions | undefined); generateImages(prompt: string): Promise>; // @internal generateImagesGCS(prompt: string, gcsURI: string): Promise>; @@ -587,7 +718,6 @@ export const POSSIBLE_ROLES: readonly ["user", "model", "function", "system"]; export interface PromptFeedback { // (undocumented) blockReason?: BlockReason; - // (undocumented) blockReasonMessage?: string; // (undocumented) safetyRatings: SafetyRating[]; @@ -627,11 +757,8 @@ export interface SafetyRating { category: HarmCategory; // (undocumented) probability: HarmProbability; - // (undocumented) probabilityScore: number; - // (undocumented) severity: HarmSeverity; - // (undocumented) severityScore: number; } @@ -639,7 +766,6 @@ export interface SafetyRating { export interface SafetySetting { // (undocumented) category: HarmCategory; - // (undocumented) method?: HarmBlockMethod; // (undocumented) threshold: HarmBlockThreshold; @@ -791,46 +917,19 @@ export interface UsageMetadata { } // @public -export interface VertexAI { - app: FirebaseApp; - // (undocumented) - location: string; -} +export type VertexAI = AI; // @public -export class VertexAIError extends FirebaseError { - constructor(code: VertexAIErrorCode, message: string, customErrorData?: CustomErrorData | undefined); - // (undocumented) - readonly code: VertexAIErrorCode; - // (undocumented) - readonly customErrorData?: CustomErrorData | undefined; +export class VertexAIBackend extends Backend { + constructor(location?: string); + readonly location: string; } // @public -export const enum VertexAIErrorCode { - API_NOT_ENABLED = "api-not-enabled", - ERROR = "error", - FETCH_ERROR = "fetch-error", - INVALID_CONTENT = "invalid-content", - INVALID_SCHEMA = "invalid-schema", - NO_API_KEY = "no-api-key", - NO_APP_ID = "no-app-id", - NO_MODEL = "no-model", - NO_PROJECT_ID = "no-project-id", - PARSE_FAILED = "parse-failed", - REQUEST_ERROR = "request-error", - RESPONSE_ERROR = "response-error" -} +export const VertexAIError: typeof AIError; // @public -export abstract class VertexAIModel { - // @internal - protected constructor(vertexAI: VertexAI, modelName: string); - // @internal (undocumented) - protected _apiSettings: ApiSettings; - readonly model: string; - static normalizeModelName(modelName: string): string; -} +export const VertexAIModel: typeof AIModel; // @public export interface VertexAIOptions { diff --git a/docs-devsite/_toc.yaml b/docs-devsite/_toc.yaml index 665222edb9d..c507b44ce99 100644 --- a/docs-devsite/_toc.yaml +++ b/docs-devsite/_toc.yaml @@ -472,8 +472,18 @@ toc: - title: vertexai path: /docs/reference/js/vertexai.md section: + - title: AI + path: /docs/reference/js/vertexai.ai.md + - title: AIError + path: /docs/reference/js/vertexai.aierror.md + - title: AIModel + path: /docs/reference/js/vertexai.aimodel.md + - title: AIOptions + path: /docs/reference/js/vertexai.aioptions.md - title: ArraySchema path: /docs/reference/js/vertexai.arrayschema.md + - title: Backend + path: /docs/reference/js/vertexai.backend.md - title: BaseParams path: /docs/reference/js/vertexai.baseparams.md - title: BooleanSchema @@ -532,6 +542,8 @@ toc: path: /docs/reference/js/vertexai.generativecontentblob.md - title: GenerativeModel path: /docs/reference/js/vertexai.generativemodel.md + - title: GoogleAIBackend + path: /docs/reference/js/vertexai.googleaibackend.md - title: GroundingAttribution path: /docs/reference/js/vertexai.groundingattribution.md - title: GroundingMetadata @@ -598,12 +610,8 @@ toc: path: /docs/reference/js/vertexai.toolconfig.md - title: UsageMetadata path: /docs/reference/js/vertexai.usagemetadata.md - - title: VertexAI - path: /docs/reference/js/vertexai.vertexai.md - - title: VertexAIError - path: /docs/reference/js/vertexai.vertexaierror.md - - title: VertexAIModel - path: /docs/reference/js/vertexai.vertexaimodel.md + - title: VertexAIBackend + path: /docs/reference/js/vertexai.vertexaibackend.md - title: VertexAIOptions path: /docs/reference/js/vertexai.vertexaioptions.md - title: VideoMetadata diff --git a/docs-devsite/index.md b/docs-devsite/index.md index 82fdb36f076..af34d0d0250 100644 --- a/docs-devsite/index.md +++ b/docs-devsite/index.md @@ -27,5 +27,5 @@ https://github.com/firebase/firebase-js-sdk | [@firebase/performance](./performance.md#performance_package) | The Firebase Performance Monitoring Web SDK. This SDK does not work in a Node.js environment. | | [@firebase/remote-config](./remote-config.md#remote-config_package) | The Firebase Remote Config Web SDK. This SDK does not work in a Node.js environment. | | [@firebase/storage](./storage.md#storage_package) | Cloud Storage for Firebase | -| [@firebase/vertexai](./vertexai.md#vertexai_package) | The Vertex AI in Firebase Web SDK. | +| [@firebase/vertexai](./vertexai.md#vertexai_package) | The Firebase AI Web SDK. | diff --git a/docs-devsite/vertexai.ai.md b/docs-devsite/vertexai.ai.md new file mode 100644 index 00000000000..661bf0b4fe3 --- /dev/null +++ b/docs-devsite/vertexai.ai.md @@ -0,0 +1,64 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# AI interface +An instance of the Firebase AI SDK. + +Do not create this instance directly. Instead, use [getAI()](./vertexai.md#getai_a94a413). + +Signature: + +```typescript +export interface AI +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [app](./vertexai.ai.md#aiapp) | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [AI](./vertexai.ai.md#ai_interface) instance is associated with. | +| [backend](./vertexai.ai.md#aibackend) | [Backend](./vertexai.backend.md#backend_class) | A [Backend](./vertexai.backend.md#backend_class) instance that specifies the configuration for the target backend, either the Gemini Developer API (using [GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)) or the Vertex AI Gemini API (using [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). | +| [location](./vertexai.ai.md#ailocation) | string | The location configured for this AI service instance, relevant for Vertex AI backends. | + +## AI.app + +The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [AI](./vertexai.ai.md#ai_interface) instance is associated with. + +Signature: + +```typescript +app: FirebaseApp; +``` + +## AI.backend + +A [Backend](./vertexai.backend.md#backend_class) instance that specifies the configuration for the target backend, either the Gemini Developer API (using [GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)) or the Vertex AI Gemini API (using [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). + +Signature: + +```typescript +backend: Backend; +``` + +## AI.location + +> Warning: This API is now obsolete. +> +> use `AI.backend.location` instead. +> + +The location configured for this AI service instance, relevant for Vertex AI backends. + +Signature: + +```typescript +location: string; +``` diff --git a/docs-devsite/vertexai.vertexaierror.md b/docs-devsite/vertexai.aierror.md similarity index 50% rename from docs-devsite/vertexai.vertexaierror.md rename to docs-devsite/vertexai.aierror.md index 31f527e59b3..dac50815b6a 100644 --- a/docs-devsite/vertexai.vertexaierror.md +++ b/docs-devsite/vertexai.aierror.md @@ -9,13 +9,13 @@ overwritten. Changes should be made in the source code at https://github.com/firebase/firebase-js-sdk {% endcomment %} -# VertexAIError class -Error class for the Vertex AI in Firebase SDK. +# AIError class +Error class for the Firebase AI SDK. Signature: ```typescript -export declare class VertexAIError extends FirebaseError +export declare class AIError extends FirebaseError ``` Extends: [FirebaseError](./util.firebaseerror.md#firebaseerror_class) @@ -23,42 +23,42 @@ export declare class VertexAIError extends FirebaseError | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(code, message, customErrorData)](./vertexai.vertexaierror.md#vertexaierrorconstructor) | | Constructs a new instance of the VertexAIError class. | +| [(constructor)(code, message, customErrorData)](./vertexai.aierror.md#aierrorconstructor) | | Constructs a new instance of the AIError class. | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [code](./vertexai.vertexaierror.md#vertexaierrorcode) | | [VertexAIErrorCode](./vertexai.md#vertexaierrorcode) | | -| [customErrorData](./vertexai.vertexaierror.md#vertexaierrorcustomerrordata) | | [CustomErrorData](./vertexai.customerrordata.md#customerrordata_interface) \| undefined | | +| [code](./vertexai.aierror.md#aierrorcode) | | [AIErrorCode](./vertexai.md#aierrorcode) | | +| [customErrorData](./vertexai.aierror.md#aierrorcustomerrordata) | | [CustomErrorData](./vertexai.customerrordata.md#customerrordata_interface) \| undefined | | -## VertexAIError.(constructor) +## AIError.(constructor) -Constructs a new instance of the `VertexAIError` class. +Constructs a new instance of the `AIError` class. Signature: ```typescript -constructor(code: VertexAIErrorCode, message: string, customErrorData?: CustomErrorData | undefined); +constructor(code: AIErrorCode, message: string, customErrorData?: CustomErrorData | undefined); ``` #### Parameters | Parameter | Type | Description | | --- | --- | --- | -| code | [VertexAIErrorCode](./vertexai.md#vertexaierrorcode) | The error code from [VertexAIErrorCode](./vertexai.md#vertexaierrorcode). | +| code | [AIErrorCode](./vertexai.md#aierrorcode) | The error code from [AIErrorCode](./vertexai.md#aierrorcode). | | message | string | A human-readable message describing the error. | | customErrorData | [CustomErrorData](./vertexai.customerrordata.md#customerrordata_interface) \| undefined | Optional error data. | -## VertexAIError.code +## AIError.code Signature: ```typescript -readonly code: VertexAIErrorCode; +readonly code: AIErrorCode; ``` -## VertexAIError.customErrorData +## AIError.customErrorData Signature: diff --git a/docs-devsite/vertexai.aimodel.md b/docs-devsite/vertexai.aimodel.md new file mode 100644 index 00000000000..0ff091a0d03 --- /dev/null +++ b/docs-devsite/vertexai.aimodel.md @@ -0,0 +1,39 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# AIModel class +Base class for Firebase AI model APIs. + +Instances of this class are associated with a specific Firebase AI [Backend](./vertexai.backend.md#backend_class) and provide methods for interacting with the configured generative model. + +The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `AIModel` class. + +Signature: + +```typescript +export declare abstract class AIModel +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [model](./vertexai.aimodel.md#aimodelmodel) | | string | The fully qualified model resource name to use for generating images (for example, publishers/google/models/imagen-3.0-generate-002). | + +## AIModel.model + +The fully qualified model resource name to use for generating images (for example, `publishers/google/models/imagen-3.0-generate-002`). + +Signature: + +```typescript +readonly model: string; +``` diff --git a/docs-devsite/vertexai.aioptions.md b/docs-devsite/vertexai.aioptions.md new file mode 100644 index 00000000000..00ff0153527 --- /dev/null +++ b/docs-devsite/vertexai.aioptions.md @@ -0,0 +1,35 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# AIOptions interface +Options for initializing the AI service using [getAI()](./vertexai.md#getai_a94a413). This allows specifying which backend to use (Vertex AI Gemini API or Gemini Developer API) and configuring its specific options (like location for Vertex AI). + +Signature: + +```typescript +export interface AIOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [backend](./vertexai.aioptions.md#aioptionsbackend) | [Backend](./vertexai.backend.md#backend_class) | The backend configuration to use for the AI service instance. | + +## AIOptions.backend + +The backend configuration to use for the AI service instance. + +Signature: + +```typescript +backend: Backend; +``` diff --git a/docs-devsite/vertexai.backend.md b/docs-devsite/vertexai.backend.md new file mode 100644 index 00000000000..e6a2606901e --- /dev/null +++ b/docs-devsite/vertexai.backend.md @@ -0,0 +1,57 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# Backend class +Abstract base class representing the configuration for an AI service backend. This class should not be instantiated directly. Use its subclasses; [GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class) for the Gemini Developer API (via [Google AI](https://ai.google/)), and [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class) for the Vertex AI Gemini API. + +Signature: + +```typescript +export declare abstract class Backend +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(type)](./vertexai.backend.md#backendconstructor) | | Protected constructor for use by subclasses. | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [backendType](./vertexai.backend.md#backendbackendtype) | | [BackendType](./vertexai.md#backendtype) | Specifies the backend type. | + +## Backend.(constructor) + +Protected constructor for use by subclasses. + +Signature: + +```typescript +protected constructor(type: BackendType); +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | [BackendType](./vertexai.md#backendtype) | The backend type. | + +## Backend.backendType + +Specifies the backend type. + +Signature: + +```typescript +readonly backendType: BackendType; +``` diff --git a/docs-devsite/vertexai.citation.md b/docs-devsite/vertexai.citation.md index b5f5a19f231..c694f6c2a9c 100644 --- a/docs-devsite/vertexai.citation.md +++ b/docs-devsite/vertexai.citation.md @@ -24,9 +24,9 @@ export interface Citation | --- | --- | --- | | [endIndex](./vertexai.citation.md#citationendindex) | number | | | [license](./vertexai.citation.md#citationlicense) | string | | -| [publicationDate](./vertexai.citation.md#citationpublicationdate) | Date | | +| [publicationDate](./vertexai.citation.md#citationpublicationdate) | Date | The publication date of the cited source, if available.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). | | [startIndex](./vertexai.citation.md#citationstartindex) | number | | -| [title](./vertexai.citation.md#citationtitle) | string | | +| [title](./vertexai.citation.md#citationtitle) | string | The title of the cited source, if available.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). | | [uri](./vertexai.citation.md#citationuri) | string | | ## Citation.endIndex @@ -47,6 +47,10 @@ license?: string; ## Citation.publicationDate +The publication date of the cited source, if available. + +This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). + Signature: ```typescript @@ -63,6 +67,10 @@ startIndex?: number; ## Citation.title +The title of the cited source, if available. + +This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). + Signature: ```typescript diff --git a/docs-devsite/vertexai.counttokensresponse.md b/docs-devsite/vertexai.counttokensresponse.md index d67cc99fab2..b304ccb82a0 100644 --- a/docs-devsite/vertexai.counttokensresponse.md +++ b/docs-devsite/vertexai.counttokensresponse.md @@ -23,7 +23,7 @@ export interface CountTokensResponse | Property | Type | Description | | --- | --- | --- | | [promptTokensDetails](./vertexai.counttokensresponse.md#counttokensresponseprompttokensdetails) | [ModalityTokenCount](./vertexai.modalitytokencount.md#modalitytokencount_interface)\[\] | The breakdown, by modality, of how many tokens are consumed by the prompt. | -| [totalBillableCharacters](./vertexai.counttokensresponse.md#counttokensresponsetotalbillablecharacters) | number | The total number of billable characters counted across all instances from the request. | +| [totalBillableCharacters](./vertexai.counttokensresponse.md#counttokensresponsetotalbillablecharacters) | number | The total number of billable characters counted across all instances from the request.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. | | [totalTokens](./vertexai.counttokensresponse.md#counttokensresponsetotaltokens) | number | The total number of tokens counted across all instances from the request. | ## CountTokensResponse.promptTokensDetails @@ -40,6 +40,8 @@ promptTokensDetails?: ModalityTokenCount[]; The total number of billable characters counted across all instances from the request. +This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. + Signature: ```typescript diff --git a/docs-devsite/vertexai.generativemodel.md b/docs-devsite/vertexai.generativemodel.md index e4a238b0af5..ba82b65aceb 100644 --- a/docs-devsite/vertexai.generativemodel.md +++ b/docs-devsite/vertexai.generativemodel.md @@ -15,15 +15,15 @@ Class for generative model APIs. Signature: ```typescript -export declare class GenerativeModel extends VertexAIModel +export declare class GenerativeModel extends AIModel ``` -Extends: [VertexAIModel](./vertexai.vertexaimodel.md#vertexaimodel_class) +Extends: [AIModel](./vertexai.aimodel.md#aimodel_class) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(vertexAI, modelParams, requestOptions)](./vertexai.generativemodel.md#generativemodelconstructor) | | Constructs a new instance of the GenerativeModel class | +| [(constructor)(ai, modelParams, requestOptions)](./vertexai.generativemodel.md#generativemodelconstructor) | | Constructs a new instance of the GenerativeModel class | ## Properties @@ -52,14 +52,14 @@ Constructs a new instance of the `GenerativeModel` class Signature: ```typescript -constructor(vertexAI: VertexAI, modelParams: ModelParams, requestOptions?: RequestOptions); +constructor(ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions); ``` #### Parameters | Parameter | Type | Description | | --- | --- | --- | -| vertexAI | [VertexAI](./vertexai.vertexai.md#vertexai_interface) | | +| ai | [AI](./vertexai.ai.md#ai_interface) | | | modelParams | [ModelParams](./vertexai.modelparams.md#modelparams_interface) | | | requestOptions | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | | diff --git a/docs-devsite/vertexai.googleaibackend.md b/docs-devsite/vertexai.googleaibackend.md new file mode 100644 index 00000000000..e302a0eba91 --- /dev/null +++ b/docs-devsite/vertexai.googleaibackend.md @@ -0,0 +1,38 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# GoogleAIBackend class +Configuration class for the Gemini Developer API. + +Use this with [AIOptions](./vertexai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./vertexai.md#getai_a94a413) to specify the Gemini Developer API as the backend. + +Signature: + +```typescript +export declare class GoogleAIBackend extends Backend +``` +Extends: [Backend](./vertexai.backend.md#backend_class) + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)()](./vertexai.googleaibackend.md#googleaibackendconstructor) | | Creates a configuration object for the Gemini Developer API backend. | + +## GoogleAIBackend.(constructor) + +Creates a configuration object for the Gemini Developer API backend. + +Signature: + +```typescript +constructor(); +``` diff --git a/docs-devsite/vertexai.imagengenerationconfig.md b/docs-devsite/vertexai.imagengenerationconfig.md index b6785b9b2bb..51a66b147dc 100644 --- a/docs-devsite/vertexai.imagengenerationconfig.md +++ b/docs-devsite/vertexai.imagengenerationconfig.md @@ -27,10 +27,10 @@ export interface ImagenGenerationConfig | Property | Type | Description | | --- | --- | --- | -| [addWatermark](./vertexai.imagengenerationconfig.md#imagengenerationconfigaddwatermark) | boolean | (Public Preview) Whether to add an invisible watermark to generated images.If set to true, an invisible SynthID watermark is embedded in generated images to indicate that they are AI generated. If set to false, watermarking will be disabled.For Imagen 3 models, the default value is true; see the addWatermark documentation for more details. | +| [addWatermark](./vertexai.imagengenerationconfig.md#imagengenerationconfigaddwatermark) | boolean | (Public Preview) Whether to add an invisible watermark to generated images.If set to true, an invisible SynthID watermark is embedded in generated images to indicate that they are AI generated. If set to false, watermarking will be disabled.For Imagen 3 models, the default value is true; see the addWatermark documentation for more details.When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this will default to true, and cannot be turned off. | | [aspectRatio](./vertexai.imagengenerationconfig.md#imagengenerationconfigaspectratio) | [ImagenAspectRatio](./vertexai.md#imagenaspectratio) | (Public Preview) The aspect ratio of the generated images. The default value is square 1:1. Supported aspect ratios depend on the Imagen model, see [ImagenAspectRatio](./vertexai.md#imagenaspectratio) for more details. | | [imageFormat](./vertexai.imagengenerationconfig.md#imagengenerationconfigimageformat) | [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) | (Public Preview) The image format of the generated images. The default is PNG.See [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for more details. | -| [negativePrompt](./vertexai.imagengenerationconfig.md#imagengenerationconfignegativeprompt) | string | (Public Preview) A description of what should be omitted from the generated images.Support for negative prompts depends on the Imagen model.See the [documentation](http://firebase.google.com/docs/vertex-ai/model-parameters#imagen) for more details. | +| [negativePrompt](./vertexai.imagengenerationconfig.md#imagengenerationconfignegativeprompt) | string | (Public Preview) A description of what should be omitted from the generated images.Support for negative prompts depends on the Imagen model.See the [documentation](http://firebase.google.com/docs/vertex-ai/model-parameters#imagen) for more details.This is no longer supported in the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)) in versions greater than imagen-3.0-generate-002. | | [numberOfImages](./vertexai.imagengenerationconfig.md#imagengenerationconfignumberofimages) | number | (Public Preview) The number of images to generate. The default value is 1.The number of sample images that may be generated in each request depends on the model (typically up to 4); see the sampleCount documentation for more details. | ## ImagenGenerationConfig.addWatermark @@ -44,6 +44,8 @@ If set to `true`, an invisible SynthID watermark is embedded in generate For Imagen 3 models, the default value is `true`; see the addWatermark documentation for more details. +When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this will default to true, and cannot be turned off. + Signature: ```typescript @@ -89,6 +91,8 @@ Support for negative prompts depends on the Imagen model. See the [documentation](http://firebase.google.com/docs/vertex-ai/model-parameters#imagen) for more details. +This is no longer supported in the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)) in versions greater than `imagen-3.0-generate-002`. + Signature: ```typescript diff --git a/docs-devsite/vertexai.imagenmodel.md b/docs-devsite/vertexai.imagenmodel.md index ed40dc8f578..e69c49b8572 100644 --- a/docs-devsite/vertexai.imagenmodel.md +++ b/docs-devsite/vertexai.imagenmodel.md @@ -20,15 +20,15 @@ This class provides methods for generating images using the Imagen model. Signature: ```typescript -export declare class ImagenModel extends VertexAIModel +export declare class ImagenModel extends AIModel ``` -Extends: [VertexAIModel](./vertexai.vertexaimodel.md#vertexaimodel_class) +Extends: [AIModel](./vertexai.aimodel.md#aimodel_class) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(vertexAI, modelParams, requestOptions)](./vertexai.imagenmodel.md#imagenmodelconstructor) | | (Public Preview) Constructs a new instance of the [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class. | +| [(constructor)(ai, modelParams, requestOptions)](./vertexai.imagenmodel.md#imagenmodelconstructor) | | (Public Preview) Constructs a new instance of the [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class. | ## Properties @@ -54,14 +54,14 @@ Constructs a new instance of the [ImagenModel](./vertexai.imagenmodel.md#imagenm Signature: ```typescript -constructor(vertexAI: VertexAI, modelParams: ImagenModelParams, requestOptions?: RequestOptions | undefined); +constructor(ai: AI, modelParams: ImagenModelParams, requestOptions?: RequestOptions | undefined); ``` #### Parameters | Parameter | Type | Description | | --- | --- | --- | -| vertexAI | [VertexAI](./vertexai.vertexai.md#vertexai_interface) | An instance of the Vertex AI in Firebase SDK. | +| ai | [AI](./vertexai.ai.md#ai_interface) | an [AI](./vertexai.ai.md#ai_interface) instance. | | modelParams | [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface) | Parameters to use when making requests to Imagen. | | requestOptions | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) \| undefined | Additional options to use when making requests. | @@ -142,7 +142,7 @@ If the request to generate images fails. This happens if the prompt is blocked. ```javascript const imagen = new ImagenModel( - vertexAI, + ai, { model: 'imagen-3.0-generate-002' } diff --git a/docs-devsite/vertexai.md b/docs-devsite/vertexai.md index 47d45a492ec..7f56e0b373c 100644 --- a/docs-devsite/vertexai.md +++ b/docs-devsite/vertexai.md @@ -10,26 +10,31 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # vertexai package -The Vertex AI in Firebase Web SDK. +The Firebase AI Web SDK. ## Functions | Function | Description | | --- | --- | | function(app, ...) | -| [getVertexAI(app, options)](./vertexai.md#getvertexai_04094cf) | Returns a [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance for the given app. | -| function(vertexAI, ...) | -| [getGenerativeModel(vertexAI, modelParams, requestOptions)](./vertexai.md#getgenerativemodel_e3037c9) | Returns a [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) class with methods for inference and other functionality. | -| [getImagenModel(vertexAI, modelParams, requestOptions)](./vertexai.md#getimagenmodel_812c375) | (Public Preview) Returns an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class with methods for using Imagen.Only Imagen 3 models (named imagen-3.0-*) are supported. | +| [getAI(app, options)](./vertexai.md#getai_a94a413) | Returns the default [AI](./vertexai.ai.md#ai_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. | +| [getVertexAI(app, options)](./vertexai.md#getvertexai_04094cf) | It is recommended to use the new [getAI()](./vertexai.md#getai_a94a413).Returns a [VertexAI](./vertexai.md#vertexai) instance for the given app, configured to use the Vertex AI Gemini API. This instance will be configured to use the Vertex AI Gemini API. | +| function(ai, ...) | +| [getGenerativeModel(ai, modelParams, requestOptions)](./vertexai.md#getgenerativemodel_80bd839) | Returns a [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) class with methods for inference and other functionality. | +| [getImagenModel(ai, modelParams, requestOptions)](./vertexai.md#getimagenmodel_e1f6645) | (Public Preview) Returns an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class with methods for using Imagen.Only Imagen 3 models (named imagen-3.0-*) are supported. | ## Classes | Class | Description | | --- | --- | +| [AIError](./vertexai.aierror.md#aierror_class) | Error class for the Firebase AI SDK. | +| [AIModel](./vertexai.aimodel.md#aimodel_class) | Base class for Firebase AI model APIs.Instances of this class are associated with a specific Firebase AI [Backend](./vertexai.backend.md#backend_class) and provide methods for interacting with the configured generative model. | | [ArraySchema](./vertexai.arrayschema.md#arrayschema_class) | Schema class for "array" types. The items param should refer to the type of item that can be a member of the array. | +| [Backend](./vertexai.backend.md#backend_class) | Abstract base class representing the configuration for an AI service backend. This class should not be instantiated directly. Use its subclasses; [GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class) for the Gemini Developer API (via [Google AI](https://ai.google/)), and [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class) for the Vertex AI Gemini API. | | [BooleanSchema](./vertexai.booleanschema.md#booleanschema_class) | Schema class for "boolean" types. | | [ChatSession](./vertexai.chatsession.md#chatsession_class) | ChatSession class that enables sending chat messages and stores history of sent and received messages so far. | | [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) | Class for generative model APIs. | +| [GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class) | Configuration class for the Gemini Developer API.Use this with [AIOptions](./vertexai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./vertexai.md#getai_a94a413) to specify the Gemini Developer API as the backend. | | [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) | (Public Preview) Defines the image format for images generated by Imagen.Use this class to specify the desired format (JPEG or PNG) and compression quality for images generated by Imagen. This is typically included as part of [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface). | | [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) | (Public Preview) Class for Imagen model APIs.This class provides methods for generating images using the Imagen model. | | [IntegerSchema](./vertexai.integerschema.md#integerschema_class) | Schema class for "integer" types. | @@ -37,17 +42,17 @@ The Vertex AI in Firebase Web SDK. | [ObjectSchema](./vertexai.objectschema.md#objectschema_class) | Schema class for "object" types. The properties param must be a map of Schema objects. | | [Schema](./vertexai.schema.md#schema_class) | Parent class encompassing all Schema types, with static methods that allow building specific Schema types. This class can be converted with JSON.stringify() into a JSON string accepted by Vertex AI REST endpoints. (This string conversion is automatically done when calling SDK methods.) | | [StringSchema](./vertexai.stringschema.md#stringschema_class) | Schema class for "string" types. Can be used with or without enum values. | -| [VertexAIError](./vertexai.vertexaierror.md#vertexaierror_class) | Error class for the Vertex AI in Firebase SDK. | -| [VertexAIModel](./vertexai.vertexaimodel.md#vertexaimodel_class) | Base class for Vertex AI in Firebase model APIs. | +| [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class) | Configuration class for the Vertex AI Gemini API.Use this with [AIOptions](./vertexai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./vertexai.md#getai_a94a413) to specify the Vertex AI Gemini API as the backend. | ## Enumerations | Enumeration | Description | | --- | --- | +| [AIErrorCode](./vertexai.md#aierrorcode) | Standardized error codes that [AIError](./vertexai.aierror.md#aierror_class) can have. | | [BlockReason](./vertexai.md#blockreason) | Reason that a prompt was blocked. | | [FinishReason](./vertexai.md#finishreason) | Reason that a candidate finished. | | [FunctionCallingMode](./vertexai.md#functioncallingmode) | | -| [HarmBlockMethod](./vertexai.md#harmblockmethod) | | +| [HarmBlockMethod](./vertexai.md#harmblockmethod) | This property is not supported in the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)). | | [HarmBlockThreshold](./vertexai.md#harmblockthreshold) | Threshold above which a prompt or candidate will be blocked. | | [HarmCategory](./vertexai.md#harmcategory) | Harm categories that would cause prompts or candidates to be blocked. | | [HarmProbability](./vertexai.md#harmprobability) | Probability that a prompt or candidate matches a harm category. | @@ -57,12 +62,13 @@ The Vertex AI in Firebase Web SDK. | [ImagenSafetyFilterLevel](./vertexai.md#imagensafetyfilterlevel) | (Public Preview) A filter level controlling how aggressively to filter sensitive content.Text prompts provided as inputs and images (generated or uploaded) through Imagen on Vertex AI are assessed against a list of safety filters, which include 'harmful categories' (for example, violence, sexual, derogatory, and toxic). This filter level controls how aggressively to filter out potentially harmful content from responses. See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) and the [Responsible AI and usage guidelines](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#safety-filters) for more details. | | [Modality](./vertexai.md#modality) | Content part modality. | | [SchemaType](./vertexai.md#schematype) | Contains the list of OpenAPI data types as defined by the [OpenAPI specification](https://swagger.io/docs/specification/data-models/data-types/) | -| [VertexAIErrorCode](./vertexai.md#vertexaierrorcode) | Standardized error codes that [VertexAIError](./vertexai.vertexaierror.md#vertexaierror_class) can have. | ## Interfaces | Interface | Description | | --- | --- | +| [AI](./vertexai.ai.md#ai_interface) | An instance of the Firebase AI SDK.Do not create this instance directly. Instead, use [getAI()](./vertexai.md#getai_a94a413). | +| [AIOptions](./vertexai.aioptions.md#aioptions_interface) | Options for initializing the AI service using [getAI()](./vertexai.md#getai_a94a413). This allows specifying which backend to use (Vertex AI Gemini API or Gemini Developer API) and configuring its specific options (like location for Vertex AI). | | [BaseParams](./vertexai.baseparams.md#baseparams_interface) | Base parameters for a number of methods. | | [Citation](./vertexai.citation.md#citation_interface) | A single citation. | | [CitationMetadata](./vertexai.citationmetadata.md#citationmetadata_interface) | Citation metadata that may be found on a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface). | @@ -99,10 +105,10 @@ The Vertex AI in Firebase Web SDK. | [ImagenSafetySettings](./vertexai.imagensafetysettings.md#imagensafetysettings_interface) | (Public Preview) Settings for controlling the aggressiveness of filtering out sensitive content.See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) for more details. | | [InlineDataPart](./vertexai.inlinedatapart.md#inlinedatapart_interface) | Content part interface if the part represents an image. | | [ModalityTokenCount](./vertexai.modalitytokencount.md#modalitytokencount_interface) | Represents token counting info for a single modality. | -| [ModelParams](./vertexai.modelparams.md#modelparams_interface) | Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). | +| [ModelParams](./vertexai.modelparams.md#modelparams_interface) | Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_80bd839). | | [ObjectSchemaInterface](./vertexai.objectschemainterface.md#objectschemainterface_interface) | Interface for [ObjectSchema](./vertexai.objectschema.md#objectschema_class) class. | | [PromptFeedback](./vertexai.promptfeedback.md#promptfeedback_interface) | If the prompt was blocked, this will be populated with blockReason and the relevant safetyRatings. | -| [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). | +| [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_80bd839). | | [RetrievedContextAttribution](./vertexai.retrievedcontextattribution.md#retrievedcontextattribution_interface) | | | [SafetyRating](./vertexai.safetyrating.md#safetyrating_interface) | A safety rating associated with a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface) | | [SafetySetting](./vertexai.safetysetting.md#safetysetting_interface) | Safety setting that can be sent as part of request parameters. | @@ -115,8 +121,7 @@ The Vertex AI in Firebase Web SDK. | [TextPart](./vertexai.textpart.md#textpart_interface) | Content part interface if the part represents a text string. | | [ToolConfig](./vertexai.toolconfig.md#toolconfig_interface) | Tool config. This config is shared for all tools provided in the request. | | [UsageMetadata](./vertexai.usagemetadata.md#usagemetadata_interface) | Usage metadata about a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). | -| [VertexAI](./vertexai.vertexai.md#vertexai_interface) | An instance of the Vertex AI in Firebase SDK. | -| [VertexAIOptions](./vertexai.vertexaioptions.md#vertexaioptions_interface) | Options when initializing the Vertex AI in Firebase SDK. | +| [VertexAIOptions](./vertexai.vertexaioptions.md#vertexaioptions_interface) | Options when initializing the Firebase AI SDK. | | [VideoMetadata](./vertexai.videometadata.md#videometadata_interface) | Describes the input video content. | | [WebAttribution](./vertexai.webattribution.md#webattribution_interface) | | @@ -124,24 +129,80 @@ The Vertex AI in Firebase Web SDK. | Variable | Description | | --- | --- | +| [BackendType](./vertexai.md#backendtype) | An enum-like object containing constants that represent the supported backends for the Firebase AI SDK. This determines which backend service (Vertex AI Gemini API or Gemini Developer API) the SDK will communicate with.These values are assigned to the backendType property within the specific backend configuration objects ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class) or [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)) to identify which service to target. | | [POSSIBLE\_ROLES](./vertexai.md#possible_roles) | Possible roles. | | [ResponseModality](./vertexai.md#responsemodality) | (Public Preview) Generation modalities to be returned in generation responses. | +| [VertexAIError](./vertexai.md#vertexaierror) | Error class for the Firebase AI SDK.For more information, refer to the documentation for the new [AIError](./vertexai.aierror.md#aierror_class). | +| [VertexAIModel](./vertexai.md#vertexaimodel) | Base class for Firebase AI model APIs.For more information, refer to the documentation for the new [AIModel](./vertexai.aimodel.md#aimodel_class). | ## Type Aliases | Type Alias | Description | | --- | --- | +| [BackendType](./vertexai.md#backendtype) | Type alias representing valid backend types. It can be either 'VERTEX_AI' or 'GOOGLE_AI'. | | [Part](./vertexai.md#part) | Content part - includes text, image/video, or function call/response part types. | | [ResponseModality](./vertexai.md#responsemodality) | (Public Preview) Generation modalities to be returned in generation responses. | | [Role](./vertexai.md#role) | Role is the producer of the content. | | [Tool](./vertexai.md#tool) | Defines a tool that model can call to access external knowledge. | | [TypedSchema](./vertexai.md#typedschema) | A type that includes all specific Schema types. | +| [VertexAI](./vertexai.md#vertexai) | An instance of the Firebase AI SDK.For more information, refer to the documentation for the new [AI](./vertexai.ai.md#ai_interface) interface. | ## function(app, ...) +### getAI(app, options) {:#getai_a94a413} + +Returns the default [AI](./vertexai.ai.md#ai_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. + +Signature: + +```typescript +export declare function getAI(app?: FirebaseApp, options?: AIOptions): AI; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| app | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) to use. | +| options | [AIOptions](./vertexai.aioptions.md#aioptions_interface) | [AIOptions](./vertexai.aioptions.md#aioptions_interface) that configure the AI instance. | + +Returns: + +[AI](./vertexai.ai.md#ai_interface) + +The default [AI](./vertexai.ai.md#ai_interface) instance for the given [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). + +### Example 1 + + +```javascript +const ai = getAI(app); + +``` + +### Example 2 + + +```javascript +// Get an AI instance configured to use the Gemini Developer API (via Google AI). +const ai = getAI(app, { backend: new GoogleAIBackend() }); + +``` + +### Example 3 + + +```javascript +// Get an AI instance configured to use the Vertex AI Gemini API. +const ai = getAI(app, { backend: new VertexAIBackend() }); + +``` + ### getVertexAI(app, options) {:#getvertexai_04094cf} -Returns a [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance for the given app. +It is recommended to use the new [getAI()](./vertexai.md#getai_a94a413). + +Returns a [VertexAI](./vertexai.md#vertexai) instance for the given app, configured to use the Vertex AI Gemini API. This instance will be configured to use the Vertex AI Gemini API. Signature: @@ -154,29 +215,29 @@ export declare function getVertexAI(app?: FirebaseApp, options?: VertexAIOptions | Parameter | Type | Description | | --- | --- | --- | | app | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) to use. | -| options | [VertexAIOptions](./vertexai.vertexaioptions.md#vertexaioptions_interface) | | +| options | [VertexAIOptions](./vertexai.vertexaioptions.md#vertexaioptions_interface) | Options to configure the Vertex AI instance, including the location. | Returns: -[VertexAI](./vertexai.vertexai.md#vertexai_interface) +[VertexAI](./vertexai.md#vertexai) -## function(vertexAI, ...) +## function(ai, ...) -### getGenerativeModel(vertexAI, modelParams, requestOptions) {:#getgenerativemodel_e3037c9} +### getGenerativeModel(ai, modelParams, requestOptions) {:#getgenerativemodel_80bd839} Returns a [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) class with methods for inference and other functionality. Signature: ```typescript -export declare function getGenerativeModel(vertexAI: VertexAI, modelParams: ModelParams, requestOptions?: RequestOptions): GenerativeModel; +export declare function getGenerativeModel(ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions): GenerativeModel; ``` #### Parameters | Parameter | Type | Description | | --- | --- | --- | -| vertexAI | [VertexAI](./vertexai.vertexai.md#vertexai_interface) | | +| ai | [AI](./vertexai.ai.md#ai_interface) | | | modelParams | [ModelParams](./vertexai.modelparams.md#modelparams_interface) | | | requestOptions | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | | @@ -184,7 +245,7 @@ export declare function getGenerativeModel(vertexAI: VertexAI, modelParams: Mode [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) -### getImagenModel(vertexAI, modelParams, requestOptions) {:#getimagenmodel_812c375} +### getImagenModel(ai, modelParams, requestOptions) {:#getimagenmodel_e1f6645} > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > @@ -196,14 +257,14 @@ Only Imagen 3 models (named `imagen-3.0-*`) are supported. Signature: ```typescript -export declare function getImagenModel(vertexAI: VertexAI, modelParams: ImagenModelParams, requestOptions?: RequestOptions): ImagenModel; +export declare function getImagenModel(ai: AI, modelParams: ImagenModelParams, requestOptions?: RequestOptions): ImagenModel; ``` #### Parameters | Parameter | Type | Description | | --- | --- | --- | -| vertexAI | [VertexAI](./vertexai.vertexai.md#vertexai_interface) | An instance of the Vertex AI in Firebase SDK. | +| ai | [AI](./vertexai.ai.md#ai_interface) | An [AI](./vertexai.ai.md#ai_interface) instance. | | modelParams | [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface) | Parameters to use when making Imagen requests. | | requestOptions | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | Additional options to use when making requests. | @@ -215,6 +276,21 @@ export declare function getImagenModel(vertexAI: VertexAI, modelParams: ImagenMo If the `apiKey` or `projectId` fields are missing in your Firebase config. +## BackendType + +An enum-like object containing constants that represent the supported backends for the Firebase AI SDK. This determines which backend service (Vertex AI Gemini API or Gemini Developer API) the SDK will communicate with. + +These values are assigned to the `backendType` property within the specific backend configuration objects ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class) or [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)) to identify which service to target. + +Signature: + +```typescript +BackendType: { + readonly VERTEX_AI: "VERTEX_AI"; + readonly GOOGLE_AI: "GOOGLE_AI"; +} +``` + ## POSSIBLE\_ROLES Possible roles. @@ -241,6 +317,40 @@ ResponseModality: { } ``` +## VertexAIError + +Error class for the Firebase AI SDK. + +For more information, refer to the documentation for the new [AIError](./vertexai.aierror.md#aierror_class). + +Signature: + +```typescript +VertexAIError: typeof AIError +``` + +## VertexAIModel + +Base class for Firebase AI model APIs. + +For more information, refer to the documentation for the new [AIModel](./vertexai.aimodel.md#aimodel_class). + +Signature: + +```typescript +VertexAIModel: typeof AIModel +``` + +## BackendType + +Type alias representing valid backend types. It can be either `'VERTEX_AI'` or `'GOOGLE_AI'`. + +Signature: + +```typescript +export type BackendType = (typeof BackendType)[keyof typeof BackendType]; +``` + ## Part Content part - includes text, image/video, or function call/response part types. @@ -294,6 +404,46 @@ A type that includes all specific Schema types. export type TypedSchema = IntegerSchema | NumberSchema | StringSchema | BooleanSchema | ObjectSchema | ArraySchema; ``` +## VertexAI + +An instance of the Firebase AI SDK. + +For more information, refer to the documentation for the new [AI](./vertexai.ai.md#ai_interface) interface. + +Signature: + +```typescript +export type VertexAI = AI; +``` + +## AIErrorCode + +Standardized error codes that [AIError](./vertexai.aierror.md#aierror_class) can have. + +Signature: + +```typescript +export declare const enum AIErrorCode +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| API\_NOT\_ENABLED | "api-not-enabled" | An error due to the Firebase API not being enabled in the Console. | +| ERROR | "error" | A generic error occurred. | +| FETCH\_ERROR | "fetch-error" | An error occurred while performing a fetch. | +| INVALID\_CONTENT | "invalid-content" | An error associated with a Content object. | +| INVALID\_SCHEMA | "invalid-schema" | An error due to invalid Schema input. | +| NO\_API\_KEY | "no-api-key" | An error occurred due to a missing Firebase API key. | +| NO\_APP\_ID | "no-app-id" | An error occured due to a missing Firebase app ID. | +| NO\_MODEL | "no-model" | An error occurred due to a model name not being specified during initialization. | +| NO\_PROJECT\_ID | "no-project-id" | An error occurred due to a missing project ID. | +| PARSE\_FAILED | "parse-failed" | An error occurred while parsing. | +| REQUEST\_ERROR | "request-error" | An error occurred in a request. | +| RESPONSE\_ERROR | "response-error" | An error occurred in a response. | +| UNSUPPORTED | "unsupported" | An error occured due an attempt to use an unsupported feature. | + ## BlockReason Reason that a prompt was blocked. @@ -356,6 +506,7 @@ export declare enum FunctionCallingMode ## HarmBlockMethod +This property is not supported in the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)). Signature: @@ -445,6 +596,7 @@ export declare enum HarmSeverity | HARM\_SEVERITY\_LOW | "HARM_SEVERITY_LOW" | Low level of harm severity. | | HARM\_SEVERITY\_MEDIUM | "HARM_SEVERITY_MEDIUM" | Medium level of harm severity. | | HARM\_SEVERITY\_NEGLIGIBLE | "HARM_SEVERITY_NEGLIGIBLE" | Negligible level of harm severity. | +| HARM\_SEVERITY\_UNSUPPORTED | "HARM_SEVERITY_UNSUPPORTED" | Harm severity is not supported. | ## ImagenAspectRatio @@ -562,30 +714,3 @@ export declare enum SchemaType | OBJECT | "object" | Object type. | | STRING | "string" | String type. | -## VertexAIErrorCode - -Standardized error codes that [VertexAIError](./vertexai.vertexaierror.md#vertexaierror_class) can have. - -Signature: - -```typescript -export declare const enum VertexAIErrorCode -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| API\_NOT\_ENABLED | "api-not-enabled" | An error due to the Firebase API not being enabled in the Console. | -| ERROR | "error" | A generic error occurred. | -| FETCH\_ERROR | "fetch-error" | An error occurred while performing a fetch. | -| INVALID\_CONTENT | "invalid-content" | An error associated with a Content object. | -| INVALID\_SCHEMA | "invalid-schema" | An error due to invalid Schema input. | -| NO\_API\_KEY | "no-api-key" | An error occurred due to a missing Firebase API key. | -| NO\_APP\_ID | "no-app-id" | An error occured due to a missing Firebase app ID. | -| NO\_MODEL | "no-model" | An error occurred due to a model name not being specified during initialization. | -| NO\_PROJECT\_ID | "no-project-id" | An error occurred due to a missing project ID. | -| PARSE\_FAILED | "parse-failed" | An error occurred while parsing. | -| REQUEST\_ERROR | "request-error" | An error occurred in a request. | -| RESPONSE\_ERROR | "response-error" | An error occurred in a response. | - diff --git a/docs-devsite/vertexai.modelparams.md b/docs-devsite/vertexai.modelparams.md index d3963d240eb..bb8a87d5fb2 100644 --- a/docs-devsite/vertexai.modelparams.md +++ b/docs-devsite/vertexai.modelparams.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # ModelParams interface -Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). +Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_80bd839). Signature: diff --git a/docs-devsite/vertexai.promptfeedback.md b/docs-devsite/vertexai.promptfeedback.md index 369ef02051d..08ea4aaf4cf 100644 --- a/docs-devsite/vertexai.promptfeedback.md +++ b/docs-devsite/vertexai.promptfeedback.md @@ -23,7 +23,7 @@ export interface PromptFeedback | Property | Type | Description | | --- | --- | --- | | [blockReason](./vertexai.promptfeedback.md#promptfeedbackblockreason) | [BlockReason](./vertexai.md#blockreason) | | -| [blockReasonMessage](./vertexai.promptfeedback.md#promptfeedbackblockreasonmessage) | string | | +| [blockReasonMessage](./vertexai.promptfeedback.md#promptfeedbackblockreasonmessage) | string | A human-readable description of the blockReason.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). | | [safetyRatings](./vertexai.promptfeedback.md#promptfeedbacksafetyratings) | [SafetyRating](./vertexai.safetyrating.md#safetyrating_interface)\[\] | | ## PromptFeedback.blockReason @@ -36,6 +36,10 @@ blockReason?: BlockReason; ## PromptFeedback.blockReasonMessage +A human-readable description of the `blockReason`. + +This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). + Signature: ```typescript diff --git a/docs-devsite/vertexai.requestoptions.md b/docs-devsite/vertexai.requestoptions.md index dcd0c552ecb..3c233d72b90 100644 --- a/docs-devsite/vertexai.requestoptions.md +++ b/docs-devsite/vertexai.requestoptions.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # RequestOptions interface -Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_e3037c9). +Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_80bd839). Signature: diff --git a/docs-devsite/vertexai.safetyrating.md b/docs-devsite/vertexai.safetyrating.md index 28493bafef0..ebe5003c662 100644 --- a/docs-devsite/vertexai.safetyrating.md +++ b/docs-devsite/vertexai.safetyrating.md @@ -25,9 +25,9 @@ export interface SafetyRating | [blocked](./vertexai.safetyrating.md#safetyratingblocked) | boolean | | | [category](./vertexai.safetyrating.md#safetyratingcategory) | [HarmCategory](./vertexai.md#harmcategory) | | | [probability](./vertexai.safetyrating.md#safetyratingprobability) | [HarmProbability](./vertexai.md#harmprobability) | | -| [probabilityScore](./vertexai.safetyrating.md#safetyratingprobabilityscore) | number | | -| [severity](./vertexai.safetyrating.md#safetyratingseverity) | [HarmSeverity](./vertexai.md#harmseverity) | | -| [severityScore](./vertexai.safetyrating.md#safetyratingseverityscore) | number | | +| [probabilityScore](./vertexai.safetyrating.md#safetyratingprobabilityscore) | number | The probability score of the harm category.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. | +| [severity](./vertexai.safetyrating.md#safetyratingseverity) | [HarmSeverity](./vertexai.md#harmseverity) | The harm severity level.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to HarmSeverity.UNSUPPORTED. | +| [severityScore](./vertexai.safetyrating.md#safetyratingseverityscore) | number | The severity score of the harm category.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. | ## SafetyRating.blocked @@ -55,6 +55,10 @@ probability: HarmProbability; ## SafetyRating.probabilityScore +The probability score of the harm category. + +This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. + Signature: ```typescript @@ -63,6 +67,10 @@ probabilityScore: number; ## SafetyRating.severity +The harm severity level. + +This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to `HarmSeverity.UNSUPPORTED`. + Signature: ```typescript @@ -71,6 +79,10 @@ severity: HarmSeverity; ## SafetyRating.severityScore +The severity score of the harm category. + +This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. + Signature: ```typescript diff --git a/docs-devsite/vertexai.safetysetting.md b/docs-devsite/vertexai.safetysetting.md index 17fa1cff839..a91843faaa5 100644 --- a/docs-devsite/vertexai.safetysetting.md +++ b/docs-devsite/vertexai.safetysetting.md @@ -23,7 +23,7 @@ export interface SafetySetting | Property | Type | Description | | --- | --- | --- | | [category](./vertexai.safetysetting.md#safetysettingcategory) | [HarmCategory](./vertexai.md#harmcategory) | | -| [method](./vertexai.safetysetting.md#safetysettingmethod) | [HarmBlockMethod](./vertexai.md#harmblockmethod) | | +| [method](./vertexai.safetysetting.md#safetysettingmethod) | [HarmBlockMethod](./vertexai.md#harmblockmethod) | The harm block method.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), an [AIError](./vertexai.aierror.md#aierror_class) will be thrown if this property is defined. | | [threshold](./vertexai.safetysetting.md#safetysettingthreshold) | [HarmBlockThreshold](./vertexai.md#harmblockthreshold) | | ## SafetySetting.category @@ -36,6 +36,10 @@ category: HarmCategory; ## SafetySetting.method +The harm block method. + +This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), an [AIError](./vertexai.aierror.md#aierror_class) will be thrown if this property is defined. + Signature: ```typescript diff --git a/docs-devsite/vertexai.schemashared.md b/docs-devsite/vertexai.schemashared.md index 0764a53bdc0..4fdf8941438 100644 --- a/docs-devsite/vertexai.schemashared.md +++ b/docs-devsite/vertexai.schemashared.md @@ -25,7 +25,7 @@ export interface SchemaShared | [description](./vertexai.schemashared.md#schemashareddescription) | string | Optional. The description of the property. | | [enum](./vertexai.schemashared.md#schemasharedenum) | string\[\] | Optional. The enum of the property. | | [example](./vertexai.schemashared.md#schemasharedexample) | unknown | Optional. The example of the property. | -| [format](./vertexai.schemashared.md#schemasharedformat) | string | Optional. The format of the property. | +| [format](./vertexai.schemashared.md#schemasharedformat) | string | Optional. The format of the property. When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this must be either 'enum' or 'date-time', otherwise requests will fail. | | [items](./vertexai.schemashared.md#schemashareditems) | T | Optional. The items of the property. | | [nullable](./vertexai.schemashared.md#schemasharednullable) | boolean | Optional. Whether the property is nullable. | | [properties](./vertexai.schemashared.md#schemasharedproperties) | { \[k: string\]: T; } | Optional. Map of Schema objects. | @@ -62,7 +62,7 @@ example?: unknown; ## SchemaShared.format -Optional. The format of the property. +Optional. The format of the property. When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this must be either `'enum'` or `'date-time'`, otherwise requests will fail. Signature: diff --git a/docs-devsite/vertexai.vertexai.md b/docs-devsite/vertexai.vertexai.md deleted file mode 100644 index d30d0f7113e..00000000000 --- a/docs-devsite/vertexai.vertexai.md +++ /dev/null @@ -1,44 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# VertexAI interface -An instance of the Vertex AI in Firebase SDK. - -Signature: - -```typescript -export interface VertexAI -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [app](./vertexai.vertexai.md#vertexaiapp) | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance is associated with. | -| [location](./vertexai.vertexai.md#vertexailocation) | string | | - -## VertexAI.app - -The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [VertexAI](./vertexai.vertexai.md#vertexai_interface) instance is associated with. - -Signature: - -```typescript -app: FirebaseApp; -``` - -## VertexAI.location - -Signature: - -```typescript -location: string; -``` diff --git a/docs-devsite/vertexai.vertexaibackend.md b/docs-devsite/vertexai.vertexaibackend.md new file mode 100644 index 00000000000..ba82c775ca8 --- /dev/null +++ b/docs-devsite/vertexai.vertexaibackend.md @@ -0,0 +1,60 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# VertexAIBackend class +Configuration class for the Vertex AI Gemini API. + +Use this with [AIOptions](./vertexai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./vertexai.md#getai_a94a413) to specify the Vertex AI Gemini API as the backend. + +Signature: + +```typescript +export declare class VertexAIBackend extends Backend +``` +Extends: [Backend](./vertexai.backend.md#backend_class) + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(location)](./vertexai.vertexaibackend.md#vertexaibackendconstructor) | | Creates a configuration object for the Vertex AI backend. | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [location](./vertexai.vertexaibackend.md#vertexaibackendlocation) | | string | The region identifier. See [Vertex AI locations](https://firebase.google.com/docs/vertex-ai/locations#available-locations) for a list of supported locations. | + +## VertexAIBackend.(constructor) + +Creates a configuration object for the Vertex AI backend. + +Signature: + +```typescript +constructor(location?: string); +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| location | string | The region identifier, defaulting to us-central1; see [Vertex AI locations](https://firebase.google.com/docs/vertex-ai/locations#available-locations) for a list of supported locations. | + +## VertexAIBackend.location + +The region identifier. See [Vertex AI locations](https://firebase.google.com/docs/vertex-ai/locations#available-locations) for a list of supported locations. + +Signature: + +```typescript +readonly location: string; +``` diff --git a/docs-devsite/vertexai.vertexaimodel.md b/docs-devsite/vertexai.vertexaimodel.md deleted file mode 100644 index 5c3244fe1e5..00000000000 --- a/docs-devsite/vertexai.vertexaimodel.md +++ /dev/null @@ -1,66 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# VertexAIModel class -Base class for Vertex AI in Firebase model APIs. - -The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `VertexAIModel` class. - -Signature: - -```typescript -export declare abstract class VertexAIModel -``` - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [model](./vertexai.vertexaimodel.md#vertexaimodelmodel) | | string | The fully qualified model resource name to use for generating images (for example, publishers/google/models/imagen-3.0-generate-002). | - -## Methods - -| Method | Modifiers | Description | -| --- | --- | --- | -| [normalizeModelName(modelName)](./vertexai.vertexaimodel.md#vertexaimodelnormalizemodelname) | static | Normalizes the given model name to a fully qualified model resource name. | - -## VertexAIModel.model - -The fully qualified model resource name to use for generating images (for example, `publishers/google/models/imagen-3.0-generate-002`). - -Signature: - -```typescript -readonly model: string; -``` - -## VertexAIModel.normalizeModelName() - -Normalizes the given model name to a fully qualified model resource name. - -Signature: - -```typescript -static normalizeModelName(modelName: string): string; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| modelName | string | The model name to normalize. | - -Returns: - -string - -The fully qualified model resource name. - diff --git a/docs-devsite/vertexai.vertexaioptions.md b/docs-devsite/vertexai.vertexaioptions.md index e15b525bfed..776dfd29374 100644 --- a/docs-devsite/vertexai.vertexaioptions.md +++ b/docs-devsite/vertexai.vertexaioptions.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # VertexAIOptions interface -Options when initializing the Vertex AI in Firebase SDK. +Options when initializing the Firebase AI SDK. Signature: diff --git a/packages/firebase/ai/index.ts b/packages/firebase/ai/index.ts new file mode 100644 index 00000000000..2645fd3004f --- /dev/null +++ b/packages/firebase/ai/index.ts @@ -0,0 +1,18 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from '@firebase/vertexai'; diff --git a/packages/firebase/ai/package.json b/packages/firebase/ai/package.json new file mode 100644 index 00000000000..75405002478 --- /dev/null +++ b/packages/firebase/ai/package.json @@ -0,0 +1,7 @@ +{ + "name": "firebase/ai", + "main": "dist/index.cjs.js", + "browser": "dist/esm/index.esm.js", + "module": "dist/esm/index.esm.js", + "typings": "dist/vertexai/index.d.ts" +} \ No newline at end of file diff --git a/packages/firebase/package.json b/packages/firebase/package.json index f4fe3bb2d50..c61bc701426 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -227,6 +227,18 @@ }, "default": "./storage/dist/esm/index.esm.js" }, + "./ai": { + "types": "./vertexai/dist/vertexai/index.d.ts", + "node": { + "require": "./vertexai/dist/index.cjs.js", + "import": "./vertexai/dist/index.mjs" + }, + "browser": { + "require": "./vertexai/dist/index.cjs.js", + "import": "./vertexai/dist/esm/index.esm.js" + }, + "default": "./vertexai/dist/esm/index.esm.js" + }, "./vertexai": { "types": "./vertexai/dist/vertexai/index.d.ts", "node": { @@ -443,6 +455,7 @@ "rollup-plugin-license": "3.5.3" }, "components": [ + "ai", "analytics", "app", "app-check", diff --git a/packages/vertexai/src/api.test.ts b/packages/vertexai/src/api.test.ts index 4a0b978d858..27237b4edd3 100644 --- a/packages/vertexai/src/api.test.ts +++ b/packages/vertexai/src/api.test.ts @@ -14,14 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ImagenModelParams, ModelParams, VertexAIErrorCode } from './types'; -import { VertexAIError } from './errors'; +import { ImagenModelParams, ModelParams, AIErrorCode } from './types'; +import { AIError } from './errors'; import { ImagenModel, getGenerativeModel, getImagenModel } from './api'; import { expect } from 'chai'; -import { VertexAI } from './public-types'; +import { AI } from './public-types'; import { GenerativeModel } from './models/generative-model'; +import { VertexAIBackend } from './backend'; +import { AI_TYPE } from './constants'; -const fakeVertexAI: VertexAI = { +const fakeAI: AI = { app: { name: 'DEFAULT', automaticDataCollectionEnabled: true, @@ -31,139 +33,136 @@ const fakeVertexAI: VertexAI = { appId: 'my-appid' } }, + backend: new VertexAIBackend('us-central1'), location: 'us-central1' }; describe('Top level API', () => { it('getGenerativeModel throws if no model is provided', () => { try { - getGenerativeModel(fakeVertexAI, {} as ModelParams); + getGenerativeModel(fakeAI, {} as ModelParams); } catch (e) { - expect((e as VertexAIError).code).includes(VertexAIErrorCode.NO_MODEL); - expect((e as VertexAIError).message).includes( - `VertexAI: Must provide a model name. Example: ` + - `getGenerativeModel({ model: 'my-model-name' }) (vertexAI/${VertexAIErrorCode.NO_MODEL})` + expect((e as AIError).code).includes(AIErrorCode.NO_MODEL); + expect((e as AIError).message).includes( + `AI: Must provide a model name. Example: ` + + `getGenerativeModel({ model: 'my-model-name' }) (${AI_TYPE}/${AIErrorCode.NO_MODEL})` ); } }); it('getGenerativeModel throws if no apiKey is provided', () => { const fakeVertexNoApiKey = { - ...fakeVertexAI, + ...fakeAI, app: { options: { projectId: 'my-project', appId: 'my-appid' } } - } as VertexAI; + } as AI; try { getGenerativeModel(fakeVertexNoApiKey, { model: 'my-model' }); } catch (e) { - expect((e as VertexAIError).code).includes(VertexAIErrorCode.NO_API_KEY); - expect((e as VertexAIError).message).equals( - `VertexAI: The "apiKey" field is empty in the local ` + - `Firebase config. Firebase VertexAI requires this field to` + - ` contain a valid API key. (vertexAI/${VertexAIErrorCode.NO_API_KEY})` + expect((e as AIError).code).includes(AIErrorCode.NO_API_KEY); + expect((e as AIError).message).equals( + `AI: The "apiKey" field is empty in the local ` + + `Firebase config. Firebase AI requires this field to` + + ` contain a valid API key. (${AI_TYPE}/${AIErrorCode.NO_API_KEY})` ); } }); it('getGenerativeModel throws if no projectId is provided', () => { const fakeVertexNoProject = { - ...fakeVertexAI, + ...fakeAI, app: { options: { apiKey: 'my-key', appId: 'my-appid' } } - } as VertexAI; + } as AI; try { getGenerativeModel(fakeVertexNoProject, { model: 'my-model' }); } catch (e) { - expect((e as VertexAIError).code).includes( - VertexAIErrorCode.NO_PROJECT_ID - ); - expect((e as VertexAIError).message).equals( - `VertexAI: The "projectId" field is empty in the local` + - ` Firebase config. Firebase VertexAI requires this field ` + - `to contain a valid project ID. (vertexAI/${VertexAIErrorCode.NO_PROJECT_ID})` + expect((e as AIError).code).includes(AIErrorCode.NO_PROJECT_ID); + expect((e as AIError).message).equals( + `AI: The "projectId" field is empty in the local` + + ` Firebase config. Firebase AI requires this field ` + + `to contain a valid project ID. (${AI_TYPE}/${AIErrorCode.NO_PROJECT_ID})` ); } }); it('getGenerativeModel throws if no appId is provided', () => { const fakeVertexNoProject = { - ...fakeVertexAI, + ...fakeAI, app: { options: { apiKey: 'my-key', projectId: 'my-projectid' } } - } as VertexAI; + } as AI; try { getGenerativeModel(fakeVertexNoProject, { model: 'my-model' }); } catch (e) { - expect((e as VertexAIError).code).includes(VertexAIErrorCode.NO_APP_ID); - expect((e as VertexAIError).message).equals( - `VertexAI: The "appId" field is empty in the local` + - ` Firebase config. Firebase VertexAI requires this field ` + - `to contain a valid app ID. (vertexAI/${VertexAIErrorCode.NO_APP_ID})` + expect((e as AIError).code).includes(AIErrorCode.NO_APP_ID); + expect((e as AIError).message).equals( + `AI: The "appId" field is empty in the local` + + ` Firebase config. Firebase AI requires this field ` + + `to contain a valid app ID. (${AI_TYPE}/${AIErrorCode.NO_APP_ID})` ); } }); it('getGenerativeModel gets a GenerativeModel', () => { - const genModel = getGenerativeModel(fakeVertexAI, { model: 'my-model' }); + const genModel = getGenerativeModel(fakeAI, { model: 'my-model' }); expect(genModel).to.be.an.instanceOf(GenerativeModel); expect(genModel.model).to.equal('publishers/google/models/my-model'); }); it('getImagenModel throws if no model is provided', () => { try { - getImagenModel(fakeVertexAI, {} as ImagenModelParams); + getImagenModel(fakeAI, {} as ImagenModelParams); } catch (e) { - expect((e as VertexAIError).code).includes(VertexAIErrorCode.NO_MODEL); - expect((e as VertexAIError).message).includes( - `VertexAI: Must provide a model name. Example: ` + - `getImagenModel({ model: 'my-model-name' }) (vertexAI/${VertexAIErrorCode.NO_MODEL})` + expect((e as AIError).code).includes(AIErrorCode.NO_MODEL); + expect((e as AIError).message).includes( + `AI: Must provide a model name. Example: ` + + `getImagenModel({ model: 'my-model-name' }) (${AI_TYPE}/${AIErrorCode.NO_MODEL})` ); } }); it('getImagenModel throws if no apiKey is provided', () => { const fakeVertexNoApiKey = { - ...fakeVertexAI, + ...fakeAI, app: { options: { projectId: 'my-project', appId: 'my-appid' } } - } as VertexAI; + } as AI; try { getImagenModel(fakeVertexNoApiKey, { model: 'my-model' }); } catch (e) { - expect((e as VertexAIError).code).includes(VertexAIErrorCode.NO_API_KEY); - expect((e as VertexAIError).message).equals( - `VertexAI: The "apiKey" field is empty in the local ` + - `Firebase config. Firebase VertexAI requires this field to` + - ` contain a valid API key. (vertexAI/${VertexAIErrorCode.NO_API_KEY})` + expect((e as AIError).code).includes(AIErrorCode.NO_API_KEY); + expect((e as AIError).message).equals( + `AI: The "apiKey" field is empty in the local ` + + `Firebase config. Firebase AI requires this field to` + + ` contain a valid API key. (${AI_TYPE}/${AIErrorCode.NO_API_KEY})` ); } }); it('getImagenModel throws if no projectId is provided', () => { const fakeVertexNoProject = { - ...fakeVertexAI, + ...fakeAI, app: { options: { apiKey: 'my-key', appId: 'my-appid' } } - } as VertexAI; + } as AI; try { getImagenModel(fakeVertexNoProject, { model: 'my-model' }); } catch (e) { - expect((e as VertexAIError).code).includes( - VertexAIErrorCode.NO_PROJECT_ID - ); - expect((e as VertexAIError).message).equals( - `VertexAI: The "projectId" field is empty in the local` + - ` Firebase config. Firebase VertexAI requires this field ` + - `to contain a valid project ID. (vertexAI/${VertexAIErrorCode.NO_PROJECT_ID})` + expect((e as AIError).code).includes(AIErrorCode.NO_PROJECT_ID); + expect((e as AIError).message).equals( + `AI: The "projectId" field is empty in the local` + + ` Firebase config. Firebase AI requires this field ` + + `to contain a valid project ID. (${AI_TYPE}/${AIErrorCode.NO_PROJECT_ID})` ); } }); it('getImagenModel throws if no appId is provided', () => { const fakeVertexNoProject = { - ...fakeVertexAI, + ...fakeAI, app: { options: { apiKey: 'my-key', projectId: 'my-project' } } - } as VertexAI; + } as AI; try { getImagenModel(fakeVertexNoProject, { model: 'my-model' }); } catch (e) { - expect((e as VertexAIError).code).includes(VertexAIErrorCode.NO_APP_ID); - expect((e as VertexAIError).message).equals( - `VertexAI: The "appId" field is empty in the local` + - ` Firebase config. Firebase VertexAI requires this field ` + - `to contain a valid app ID. (vertexAI/${VertexAIErrorCode.NO_APP_ID})` + expect((e as AIError).code).includes(AIErrorCode.NO_APP_ID); + expect((e as AIError).message).equals( + `AI: The "appId" field is empty in the local` + + ` Firebase config. Firebase AI requires this field ` + + `to contain a valid app ID. (${AI_TYPE}/${AIErrorCode.NO_APP_ID})` ); } }); it('getImagenModel gets an ImagenModel', () => { - const genModel = getImagenModel(fakeVertexAI, { model: 'my-model' }); + const genModel = getImagenModel(fakeAI, { model: 'my-model' }); expect(genModel).to.be.an.instanceOf(ImagenModel); expect(genModel.model).to.equal('publishers/google/models/my-model'); }); diff --git a/packages/vertexai/src/api.ts b/packages/vertexai/src/api.ts index 7843a5bdeee..06bd747746a 100644 --- a/packages/vertexai/src/api.ts +++ b/packages/vertexai/src/api.ts @@ -18,36 +18,63 @@ import { FirebaseApp, getApp, _getProvider } from '@firebase/app'; import { Provider } from '@firebase/component'; import { getModularInstance } from '@firebase/util'; -import { DEFAULT_LOCATION, VERTEX_TYPE } from './constants'; -import { VertexAIService } from './service'; -import { VertexAI, VertexAIOptions } from './public-types'; +import { AI_TYPE } from './constants'; +import { AIService } from './service'; +import { AI, AIOptions, VertexAI, VertexAIOptions } from './public-types'; import { ImagenModelParams, ModelParams, RequestOptions, - VertexAIErrorCode + AIErrorCode } from './types'; -import { VertexAIError } from './errors'; -import { VertexAIModel, GenerativeModel, ImagenModel } from './models'; +import { AIError } from './errors'; +import { AIModel, GenerativeModel, ImagenModel } from './models'; +import { encodeInstanceIdentifier } from './helpers'; +import { GoogleAIBackend, VertexAIBackend } from './backend'; export { ChatSession } from './methods/chat-session'; export * from './requests/schema-builder'; export { ImagenImageFormat } from './requests/imagen-image-format'; -export { VertexAIModel, GenerativeModel, ImagenModel }; -export { VertexAIError }; +export { AIModel, GenerativeModel, ImagenModel, AIError }; +export { Backend, VertexAIBackend, GoogleAIBackend } from './backend'; + +export { AIErrorCode as VertexAIErrorCode }; + +/** + * Base class for Firebase AI model APIs. + * + * For more information, refer to the documentation for the new {@link AIModel}. + * + * @public + */ +export const VertexAIModel = AIModel; + +/** + * Error class for the Firebase AI SDK. + * + * For more information, refer to the documentation for the new {@link AIError}. + * + * @public + */ +export const VertexAIError = AIError; declare module '@firebase/component' { interface NameServiceMapping { - [VERTEX_TYPE]: VertexAIService; + [AI_TYPE]: AIService; } } /** - * Returns a {@link VertexAI} instance for the given app. + * It is recommended to use the new {@link getAI | getAI()}. * - * @public + * Returns a {@link VertexAI} instance for the given app, configured to use the + * Vertex AI Gemini API. This instance will be + * configured to use the Vertex AI Gemini API. * * @param app - The {@link @firebase/app#FirebaseApp} to use. + * @param options - Options to configure the Vertex AI instance, including the location. + * + * @public */ export function getVertexAI( app: FirebaseApp = getApp(), @@ -55,10 +82,54 @@ export function getVertexAI( ): VertexAI { app = getModularInstance(app); // Dependencies - const vertexProvider: Provider<'vertexAI'> = _getProvider(app, VERTEX_TYPE); + const AIProvider: Provider<'AI'> = _getProvider(app, AI_TYPE); + + const backend = new VertexAIBackend(options?.location); + const identifier = encodeInstanceIdentifier(backend); + return AIProvider.getImmediate({ + identifier + }); +} + +/** + * Returns the default {@link AI} instance that is associated with the provided + * {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new instance with the + * default settings. + * + * @example + * ```javascript + * const ai = getAI(app); + * ``` + * + * @example + * ```javascript + * // Get an AI instance configured to use the Gemini Developer API (via Google AI). + * const ai = getAI(app, { backend: new GoogleAIBackend() }); + * ``` + * + * @example + * ```javascript + * // Get an AI instance configured to use the Vertex AI Gemini API. + * const ai = getAI(app, { backend: new VertexAIBackend() }); + * ``` + * + * @param app - The {@link @firebase/app#FirebaseApp} to use. + * @param options - {@link AIOptions} that configure the AI instance. + * @returns The default {@link AI} instance for the given {@link @firebase/app#FirebaseApp}. + * + * @public + */ +export function getAI( + app: FirebaseApp = getApp(), + options: AIOptions = { backend: new GoogleAIBackend() } +): AI { + app = getModularInstance(app); + // Dependencies + const AIProvider: Provider<'AI'> = _getProvider(app, AI_TYPE); - return vertexProvider.getImmediate({ - identifier: options?.location || DEFAULT_LOCATION + const identifier = encodeInstanceIdentifier(options.backend); + return AIProvider.getImmediate({ + identifier }); } @@ -69,17 +140,17 @@ export function getVertexAI( * @public */ export function getGenerativeModel( - vertexAI: VertexAI, + ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions ): GenerativeModel { if (!modelParams.model) { - throw new VertexAIError( - VertexAIErrorCode.NO_MODEL, + throw new AIError( + AIErrorCode.NO_MODEL, `Must provide a model name. Example: getGenerativeModel({ model: 'my-model-name' })` ); } - return new GenerativeModel(vertexAI, modelParams, requestOptions); + return new GenerativeModel(ai, modelParams, requestOptions); } /** @@ -87,7 +158,7 @@ export function getGenerativeModel( * * Only Imagen 3 models (named `imagen-3.0-*`) are supported. * - * @param vertexAI - An instance of the Vertex AI in Firebase SDK. + * @param ai - An {@link AI} instance. * @param modelParams - Parameters to use when making Imagen requests. * @param requestOptions - Additional options to use when making requests. * @@ -97,15 +168,15 @@ export function getGenerativeModel( * @beta */ export function getImagenModel( - vertexAI: VertexAI, + ai: AI, modelParams: ImagenModelParams, requestOptions?: RequestOptions ): ImagenModel { if (!modelParams.model) { - throw new VertexAIError( - VertexAIErrorCode.NO_MODEL, + throw new AIError( + AIErrorCode.NO_MODEL, `Must provide a model name. Example: getImagenModel({ model: 'my-model-name' })` ); } - return new ImagenModel(vertexAI, modelParams, requestOptions); + return new ImagenModel(ai, modelParams, requestOptions); } diff --git a/packages/vertexai/src/backend.test.ts b/packages/vertexai/src/backend.test.ts new file mode 100644 index 00000000000..0c6609277e3 --- /dev/null +++ b/packages/vertexai/src/backend.test.ts @@ -0,0 +1,52 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { GoogleAIBackend, VertexAIBackend } from './backend'; +import { BackendType } from './public-types'; +import { DEFAULT_LOCATION } from './constants'; + +describe('Backend', () => { + describe('GoogleAIBackend', () => { + it('sets backendType to GOOGLE_AI', () => { + const backend = new GoogleAIBackend(); + expect(backend.backendType).to.equal(BackendType.GOOGLE_AI); + }); + }); + describe('VertexAIBackend', () => { + it('set backendType to VERTEX_AI', () => { + const backend = new VertexAIBackend(); + expect(backend.backendType).to.equal(BackendType.VERTEX_AI); + expect(backend.location).to.equal(DEFAULT_LOCATION); + }); + it('sets custom location', () => { + const backend = new VertexAIBackend('test-location'); + expect(backend.backendType).to.equal(BackendType.VERTEX_AI); + expect(backend.location).to.equal('test-location'); + }); + it('uses default location if location is empty string', () => { + const backend = new VertexAIBackend(''); + expect(backend.backendType).to.equal(BackendType.VERTEX_AI); + expect(backend.location).to.equal(DEFAULT_LOCATION); + }); + it('uses default location if location is null', () => { + const backend = new VertexAIBackend(null as any); + expect(backend.backendType).to.equal(BackendType.VERTEX_AI); + expect(backend.location).to.equal(DEFAULT_LOCATION); + }); + }); +}); diff --git a/packages/vertexai/src/backend.ts b/packages/vertexai/src/backend.ts new file mode 100644 index 00000000000..7209828122b --- /dev/null +++ b/packages/vertexai/src/backend.ts @@ -0,0 +1,92 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DEFAULT_LOCATION } from './constants'; +import { BackendType } from './public-types'; + +/** + * Abstract base class representing the configuration for an AI service backend. + * This class should not be instantiated directly. Use its subclasses; {@link GoogleAIBackend} for + * the Gemini Developer API (via {@link https://ai.google/ | Google AI}), and + * {@link VertexAIBackend} for the Vertex AI Gemini API. + * + * @public + */ +export abstract class Backend { + /** + * Specifies the backend type. + */ + readonly backendType: BackendType; + + /** + * Protected constructor for use by subclasses. + * @param type - The backend type. + */ + protected constructor(type: BackendType) { + this.backendType = type; + } +} + +/** + * Configuration class for the Gemini Developer API. + * + * Use this with {@link AIOptions} when initializing the AI service via + * {@link getAI | getAI()} to specify the Gemini Developer API as the backend. + * + * @public + */ +export class GoogleAIBackend extends Backend { + /** + * Creates a configuration object for the Gemini Developer API backend. + */ + constructor() { + super(BackendType.GOOGLE_AI); + } +} + +/** + * Configuration class for the Vertex AI Gemini API. + * + * Use this with {@link AIOptions} when initializing the AI service via + * {@link getAI | getAI()} to specify the Vertex AI Gemini API as the backend. + * + * @public + */ +export class VertexAIBackend extends Backend { + /** + * The region identifier. + * See {@link https://firebase.google.com/docs/vertex-ai/locations#available-locations | Vertex AI locations} + * for a list of supported locations. + */ + readonly location: string; + + /** + * Creates a configuration object for the Vertex AI backend. + * + * @param location - The region identifier, defaulting to `us-central1`; + * see {@link https://firebase.google.com/docs/vertex-ai/locations#available-locations | Vertex AI locations} + * for a list of supported locations. + */ + constructor(location: string = DEFAULT_LOCATION) { + super(BackendType.VERTEX_AI); + if (!location) { + this.location = DEFAULT_LOCATION; + } else { + this.location = location; + } + } +} diff --git a/packages/vertexai/src/backwards-compatbility.test.ts b/packages/vertexai/src/backwards-compatbility.test.ts new file mode 100644 index 00000000000..62463009b24 --- /dev/null +++ b/packages/vertexai/src/backwards-compatbility.test.ts @@ -0,0 +1,85 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { + AIError, + AIModel, + GenerativeModel, + VertexAIError, + VertexAIErrorCode, + VertexAIModel, + getGenerativeModel, + getImagenModel +} from './api'; +import { AI, VertexAI, AIErrorCode } from './public-types'; +import { VertexAIBackend } from './backend'; + +function assertAssignable(): void {} + +const fakeAI: AI = { + app: { + name: 'DEFAULT', + automaticDataCollectionEnabled: true, + options: { + apiKey: 'key', + projectId: 'my-project', + appId: 'app-id' + } + }, + backend: new VertexAIBackend('us-central1'), + location: 'us-central1' +}; + +const fakeVertexAI: VertexAI = fakeAI; + +describe('backwards-compatible types', () => { + it('AI is backwards compatible with VertexAI', () => { + assertAssignable(); + }); + it('AIError is backwards compatible with VertexAIError', () => { + assertAssignable(); + const err = new VertexAIError(VertexAIErrorCode.ERROR, ''); + expect(err).instanceOf(AIError); + expect(err).instanceOf(VertexAIError); + }); + it('AIErrorCode is backwards compatible with VertexAIErrorCode', () => { + assertAssignable(); + const errCode = AIErrorCode.ERROR; + expect(errCode).to.equal(VertexAIErrorCode.ERROR); + }); + it('AIModel is backwards compatible with VertexAIModel', () => { + assertAssignable(); + + const model = new GenerativeModel(fakeAI, { model: 'model-name' }); + expect(model).to.be.instanceOf(AIModel); + expect(model).to.be.instanceOf(VertexAIModel); + }); +}); + +describe('backward-compatible functions', () => { + it('getGenerativeModel', () => { + const model = getGenerativeModel(fakeVertexAI, { model: 'model-name' }); + expect(model).to.be.instanceOf(AIModel); + expect(model).to.be.instanceOf(VertexAIModel); + }); + it('getImagenModel', () => { + const model = getImagenModel(fakeVertexAI, { model: 'model-name' }); + expect(model).to.be.instanceOf(AIModel); + expect(model).to.be.instanceOf(VertexAIModel); + }); +}); diff --git a/packages/vertexai/src/constants.ts b/packages/vertexai/src/constants.ts index 357e6c4e77c..6339ce63017 100644 --- a/packages/vertexai/src/constants.ts +++ b/packages/vertexai/src/constants.ts @@ -17,8 +17,11 @@ import { version } from '../package.json'; +// TODO (v12): Remove this export const VERTEX_TYPE = 'vertexAI'; +export const AI_TYPE = 'AI'; + export const DEFAULT_LOCATION = 'us-central1'; export const DEFAULT_BASE_URL = 'https://firebasevertexai.googleapis.com'; diff --git a/packages/vertexai/src/errors.ts b/packages/vertexai/src/errors.ts index ad3f9b72f5a..2e9787d0bf2 100644 --- a/packages/vertexai/src/errors.ts +++ b/packages/vertexai/src/errors.ts @@ -16,32 +16,31 @@ */ import { FirebaseError } from '@firebase/util'; -import { VertexAIErrorCode, CustomErrorData } from './types'; -import { VERTEX_TYPE } from './constants'; +import { AIErrorCode, CustomErrorData } from './types'; +import { AI_TYPE } from './constants'; /** - * Error class for the Vertex AI in Firebase SDK. + * Error class for the Firebase AI SDK. * * @public */ -export class VertexAIError extends FirebaseError { +export class AIError extends FirebaseError { /** - * Constructs a new instance of the `VertexAIError` class. + * Constructs a new instance of the `AIError` class. * - * @param code - The error code from {@link VertexAIErrorCode}. + * @param code - The error code from {@link AIErrorCode}. * @param message - A human-readable message describing the error. * @param customErrorData - Optional error data. */ constructor( - readonly code: VertexAIErrorCode, + readonly code: AIErrorCode, message: string, readonly customErrorData?: CustomErrorData ) { // Match error format used by FirebaseError from ErrorFactory - const service = VERTEX_TYPE; - const serviceName = 'VertexAI'; + const service = AI_TYPE; const fullCode = `${service}/${code}`; - const fullMessage = `${serviceName}: ${message} (${fullCode})`; + const fullMessage = `${service}: ${message} (${fullCode})`; super(code, fullMessage); // FirebaseError initializes a stack trace, but it assumes the error is created from the error @@ -51,14 +50,14 @@ export class VertexAIError extends FirebaseError { if (Error.captureStackTrace) { // Allows us to initialize the stack trace without including the constructor itself at the // top level of the stack trace. - Error.captureStackTrace(this, VertexAIError); + Error.captureStackTrace(this, AIError); } - // Allows instanceof VertexAIError in ES5/ES6 + // Allows instanceof AIError in ES5/ES6 // https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work // TODO(dlarocque): Replace this with `new.target`: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget // which we can now use since we no longer target ES5. - Object.setPrototypeOf(this, VertexAIError.prototype); + Object.setPrototypeOf(this, AIError.prototype); // Since Error is an interface, we don't inherit toString and so we define it ourselves. this.toString = () => fullMessage; diff --git a/packages/vertexai/src/googleai-mappers.test.ts b/packages/vertexai/src/googleai-mappers.test.ts new file mode 100644 index 00000000000..12f422625f5 --- /dev/null +++ b/packages/vertexai/src/googleai-mappers.test.ts @@ -0,0 +1,391 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from 'chai'; +import sinon, { restore, stub } from 'sinon'; +import sinonChai from 'sinon-chai'; +import { + mapCountTokensRequest, + mapGenerateContentCandidates, + mapGenerateContentRequest, + mapGenerateContentResponse, + mapPromptFeedback +} from './googleai-mappers'; +import { + BlockReason, + Content, + CountTokensRequest, + GenerateContentRequest, + HarmBlockMethod, + HarmBlockThreshold, + HarmCategory, + HarmProbability, + HarmSeverity, + SafetyRating, + AIErrorCode, + FinishReason, + PromptFeedback +} from './types'; +import { + GoogleAIGenerateContentResponse, + GoogleAIGenerateContentCandidate, + GoogleAICountTokensRequest +} from './types/googleai'; +import { logger } from './logger'; +import { AIError } from './errors'; +import { getMockResponse } from '../test-utils/mock-response'; + +use(sinonChai); + +const fakeModel = 'models/gemini-pro'; + +const fakeContents: Content[] = [{ role: 'user', parts: [{ text: 'hello' }] }]; + +describe('Google AI Mappers', () => { + let loggerWarnStub: sinon.SinonStub; + + beforeEach(() => { + loggerWarnStub = stub(logger, 'warn'); + }); + + afterEach(() => { + restore(); + }); + + describe('mapGenerateContentRequest', () => { + it('should throw if safetySettings contain method', () => { + const request: GenerateContentRequest = { + contents: fakeContents, + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, + method: HarmBlockMethod.SEVERITY + } + ] + }; + expect(() => mapGenerateContentRequest(request)) + .to.throw(AIError, /SafetySetting.method is not supported/i) + .with.property('code', AIErrorCode.UNSUPPORTED); + }); + + it('should warn and round topK if present', () => { + const request: GenerateContentRequest = { + contents: fakeContents, + generationConfig: { + topK: 15.7 + } + }; + const mappedRequest = mapGenerateContentRequest(request); + expect(loggerWarnStub).to.have.been.calledOnceWith( + 'topK in GenerationConfig has been rounded to the nearest integer to match the format for requests to the Gemini Developer API.' + ); + expect(mappedRequest.generationConfig?.topK).to.equal(16); + }); + + it('should not modify topK if it is already an integer', () => { + const request: GenerateContentRequest = { + contents: fakeContents, + generationConfig: { + topK: 16 + } + }; + const mappedRequest = mapGenerateContentRequest(request); + expect(loggerWarnStub).to.not.have.been.called; + expect(mappedRequest.generationConfig?.topK).to.equal(16); + }); + + it('should return the request mostly unchanged if valid', () => { + const request: GenerateContentRequest = { + contents: fakeContents, + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, + threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE + } + ], + generationConfig: { + temperature: 0.5 + } + }; + const mappedRequest = mapGenerateContentRequest({ ...request }); + expect(mappedRequest).to.deep.equal(request); + expect(loggerWarnStub).to.not.have.been.called; + }); + }); + + describe('mapGenerateContentResponse', () => { + it('should map a full Google AI response', async () => { + const googleAIMockResponse: GoogleAIGenerateContentResponse = await ( + getMockResponse('googleAI', 'unary-success-citations.json') as Response + ).json(); + const mappedResponse = mapGenerateContentResponse(googleAIMockResponse); + + expect(mappedResponse.candidates).to.exist; + expect(mappedResponse.candidates?.[0].content.parts[0].text).to.contain( + 'quantum mechanics' + ); + + // Mapped citations + expect( + mappedResponse.candidates?.[0].citationMetadata?.citations[0].startIndex + ).to.equal( + googleAIMockResponse.candidates?.[0].citationMetadata + ?.citationSources[0].startIndex + ); + expect( + mappedResponse.candidates?.[0].citationMetadata?.citations[0].endIndex + ).to.equal( + googleAIMockResponse.candidates?.[0].citationMetadata + ?.citationSources[0].endIndex + ); + + // Mapped safety ratings + expect( + mappedResponse.candidates?.[0].safetyRatings?.[0].probabilityScore + ).to.equal(0); + expect( + mappedResponse.candidates?.[0].safetyRatings?.[0].severityScore + ).to.equal(0); + expect( + mappedResponse.candidates?.[0].safetyRatings?.[0].severity + ).to.equal(HarmSeverity.HARM_SEVERITY_UNSUPPORTED); + + expect(mappedResponse.candidates?.[0].finishReason).to.equal( + FinishReason.STOP + ); + + // Check usage metadata passthrough + expect(mappedResponse.usageMetadata).to.deep.equal( + googleAIMockResponse.usageMetadata + ); + }); + + it('should handle missing candidates and promptFeedback', () => { + const googleAIResponse: GoogleAIGenerateContentResponse = { + // No candidates + // No promptFeedback + usageMetadata: { + promptTokenCount: 5, + candidatesTokenCount: 0, + totalTokenCount: 5 + } + }; + const mappedResponse = mapGenerateContentResponse(googleAIResponse); + expect(mappedResponse.candidates).to.be.undefined; + expect(mappedResponse.promptFeedback).to.be.undefined; // Mapped to undefined + expect(mappedResponse.usageMetadata).to.deep.equal( + googleAIResponse.usageMetadata + ); + }); + + it('should handle empty candidates array', () => { + const googleAIResponse: GoogleAIGenerateContentResponse = { + candidates: [], + usageMetadata: { + promptTokenCount: 5, + candidatesTokenCount: 0, + totalTokenCount: 5 + } + }; + const mappedResponse = mapGenerateContentResponse(googleAIResponse); + expect(mappedResponse.candidates).to.deep.equal([]); + expect(mappedResponse.promptFeedback).to.be.undefined; + expect(mappedResponse.usageMetadata).to.deep.equal( + googleAIResponse.usageMetadata + ); + }); + }); + + describe('mapCountTokensRequest', () => { + it('should map a Vertex AI CountTokensRequest to Google AI format', () => { + const vertexRequest: CountTokensRequest = { + contents: fakeContents, + systemInstruction: { role: 'system', parts: [{ text: 'Be nice' }] }, + tools: [ + { functionDeclarations: [{ name: 'foo', description: 'bar' }] } + ], + generationConfig: { temperature: 0.8 } + }; + + const expectedGoogleAIRequest: GoogleAICountTokensRequest = { + generateContentRequest: { + model: fakeModel, + contents: vertexRequest.contents, + systemInstruction: vertexRequest.systemInstruction, + tools: vertexRequest.tools, + generationConfig: vertexRequest.generationConfig + } + }; + + const mappedRequest = mapCountTokensRequest(vertexRequest, fakeModel); + expect(mappedRequest).to.deep.equal(expectedGoogleAIRequest); + }); + + it('should map a minimal Vertex AI CountTokensRequest', () => { + const vertexRequest: CountTokensRequest = { + contents: fakeContents, + systemInstruction: { role: 'system', parts: [{ text: 'Be nice' }] }, + generationConfig: { temperature: 0.8 } + }; + + const expectedGoogleAIRequest: GoogleAICountTokensRequest = { + generateContentRequest: { + model: fakeModel, + contents: vertexRequest.contents, + systemInstruction: { role: 'system', parts: [{ text: 'Be nice' }] }, + generationConfig: { temperature: 0.8 } + } + }; + + const mappedRequest = mapCountTokensRequest(vertexRequest, fakeModel); + expect(mappedRequest).to.deep.equal(expectedGoogleAIRequest); + }); + }); + + describe('mapGenerateContentCandidates', () => { + it('should map citationSources to citationMetadata.citations', () => { + const candidates: GoogleAIGenerateContentCandidate[] = [ + { + index: 0, + content: { role: 'model', parts: [{ text: 'Cited text' }] }, + citationMetadata: { + citationSources: [ + { startIndex: 0, endIndex: 5, uri: 'uri1', license: 'MIT' }, + { startIndex: 6, endIndex: 10, uri: 'uri2' } + ] + } + } + ]; + const mapped = mapGenerateContentCandidates(candidates); + expect(mapped[0].citationMetadata).to.exist; + expect(mapped[0].citationMetadata?.citations).to.deep.equal( + candidates[0].citationMetadata?.citationSources + ); + expect(mapped[0].citationMetadata?.citations[0].title).to.be.undefined; // Not in Google AI + expect(mapped[0].citationMetadata?.citations[0].publicationDate).to.be + .undefined; // Not in Google AI + }); + + it('should add default safety rating properties', () => { + const candidates: GoogleAIGenerateContentCandidate[] = [ + { + index: 0, + content: { role: 'model', parts: [{ text: 'Maybe unsafe' }] }, + safetyRatings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + probability: HarmProbability.MEDIUM, + blocked: false + // Missing severity, probabilityScore, severityScore + } as any + ] + } + ]; + const mapped = mapGenerateContentCandidates(candidates); + expect(mapped[0].safetyRatings).to.exist; + const safetyRating = mapped[0].safetyRatings?.[0] as SafetyRating; // Type assertion + expect(safetyRating.severity).to.equal( + HarmSeverity.HARM_SEVERITY_UNSUPPORTED + ); + expect(safetyRating.probabilityScore).to.equal(0); + expect(safetyRating.severityScore).to.equal(0); + // Existing properties should be preserved + expect(safetyRating.category).to.equal( + HarmCategory.HARM_CATEGORY_HARASSMENT + ); + expect(safetyRating.probability).to.equal(HarmProbability.MEDIUM); + expect(safetyRating.blocked).to.be.false; + }); + + it('should throw if videoMetadata is present in parts', () => { + const candidates: GoogleAIGenerateContentCandidate[] = [ + { + index: 0, + content: { + role: 'model', + parts: [ + { + inlineData: { mimeType: 'video/mp4', data: 'base64==' }, + videoMetadata: { startOffset: '0s', endOffset: '5s' } // Unsupported + } + ] + } + } + ]; + expect(() => mapGenerateContentCandidates(candidates)) + .to.throw(AIError, /Part.videoMetadata is not supported/i) + .with.property('code', AIErrorCode.UNSUPPORTED); + }); + + it('should handle candidates without citation or safety ratings', () => { + const candidates: GoogleAIGenerateContentCandidate[] = [ + { + index: 0, + content: { role: 'model', parts: [{ text: 'Simple text' }] }, + finishReason: FinishReason.STOP + } + ]; + const mapped = mapGenerateContentCandidates(candidates); + expect(mapped[0].citationMetadata).to.be.undefined; + expect(mapped[0].safetyRatings).to.be.undefined; + expect(mapped[0].content.parts[0].text).to.equal('Simple text'); + expect(loggerWarnStub).to.not.have.been.called; + }); + + it('should handle empty candidate array', () => { + const candidates: GoogleAIGenerateContentCandidate[] = []; + const mapped = mapGenerateContentCandidates(candidates); + expect(mapped).to.deep.equal([]); + expect(loggerWarnStub).to.not.have.been.called; + }); + }); + + describe('mapPromptFeedback', () => { + it('should add default safety rating properties', () => { + const feedback: PromptFeedback = { + blockReason: BlockReason.OTHER, + safetyRatings: [ + { + category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, + probability: HarmProbability.HIGH, + blocked: true + // Missing severity, probabilityScore, severityScore + } as any + ] + // Missing blockReasonMessage + }; + const mapped = mapPromptFeedback(feedback); + expect(mapped.safetyRatings).to.exist; + const safetyRating = mapped.safetyRatings[0] as SafetyRating; // Type assertion + expect(safetyRating.severity).to.equal( + HarmSeverity.HARM_SEVERITY_UNSUPPORTED + ); + expect(safetyRating.probabilityScore).to.equal(0); + expect(safetyRating.severityScore).to.equal(0); + // Existing properties should be preserved + expect(safetyRating.category).to.equal( + HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT + ); + expect(safetyRating.probability).to.equal(HarmProbability.HIGH); + expect(safetyRating.blocked).to.be.true; + // Other properties + expect(mapped.blockReason).to.equal(BlockReason.OTHER); + expect(mapped.blockReasonMessage).to.be.undefined; // Not present in input + }); + }); +}); diff --git a/packages/vertexai/src/googleai-mappers.ts b/packages/vertexai/src/googleai-mappers.ts new file mode 100644 index 00000000000..23c238c1e3b --- /dev/null +++ b/packages/vertexai/src/googleai-mappers.ts @@ -0,0 +1,227 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AIError } from './errors'; +import { logger } from './logger'; +import { + CitationMetadata, + CountTokensRequest, + GenerateContentCandidate, + GenerateContentRequest, + GenerateContentResponse, + HarmSeverity, + InlineDataPart, + PromptFeedback, + SafetyRating, + AIErrorCode +} from './types'; +import { + GoogleAIGenerateContentResponse, + GoogleAIGenerateContentCandidate, + GoogleAICountTokensRequest +} from './types/googleai'; + +/** + * This SDK supports both the Vertex AI Gemini API and the Gemini Developer API (using Google AI). + * The public API prioritizes the format used by the Vertex AI Gemini API. + * We avoid having two sets of types by translating requests and responses between the two API formats. + * This translation allows developers to switch between the Vertex AI Gemini API and the Gemini Developer API + * with minimal code changes. + * + * In here are functions that map requests and responses between the two API formats. + * Requests in the Vertex AI format are mapped to the Google AI format before being sent. + * Responses from the Google AI backend are mapped back to the Vertex AI format before being returned to the user. + */ + +/** + * Maps a Vertex AI {@link GenerateContentRequest} to a format that can be sent to Google AI. + * + * @param generateContentRequest The {@link GenerateContentRequest} to map. + * @returns A {@link GenerateContentResponse} that conforms to the Google AI format. + * + * @throws If the request contains properties that are unsupported by Google AI. + * + * @internal + */ +export function mapGenerateContentRequest( + generateContentRequest: GenerateContentRequest +): GenerateContentRequest { + generateContentRequest.safetySettings?.forEach(safetySetting => { + if (safetySetting.method) { + throw new AIError( + AIErrorCode.UNSUPPORTED, + 'SafetySetting.method is not supported in the the Gemini Developer API. Please remove this property.' + ); + } + }); + + if (generateContentRequest.generationConfig?.topK) { + const roundedTopK = Math.round( + generateContentRequest.generationConfig.topK + ); + + if (roundedTopK !== generateContentRequest.generationConfig.topK) { + logger.warn( + 'topK in GenerationConfig has been rounded to the nearest integer to match the format for requests to the Gemini Developer API.' + ); + generateContentRequest.generationConfig.topK = roundedTopK; + } + } + + return generateContentRequest; +} + +/** + * Maps a {@link GenerateContentResponse} from Google AI to the format of the + * {@link GenerateContentResponse} that we get from VertexAI that is exposed in the public API. + * + * @param googleAIResponse The {@link GenerateContentResponse} from Google AI. + * @returns A {@link GenerateContentResponse} that conforms to the public API's format. + * + * @internal + */ +export function mapGenerateContentResponse( + googleAIResponse: GoogleAIGenerateContentResponse +): GenerateContentResponse { + const generateContentResponse = { + candidates: googleAIResponse.candidates + ? mapGenerateContentCandidates(googleAIResponse.candidates) + : undefined, + prompt: googleAIResponse.promptFeedback + ? mapPromptFeedback(googleAIResponse.promptFeedback) + : undefined, + usageMetadata: googleAIResponse.usageMetadata + }; + + return generateContentResponse; +} + +/** + * Maps a Vertex AI {@link CountTokensRequest} to a format that can be sent to Google AI. + * + * @param countTokensRequest The {@link CountTokensRequest} to map. + * @param model The model to count tokens with. + * @returns A {@link CountTokensRequest} that conforms to the Google AI format. + * + * @internal + */ +export function mapCountTokensRequest( + countTokensRequest: CountTokensRequest, + model: string +): GoogleAICountTokensRequest { + const mappedCountTokensRequest: GoogleAICountTokensRequest = { + generateContentRequest: { + model, + ...countTokensRequest + } + }; + + return mappedCountTokensRequest; +} + +/** + * Maps a Google AI {@link GoogleAIGenerateContentCandidate} to a format that conforms + * to the Vertex AI API format. + * + * @param candidates The {@link GoogleAIGenerateContentCandidate} to map. + * @returns A {@link GenerateContentCandidate} that conforms to the Vertex AI format. + * + * @throws If any {@link Part} in the candidates has a `videoMetadata` property. + * + * @internal + */ +export function mapGenerateContentCandidates( + candidates: GoogleAIGenerateContentCandidate[] +): GenerateContentCandidate[] { + const mappedCandidates: GenerateContentCandidate[] = []; + let mappedSafetyRatings: SafetyRating[]; + if (mappedCandidates) { + candidates.forEach(candidate => { + // Map citationSources to citations. + let citationMetadata: CitationMetadata | undefined; + if (candidate.citationMetadata) { + citationMetadata = { + citations: candidate.citationMetadata.citationSources + }; + } + + // Assign missing candidate SafetyRatings properties to their defaults if undefined. + if (candidate.safetyRatings) { + mappedSafetyRatings = candidate.safetyRatings.map(safetyRating => { + return { + ...safetyRating, + severity: + safetyRating.severity ?? HarmSeverity.HARM_SEVERITY_UNSUPPORTED, + probabilityScore: safetyRating.probabilityScore ?? 0, + severityScore: safetyRating.severityScore ?? 0 + }; + }); + } + + // videoMetadata is not supported. + // Throw early since developers may send a long video as input and only expect to pay + // for inference on a small portion of the video. + if ( + candidate.content?.parts.some( + part => (part as InlineDataPart)?.videoMetadata + ) + ) { + throw new AIError( + AIErrorCode.UNSUPPORTED, + 'Part.videoMetadata is not supported in the Gemini Developer API. Please remove this property.' + ); + } + + const mappedCandidate = { + index: candidate.index, + content: candidate.content, + finishReason: candidate.finishReason, + finishMessage: candidate.finishMessage, + safetyRatings: mappedSafetyRatings, + citationMetadata, + groundingMetadata: candidate.groundingMetadata + }; + mappedCandidates.push(mappedCandidate); + }); + } + + return mappedCandidates; +} + +export function mapPromptFeedback( + promptFeedback: PromptFeedback +): PromptFeedback { + // Assign missing SafetyRating properties to their defaults if undefined. + const mappedSafetyRatings: SafetyRating[] = []; + promptFeedback.safetyRatings.forEach(safetyRating => { + mappedSafetyRatings.push({ + category: safetyRating.category, + probability: safetyRating.probability, + severity: safetyRating.severity ?? HarmSeverity.HARM_SEVERITY_UNSUPPORTED, + probabilityScore: safetyRating.probabilityScore ?? 0, + severityScore: safetyRating.severityScore ?? 0, + blocked: safetyRating.blocked + }); + }); + + const mappedPromptFeedback: PromptFeedback = { + blockReason: promptFeedback.blockReason, + safetyRatings: mappedSafetyRatings, + blockReasonMessage: promptFeedback.blockReasonMessage + }; + return mappedPromptFeedback; +} diff --git a/packages/vertexai/src/helpers.test.ts b/packages/vertexai/src/helpers.test.ts new file mode 100644 index 00000000000..8f5f164d6b8 --- /dev/null +++ b/packages/vertexai/src/helpers.test.ts @@ -0,0 +1,121 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { expect } from 'chai'; +import { AI_TYPE, DEFAULT_LOCATION } from './constants'; +import { encodeInstanceIdentifier, decodeInstanceIdentifier } from './helpers'; +import { AIError } from './errors'; +import { AIErrorCode } from './types'; +import { GoogleAIBackend, VertexAIBackend } from './backend'; + +describe('Identifier Encoding/Decoding', () => { + describe('encodeInstanceIdentifier', () => { + it('should encode Vertex AI identifier with a specific location', () => { + const backend = new VertexAIBackend('us-east1'); + const expected = `${AI_TYPE}/vertexai/us-east1`; + expect(encodeInstanceIdentifier(backend)).to.equal(expected); + }); + + it('should encode Vertex AI identifier using default location if location is empty string', () => { + const backend = new VertexAIBackend(''); + const expected = `${AI_TYPE}/vertexai/${DEFAULT_LOCATION}`; + expect(encodeInstanceIdentifier(backend)).to.equal(expected); + }); + + it('should encode Google AI identifier', () => { + const backend = new GoogleAIBackend(); + const expected = `${AI_TYPE}/googleai`; + expect(encodeInstanceIdentifier(backend)).to.equal(expected); + }); + + it('should throw AIError for unknown backend type', () => { + expect(() => encodeInstanceIdentifier({} as any)).to.throw(AIError); + + try { + encodeInstanceIdentifier({} as any); + expect.fail('Expected encodeInstanceIdentifier to throw'); + } catch (e) { + expect(e).to.be.instanceOf(AIError); + const error = e as AIError; + expect(error.message).to.contain('Invalid backend'); + expect(error.code).to.equal(AIErrorCode.ERROR); + } + }); + }); + + describe('decodeInstanceIdentifier', () => { + it('should decode Vertex AI identifier with location', () => { + const encoded = `${AI_TYPE}/vertexai/europe-west1`; + const backend = new VertexAIBackend('europe-west1'); + expect(decodeInstanceIdentifier(encoded)).to.deep.equal(backend); + }); + + it('should throw an error if Vertex AI identifier string without explicit location part', () => { + const encoded = `${AI_TYPE}/vertexai`; + expect(() => decodeInstanceIdentifier(encoded)).to.throw(AIError); + + try { + decodeInstanceIdentifier(encoded); + expect.fail('Expected encodeInstanceIdentifier to throw'); + } catch (e) { + expect(e).to.be.instanceOf(AIError); + const error = e as AIError; + expect(error.message).to.contain( + `Invalid instance identifier, unknown location` + ); + expect(error.code).to.equal(AIErrorCode.ERROR); + } + }); + + it('should decode Google AI identifier', () => { + const encoded = `${AI_TYPE}/googleai`; + const backend = new GoogleAIBackend(); + expect(decodeInstanceIdentifier(encoded)).to.deep.equal(backend); + }); + + it('should throw AIError for invalid backend string', () => { + const encoded = `${AI_TYPE}/someotherbackend/location`; + expect(() => decodeInstanceIdentifier(encoded)).to.throw( + AIError, + `Invalid instance identifier string: '${encoded}'` + ); + try { + decodeInstanceIdentifier(encoded); + expect.fail('Expected decodeInstanceIdentifier to throw'); + } catch (e) { + expect(e).to.be.instanceOf(AIError); + expect((e as AIError).code).to.equal(AIErrorCode.ERROR); + } + }); + + it('should throw AIError for malformed identifier string (too few parts)', () => { + const encoded = AI_TYPE; + expect(() => decodeInstanceIdentifier(encoded)).to.throw( + AIError, + `Invalid instance identifier string: '${encoded}'` + ); + }); + + it('should throw AIError for malformed identifier string (incorrect prefix)', () => { + const encoded = 'firebase/AI/location'; + // This will also hit the default case in the switch statement + expect(() => decodeInstanceIdentifier(encoded)).to.throw( + AIError, + `Invalid instance identifier, unknown prefix 'firebase'` + ); + }); + }); +}); diff --git a/packages/vertexai/src/helpers.ts b/packages/vertexai/src/helpers.ts new file mode 100644 index 00000000000..709bf4369c5 --- /dev/null +++ b/packages/vertexai/src/helpers.ts @@ -0,0 +1,74 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AI_TYPE } from './constants'; +import { AIError } from './errors'; +import { AIErrorCode } from './types'; +import { Backend, GoogleAIBackend, VertexAIBackend } from './backend'; + +/** + * Encodes a {@link Backend} into a string that will be used to uniquely identify {@link AI} + * instances by backend type. + * + * @internal + */ +export function encodeInstanceIdentifier(backend: Backend): string { + if (backend instanceof GoogleAIBackend) { + return `${AI_TYPE}/googleai`; + } else if (backend instanceof VertexAIBackend) { + return `${AI_TYPE}/vertexai/${backend.location}`; + } else { + throw new AIError( + AIErrorCode.ERROR, + `Invalid backend: ${JSON.stringify(backend.backendType)}` + ); + } +} + +/** + * Decodes an instance identifier string into a {@link Backend}. + * + * @internal + */ +export function decodeInstanceIdentifier(instanceIdentifier: string): Backend { + const identifierParts = instanceIdentifier.split('/'); + if (identifierParts[0] !== AI_TYPE) { + throw new AIError( + AIErrorCode.ERROR, + `Invalid instance identifier, unknown prefix '${identifierParts[0]}'` + ); + } + const backendType = identifierParts[1]; + switch (backendType) { + case 'vertexai': + const location: string | undefined = identifierParts[2]; + if (!location) { + throw new AIError( + AIErrorCode.ERROR, + `Invalid instance identifier, unknown location '${instanceIdentifier}'` + ); + } + return new VertexAIBackend(location); + case 'googleai': + return new GoogleAIBackend(); + default: + throw new AIError( + AIErrorCode.ERROR, + `Invalid instance identifier string: '${instanceIdentifier}'` + ); + } +} diff --git a/packages/vertexai/src/index.node.ts b/packages/vertexai/src/index.node.ts index 6a18788141a..1908e65b1cd 100644 --- a/packages/vertexai/src/index.node.ts +++ b/packages/vertexai/src/index.node.ts @@ -1,5 +1,5 @@ /** - * The Vertex AI in Firebase Web SDK. + * The Firebase AI Web SDK. * * @packageDocumentation */ @@ -22,21 +22,33 @@ */ import { registerVersion, _registerComponent } from '@firebase/app'; -import { VertexAIService } from './service'; -import { VERTEX_TYPE } from './constants'; +import { AIService } from './service'; +import { AI_TYPE } from './constants'; import { Component, ComponentType } from '@firebase/component'; import { name, version } from '../package.json'; +import { decodeInstanceIdentifier } from './helpers'; +import { AIError } from './errors'; +import { AIErrorCode } from './public-types'; -function registerVertex(): void { +function registerAI(): void { _registerComponent( new Component( - VERTEX_TYPE, - (container, { instanceIdentifier: location }) => { + AI_TYPE, + (container, { instanceIdentifier }) => { + if (!instanceIdentifier) { + throw new AIError( + AIErrorCode.ERROR, + 'AIService instance identifier is undefined.' + ); + } + + const backend = decodeInstanceIdentifier(instanceIdentifier); + // getImmediate for FirebaseApp will always succeed const app = container.getProvider('app').getImmediate(); const auth = container.getProvider('auth-internal'); const appCheckProvider = container.getProvider('app-check-internal'); - return new VertexAIService(app, auth, appCheckProvider, { location }); + return new AIService(app, backend, auth, appCheckProvider); }, ComponentType.PUBLIC ).setMultipleInstances(true) @@ -47,7 +59,7 @@ function registerVertex(): void { registerVersion(name, version, '__BUILD_TARGET__'); } -registerVertex(); +registerAI(); export * from './api'; export * from './public-types'; diff --git a/packages/vertexai/src/index.ts b/packages/vertexai/src/index.ts index 5d646e8d9d0..8451d68bbf0 100644 --- a/packages/vertexai/src/index.ts +++ b/packages/vertexai/src/index.ts @@ -1,12 +1,12 @@ /** - * The Vertex AI in Firebase Web SDK. + * The Firebase AI Web SDK. * * @packageDocumentation */ /** * @license - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,10 +22,13 @@ */ import { registerVersion, _registerComponent } from '@firebase/app'; -import { VertexAIService } from './service'; -import { VERTEX_TYPE } from './constants'; +import { AIService } from './service'; +import { AI_TYPE } from './constants'; import { Component, ComponentType } from '@firebase/component'; import { name, version } from '../package.json'; +import { decodeInstanceIdentifier } from './helpers'; +import { AIError } from './api'; +import { AIErrorCode } from './types'; declare global { interface Window { @@ -33,16 +36,25 @@ declare global { } } -function registerVertex(): void { +function registerAI(): void { _registerComponent( new Component( - VERTEX_TYPE, - (container, { instanceIdentifier: location }) => { + AI_TYPE, + (container, { instanceIdentifier }) => { + if (!instanceIdentifier) { + throw new AIError( + AIErrorCode.ERROR, + 'AIService instance identifier is undefined.' + ); + } + + const backend = decodeInstanceIdentifier(instanceIdentifier); + // getImmediate for FirebaseApp will always succeed const app = container.getProvider('app').getImmediate(); const auth = container.getProvider('auth-internal'); const appCheckProvider = container.getProvider('app-check-internal'); - return new VertexAIService(app, auth, appCheckProvider, { location }); + return new AIService(app, backend, auth, appCheckProvider); }, ComponentType.PUBLIC ).setMultipleInstances(true) @@ -53,7 +65,7 @@ function registerVertex(): void { registerVersion(name, version, '__BUILD_TARGET__'); } -registerVertex(); +registerAI(); export * from './api'; export * from './public-types'; diff --git a/packages/vertexai/src/methods/chat-session-helpers.ts b/packages/vertexai/src/methods/chat-session-helpers.ts index 2106a40b90b..1bb0e2798f2 100644 --- a/packages/vertexai/src/methods/chat-session-helpers.ts +++ b/packages/vertexai/src/methods/chat-session-helpers.ts @@ -15,14 +15,8 @@ * limitations under the License. */ -import { - Content, - POSSIBLE_ROLES, - Part, - Role, - VertexAIErrorCode -} from '../types'; -import { VertexAIError } from '../errors'; +import { Content, POSSIBLE_ROLES, Part, Role, AIErrorCode } from '../types'; +import { AIError } from '../errors'; // https://ai.google.dev/api/rest/v1beta/Content#part @@ -54,14 +48,14 @@ export function validateChatHistory(history: Content[]): void { for (const currContent of history) { const { role, parts } = currContent; if (!prevContent && role !== 'user') { - throw new VertexAIError( - VertexAIErrorCode.INVALID_CONTENT, + throw new AIError( + AIErrorCode.INVALID_CONTENT, `First Content should be with role 'user', got ${role}` ); } if (!POSSIBLE_ROLES.includes(role)) { - throw new VertexAIError( - VertexAIErrorCode.INVALID_CONTENT, + throw new AIError( + AIErrorCode.INVALID_CONTENT, `Each item should include role field. Got ${role} but valid roles are: ${JSON.stringify( POSSIBLE_ROLES )}` @@ -69,15 +63,15 @@ export function validateChatHistory(history: Content[]): void { } if (!Array.isArray(parts)) { - throw new VertexAIError( - VertexAIErrorCode.INVALID_CONTENT, + throw new AIError( + AIErrorCode.INVALID_CONTENT, `Content should have 'parts' but property with an array of Parts` ); } if (parts.length === 0) { - throw new VertexAIError( - VertexAIErrorCode.INVALID_CONTENT, + throw new AIError( + AIErrorCode.INVALID_CONTENT, `Each Content should have at least one part` ); } @@ -99,8 +93,8 @@ export function validateChatHistory(history: Content[]): void { const validParts = VALID_PARTS_PER_ROLE[role]; for (const key of VALID_PART_FIELDS) { if (!validParts.includes(key) && countFields[key] > 0) { - throw new VertexAIError( - VertexAIErrorCode.INVALID_CONTENT, + throw new AIError( + AIErrorCode.INVALID_CONTENT, `Content with role '${role}' can't contain '${key}' part` ); } @@ -109,8 +103,8 @@ export function validateChatHistory(history: Content[]): void { if (prevContent) { const validPreviousContentRoles = VALID_PREVIOUS_CONTENT_ROLES[role]; if (!validPreviousContentRoles.includes(prevContent.role)) { - throw new VertexAIError( - VertexAIErrorCode.INVALID_CONTENT, + throw new AIError( + AIErrorCode.INVALID_CONTENT, `Content with role '${role}' can't follow '${ prevContent.role }'. Valid previous roles: ${JSON.stringify( diff --git a/packages/vertexai/src/methods/chat-session.test.ts b/packages/vertexai/src/methods/chat-session.test.ts index bd389a3d778..0564aa84ed6 100644 --- a/packages/vertexai/src/methods/chat-session.test.ts +++ b/packages/vertexai/src/methods/chat-session.test.ts @@ -23,6 +23,7 @@ import * as generateContentMethods from './generate-content'; import { GenerateContentStreamResult } from '../types'; import { ChatSession } from './chat-session'; import { ApiSettings } from '../types/internal'; +import { VertexAIBackend } from '../backend'; use(sinonChai); use(chaiAsPromised); @@ -31,7 +32,8 @@ const fakeApiSettings: ApiSettings = { apiKey: 'key', project: 'my-project', appId: 'my-appid', - location: 'us-central1' + location: 'us-central1', + backend: new VertexAIBackend() }; describe('ChatSession', () => { diff --git a/packages/vertexai/src/methods/count-tokens.test.ts b/packages/vertexai/src/methods/count-tokens.test.ts index 9eccbf702fe..7e04ddb3561 100644 --- a/packages/vertexai/src/methods/count-tokens.test.ts +++ b/packages/vertexai/src/methods/count-tokens.test.ts @@ -16,7 +16,7 @@ */ import { expect, use } from 'chai'; -import { match, restore, stub } from 'sinon'; +import Sinon, { match, restore, stub } from 'sinon'; import sinonChai from 'sinon-chai'; import chaiAsPromised from 'chai-as-promised'; import { getMockResponse } from '../../test-utils/mock-response'; @@ -25,6 +25,8 @@ import { countTokens } from './count-tokens'; import { CountTokensRequest } from '../types'; import { ApiSettings } from '../types/internal'; import { Task } from '../requests/request'; +import { mapCountTokensRequest } from '../googleai-mappers'; +import { GoogleAIBackend, VertexAIBackend } from '../backend'; use(sinonChai); use(chaiAsPromised); @@ -33,7 +35,16 @@ const fakeApiSettings: ApiSettings = { apiKey: 'key', project: 'my-project', appId: 'my-appid', - location: 'us-central1' + location: 'us-central1', + backend: new VertexAIBackend() +}; + +const fakeGoogleAIApiSettings: ApiSettings = { + apiKey: 'key', + project: 'my-project', + appId: 'my-appid', + location: '', + backend: new GoogleAIBackend() }; const fakeRequestParams: CountTokensRequest = { @@ -139,4 +150,30 @@ describe('countTokens()', () => { ).to.be.rejectedWith(/404.*not found/); expect(mockFetch).to.be.called; }); + describe('googleAI', () => { + let makeRequestStub: Sinon.SinonStub; + + beforeEach(() => { + makeRequestStub = stub(request, 'makeRequest'); + }); + + afterEach(() => { + restore(); + }); + + it('maps request to GoogleAI format', async () => { + makeRequestStub.resolves({ ok: true, json: () => {} } as Response); // Unused + + await countTokens(fakeGoogleAIApiSettings, 'model', fakeRequestParams); + + expect(makeRequestStub).to.be.calledWith( + 'model', + Task.COUNT_TOKENS, + fakeGoogleAIApiSettings, + false, + JSON.stringify(mapCountTokensRequest(fakeRequestParams, 'model')), + undefined + ); + }); + }); }); diff --git a/packages/vertexai/src/methods/count-tokens.ts b/packages/vertexai/src/methods/count-tokens.ts index c9d43a5b6fd..b1e60e3a182 100644 --- a/packages/vertexai/src/methods/count-tokens.ts +++ b/packages/vertexai/src/methods/count-tokens.ts @@ -22,6 +22,8 @@ import { } from '../types'; import { Task, makeRequest } from '../requests/request'; import { ApiSettings } from '../types/internal'; +import * as GoogleAIMapper from '../googleai-mappers'; +import { BackendType } from '../public-types'; export async function countTokens( apiSettings: ApiSettings, @@ -29,12 +31,19 @@ export async function countTokens( params: CountTokensRequest, requestOptions?: RequestOptions ): Promise { + let body: string = ''; + if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) { + const mappedParams = GoogleAIMapper.mapCountTokensRequest(params, model); + body = JSON.stringify(mappedParams); + } else { + body = JSON.stringify(params); + } const response = await makeRequest( model, Task.COUNT_TOKENS, apiSettings, false, - JSON.stringify(params), + body, requestOptions ); return response.json(); diff --git a/packages/vertexai/src/methods/generate-content.test.ts b/packages/vertexai/src/methods/generate-content.test.ts index 1d15632f828..13250fd83dd 100644 --- a/packages/vertexai/src/methods/generate-content.test.ts +++ b/packages/vertexai/src/methods/generate-content.test.ts @@ -16,13 +16,14 @@ */ import { expect, use } from 'chai'; -import { match, restore, stub } from 'sinon'; +import Sinon, { match, restore, stub } from 'sinon'; import sinonChai from 'sinon-chai'; import chaiAsPromised from 'chai-as-promised'; import { getMockResponse } from '../../test-utils/mock-response'; import * as request from '../requests/request'; import { generateContent } from './generate-content'; import { + AIErrorCode, GenerateContentRequest, HarmBlockMethod, HarmBlockThreshold, @@ -30,6 +31,9 @@ import { } from '../types'; import { ApiSettings } from '../types/internal'; import { Task } from '../requests/request'; +import { AIError } from '../api'; +import { mapGenerateContentRequest } from '../googleai-mappers'; +import { GoogleAIBackend, VertexAIBackend } from '../backend'; use(sinonChai); use(chaiAsPromised); @@ -38,7 +42,16 @@ const fakeApiSettings: ApiSettings = { apiKey: 'key', project: 'my-project', appId: 'my-appid', - location: 'us-central1' + location: 'us-central1', + backend: new VertexAIBackend() +}; + +const fakeGoogleAIApiSettings: ApiSettings = { + apiKey: 'key', + project: 'my-project', + appId: 'my-appid', + location: 'us-central1', + backend: new GoogleAIBackend() }; const fakeRequestParams: GenerateContentRequest = { @@ -55,6 +68,19 @@ const fakeRequestParams: GenerateContentRequest = { ] }; +const fakeGoogleAIRequestParams: GenerateContentRequest = { + contents: [{ parts: [{ text: 'hello' }], role: 'user' }], + generationConfig: { + topK: 16 + }, + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE + } + ] +}; + describe('generateContent()', () => { afterEach(() => { restore(); @@ -78,9 +104,7 @@ describe('generateContent()', () => { Task.GENERATE_CONTENT, fakeApiSettings, false, - match((value: string) => { - return value.includes('contents'); - }), + JSON.stringify(fakeRequestParams), undefined ); }); @@ -289,4 +313,66 @@ describe('generateContent()', () => { ); expect(mockFetch).to.be.called; }); + describe('googleAI', () => { + let makeRequestStub: Sinon.SinonStub; + + beforeEach(() => { + makeRequestStub = stub(request, 'makeRequest'); + }); + + afterEach(() => { + restore(); + }); + + it('throws error when method is defined', async () => { + const mockResponse = getMockResponse( + 'googleAI', + 'unary-success-basic-reply-short.txt' + ); + makeRequestStub.resolves(mockResponse as Response); + + const requestParamsWithMethod: GenerateContentRequest = { + contents: [{ parts: [{ text: 'hello' }], role: 'user' }], + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, + method: HarmBlockMethod.SEVERITY // Unsupported in Google AI. + } + ] + }; + + // Expect generateContent to throw a AIError that method is not supported. + await expect( + generateContent( + fakeGoogleAIApiSettings, + 'model', + requestParamsWithMethod + ) + ).to.be.rejectedWith(AIError, AIErrorCode.UNSUPPORTED); + expect(makeRequestStub).to.not.be.called; + }); + it('maps request to GoogleAI format', async () => { + const mockResponse = getMockResponse( + 'googleAI', + 'unary-success-basic-reply-short.txt' + ); + makeRequestStub.resolves(mockResponse as Response); + + await generateContent( + fakeGoogleAIApiSettings, + 'model', + fakeGoogleAIRequestParams + ); + + expect(makeRequestStub).to.be.calledWith( + 'model', + Task.GENERATE_CONTENT, + fakeGoogleAIApiSettings, + false, + JSON.stringify(mapGenerateContentRequest(fakeGoogleAIRequestParams)), + undefined + ); + }); + }); }); diff --git a/packages/vertexai/src/methods/generate-content.ts b/packages/vertexai/src/methods/generate-content.ts index 0944b38016a..5f7902f5954 100644 --- a/packages/vertexai/src/methods/generate-content.ts +++ b/packages/vertexai/src/methods/generate-content.ts @@ -26,6 +26,8 @@ import { Task, makeRequest } from '../requests/request'; import { createEnhancedContentResponse } from '../requests/response-helpers'; import { processStream } from '../requests/stream-reader'; import { ApiSettings } from '../types/internal'; +import * as GoogleAIMapper from '../googleai-mappers'; +import { BackendType } from '../public-types'; export async function generateContentStream( apiSettings: ApiSettings, @@ -33,6 +35,9 @@ export async function generateContentStream( params: GenerateContentRequest, requestOptions?: RequestOptions ): Promise { + if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) { + params = GoogleAIMapper.mapGenerateContentRequest(params); + } const response = await makeRequest( model, Task.STREAM_GENERATE_CONTENT, @@ -41,7 +46,7 @@ export async function generateContentStream( JSON.stringify(params), requestOptions ); - return processStream(response); + return processStream(response, apiSettings); // TODO: Map streaming responses } export async function generateContent( @@ -50,6 +55,9 @@ export async function generateContent( params: GenerateContentRequest, requestOptions?: RequestOptions ): Promise { + if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) { + params = GoogleAIMapper.mapGenerateContentRequest(params); + } const response = await makeRequest( model, Task.GENERATE_CONTENT, @@ -58,9 +66,26 @@ export async function generateContent( JSON.stringify(params), requestOptions ); - const responseJson: GenerateContentResponse = await response.json(); - const enhancedResponse = createEnhancedContentResponse(responseJson); + const generateContentResponse = await processGenerateContentResponse( + response, + apiSettings + ); + const enhancedResponse = createEnhancedContentResponse( + generateContentResponse + ); return { response: enhancedResponse }; } + +async function processGenerateContentResponse( + response: Response, + apiSettings: ApiSettings +): Promise { + const responseJson = await response.json(); + if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) { + return GoogleAIMapper.mapGenerateContentResponse(responseJson); + } else { + return responseJson; + } +} diff --git a/packages/vertexai/src/models/vertexai-model.test.ts b/packages/vertexai/src/models/ai-model.test.ts similarity index 66% rename from packages/vertexai/src/models/vertexai-model.test.ts rename to packages/vertexai/src/models/ai-model.test.ts index 7aa7f806e7f..4f23fe9d06f 100644 --- a/packages/vertexai/src/models/vertexai-model.test.ts +++ b/packages/vertexai/src/models/ai-model.test.ts @@ -15,24 +15,25 @@ * limitations under the License. */ import { use, expect } from 'chai'; -import { VertexAI, VertexAIErrorCode } from '../public-types'; +import { AI, AIErrorCode } from '../public-types'; import sinonChai from 'sinon-chai'; -import { VertexAIModel } from './vertexai-model'; -import { VertexAIError } from '../errors'; +import { AIModel } from './ai-model'; +import { AIError } from '../errors'; +import { VertexAIBackend } from '../backend'; use(sinonChai); /** - * A class that extends VertexAIModel that allows us to test the protected constructor. + * A class that extends AIModel that allows us to test the protected constructor. */ -class TestModel extends VertexAIModel { +class TestModel extends AIModel { /* eslint-disable @typescript-eslint/no-useless-constructor */ - constructor(vertexAI: VertexAI, modelName: string) { - super(vertexAI, modelName); + constructor(ai: AI, modelName: string) { + super(ai, modelName); } } -const fakeVertexAI: VertexAI = { +const fakeAI: AI = { app: { name: 'DEFAULT', automaticDataCollectionEnabled: true, @@ -42,31 +43,32 @@ const fakeVertexAI: VertexAI = { appId: 'my-appid' } }, + backend: new VertexAIBackend('us-central1'), location: 'us-central1' }; -describe('VertexAIModel', () => { +describe('AIModel', () => { it('handles plain model name', () => { - const testModel = new TestModel(fakeVertexAI, 'my-model'); + const testModel = new TestModel(fakeAI, 'my-model'); expect(testModel.model).to.equal('publishers/google/models/my-model'); }); it('handles models/ prefixed model name', () => { - const testModel = new TestModel(fakeVertexAI, 'models/my-model'); + const testModel = new TestModel(fakeAI, 'models/my-model'); expect(testModel.model).to.equal('publishers/google/models/my-model'); }); it('handles full model name', () => { const testModel = new TestModel( - fakeVertexAI, + fakeAI, 'publishers/google/models/my-model' ); expect(testModel.model).to.equal('publishers/google/models/my-model'); }); it('handles prefixed tuned model name', () => { - const testModel = new TestModel(fakeVertexAI, 'tunedModels/my-model'); + const testModel = new TestModel(fakeAI, 'tunedModels/my-model'); expect(testModel.model).to.equal('tunedModels/my-model'); }); it('throws if not passed an api key', () => { - const fakeVertexAI: VertexAI = { + const fakeAI: AI = { app: { name: 'DEFAULT', automaticDataCollectionEnabled: true, @@ -74,16 +76,17 @@ describe('VertexAIModel', () => { projectId: 'my-project' } }, + backend: new VertexAIBackend('us-central1'), location: 'us-central1' }; try { - new TestModel(fakeVertexAI, 'my-model'); + new TestModel(fakeAI, 'my-model'); } catch (e) { - expect((e as VertexAIError).code).to.equal(VertexAIErrorCode.NO_API_KEY); + expect((e as AIError).code).to.equal(AIErrorCode.NO_API_KEY); } }); it('throws if not passed a project ID', () => { - const fakeVertexAI: VertexAI = { + const fakeAI: AI = { app: { name: 'DEFAULT', automaticDataCollectionEnabled: true, @@ -91,18 +94,17 @@ describe('VertexAIModel', () => { apiKey: 'key' } }, + backend: new VertexAIBackend('us-central1'), location: 'us-central1' }; try { - new TestModel(fakeVertexAI, 'my-model'); + new TestModel(fakeAI, 'my-model'); } catch (e) { - expect((e as VertexAIError).code).to.equal( - VertexAIErrorCode.NO_PROJECT_ID - ); + expect((e as AIError).code).to.equal(AIErrorCode.NO_PROJECT_ID); } }); it('throws if not passed an app ID', () => { - const fakeVertexAI: VertexAI = { + const fakeAI: AI = { app: { name: 'DEFAULT', automaticDataCollectionEnabled: true, @@ -111,12 +113,13 @@ describe('VertexAIModel', () => { projectId: 'my-project' } }, + backend: new VertexAIBackend('us-central1'), location: 'us-central1' }; try { - new TestModel(fakeVertexAI, 'my-model'); + new TestModel(fakeAI, 'my-model'); } catch (e) { - expect((e as VertexAIError).code).to.equal(VertexAIErrorCode.NO_APP_ID); + expect((e as AIError).code).to.equal(AIErrorCode.NO_APP_ID); } }); }); diff --git a/packages/vertexai/src/models/vertexai-model.ts b/packages/vertexai/src/models/ai-model.ts similarity index 55% rename from packages/vertexai/src/models/vertexai-model.ts rename to packages/vertexai/src/models/ai-model.ts index cac14845961..084dbe329cc 100644 --- a/packages/vertexai/src/models/vertexai-model.ts +++ b/packages/vertexai/src/models/ai-model.ts @@ -15,18 +15,21 @@ * limitations under the License. */ -import { VertexAIError } from '../errors'; -import { VertexAI, VertexAIErrorCode } from '../public-types'; -import { VertexAIService } from '../service'; +import { AIError } from '../errors'; +import { AIErrorCode, AI, BackendType } from '../public-types'; +import { AIService } from '../service'; import { ApiSettings } from '../types/internal'; import { _isFirebaseServerApp } from '@firebase/app'; /** - * Base class for Vertex AI in Firebase model APIs. + * Base class for Firebase AI model APIs. + * + * Instances of this class are associated with a specific Firebase AI {@link Backend} + * and provide methods for interacting with the configured generative model. * * @public */ -export abstract class VertexAIModel { +export abstract class AIModel { /** * The fully qualified model resource name to use for generating images * (for example, `publishers/google/models/imagen-3.0-generate-002`). @@ -39,12 +42,12 @@ export abstract class VertexAIModel { protected _apiSettings: ApiSettings; /** - * Constructs a new instance of the {@link VertexAIModel} class. + * Constructs a new instance of the {@link AIModel} class. * * This constructor should only be called from subclasses that provide * a model API. * - * @param vertexAI - An instance of the Vertex AI in Firebase SDK. + * @param ai - an {@link AI} instance. * @param modelName - The name of the model being used. It can be in one of the following formats: * - `my-model` (short name, will resolve to `publishers/google/models/my-model`) * - `models/my-model` (will resolve to `publishers/google/models/my-model`) @@ -55,51 +58,51 @@ export abstract class VertexAIModel { * * @internal */ - protected constructor(vertexAI: VertexAI, modelName: string) { - this.model = VertexAIModel.normalizeModelName(modelName); - - if (!vertexAI.app?.options?.apiKey) { - throw new VertexAIError( - VertexAIErrorCode.NO_API_KEY, - `The "apiKey" field is empty in the local Firebase config. Firebase VertexAI requires this field to contain a valid API key.` + protected constructor(ai: AI, modelName: string) { + if (!ai.app?.options?.apiKey) { + throw new AIError( + AIErrorCode.NO_API_KEY, + `The "apiKey" field is empty in the local Firebase config. Firebase AI requires this field to contain a valid API key.` ); - } else if (!vertexAI.app?.options?.projectId) { - throw new VertexAIError( - VertexAIErrorCode.NO_PROJECT_ID, - `The "projectId" field is empty in the local Firebase config. Firebase VertexAI requires this field to contain a valid project ID.` + } else if (!ai.app?.options?.projectId) { + throw new AIError( + AIErrorCode.NO_PROJECT_ID, + `The "projectId" field is empty in the local Firebase config. Firebase AI requires this field to contain a valid project ID.` ); - } else if (!vertexAI.app?.options?.appId) { - throw new VertexAIError( - VertexAIErrorCode.NO_APP_ID, - `The "appId" field is empty in the local Firebase config. Firebase VertexAI requires this field to contain a valid app ID.` + } else if (!ai.app?.options?.appId) { + throw new AIError( + AIErrorCode.NO_APP_ID, + `The "appId" field is empty in the local Firebase config. Firebase AI requires this field to contain a valid app ID.` ); } else { this._apiSettings = { - apiKey: vertexAI.app.options.apiKey, - project: vertexAI.app.options.projectId, - appId: vertexAI.app.options.appId, - automaticDataCollectionEnabled: - vertexAI.app.automaticDataCollectionEnabled, - location: vertexAI.location + apiKey: ai.app.options.apiKey, + project: ai.app.options.projectId, + appId: ai.app.options.appId, + automaticDataCollectionEnabled: ai.app.automaticDataCollectionEnabled, + location: ai.location, + backend: ai.backend }; - if ( - _isFirebaseServerApp(vertexAI.app) && - vertexAI.app.settings.appCheckToken - ) { - const token = vertexAI.app.settings.appCheckToken; + if (_isFirebaseServerApp(ai.app) && ai.app.settings.appCheckToken) { + const token = ai.app.settings.appCheckToken; this._apiSettings.getAppCheckToken = () => { return Promise.resolve({ token }); }; - } else if ((vertexAI as VertexAIService).appCheck) { + } else if ((ai as AIService).appCheck) { this._apiSettings.getAppCheckToken = () => - (vertexAI as VertexAIService).appCheck!.getToken(); + (ai as AIService).appCheck!.getToken(); } - if ((vertexAI as VertexAIService).auth) { + if ((ai as AIService).auth) { this._apiSettings.getAuthToken = () => - (vertexAI as VertexAIService).auth!.getToken(); + (ai as AIService).auth!.getToken(); } + + this.model = AIModel.normalizeModelName( + modelName, + this._apiSettings.backend.backendType + ); } } @@ -108,8 +111,31 @@ export abstract class VertexAIModel { * * @param modelName - The model name to normalize. * @returns The fully qualified model resource name. + * + * @internal + */ + static normalizeModelName( + modelName: string, + backendType: BackendType + ): string { + if (backendType === BackendType.GOOGLE_AI) { + return AIModel.normalizeGoogleAIModelName(modelName); + } else { + return AIModel.normalizeVertexAIModelName(modelName); + } + } + + /** + * @internal + */ + private static normalizeGoogleAIModelName(modelName: string): string { + return `models/${modelName}`; + } + + /** + * @internal */ - static normalizeModelName(modelName: string): string { + private static normalizeVertexAIModelName(modelName: string): string { let model: string; if (modelName.includes('/')) { if (modelName.startsWith('models/')) { diff --git a/packages/vertexai/src/models/generative-model.test.ts b/packages/vertexai/src/models/generative-model.test.ts index 51ea8aafead..d055b82b1be 100644 --- a/packages/vertexai/src/models/generative-model.test.ts +++ b/packages/vertexai/src/models/generative-model.test.ts @@ -16,15 +16,16 @@ */ import { use, expect } from 'chai'; import { GenerativeModel } from './generative-model'; -import { FunctionCallingMode, VertexAI } from '../public-types'; +import { FunctionCallingMode, AI } from '../public-types'; import * as request from '../requests/request'; import { match, restore, stub } from 'sinon'; import { getMockResponse } from '../../test-utils/mock-response'; import sinonChai from 'sinon-chai'; +import { VertexAIBackend } from '../backend'; use(sinonChai); -const fakeVertexAI: VertexAI = { +const fakeAI: AI = { app: { name: 'DEFAULT', automaticDataCollectionEnabled: true, @@ -34,12 +35,13 @@ const fakeVertexAI: VertexAI = { appId: 'my-appid' } }, + backend: new VertexAIBackend('us-central1'), location: 'us-central1' }; describe('GenerativeModel', () => { it('passes params through to generateContent', async () => { - const genModel = new GenerativeModel(fakeVertexAI, { + const genModel = new GenerativeModel(fakeAI, { model: 'my-model', tools: [ { @@ -84,7 +86,7 @@ describe('GenerativeModel', () => { restore(); }); it('passes text-only systemInstruction through to generateContent', async () => { - const genModel = new GenerativeModel(fakeVertexAI, { + const genModel = new GenerativeModel(fakeAI, { model: 'my-model', systemInstruction: 'be friendly' }); @@ -110,7 +112,7 @@ describe('GenerativeModel', () => { restore(); }); it('generateContent overrides model values', async () => { - const genModel = new GenerativeModel(fakeVertexAI, { + const genModel = new GenerativeModel(fakeAI, { model: 'my-model', tools: [ { @@ -165,8 +167,37 @@ describe('GenerativeModel', () => { ); restore(); }); + it('passes base model params through to ChatSession when there are no startChatParams', async () => { + const genModel = new GenerativeModel(fakeAI, { + model: 'my-model', + generationConfig: { + topK: 1 + } + }); + const chatSession = genModel.startChat(); + expect(chatSession.params?.generationConfig).to.deep.equal({ + topK: 1 + }); + restore(); + }); + it('overrides base model params with startChatParams', () => { + const genModel = new GenerativeModel(fakeAI, { + model: 'my-model', + generationConfig: { + topK: 1 + } + }); + const chatSession = genModel.startChat({ + generationConfig: { + topK: 2 + } + }); + expect(chatSession.params?.generationConfig).to.deep.equal({ + topK: 2 + }); + }); it('passes params through to chat.sendMessage', async () => { - const genModel = new GenerativeModel(fakeVertexAI, { + const genModel = new GenerativeModel(fakeAI, { model: 'my-model', tools: [ { functionDeclarations: [{ name: 'myfunc', description: 'mydesc' }] } @@ -208,7 +239,7 @@ describe('GenerativeModel', () => { restore(); }); it('passes text-only systemInstruction through to chat.sendMessage', async () => { - const genModel = new GenerativeModel(fakeVertexAI, { + const genModel = new GenerativeModel(fakeAI, { model: 'my-model', systemInstruction: 'be friendly' }); @@ -234,7 +265,7 @@ describe('GenerativeModel', () => { restore(); }); it('startChat overrides model values', async () => { - const genModel = new GenerativeModel(fakeVertexAI, { + const genModel = new GenerativeModel(fakeAI, { model: 'my-model', tools: [ { functionDeclarations: [{ name: 'myfunc', description: 'mydesc' }] } @@ -294,7 +325,7 @@ describe('GenerativeModel', () => { restore(); }); it('calls countTokens', async () => { - const genModel = new GenerativeModel(fakeVertexAI, { model: 'my-model' }); + const genModel = new GenerativeModel(fakeAI, { model: 'my-model' }); const mockResponse = getMockResponse( 'vertexAI', 'unary-success-total-tokens.json' diff --git a/packages/vertexai/src/models/generative-model.ts b/packages/vertexai/src/models/generative-model.ts index 1af1ee700d5..b09a9290aa4 100644 --- a/packages/vertexai/src/models/generative-model.ts +++ b/packages/vertexai/src/models/generative-model.ts @@ -41,14 +41,14 @@ import { formatGenerateContentInput, formatSystemInstruction } from '../requests/request-helpers'; -import { VertexAI } from '../public-types'; -import { VertexAIModel } from './vertexai-model'; +import { AI } from '../public-types'; +import { AIModel } from './ai-model'; /** * Class for generative model APIs. * @public */ -export class GenerativeModel extends VertexAIModel { +export class GenerativeModel extends AIModel { generationConfig: GenerationConfig; safetySettings: SafetySetting[]; requestOptions?: RequestOptions; @@ -57,11 +57,11 @@ export class GenerativeModel extends VertexAIModel { systemInstruction?: Content; constructor( - vertexAI: VertexAI, + ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions ) { - super(vertexAI, modelParams.model); + super(ai, modelParams.model); this.generationConfig = modelParams.generationConfig || {}; this.safetySettings = modelParams.safetySettings || []; this.tools = modelParams.tools; diff --git a/packages/vertexai/src/models/imagen-model.test.ts b/packages/vertexai/src/models/imagen-model.test.ts index 9e534f2195a..f4121e18f2d 100644 --- a/packages/vertexai/src/models/imagen-model.test.ts +++ b/packages/vertexai/src/models/imagen-model.test.ts @@ -20,18 +20,19 @@ import { ImagenAspectRatio, ImagenPersonFilterLevel, ImagenSafetyFilterLevel, - VertexAI, - VertexAIErrorCode + AI, + AIErrorCode } from '../public-types'; import * as request from '../requests/request'; import sinonChai from 'sinon-chai'; -import { VertexAIError } from '../errors'; +import { AIError } from '../errors'; import { getMockResponse } from '../../test-utils/mock-response'; import { match, restore, stub } from 'sinon'; +import { VertexAIBackend } from '../backend'; use(sinonChai); -const fakeVertexAI: VertexAI = { +const fakeAI: AI = { app: { name: 'DEFAULT', automaticDataCollectionEnabled: true, @@ -41,6 +42,7 @@ const fakeVertexAI: VertexAI = { appId: 'my-appid' } }, + backend: new VertexAIBackend('us-central1'), location: 'us-central1' }; @@ -54,7 +56,7 @@ describe('ImagenModel', () => { mockResponse as Response ); - const imagenModel = new ImagenModel(fakeVertexAI, { + const imagenModel = new ImagenModel(fakeAI, { model: 'my-model' }); const prompt = 'A photorealistic image of a toy boat at sea.'; @@ -75,7 +77,7 @@ describe('ImagenModel', () => { restore(); }); it('generateImages makes a request to predict with generation config and safety settings', async () => { - const imagenModel = new ImagenModel(fakeVertexAI, { + const imagenModel = new ImagenModel(fakeAI, { model: 'my-model', generationConfig: { negativePrompt: 'do not hallucinate', @@ -146,15 +148,15 @@ describe('ImagenModel', () => { json: mockResponse.json } as Response); - const imagenModel = new ImagenModel(fakeVertexAI, { + const imagenModel = new ImagenModel(fakeAI, { model: 'my-model' }); try { await imagenModel.generateImages('some inappropriate prompt.'); } catch (e) { - expect((e as VertexAIError).code).to.equal(VertexAIErrorCode.FETCH_ERROR); - expect((e as VertexAIError).message).to.include('400'); - expect((e as VertexAIError).message).to.include( + expect((e as AIError).code).to.equal(AIErrorCode.FETCH_ERROR); + expect((e as AIError).message).to.include('400'); + expect((e as AIError).message).to.include( "Image generation failed with the following error: The prompt could not be submitted. This prompt contains sensitive words that violate Google's Responsible AI practices. Try rephrasing the prompt. If you think this was an error, send feedback." ); } finally { diff --git a/packages/vertexai/src/models/imagen-model.ts b/packages/vertexai/src/models/imagen-model.ts index 04514ef6ffd..3c76a1c721c 100644 --- a/packages/vertexai/src/models/imagen-model.ts +++ b/packages/vertexai/src/models/imagen-model.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { VertexAI } from '../public-types'; +import { AI } from '../public-types'; import { Task, makeRequest } from '../requests/request'; import { createPredictRequestBody } from '../requests/request-helpers'; import { handlePredictResponse } from '../requests/response-helpers'; @@ -28,7 +28,7 @@ import { ImagenGenerationResponse, ImagenSafetySettings } from '../types'; -import { VertexAIModel } from './vertexai-model'; +import { AIModel } from './ai-model'; /** * Class for Imagen model APIs. @@ -38,7 +38,7 @@ import { VertexAIModel } from './vertexai-model'; * @example * ```javascript * const imagen = new ImagenModel( - * vertexAI, + * ai, * { * model: 'imagen-3.0-generate-002' * } @@ -52,7 +52,7 @@ import { VertexAIModel } from './vertexai-model'; * * @beta */ -export class ImagenModel extends VertexAIModel { +export class ImagenModel extends AIModel { /** * The Imagen generation configuration. */ @@ -65,7 +65,7 @@ export class ImagenModel extends VertexAIModel { /** * Constructs a new instance of the {@link ImagenModel} class. * - * @param vertexAI - An instance of the Vertex AI in Firebase SDK. + * @param ai - an {@link AI} instance. * @param modelParams - Parameters to use when making requests to Imagen. * @param requestOptions - Additional options to use when making requests. * @@ -73,12 +73,12 @@ export class ImagenModel extends VertexAIModel { * Firebase config. */ constructor( - vertexAI: VertexAI, + ai: AI, modelParams: ImagenModelParams, public requestOptions?: RequestOptions ) { const { model, generationConfig, safetySettings } = modelParams; - super(vertexAI, model); + super(ai, model); this.generationConfig = generationConfig; this.safetySettings = safetySettings; } diff --git a/packages/vertexai/src/models/index.ts b/packages/vertexai/src/models/index.ts index aec06be26fd..cb694a5360b 100644 --- a/packages/vertexai/src/models/index.ts +++ b/packages/vertexai/src/models/index.ts @@ -15,6 +15,6 @@ * limitations under the License. */ -export * from './vertexai-model'; +export * from './ai-model'; export * from './generative-model'; export * from './imagen-model'; diff --git a/packages/vertexai/src/public-types.ts b/packages/vertexai/src/public-types.ts index fbc5d51084d..b12615a81ed 100644 --- a/packages/vertexai/src/public-types.ts +++ b/packages/vertexai/src/public-types.ts @@ -16,25 +16,98 @@ */ import { FirebaseApp } from '@firebase/app'; +import { Backend } from './backend'; export * from './types'; /** - * An instance of the Vertex AI in Firebase SDK. + * An instance of the Firebase AI SDK. + * + * For more information, refer to the documentation for the new {@link AI} interface. + * * @public */ -export interface VertexAI { +export type VertexAI = AI; + +/** + * Options when initializing the Firebase AI SDK. + * + * @public + */ +export interface VertexAIOptions { + location?: string; +} + +/** + * An instance of the Firebase AI SDK. + * + * Do not create this instance directly. Instead, use {@link getAI | getAI()}. + * + * @public + */ +export interface AI { /** - * The {@link @firebase/app#FirebaseApp} this {@link VertexAI} instance is associated with. + * The {@link @firebase/app#FirebaseApp} this {@link AI} instance is associated with. */ app: FirebaseApp; + /** + * A {@link Backend} instance that specifies the configuration for the target backend, + * either the Gemini Developer API (using {@link GoogleAIBackend}) or the + * Vertex AI Gemini API (using {@link VertexAIBackend}). + */ + backend: Backend; + /** + * The location configured for this AI service instance, relevant for Vertex AI backends. + * + * @deprecated use `AI.backend.location` instead. + */ location: string; } /** - * Options when initializing the Vertex AI in Firebase SDK. + * An enum-like object containing constants that represent the supported backends + * for the Firebase AI SDK. + * This determines which backend service (Vertex AI Gemini API or Gemini Developer API) + * the SDK will communicate with. + * + * These values are assigned to the `backendType` property within the specific backend + * configuration objects ({@link GoogleAIBackend} or {@link VertexAIBackend}) to identify + * which service to target. + * * @public */ -export interface VertexAIOptions { - location?: string; +export const BackendType = { + /** + * Identifies the backend service for the Vertex AI Gemini API provided through Google Cloud. + * Use this constant when creating a {@link VertexAIBackend} configuration. + */ + VERTEX_AI: 'VERTEX_AI', + + /** + * Identifies the backend service for the Gemini Developer API ({@link https://ai.google/ | Google AI}). + * Use this constant when creating a {@link GoogleAIBackend} configuration. + */ + GOOGLE_AI: 'GOOGLE_AI' +} as const; // Using 'as const' makes the string values literal types + +/** + * Type alias representing valid backend types. + * It can be either `'VERTEX_AI'` or `'GOOGLE_AI'`. + * + * @public + */ +export type BackendType = (typeof BackendType)[keyof typeof BackendType]; + +/** + * Options for initializing the AI service using {@link getAI | getAI()}. + * This allows specifying which backend to use (Vertex AI Gemini API or Gemini Developer API) + * and configuring its specific options (like location for Vertex AI). + * + * @public + */ +export interface AIOptions { + /** + * The backend configuration to use for the AI service instance. + */ + backend: Backend; } diff --git a/packages/vertexai/src/requests/request-helpers.ts b/packages/vertexai/src/requests/request-helpers.ts index fd2cd04e0fd..c4cc1a20acc 100644 --- a/packages/vertexai/src/requests/request-helpers.ts +++ b/packages/vertexai/src/requests/request-helpers.ts @@ -15,13 +15,8 @@ * limitations under the License. */ -import { - Content, - GenerateContentRequest, - Part, - VertexAIErrorCode -} from '../types'; -import { VertexAIError } from '../errors'; +import { Content, GenerateContentRequest, Part, AIErrorCode } from '../types'; +import { AIError } from '../errors'; import { ImagenGenerationParams, PredictRequestBody } from '../types/internal'; export function formatSystemInstruction( @@ -87,15 +82,15 @@ function assignRoleToPartsAndValidateSendMessageRequest( } if (hasUserContent && hasFunctionContent) { - throw new VertexAIError( - VertexAIErrorCode.INVALID_CONTENT, + throw new AIError( + AIErrorCode.INVALID_CONTENT, 'Within a single message, FunctionResponse cannot be mixed with other type of Part in the request for sending chat message.' ); } if (!hasUserContent && !hasFunctionContent) { - throw new VertexAIError( - VertexAIErrorCode.INVALID_CONTENT, + throw new AIError( + AIErrorCode.INVALID_CONTENT, 'No Content is provided for sending chat message.' ); } diff --git a/packages/vertexai/src/requests/request.test.ts b/packages/vertexai/src/requests/request.test.ts index cd39a0f8ae5..0d162906fdc 100644 --- a/packages/vertexai/src/requests/request.test.ts +++ b/packages/vertexai/src/requests/request.test.ts @@ -22,9 +22,10 @@ import chaiAsPromised from 'chai-as-promised'; import { RequestUrl, Task, getHeaders, makeRequest } from './request'; import { ApiSettings } from '../types/internal'; import { DEFAULT_API_VERSION } from '../constants'; -import { VertexAIErrorCode } from '../types'; -import { VertexAIError } from '../errors'; +import { AIErrorCode } from '../types'; +import { AIError } from '../errors'; import { getMockResponse } from '../../test-utils/mock-response'; +import { VertexAIBackend } from '../backend'; use(sinonChai); use(chaiAsPromised); @@ -33,7 +34,8 @@ const fakeApiSettings: ApiSettings = { apiKey: 'key', project: 'my-project', appId: 'my-appid', - location: 'us-central1' + location: 'us-central1', + backend: new VertexAIBackend() }; describe('request methods', () => { @@ -106,6 +108,7 @@ describe('request methods', () => { project: 'myproject', appId: 'my-appid', location: 'moon', + backend: new VertexAIBackend(), getAuthToken: () => Promise.resolve({ accessToken: 'authtoken' }), getAppCheckToken: () => Promise.resolve({ token: 'appchecktoken' }) }; @@ -132,6 +135,7 @@ describe('request methods', () => { project: 'myproject', appId: 'my-appid', location: 'moon', + backend: new VertexAIBackend(), automaticDataCollectionEnabled: true, getAuthToken: () => Promise.resolve({ accessToken: 'authtoken' }), getAppCheckToken: () => Promise.resolve({ token: 'appchecktoken' }) @@ -156,6 +160,7 @@ describe('request methods', () => { project: 'myproject', appId: 'my-appid', location: 'moon', + backend: new VertexAIBackend(), automaticDataCollectionEnabled: false, getAuthToken: () => Promise.resolve({ accessToken: 'authtoken' }), getAppCheckToken: () => Promise.resolve({ token: 'appchecktoken' }) @@ -182,7 +187,8 @@ describe('request methods', () => { apiKey: 'key', project: 'myproject', appId: 'my-appid', - location: 'moon' + location: 'moon', + backend: new VertexAIBackend() }, true, {} @@ -216,6 +222,7 @@ describe('request methods', () => { project: 'myproject', appId: 'my-appid', location: 'moon', + backend: new VertexAIBackend(), getAppCheckToken: () => Promise.resolve({ token: 'dummytoken', error: Error('oops') }) }, @@ -242,7 +249,8 @@ describe('request methods', () => { apiKey: 'key', project: 'myproject', appId: 'my-appid', - location: 'moon' + location: 'moon', + backend: new VertexAIBackend() }, true, {} @@ -302,14 +310,12 @@ describe('request methods', () => { } ); } catch (e) { - expect((e as VertexAIError).code).to.equal( - VertexAIErrorCode.FETCH_ERROR - ); - expect((e as VertexAIError).customErrorData?.status).to.equal(500); - expect((e as VertexAIError).customErrorData?.statusText).to.equal( + expect((e as AIError).code).to.equal(AIErrorCode.FETCH_ERROR); + expect((e as AIError).customErrorData?.status).to.equal(500); + expect((e as AIError).customErrorData?.statusText).to.equal( 'AbortError' ); - expect((e as VertexAIError).message).to.include('500 AbortError'); + expect((e as AIError).message).to.include('500 AbortError'); } expect(fetchStub).to.be.calledOnce; @@ -329,14 +335,12 @@ describe('request methods', () => { '' ); } catch (e) { - expect((e as VertexAIError).code).to.equal( - VertexAIErrorCode.FETCH_ERROR - ); - expect((e as VertexAIError).customErrorData?.status).to.equal(500); - expect((e as VertexAIError).customErrorData?.statusText).to.equal( + expect((e as AIError).code).to.equal(AIErrorCode.FETCH_ERROR); + expect((e as AIError).customErrorData?.status).to.equal(500); + expect((e as AIError).customErrorData?.statusText).to.equal( 'Server Error' ); - expect((e as VertexAIError).message).to.include('500 Server Error'); + expect((e as AIError).message).to.include('500 Server Error'); } expect(fetchStub).to.be.calledOnce; }); @@ -356,15 +360,13 @@ describe('request methods', () => { '' ); } catch (e) { - expect((e as VertexAIError).code).to.equal( - VertexAIErrorCode.FETCH_ERROR - ); - expect((e as VertexAIError).customErrorData?.status).to.equal(500); - expect((e as VertexAIError).customErrorData?.statusText).to.equal( + expect((e as AIError).code).to.equal(AIErrorCode.FETCH_ERROR); + expect((e as AIError).customErrorData?.status).to.equal(500); + expect((e as AIError).customErrorData?.statusText).to.equal( 'Server Error' ); - expect((e as VertexAIError).message).to.include('500 Server Error'); - expect((e as VertexAIError).message).to.include('extra info'); + expect((e as AIError).message).to.include('500 Server Error'); + expect((e as AIError).message).to.include('extra info'); } expect(fetchStub).to.be.calledOnce; }); @@ -396,18 +398,14 @@ describe('request methods', () => { '' ); } catch (e) { - expect((e as VertexAIError).code).to.equal( - VertexAIErrorCode.FETCH_ERROR - ); - expect((e as VertexAIError).customErrorData?.status).to.equal(500); - expect((e as VertexAIError).customErrorData?.statusText).to.equal( + expect((e as AIError).code).to.equal(AIErrorCode.FETCH_ERROR); + expect((e as AIError).customErrorData?.status).to.equal(500); + expect((e as AIError).customErrorData?.statusText).to.equal( 'Server Error' ); - expect((e as VertexAIError).message).to.include('500 Server Error'); - expect((e as VertexAIError).message).to.include('extra info'); - expect((e as VertexAIError).message).to.include( - 'generic::invalid_argument' - ); + expect((e as AIError).message).to.include('500 Server Error'); + expect((e as AIError).message).to.include('extra info'); + expect((e as AIError).message).to.include('generic::invalid_argument'); } expect(fetchStub).to.be.calledOnce; }); @@ -429,11 +427,9 @@ describe('request methods', () => { '' ); } catch (e) { - expect((e as VertexAIError).code).to.equal( - VertexAIErrorCode.API_NOT_ENABLED - ); - expect((e as VertexAIError).message).to.include('my-project'); - expect((e as VertexAIError).message).to.include('googleapis.com'); + expect((e as AIError).code).to.equal(AIErrorCode.API_NOT_ENABLED); + expect((e as AIError).message).to.include('my-project'); + expect((e as AIError).message).to.include('googleapis.com'); } expect(fetchStub).to.be.calledOnce; }); diff --git a/packages/vertexai/src/requests/request.ts b/packages/vertexai/src/requests/request.ts index 47e4c6ab446..31c5e9b8125 100644 --- a/packages/vertexai/src/requests/request.ts +++ b/packages/vertexai/src/requests/request.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import { ErrorDetails, RequestOptions, VertexAIErrorCode } from '../types'; -import { VertexAIError } from '../errors'; +import { ErrorDetails, RequestOptions, AIErrorCode } from '../types'; +import { AIError } from '../errors'; import { ApiSettings } from '../types/internal'; import { DEFAULT_API_VERSION, @@ -26,6 +26,7 @@ import { PACKAGE_VERSION } from '../constants'; import { logger } from '../logger'; +import { GoogleAIBackend, VertexAIBackend } from '../backend'; export enum Task { GENERATE_CONTENT = 'generateContent', @@ -43,29 +44,40 @@ export class RequestUrl { public requestOptions?: RequestOptions ) {} toString(): string { - // TODO: allow user-set option if that feature becomes available - const apiVersion = DEFAULT_API_VERSION; - const baseUrl = this.requestOptions?.baseUrl || DEFAULT_BASE_URL; - let url = `${baseUrl}/${apiVersion}`; - url += `/projects/${this.apiSettings.project}`; - url += `/locations/${this.apiSettings.location}`; - url += `/${this.model}`; - url += `:${this.task}`; - if (this.stream) { - url += '?alt=sse'; + const url = new URL(this.baseUrl); // Throws if the URL is invalid + url.pathname = `/${this.apiVersion}/${this.modelPath}:${this.task}`; + url.search = this.queryParams.toString(); + return url.toString(); + } + + private get baseUrl(): string { + return this.requestOptions?.baseUrl || DEFAULT_BASE_URL; + } + + private get apiVersion(): string { + return DEFAULT_API_VERSION; // TODO: allow user-set options if that feature becomes available + } + + private get modelPath(): string { + if (this.apiSettings.backend instanceof GoogleAIBackend) { + return `projects/${this.apiSettings.project}/${this.model}`; + } else if (this.apiSettings.backend instanceof VertexAIBackend) { + return `projects/${this.apiSettings.project}/locations/${this.apiSettings.backend.location}/${this.model}`; + } else { + throw new AIError( + AIErrorCode.ERROR, + `Invalid backend: ${JSON.stringify(this.apiSettings.backend)}` + ); } - return url; } - /** - * If the model needs to be passed to the backend, it needs to - * include project and location path. - */ - get fullModelString(): string { - let modelString = `projects/${this.apiSettings.project}`; - modelString += `/locations/${this.apiSettings.location}`; - modelString += `/${this.model}`; - return modelString; + private get queryParams(): URLSearchParams { + const params = new URLSearchParams(); + if (this.stream) { + params.set('alt', 'sse'); + } + + return params; } } @@ -184,9 +196,9 @@ export async function makeRequest( ) ) ) { - throw new VertexAIError( - VertexAIErrorCode.API_NOT_ENABLED, - `The Vertex AI in Firebase SDK requires the Vertex AI in Firebase ` + + throw new AIError( + AIErrorCode.API_NOT_ENABLED, + `The Firebase AI SDK requires the Firebase AI ` + `API ('firebasevertexai.googleapis.com') to be enabled in your ` + `Firebase project. Enable this API by visiting the Firebase Console ` + `at https://console.firebase.google.com/project/${url.apiSettings.project}/genai/ ` + @@ -200,8 +212,8 @@ export async function makeRequest( } ); } - throw new VertexAIError( - VertexAIErrorCode.FETCH_ERROR, + throw new AIError( + AIErrorCode.FETCH_ERROR, `Error fetching from ${url}: [${response.status} ${response.statusText}] ${message}`, { status: response.status, @@ -213,12 +225,12 @@ export async function makeRequest( } catch (e) { let err = e as Error; if ( - (e as VertexAIError).code !== VertexAIErrorCode.FETCH_ERROR && - (e as VertexAIError).code !== VertexAIErrorCode.API_NOT_ENABLED && + (e as AIError).code !== AIErrorCode.FETCH_ERROR && + (e as AIError).code !== AIErrorCode.API_NOT_ENABLED && e instanceof Error ) { - err = new VertexAIError( - VertexAIErrorCode.ERROR, + err = new AIError( + AIErrorCode.ERROR, `Error fetching from ${url.toString()}: ${e.message}` ); err.stack = e.stack; diff --git a/packages/vertexai/src/requests/response-helpers.ts b/packages/vertexai/src/requests/response-helpers.ts index d820f100a50..20678eeea68 100644 --- a/packages/vertexai/src/requests/response-helpers.ts +++ b/packages/vertexai/src/requests/response-helpers.ts @@ -23,10 +23,10 @@ import { GenerateContentResponse, ImagenGCSImage, ImagenInlineImage, - InlineDataPart, - VertexAIErrorCode + AIErrorCode, + InlineDataPart } from '../types'; -import { VertexAIError } from '../errors'; +import { AIError } from '../errors'; import { logger } from '../logger'; import { ImagenResponseInternal } from '../types/internal'; @@ -68,8 +68,8 @@ export function addHelpers( ); } if (hadBadFinishReason(response.candidates[0])) { - throw new VertexAIError( - VertexAIErrorCode.RESPONSE_ERROR, + throw new AIError( + AIErrorCode.RESPONSE_ERROR, `Response error: ${formatBlockErrorMessage( response )}. Response body stored in error.response`, @@ -80,8 +80,8 @@ export function addHelpers( } return getText(response); } else if (response.promptFeedback) { - throw new VertexAIError( - VertexAIErrorCode.RESPONSE_ERROR, + throw new AIError( + AIErrorCode.RESPONSE_ERROR, `Text not available. ${formatBlockErrorMessage(response)}`, { response @@ -102,8 +102,8 @@ export function addHelpers( ); } if (hadBadFinishReason(response.candidates[0])) { - throw new VertexAIError( - VertexAIErrorCode.RESPONSE_ERROR, + throw new AIError( + AIErrorCode.RESPONSE_ERROR, `Response error: ${formatBlockErrorMessage( response )}. Response body stored in error.response`, @@ -114,8 +114,8 @@ export function addHelpers( } return getInlineDataParts(response); } else if (response.promptFeedback) { - throw new VertexAIError( - VertexAIErrorCode.RESPONSE_ERROR, + throw new AIError( + AIErrorCode.RESPONSE_ERROR, `Data not available. ${formatBlockErrorMessage(response)}`, { response @@ -134,8 +134,8 @@ export function addHelpers( ); } if (hadBadFinishReason(response.candidates[0])) { - throw new VertexAIError( - VertexAIErrorCode.RESPONSE_ERROR, + throw new AIError( + AIErrorCode.RESPONSE_ERROR, `Response error: ${formatBlockErrorMessage( response )}. Response body stored in error.response`, @@ -146,8 +146,8 @@ export function addHelpers( } return getFunctionCalls(response); } else if (response.promptFeedback) { - throw new VertexAIError( - VertexAIErrorCode.RESPONSE_ERROR, + throw new AIError( + AIErrorCode.RESPONSE_ERROR, `Function call not available. ${formatBlockErrorMessage(response)}`, { response @@ -277,8 +277,8 @@ export async function handlePredictResponse< // The backend should always send a non-empty array of predictions if the response was successful. if (!responseJson.predictions || responseJson.predictions?.length === 0) { - throw new VertexAIError( - VertexAIErrorCode.RESPONSE_ERROR, + throw new AIError( + AIErrorCode.RESPONSE_ERROR, 'No predictions or filtered reason received from Vertex AI. Please report this issue with the full error details at https://github.com/firebase/firebase-js-sdk/issues.' ); } @@ -297,8 +297,8 @@ export async function handlePredictResponse< gcsURI: prediction.gcsUri } as T); } else { - throw new VertexAIError( - VertexAIErrorCode.RESPONSE_ERROR, + throw new AIError( + AIErrorCode.RESPONSE_ERROR, `Predictions array in response has missing properties. Response: ${JSON.stringify( responseJson )}` diff --git a/packages/vertexai/src/requests/schema-builder.test.ts b/packages/vertexai/src/requests/schema-builder.test.ts index b95acaae9f1..d05b81381ea 100644 --- a/packages/vertexai/src/requests/schema-builder.test.ts +++ b/packages/vertexai/src/requests/schema-builder.test.ts @@ -18,7 +18,7 @@ import { expect, use } from 'chai'; import sinonChai from 'sinon-chai'; import { Schema } from './schema-builder'; -import { VertexAIErrorCode } from '../types'; +import { AIErrorCode } from '../types'; use(sinonChai); @@ -243,7 +243,7 @@ describe('Schema builder', () => { }, optionalProperties: ['cat'] }); - expect(() => schema.toJSON()).to.throw(VertexAIErrorCode.INVALID_SCHEMA); + expect(() => schema.toJSON()).to.throw(AIErrorCode.INVALID_SCHEMA); }); }); diff --git a/packages/vertexai/src/requests/schema-builder.ts b/packages/vertexai/src/requests/schema-builder.ts index 3d219d58b13..524cfdb1c20 100644 --- a/packages/vertexai/src/requests/schema-builder.ts +++ b/packages/vertexai/src/requests/schema-builder.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import { VertexAIError } from '../errors'; -import { VertexAIErrorCode } from '../types'; +import { AIError } from '../errors'; +import { AIErrorCode } from '../types'; import { SchemaInterface, SchemaType, @@ -266,8 +266,8 @@ export class ObjectSchema extends Schema { if (this.optionalProperties) { for (const propertyKey of this.optionalProperties) { if (!this.properties.hasOwnProperty(propertyKey)) { - throw new VertexAIError( - VertexAIErrorCode.INVALID_SCHEMA, + throw new AIError( + AIErrorCode.INVALID_SCHEMA, `Property "${propertyKey}" specified in "optionalProperties" does not exist.` ); } diff --git a/packages/vertexai/src/requests/stream-reader.test.ts b/packages/vertexai/src/requests/stream-reader.test.ts index bf959276a93..ea832c7816f 100644 --- a/packages/vertexai/src/requests/stream-reader.test.ts +++ b/packages/vertexai/src/requests/stream-reader.test.ts @@ -34,9 +34,19 @@ import { HarmCategory, HarmProbability, SafetyRating, - VertexAIErrorCode + AIErrorCode } from '../types'; -import { VertexAIError } from '../errors'; +import { AIError } from '../errors'; +import { ApiSettings } from '../types/internal'; +import { VertexAIBackend } from '../backend'; + +const fakeApiSettings: ApiSettings = { + apiKey: 'key', + project: 'my-project', + appId: 'my-appid', + location: 'us-central1', + backend: new VertexAIBackend() +}; use(sinonChai); @@ -75,7 +85,7 @@ describe('processStream', () => { 'vertexAI', 'streaming-success-basic-reply-short.txt' ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); for await (const response of result.stream) { expect(response.text()).to.not.be.empty; } @@ -87,7 +97,7 @@ describe('processStream', () => { 'vertexAI', 'streaming-success-basic-reply-long.txt' ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); for await (const response of result.stream) { expect(response.text()).to.not.be.empty; } @@ -101,7 +111,7 @@ describe('processStream', () => { 'streaming-success-basic-reply-long.txt', 1e6 ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); for await (const response of result.stream) { expect(response.text()).to.not.be.empty; } @@ -114,7 +124,7 @@ describe('processStream', () => { 'vertexAI', 'streaming-success-utf8.txt' ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); for await (const response of result.stream) { expect(response.text()).to.not.be.empty; } @@ -127,7 +137,7 @@ describe('processStream', () => { 'vertexAI', 'streaming-success-function-call-short.txt' ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); for await (const response of result.stream) { expect(response.text()).to.be.empty; expect(response.functionCalls()).to.be.deep.equal([ @@ -151,7 +161,7 @@ describe('processStream', () => { 'vertexAI', 'streaming-failure-finish-reason-safety.txt' ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); const aggregatedResponse = await result.response; expect(aggregatedResponse.candidates?.[0].finishReason).to.equal('SAFETY'); expect(aggregatedResponse.text).to.throw('SAFETY'); @@ -164,7 +174,7 @@ describe('processStream', () => { 'vertexAI', 'streaming-failure-prompt-blocked-safety.txt' ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); const aggregatedResponse = await result.response; expect(aggregatedResponse.text).to.throw('SAFETY'); expect(aggregatedResponse.promptFeedback?.blockReason).to.equal('SAFETY'); @@ -177,7 +187,7 @@ describe('processStream', () => { 'vertexAI', 'streaming-failure-empty-content.txt' ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); const aggregatedResponse = await result.response; expect(aggregatedResponse.text()).to.equal(''); for await (const response of result.stream) { @@ -189,7 +199,7 @@ describe('processStream', () => { 'vertexAI', 'streaming-success-unknown-safety-enum.txt' ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); const aggregatedResponse = await result.response; expect(aggregatedResponse.text()).to.include('Cats'); for await (const response of result.stream) { @@ -201,7 +211,7 @@ describe('processStream', () => { 'vertexAI', 'streaming-failure-recitation-no-content.txt' ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); const aggregatedResponse = await result.response; expect(aggregatedResponse.text).to.throw('RECITATION'); expect(aggregatedResponse.candidates?.[0].content.parts[0].text).to.include( @@ -220,7 +230,7 @@ describe('processStream', () => { 'vertexAI', 'streaming-success-citations.txt' ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); const aggregatedResponse = await result.response; expect(aggregatedResponse.text()).to.include('Quantum mechanics is'); expect( @@ -240,7 +250,7 @@ describe('processStream', () => { 'vertexAI', 'streaming-success-empty-text-part.txt' ); - const result = processStream(fakeResponse as Response); + const result = processStream(fakeResponse as Response, fakeApiSettings); const aggregatedResponse = await result.response; expect(aggregatedResponse.text()).to.equal('1'); expect(aggregatedResponse.candidates?.length).to.equal(1); @@ -472,10 +482,8 @@ describe('aggregateResponses', () => { try { aggregateResponses(responsesToAggregate); } catch (e) { - expect((e as VertexAIError).code).includes( - VertexAIErrorCode.INVALID_CONTENT - ); - expect((e as VertexAIError).message).to.include( + expect((e as AIError).code).includes(AIErrorCode.INVALID_CONTENT); + expect((e as AIError).message).to.include( 'Part should have at least one property, but there are none. This is likely caused ' + 'by a malformed response from the backend.' ); diff --git a/packages/vertexai/src/requests/stream-reader.ts b/packages/vertexai/src/requests/stream-reader.ts index 5c419d114e0..543d1d02266 100644 --- a/packages/vertexai/src/requests/stream-reader.ts +++ b/packages/vertexai/src/requests/stream-reader.ts @@ -21,10 +21,14 @@ import { GenerateContentResponse, GenerateContentStreamResult, Part, - VertexAIErrorCode + AIErrorCode } from '../types'; -import { VertexAIError } from '../errors'; +import { AIError } from '../errors'; import { createEnhancedContentResponse } from './response-helpers'; +import * as GoogleAIMapper from '../googleai-mappers'; +import { GoogleAIGenerateContentResponse } from '../types/googleai'; +import { ApiSettings } from '../types/internal'; +import { BackendType } from '../public-types'; const responseLineRE = /^data\: (.*)(?:\n\n|\r\r|\r\n\r\n)/; @@ -36,7 +40,10 @@ const responseLineRE = /^data\: (.*)(?:\n\n|\r\r|\r\n\r\n)/; * * @param response - Response from a fetch call */ -export function processStream(response: Response): GenerateContentStreamResult { +export function processStream( + response: Response, + apiSettings: ApiSettings +): GenerateContentStreamResult { const inputStream = response.body!.pipeThrough( new TextDecoderStream('utf8', { fatal: true }) ); @@ -44,23 +51,27 @@ export function processStream(response: Response): GenerateContentStreamResult { getResponseStream(inputStream); const [stream1, stream2] = responseStream.tee(); return { - stream: generateResponseSequence(stream1), - response: getResponsePromise(stream2) + stream: generateResponseSequence(stream1, apiSettings), + response: getResponsePromise(stream2, apiSettings) }; } async function getResponsePromise( - stream: ReadableStream + stream: ReadableStream, + apiSettings: ApiSettings ): Promise { const allResponses: GenerateContentResponse[] = []; const reader = stream.getReader(); while (true) { const { done, value } = await reader.read(); if (done) { - const enhancedResponse = createEnhancedContentResponse( - aggregateResponses(allResponses) - ); - return enhancedResponse; + let generateContentResponse = aggregateResponses(allResponses); + if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) { + generateContentResponse = GoogleAIMapper.mapGenerateContentResponse( + generateContentResponse as GoogleAIGenerateContentResponse + ); + } + return createEnhancedContentResponse(generateContentResponse); } allResponses.push(value); @@ -68,7 +79,8 @@ async function getResponsePromise( } async function* generateResponseSequence( - stream: ReadableStream + stream: ReadableStream, + apiSettings: ApiSettings ): AsyncGenerator { const reader = stream.getReader(); while (true) { @@ -77,7 +89,17 @@ async function* generateResponseSequence( break; } - const enhancedResponse = createEnhancedContentResponse(value); + let enhancedResponse: EnhancedGenerateContentResponse; + if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) { + enhancedResponse = createEnhancedContentResponse( + GoogleAIMapper.mapGenerateContentResponse( + value as GoogleAIGenerateContentResponse + ) + ); + } else { + enhancedResponse = createEnhancedContentResponse(value); + } + yield enhancedResponse; } } @@ -100,10 +122,7 @@ export function getResponseStream( if (done) { if (currentText.trim()) { controller.error( - new VertexAIError( - VertexAIErrorCode.PARSE_FAILED, - 'Failed to parse stream' - ) + new AIError(AIErrorCode.PARSE_FAILED, 'Failed to parse stream') ); return; } @@ -119,8 +138,8 @@ export function getResponseStream( parsedResponse = JSON.parse(match[1]); } catch (e) { controller.error( - new VertexAIError( - VertexAIErrorCode.PARSE_FAILED, + new AIError( + AIErrorCode.PARSE_FAILED, `Error parsing JSON response: "${match[1]}` ) ); @@ -198,8 +217,8 @@ export function aggregateResponses( newPart.functionCall = part.functionCall; } if (Object.keys(newPart).length === 0) { - throw new VertexAIError( - VertexAIErrorCode.INVALID_CONTENT, + throw new AIError( + AIErrorCode.INVALID_CONTENT, 'Part should have at least one property, but there are none. This is likely caused ' + 'by a malformed response from the backend.' ); diff --git a/packages/vertexai/src/service.test.ts b/packages/vertexai/src/service.test.ts index d3487e9bdd2..ba4c736e810 100644 --- a/packages/vertexai/src/service.test.ts +++ b/packages/vertexai/src/service.test.ts @@ -14,8 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { VertexAIBackend } from './backend'; import { DEFAULT_LOCATION } from './constants'; -import { VertexAIService } from './service'; +import { AIService } from './service'; import { expect } from 'chai'; const fakeApp = { @@ -27,18 +28,19 @@ const fakeApp = { } }; -describe('VertexAIService', () => { +describe('AIService', () => { + // TODO (dlarocque): move some of these tests to helpers.test.ts it('uses default location if not specified', () => { - const vertexAI = new VertexAIService(fakeApp); - expect(vertexAI.location).to.equal(DEFAULT_LOCATION); + const ai = new AIService(fakeApp, new VertexAIBackend()); + expect(ai.location).to.equal(DEFAULT_LOCATION); }); it('uses custom location if specified', () => { - const vertexAI = new VertexAIService( + const ai = new AIService( fakeApp, + new VertexAIBackend('somewhere'), /* authProvider */ undefined, - /* appCheckProvider */ undefined, - { location: 'somewhere' } + /* appCheckProvider */ undefined ); - expect(vertexAI.location).to.equal('somewhere'); + expect(ai.location).to.equal('somewhere'); }); }); diff --git a/packages/vertexai/src/service.ts b/packages/vertexai/src/service.ts index 05b2d559e58..006cc45a94e 100644 --- a/packages/vertexai/src/service.ts +++ b/packages/vertexai/src/service.ts @@ -16,7 +16,7 @@ */ import { FirebaseApp, _FirebaseService } from '@firebase/app'; -import { VertexAI, VertexAIOptions } from './public-types'; +import { AI } from './public-types'; import { AppCheckInternalComponentName, FirebaseAppCheckInternal @@ -26,24 +26,29 @@ import { FirebaseAuthInternal, FirebaseAuthInternalName } from '@firebase/auth-interop-types'; -import { DEFAULT_LOCATION } from './constants'; +import { Backend, VertexAIBackend } from './backend'; -export class VertexAIService implements VertexAI, _FirebaseService { +export class AIService implements AI, _FirebaseService { auth: FirebaseAuthInternal | null; appCheck: FirebaseAppCheckInternal | null; - location: string; + location: string; // This is here for backwards-compatibility constructor( public app: FirebaseApp, + public backend: Backend, authProvider?: Provider, - appCheckProvider?: Provider, - public options?: VertexAIOptions + appCheckProvider?: Provider ) { const appCheck = appCheckProvider?.getImmediate({ optional: true }); const auth = authProvider?.getImmediate({ optional: true }); this.auth = auth || null; this.appCheck = appCheck || null; - this.location = this.options?.location || DEFAULT_LOCATION; + + if (backend instanceof VertexAIBackend) { + this.location = backend.location; + } else { + this.location = ''; + } } _delete(): Promise { diff --git a/packages/vertexai/src/types/enums.ts b/packages/vertexai/src/types/enums.ts index d6702a0f1a8..5c6612ce26a 100644 --- a/packages/vertexai/src/types/enums.ts +++ b/packages/vertexai/src/types/enums.ts @@ -62,11 +62,13 @@ export enum HarmBlockThreshold { } /** + * This property is not supported in the Gemini Developer API ({@link GoogleAIBackend}). + * * @public */ export enum HarmBlockMethod { /** - * The harm block method uses both probability and severity scores. + * The harm block method uses both probability and severity scores. */ SEVERITY = 'SEVERITY', /** @@ -118,7 +120,14 @@ export enum HarmSeverity { /** * High level of harm severity. */ - HARM_SEVERITY_HIGH = 'HARM_SEVERITY_HIGH' + HARM_SEVERITY_HIGH = 'HARM_SEVERITY_HIGH', + /** + * Harm severity is not supported. + * + * @remarks + * The GoogleAI backend does not support `HarmSeverity`, so this value is used as a fallback. + */ + HARM_SEVERITY_UNSUPPORTED = 'HARM_SEVERITY_UNSUPPORTED' } /** diff --git a/packages/vertexai/src/types/error.ts b/packages/vertexai/src/types/error.ts index b1f075101a6..ef3ad7fc30c 100644 --- a/packages/vertexai/src/types/error.ts +++ b/packages/vertexai/src/types/error.ts @@ -58,11 +58,11 @@ export interface CustomErrorData { } /** - * Standardized error codes that {@link VertexAIError} can have. + * Standardized error codes that {@link AIError} can have. * * @public */ -export const enum VertexAIErrorCode { +export const enum AIErrorCode { /** A generic error occurred. */ ERROR = 'error', @@ -97,5 +97,8 @@ export const enum VertexAIErrorCode { NO_PROJECT_ID = 'no-project-id', /** An error occurred while parsing. */ - PARSE_FAILED = 'parse-failed' + PARSE_FAILED = 'parse-failed', + + /** An error occured due an attempt to use an unsupported feature. */ + UNSUPPORTED = 'unsupported' } diff --git a/packages/vertexai/src/types/googleai.ts b/packages/vertexai/src/types/googleai.ts new file mode 100644 index 00000000000..38c27b3fe8b --- /dev/null +++ b/packages/vertexai/src/types/googleai.ts @@ -0,0 +1,70 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Tool, + GenerationConfig, + Citation, + FinishReason, + GroundingMetadata, + PromptFeedback, + SafetyRating, + UsageMetadata +} from '../public-types'; +import { Content, Part } from './content'; + +/** + * @internal + */ +export interface GoogleAICountTokensRequest { + generateContentRequest: { + model: string; // 'models/model-name' + contents: Content[]; + systemInstruction?: string | Part | Content; + tools?: Tool[]; + generationConfig?: GenerationConfig; + }; +} + +/** + * @internal + */ +export interface GoogleAIGenerateContentResponse { + candidates?: GoogleAIGenerateContentCandidate[]; + promptFeedback?: PromptFeedback; + usageMetadata?: UsageMetadata; +} + +/** + * @internal + */ +export interface GoogleAIGenerateContentCandidate { + index: number; + content: Content; + finishReason?: FinishReason; + finishMessage?: string; + safetyRatings?: SafetyRating[]; + citationMetadata?: GoogleAICitationMetadata; + groundingMetadata?: GroundingMetadata; +} + +/** + * @internal + */ +export interface GoogleAICitationMetadata { + citationSources: Citation[]; // Maps to `citations` +} diff --git a/packages/vertexai/src/types/imagen/requests.ts b/packages/vertexai/src/types/imagen/requests.ts index 70ae182238e..09bd3dedc9b 100644 --- a/packages/vertexai/src/types/imagen/requests.ts +++ b/packages/vertexai/src/types/imagen/requests.ts @@ -58,6 +58,9 @@ export interface ImagenGenerationConfig { * Support for negative prompts depends on the Imagen model. * * See the {@link http://firebase.google.com/docs/vertex-ai/model-parameters#imagen | documentation} for more details. + * + * This is no longer supported in the Gemini Developer API ({@link GoogleAIBackend}) in versions + * greater than `imagen-3.0-generate-002`. */ negativePrompt?: string; /** @@ -88,6 +91,9 @@ export interface ImagenGenerationConfig { * * For Imagen 3 models, the default value is `true`; see the addWatermark * documentation for more details. + * + * When using the Gemini Developer API ({@link GoogleAIBackend}), this will default to true, + * and cannot be turned off. */ addWatermark?: boolean; } diff --git a/packages/vertexai/src/types/index.ts b/packages/vertexai/src/types/index.ts index f575c5ba8e9..01f3e7a701a 100644 --- a/packages/vertexai/src/types/index.ts +++ b/packages/vertexai/src/types/index.ts @@ -22,3 +22,4 @@ export * from './responses'; export * from './error'; export * from './schema'; export * from './imagen'; +export * from './googleai'; diff --git a/packages/vertexai/src/types/internal.ts b/packages/vertexai/src/types/internal.ts index a3476afd028..a41ec5652d3 100644 --- a/packages/vertexai/src/types/internal.ts +++ b/packages/vertexai/src/types/internal.ts @@ -17,6 +17,7 @@ import { AppCheckTokenResult } from '@firebase/app-check-interop-types'; import { FirebaseAuthTokenData } from '@firebase/auth-interop-types'; +import { Backend } from '../backend'; export * from './imagen/internal'; @@ -24,8 +25,12 @@ export interface ApiSettings { apiKey: string; project: string; appId: string; - location: string; automaticDataCollectionEnabled?: boolean; + /** + * @deprecated Use `backend.location` instead. + */ + location: string; + backend: Backend; getAuthToken?: () => Promise; getAppCheckToken?: () => Promise; } diff --git a/packages/vertexai/src/types/requests.ts b/packages/vertexai/src/types/requests.ts index ee45b636673..67f45095c2a 100644 --- a/packages/vertexai/src/types/requests.ts +++ b/packages/vertexai/src/types/requests.ts @@ -64,6 +64,13 @@ export interface GenerateContentRequest extends BaseParams { export interface SafetySetting { category: HarmCategory; threshold: HarmBlockThreshold; + /** + * The harm block method. + * + * This property is only supported in the Vertex AI Gemini API ({@link VertexAIBackend}). + * When using the Gemini Developer API ({@link GoogleAIBackend}), an {@link AIError} will be + * thrown if this property is defined. + */ method?: HarmBlockMethod; } diff --git a/packages/vertexai/src/types/responses.ts b/packages/vertexai/src/types/responses.ts index e4a247bec49..e33b8a86bd3 100644 --- a/packages/vertexai/src/types/responses.ts +++ b/packages/vertexai/src/types/responses.ts @@ -117,6 +117,11 @@ export interface ModalityTokenCount { export interface PromptFeedback { blockReason?: BlockReason; safetyRatings: SafetyRating[]; + /** + * A human-readable description of the `blockReason`. + * + * This property is only supported in the Vertex AI Gemini API ({@link VertexAIBackend}). + */ blockReasonMessage?: string; } @@ -151,7 +156,17 @@ export interface Citation { endIndex?: number; uri?: string; license?: string; + /** + * The title of the cited source, if available. + * + * This property is only supported in the Vertex AI Gemini API ({@link VertexAIBackend}). + */ title?: string; + /** + * The publication date of the cited source, if available. + * + * This property is only supported in the Vertex AI Gemini API ({@link VertexAIBackend}). + */ publicationDate?: Date; } @@ -221,8 +236,26 @@ export interface Date { export interface SafetyRating { category: HarmCategory; probability: HarmProbability; + /** + * The harm severity level. + * + * This property is only supported when using the Vertex AI Gemini API ({@link VertexAIBackend}). + * When using the Gemini Developer API ({@link GoogleAIBackend}), this property is not supported and will default to `HarmSeverity.UNSUPPORTED`. + */ severity: HarmSeverity; + /** + * The probability score of the harm category. + * + * This property is only supported when using the Vertex AI Gemini API ({@link VertexAIBackend}). + * When using the Gemini Developer API ({@link GoogleAIBackend}), this property is not supported and will default to 0. + */ probabilityScore: number; + /** + * The severity score of the harm category. + * + * This property is only supported when using the Vertex AI Gemini API ({@link VertexAIBackend}). + * When using the Gemini Developer API ({@link GoogleAIBackend}), this property is not supported and will default to 0. + */ severityScore: number; blocked: boolean; } @@ -239,6 +272,9 @@ export interface CountTokensResponse { /** * The total number of billable characters counted across all instances * from the request. + * + * This property is only supported when using the Vertex AI Gemini API ({@link VertexAIBackend}). + * When using the Gemini Developer API ({@link GoogleAIBackend}), this property is not supported and will default to 0. */ totalBillableCharacters?: number; /** diff --git a/packages/vertexai/src/types/schema.ts b/packages/vertexai/src/types/schema.ts index 5c23655be0e..e9fe9286b61 100644 --- a/packages/vertexai/src/types/schema.ts +++ b/packages/vertexai/src/types/schema.ts @@ -42,7 +42,10 @@ export enum SchemaType { * @public */ export interface SchemaShared { - /** Optional. The format of the property. */ + /** Optional. The format of the property. + * When using the Gemini Developer API ({@link GoogleAIBackend}), this must be either `'enum'` or + * `'date-time'`, otherwise requests will fail. + */ format?: string; /** Optional. The description of the property. */ description?: string; diff --git a/scripts/update_vertexai_responses.sh b/scripts/update_vertexai_responses.sh index bf55a645a66..d80959febce 100755 --- a/scripts/update_vertexai_responses.sh +++ b/scripts/update_vertexai_responses.sh @@ -17,7 +17,7 @@ # This script replaces mock response files for Vertex AI unit tests with a fresh # clone of the shared repository of Vertex AI test data. -RESPONSES_VERSION='v10.*' # The major version of mock responses to use +RESPONSES_VERSION='v11.*' # The major version of mock responses to use REPO_NAME="vertexai-sdk-test-data" REPO_LINK="https://github.com/FirebaseExtended/$REPO_NAME.git" From 72852e12d07370a7cf07560e698246a7ff9c34dc Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Mon, 12 May 2025 13:50:11 -0700 Subject: [PATCH 35/77] Add Emulator Overlay (#8977) --- .changeset/three-singers-wonder.md | 10 ++ common/api-review/util.api.md | 3 + packages/auth/src/core/auth/emulator.ts | 16 +- packages/auth/src/platform_browser/index.ts | 8 +- packages/database/src/api/Database.ts | 8 +- packages/firestore/src/lite-api/database.ts | 11 +- packages/functions/src/api.ts | 4 +- packages/functions/src/service.ts | 7 +- packages/storage/src/api.ts | 4 +- packages/storage/src/service.ts | 4 +- packages/util/src/emulator.ts | 178 ++++++++++++++++++++ 11 files changed, 238 insertions(+), 15 deletions(-) create mode 100644 .changeset/three-singers-wonder.md diff --git a/.changeset/three-singers-wonder.md b/.changeset/three-singers-wonder.md new file mode 100644 index 00000000000..72dea9d7aa5 --- /dev/null +++ b/.changeset/three-singers-wonder.md @@ -0,0 +1,10 @@ +--- +"@firebase/auth": patch +"@firebase/firestore": patch +"@firebase/util": patch +"@firebase/database": patch +"@firebase/storage": patch +"@firebase/functions": patch +--- + +Add Emulator Overlay diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index 427cb5e174a..f263f450da3 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -487,6 +487,9 @@ export interface Subscribe { // @public (undocumented) export type Unsubscribe = () => void; +// @public +export function updateEmulatorBanner(name: string, isRunningEmulator: boolean): void; + // Warning: (ae-missing-release-tag) "validateArgCount" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public diff --git a/packages/auth/src/core/auth/emulator.ts b/packages/auth/src/core/auth/emulator.ts index 8547f7bad6c..42fbda3f095 100644 --- a/packages/auth/src/core/auth/emulator.ts +++ b/packages/auth/src/core/auth/emulator.ts @@ -18,7 +18,12 @@ import { Auth } from '../../model/public_types'; import { AuthErrorCode } from '../errors'; import { _assert } from '../util/assert'; import { _castAuth } from './auth_impl'; -import { deepEqual, isCloudWorkstation, pingServer } from '@firebase/util'; +import { + deepEqual, + isCloudWorkstation, + pingServer, + updateEmulatorBanner +} from '@firebase/util'; /** * Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production @@ -97,13 +102,12 @@ export function connectAuthEmulator( authInternal.emulatorConfig = emulatorConfig; authInternal.settings.appVerificationDisabledForTesting = true; - if (!disableWarnings) { - emitEmulatorWarning(); - } - - // Workaround to get cookies in Firebase Studio if (isCloudWorkstation(host)) { + updateEmulatorBanner('Auth', true); + // Workaround to get cookies in Firebase Studio void pingServer(`${protocol}//${host}${portStr}`); + } else if (!disableWarnings) { + emitEmulatorWarning(); } } diff --git a/packages/auth/src/platform_browser/index.ts b/packages/auth/src/platform_browser/index.ts index f94525bfeb7..99ab834cbdb 100644 --- a/packages/auth/src/platform_browser/index.ts +++ b/packages/auth/src/platform_browser/index.ts @@ -30,7 +30,11 @@ import { browserSessionPersistence } from './persistence/session_storage'; import { indexedDBLocalPersistence } from './persistence/indexed_db'; import { browserPopupRedirectResolver } from './popup_redirect'; import { Auth, User } from '../model/public_types'; -import { getDefaultEmulatorHost, getExperimentalSetting } from '@firebase/util'; +import { + getDefaultEmulatorHost, + getExperimentalSetting, + updateEmulatorBanner +} from '@firebase/util'; import { _setExternalJSProvider } from './load_js'; import { _createError } from '../core/util/assert'; import { AuthErrorCode } from '../core/errors'; @@ -110,6 +114,8 @@ export function getAuth(app: FirebaseApp = getApp()): Auth { const authEmulatorHost = getDefaultEmulatorHost('auth'); if (authEmulatorHost) { connectAuthEmulator(auth, `http://${authEmulatorHost}`); + } else { + updateEmulatorBanner('Auth', false); } return auth; diff --git a/packages/database/src/api/Database.ts b/packages/database/src/api/Database.ts index 515e278b5c5..a94b04518d7 100644 --- a/packages/database/src/api/Database.ts +++ b/packages/database/src/api/Database.ts @@ -31,7 +31,8 @@ import { EmulatorMockTokenOptions, getDefaultEmulatorHostnameAndPort, isCloudWorkstation, - pingServer + pingServer, + updateEmulatorBanner } from '@firebase/util'; import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider'; @@ -257,6 +258,10 @@ export class Database implements _FirebaseService { this.app.options['databaseAuthVariableOverride'] ); this._instanceStarted = true; + updateEmulatorBanner( + 'Database', + this._repo.repoInfo_.emulatorOptions !== null + ); } return this._repoInternal; } @@ -393,6 +398,7 @@ export function connectDatabaseEmulator( // Workaround to get cookies in Firebase Studio if (isCloudWorkstation(host)) { void pingServer(host); + updateEmulatorBanner('Database', true); } // Modify the repo to apply emulator settings diff --git a/packages/firestore/src/lite-api/database.ts b/packages/firestore/src/lite-api/database.ts index 8e7fdb27e90..fce6d5843b7 100644 --- a/packages/firestore/src/lite-api/database.ts +++ b/packages/firestore/src/lite-api/database.ts @@ -28,6 +28,7 @@ import { EmulatorMockTokenOptions, getDefaultEmulatorHostnameAndPort, isCloudWorkstation, + updateEmulatorBanner, pingServer } from '@firebase/util'; @@ -142,6 +143,7 @@ export class Firestore implements FirestoreService { _freezeSettings(): FirestoreSettingsImpl { this._settingsFrozen = true; + updateEmulatorBanner('Firestore', this._settings.isUsingEmulator); return this._settings; } @@ -334,9 +336,7 @@ export function connectFirestoreEmulator( emulatorOptions: firestore._getEmulatorOptions() }; const newHostSetting = `${host}:${port}`; - if (useSsl) { - void pingServer(`https://${newHostSetting}`); - } + if (settings.host !== DEFAULT_HOST && settings.host !== newHostSetting) { logWarn( 'Host has been set in both settings() and connectFirestoreEmulator(), emulator host ' + @@ -357,6 +357,11 @@ export function connectFirestoreEmulator( firestore._setSettings(newConfig); + if (useSsl) { + void pingServer(`https://${newHostSetting}`); + updateEmulatorBanner('Firestore', true); + } + if (options.mockUserToken) { let token: string; let user: User; diff --git a/packages/functions/src/api.ts b/packages/functions/src/api.ts index 7f92cba8343..cb987035145 100644 --- a/packages/functions/src/api.ts +++ b/packages/functions/src/api.ts @@ -29,7 +29,8 @@ import { } from './service'; import { getModularInstance, - getDefaultEmulatorHostnameAndPort + getDefaultEmulatorHostnameAndPort, + updateEmulatorBanner } from '@firebase/util'; export { FunctionsError } from './error'; @@ -47,6 +48,7 @@ export function getFunctions( app: FirebaseApp = getApp(), regionOrCustomDomain: string = DEFAULT_REGION ): Functions { + updateEmulatorBanner('Functions', false); // Dependencies const functionsProvider: Provider<'functions'> = _getProvider( getModularInstance(app), diff --git a/packages/functions/src/service.ts b/packages/functions/src/service.ts index af9d8898d2e..57504a4c7a4 100644 --- a/packages/functions/src/service.ts +++ b/packages/functions/src/service.ts @@ -30,7 +30,11 @@ import { Provider } from '@firebase/component'; import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; import { MessagingInternalComponentName } from '@firebase/messaging-interop-types'; import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types'; -import { isCloudWorkstation, pingServer } from '@firebase/util'; +import { + isCloudWorkstation, + pingServer, + updateEmulatorBanner +} from '@firebase/util'; export const DEFAULT_REGION = 'us-central1'; @@ -182,6 +186,7 @@ export function connectFunctionsEmulator( // Workaround to get cookies in Firebase Studio if (useSsl) { void pingServer(functionsInstance.emulatorOrigin); + updateEmulatorBanner('Functions', true); } } diff --git a/packages/storage/src/api.ts b/packages/storage/src/api.ts index 84c77ea0c8c..b164a1324c3 100644 --- a/packages/storage/src/api.ts +++ b/packages/storage/src/api.ts @@ -53,7 +53,8 @@ import { STORAGE_TYPE } from './constants'; import { EmulatorMockTokenOptions, getModularInstance, - getDefaultEmulatorHostnameAndPort + getDefaultEmulatorHostnameAndPort, + updateEmulatorBanner } from '@firebase/util'; import { StringFormat } from './implementation/string'; @@ -332,6 +333,7 @@ export function getStorage( bucketUrl?: string ): FirebaseStorage { app = getModularInstance(app); + updateEmulatorBanner('Storage', false); const storageProvider: Provider<'storage'> = _getProvider(app, STORAGE_TYPE); const storageInstance = storageProvider.getImmediate({ identifier: bucketUrl diff --git a/packages/storage/src/service.ts b/packages/storage/src/service.ts index 741dd6eaa1a..97d1407bb52 100644 --- a/packages/storage/src/service.ts +++ b/packages/storage/src/service.ts @@ -46,7 +46,8 @@ import { createMockUserToken, EmulatorMockTokenOptions, isCloudWorkstation, - pingServer + pingServer, + updateEmulatorBanner } from '@firebase/util'; import { Connection, ConnectionType } from './implementation/connection'; @@ -150,6 +151,7 @@ export function connectStorageEmulator( // Workaround to get cookies in Firebase Studio if (useSsl) { void pingServer(`https://${storage.host}`); + updateEmulatorBanner('Storage', true); } storage._isUsingEmulator = true; storage._protocol = useSsl ? 'https' : 'http'; diff --git a/packages/util/src/emulator.ts b/packages/util/src/emulator.ts index 2850b5be378..ff09940d88f 100644 --- a/packages/util/src/emulator.ts +++ b/packages/util/src/emulator.ts @@ -16,6 +16,7 @@ */ import { base64urlEncodeWithoutPadding } from './crypt'; +import { isCloudWorkstation } from './url'; // Firebase Auth tokens contain snake_case claims following the JWT standard / convention. /* eslint-disable camelcase */ @@ -140,3 +141,180 @@ export function createMockUserToken( signature ].join('.'); } + +interface EmulatorStatusMap { + [name: string]: boolean; +} +const emulatorStatus: EmulatorStatusMap = {}; + +interface EmulatorSummary { + prod: string[]; + emulator: string[]; +} + +// Checks whether any products are running on an emulator +function getEmulatorSummary(): EmulatorSummary { + const summary: EmulatorSummary = { + prod: [], + emulator: [] + }; + for (const key of Object.keys(emulatorStatus)) { + if (emulatorStatus[key]) { + summary.emulator.push(key); + } else { + summary.prod.push(key); + } + } + return summary; +} + +function getOrCreateEl(id: string): { created: boolean; element: HTMLElement } { + let parentDiv = document.getElementById(id); + let created = false; + if (!parentDiv) { + parentDiv = document.createElement('div'); + parentDiv.setAttribute('id', id); + created = true; + } + return { created, element: parentDiv }; +} + +let previouslyDismissed = false; +/** + * Updates Emulator Banner. Primarily used for Firebase Studio + * @param name + * @param isRunningEmulator + * @public + */ +export function updateEmulatorBanner( + name: string, + isRunningEmulator: boolean +): void { + if ( + typeof window === 'undefined' || + typeof document === 'undefined' || + !isCloudWorkstation(window.location.host) || + emulatorStatus[name] === isRunningEmulator || + emulatorStatus[name] || // If already set to use emulator, can't go back to prod. + previouslyDismissed + ) { + return; + } + + emulatorStatus[name] = isRunningEmulator; + + function prefixedId(id: string): string { + return `__firebase__banner__${id}`; + } + const bannerId = '__firebase__banner'; + const summary = getEmulatorSummary(); + const showError = summary.prod.length > 0; + + function tearDown(): void { + const element = document.getElementById(bannerId); + if (element) { + element.remove(); + } + } + + function setupBannerStyles(bannerEl: HTMLElement): void { + bannerEl.style.display = 'flex'; + bannerEl.style.background = '#7faaf0'; + bannerEl.style.position = 'absolute'; + bannerEl.style.bottom = '5px'; + bannerEl.style.left = '5px'; + bannerEl.style.padding = '.5em'; + bannerEl.style.borderRadius = '5px'; + bannerEl.style.alignItems = 'center'; + } + + function setupIconStyles(prependIcon: SVGElement, iconId: string): void { + prependIcon.setAttribute('width', '24'); + prependIcon.setAttribute('id', iconId); + prependIcon.setAttribute('height', '24'); + prependIcon.setAttribute('viewBox', '0 0 24 24'); + prependIcon.setAttribute('fill', 'none'); + prependIcon.style.marginLeft = '-6px'; + } + + function setupCloseBtn(): HTMLSpanElement { + const closeBtn = document.createElement('span'); + closeBtn.style.cursor = 'pointer'; + closeBtn.style.marginLeft = '16px'; + closeBtn.style.fontSize = '24px'; + closeBtn.innerHTML = ' ×'; + closeBtn.onclick = () => { + previouslyDismissed = true; + tearDown(); + }; + return closeBtn; + } + + function setupLinkStyles( + learnMoreLink: HTMLAnchorElement, + learnMoreId: string + ): void { + learnMoreLink.setAttribute('id', learnMoreId); + learnMoreLink.innerText = 'Learn more'; + learnMoreLink.href = + 'https://firebase.google.com/docs/studio/preview-apps#preview-backend'; + learnMoreLink.setAttribute('target', '__blank'); + learnMoreLink.style.paddingLeft = '5px'; + learnMoreLink.style.textDecoration = 'underline'; + } + + function setupDom(): void { + const banner = getOrCreateEl(bannerId); + const firebaseTextId = prefixedId('text'); + const firebaseText: HTMLSpanElement = + document.getElementById(firebaseTextId) || document.createElement('span'); + const learnMoreId = prefixedId('learnmore'); + const learnMoreLink: HTMLAnchorElement = + (document.getElementById(learnMoreId) as HTMLAnchorElement) || + document.createElement('a'); + const prependIconId = prefixedId('preprendIcon'); + const prependIcon: SVGElement = + (document.getElementById( + prependIconId + ) as HTMLOrSVGElement as SVGElement) || + document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + if (banner.created) { + // update styles + const bannerEl = banner.element; + setupBannerStyles(bannerEl); + setupLinkStyles(learnMoreLink, learnMoreId); + const closeBtn = setupCloseBtn(); + setupIconStyles(prependIcon, prependIconId); + bannerEl.append(prependIcon, firebaseText, learnMoreLink, closeBtn); + document.body.appendChild(bannerEl); + } + + if (showError) { + firebaseText.innerText = `Preview backend disconnected.`; + prependIcon.innerHTML = ` + + + + + + +`; + } else { + prependIcon.innerHTML = ` + + + + + + +`; + firebaseText.innerText = 'Preview backend running in this workspace.'; + } + firebaseText.setAttribute('id', firebaseTextId); + } + if (document.readyState === 'loading') { + window.addEventListener('DOMContentLoaded', setupDom); + } else { + setupDom(); + } +} From 39505cc72d346ed8fa75267181fd80fb046f5635 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Mon, 12 May 2025 16:54:33 -0400 Subject: [PATCH 36/77] Rename `@firebase/vertexai` package to `@firebase/ai` (#9011) --- .changeset/fast-mangos-chew.md | 2 +- .changeset/perfect-camels-try.md | 2 +- .changeset/tall-zoos-stare.md | 2 +- .github/workflows/canary-deploy.yml | 2 +- .../workflows/check-vertexai-responses.yml | 2 +- .../workflows/prerelease-manual-deploy.yml | 2 +- .github/workflows/release-prod.yml | 2 +- .github/workflows/release-staging.yml | 2 +- common/api-review/ai.api.md | 955 ++++++++++++++++++ docs-devsite/_toc.yaml | 298 +++--- docs-devsite/ai.ai.md | 64 ++ .../{vertexai.aierror.md => ai.aierror.md} | 10 +- .../{vertexai.aimodel.md => ai.aimodel.md} | 4 +- ...{vertexai.aioptions.md => ai.aioptions.md} | 4 +- ...texai.arrayschema.md => ai.arrayschema.md} | 10 +- .../{vertexai.backend.md => ai.backend.md} | 8 +- ...ertexai.baseparams.md => ai.baseparams.md} | 4 +- ...i.booleanschema.md => ai.booleanschema.md} | 6 +- ...texai.chatsession.md => ai.chatsession.md} | 32 +- .../{vertexai.citation.md => ai.citation.md} | 16 +- ...tionmetadata.md => ai.citationmetadata.md} | 4 +- .../{vertexai.content.md => ai.content.md} | 4 +- ...ensrequest.md => ai.counttokensrequest.md} | 12 +- docs-devsite/ai.counttokensresponse.md | 59 ++ ...stomerrordata.md => ai.customerrordata.md} | 10 +- .../{vertexai.date_2.md => ai.date_2.md} | 6 +- .../ai.enhancedgeneratecontentresponse.md | 56 + ...xai.errordetails.md => ai.errordetails.md} | 8 +- .../{vertexai.filedata.md => ai.filedata.md} | 4 +- ...xai.filedatapart.md => ai.filedatapart.md} | 12 +- ...xai.functioncall.md => ai.functioncall.md} | 6 +- ...gconfig.md => ai.functioncallingconfig.md} | 4 +- ...tioncallpart.md => ai.functioncallpart.md} | 10 +- ...claration.md => ai.functiondeclaration.md} | 6 +- docs-devsite/ai.functiondeclarationstool.md | 35 + ...tionresponse.md => ai.functionresponse.md} | 6 +- ...onsepart.md => ai.functionresponsepart.md} | 10 +- ...date.md => ai.generatecontentcandidate.md} | 16 +- ...equest.md => ai.generatecontentrequest.md} | 12 +- docs-devsite/ai.generatecontentresponse.md | 51 + ...tresult.md => ai.generatecontentresult.md} | 4 +- ...t.md => ai.generatecontentstreamresult.md} | 6 +- docs-devsite/ai.generationconfig.md | 134 +++ ...entblob.md => ai.generativecontentblob.md} | 4 +- docs-devsite/ai.generativemodel.md | 193 ++++ ...ogleaibackend.md => ai.googleaibackend.md} | 6 +- ...ribution.md => ai.groundingattribution.md} | 8 +- ...ingmetadata.md => ai.groundingmetadata.md} | 6 +- ...imagengcsimage.md => ai.imagengcsimage.md} | 6 +- ...config.md => ai.imagengenerationconfig.md} | 18 +- ...onse.md => ai.imagengenerationresponse.md} | 6 +- ...imageformat.md => ai.imagenimageformat.md} | 22 +- ...inlineimage.md => ai.imageninlineimage.md} | 6 +- ...texai.imagenmodel.md => ai.imagenmodel.md} | 24 +- ...modelparams.md => ai.imagenmodelparams.md} | 8 +- ...settings.md => ai.imagensafetysettings.md} | 4 +- ...inlinedatapart.md => ai.inlinedatapart.md} | 10 +- ...i.integerschema.md => ai.integerschema.md} | 6 +- docs-devsite/ai.md | 728 +++++++++++++ ...tokencount.md => ai.modalitytokencount.md} | 4 +- ...texai.modelparams.md => ai.modelparams.md} | 12 +- ...xai.numberschema.md => ai.numberschema.md} | 6 +- ...xai.objectschema.md => ai.objectschema.md} | 12 +- ...terface.md => ai.objectschemainterface.md} | 8 +- ...promptfeedback.md => ai.promptfeedback.md} | 8 +- ...requestoptions.md => ai.requestoptions.md} | 6 +- ...n.md => ai.retrievedcontextattribution.md} | 4 +- docs-devsite/ai.safetyrating.md | 90 ++ docs-devsite/ai.safetysetting.md | 55 + .../{vertexai.schema.md => ai.schema.md} | 60 +- ...hemainterface.md => ai.schemainterface.md} | 8 +- ...xai.schemaparams.md => ai.schemaparams.md} | 4 +- ...i.schemarequest.md => ai.schemarequest.md} | 10 +- ...xai.schemashared.md => ai.schemashared.md} | 18 +- .../{vertexai.segment.md => ai.segment.md} | 6 +- ...artchatparams.md => ai.startchatparams.md} | 12 +- ...xai.stringschema.md => ai.stringschema.md} | 8 +- .../{vertexai.textpart.md => ai.textpart.md} | 8 +- ...ertexai.toolconfig.md => ai.toolconfig.md} | 2 +- ...i.usagemetadata.md => ai.usagemetadata.md} | 12 +- ...rtexaibackend.md => ai.vertexaibackend.md} | 8 +- ...rtexaioptions.md => ai.vertexaioptions.md} | 2 +- ...i.videometadata.md => ai.videometadata.md} | 4 +- ...webattribution.md => ai.webattribution.md} | 4 +- docs-devsite/index.md | 2 +- docs-devsite/vertexai.ai.md | 64 -- docs-devsite/vertexai.counttokensresponse.md | 59 -- ...ertexai.enhancedgeneratecontentresponse.md | 56 - .../vertexai.functiondeclarationstool.md | 35 - .../vertexai.generatecontentresponse.md | 51 - docs-devsite/vertexai.generationconfig.md | 134 --- docs-devsite/vertexai.generativemodel.md | 193 ---- docs-devsite/vertexai.md | 716 ------------- docs-devsite/vertexai.safetyrating.md | 90 -- docs-devsite/vertexai.safetysetting.md | 55 - packages/{vertexai => ai}/.eslintrc.js | 0 packages/{vertexai => ai}/CHANGELOG.md | 2 +- packages/{vertexai => ai}/README.md | 4 +- packages/{vertexai => ai}/api-extractor.json | 0 packages/{vertexai => ai}/karma.conf.js | 0 packages/{vertexai => ai}/package.json | 12 +- packages/{vertexai => ai}/rollup.config.js | 0 packages/{vertexai => ai}/src/api.test.ts | 0 packages/{vertexai => ai}/src/api.ts | 16 +- packages/{vertexai => ai}/src/backend.test.ts | 0 packages/{vertexai => ai}/src/backend.ts | 0 .../src/backwards-compatbility.test.ts | 0 packages/{vertexai => ai}/src/constants.ts | 3 - packages/{vertexai => ai}/src/errors.ts | 0 .../src/googleai-mappers.test.ts | 0 .../{vertexai => ai}/src/googleai-mappers.ts | 0 packages/{vertexai => ai}/src/helpers.test.ts | 0 packages/{vertexai => ai}/src/helpers.ts | 0 packages/{vertexai => ai}/src/index.node.ts | 0 packages/{vertexai => ai}/src/index.ts | 0 packages/{vertexai => ai}/src/logger.ts | 0 .../src/methods/chat-session-helpers.test.ts | 0 .../src/methods/chat-session-helpers.ts | 0 .../src/methods/chat-session.test.ts | 0 .../src/methods/chat-session.ts | 0 .../src/methods/count-tokens.test.ts | 0 .../src/methods/count-tokens.ts | 0 .../src/methods/generate-content.test.ts | 0 .../src/methods/generate-content.ts | 0 .../src/models/ai-model.test.ts | 0 .../{vertexai => ai}/src/models/ai-model.ts | 0 .../src/models/generative-model.test.ts | 0 .../src/models/generative-model.ts | 0 .../src/models/imagen-model.test.ts | 0 .../src/models/imagen-model.ts | 0 packages/{vertexai => ai}/src/models/index.ts | 0 packages/{vertexai => ai}/src/public-types.ts | 10 +- .../src/requests/imagen-image-format.ts | 0 .../src/requests/request-helpers.test.ts | 0 .../src/requests/request-helpers.ts | 0 .../src/requests/request.test.ts | 0 .../{vertexai => ai}/src/requests/request.ts | 0 .../src/requests/response-helpers.test.ts | 0 .../src/requests/response-helpers.ts | 0 .../src/requests/schema-builder.test.ts | 0 .../src/requests/schema-builder.ts | 0 .../src/requests/stream-reader.test.ts | 0 .../src/requests/stream-reader.ts | 0 packages/{vertexai => ai}/src/service.test.ts | 0 packages/{vertexai => ai}/src/service.ts | 0 .../{vertexai => ai}/src/types/content.ts | 0 packages/{vertexai => ai}/src/types/enums.ts | 0 packages/{vertexai => ai}/src/types/error.ts | 0 .../{vertexai => ai}/src/types/googleai.ts | 0 .../src/types/imagen/index.ts | 0 .../src/types/imagen/internal.ts | 0 .../src/types/imagen/requests.ts | 0 .../src/types/imagen/responses.ts | 0 packages/{vertexai => ai}/src/types/index.ts | 0 .../{vertexai => ai}/src/types/internal.ts | 0 .../{vertexai => ai}/src/types/requests.ts | 0 .../{vertexai => ai}/src/types/responses.ts | 0 packages/{vertexai => ai}/src/types/schema.ts | 0 .../{vertexai => ai}/test-utils/base64cat.ts | 0 packages/{vertexai => ai}/test-utils/cat.jpeg | Bin packages/{vertexai => ai}/test-utils/cat.png | Bin .../test-utils/convert-mocks.ts | 2 +- .../test-utils/mock-response.ts | 0 packages/{vertexai => ai}/tsconfig.json | 0 packages/app/src/constants.ts | 4 +- packages/firebase/ai/index.ts | 4 +- packages/firebase/ai/package.json | 4 +- packages/firebase/package.json | 28 +- packages/firebase/vertexai/index.ts | 2 +- packages/firebase/vertexai/package.json | 4 +- scripts/docgen/docgen.ts | 2 +- scripts/update_vertexai_responses.sh | 2 +- 172 files changed, 2927 insertions(+), 1955 deletions(-) create mode 100644 common/api-review/ai.api.md create mode 100644 docs-devsite/ai.ai.md rename docs-devsite/{vertexai.aierror.md => ai.aierror.md} (65%) rename docs-devsite/{vertexai.aimodel.md => ai.aimodel.md} (74%) rename docs-devsite/{vertexai.aioptions.md => ai.aioptions.md} (59%) rename docs-devsite/{vertexai.arrayschema.md => ai.arrayschema.md} (69%) rename docs-devsite/{vertexai.backend.md => ai.backend.md} (64%) rename docs-devsite/{vertexai.baseparams.md => ai.baseparams.md} (69%) rename docs-devsite/{vertexai.booleanschema.md => ai.booleanschema.md} (71%) rename docs-devsite/{vertexai.chatsession.md => ai.chatsession.md} (52%) rename docs-devsite/{vertexai.citation.md => ai.citation.md} (55%) rename docs-devsite/{vertexai.citationmetadata.md => ai.citationmetadata.md} (73%) rename docs-devsite/{vertexai.content.md => ai.content.md} (79%) rename docs-devsite/{vertexai.counttokensrequest.md => ai.counttokensrequest.md} (52%) create mode 100644 docs-devsite/ai.counttokensresponse.md rename docs-devsite/{vertexai.customerrordata.md => ai.customerrordata.md} (55%) rename docs-devsite/{vertexai.date_2.md => ai.date_2.md} (80%) create mode 100644 docs-devsite/ai.enhancedgeneratecontentresponse.md rename docs-devsite/{vertexai.errordetails.md => ai.errordetails.md} (70%) rename docs-devsite/{vertexai.filedata.md => ai.filedata.md} (83%) rename docs-devsite/{vertexai.filedatapart.md => ai.filedatapart.md} (62%) rename docs-devsite/{vertexai.functioncall.md => ai.functioncall.md} (59%) rename docs-devsite/{vertexai.functioncallingconfig.md => ai.functioncallingconfig.md} (73%) rename docs-devsite/{vertexai.functioncallpart.md => ai.functioncallpart.md} (66%) rename docs-devsite/{vertexai.functiondeclaration.md => ai.functiondeclaration.md} (65%) create mode 100644 docs-devsite/ai.functiondeclarationstool.md rename docs-devsite/{vertexai.functionresponse.md => ai.functionresponse.md} (51%) rename docs-devsite/{vertexai.functionresponsepart.md => ai.functionresponsepart.md} (64%) rename docs-devsite/{vertexai.generatecontentcandidate.md => ai.generatecontentcandidate.md} (51%) rename docs-devsite/{vertexai.generatecontentrequest.md => ai.generatecontentrequest.md} (53%) create mode 100644 docs-devsite/ai.generatecontentresponse.md rename docs-devsite/{vertexai.generatecontentresult.md => ai.generatecontentresult.md} (71%) rename docs-devsite/{vertexai.generatecontentstreamresult.md => ai.generatecontentstreamresult.md} (54%) create mode 100644 docs-devsite/ai.generationconfig.md rename docs-devsite/{vertexai.generativecontentblob.md => ai.generativecontentblob.md} (78%) create mode 100644 docs-devsite/ai.generativemodel.md rename docs-devsite/{vertexai.googleaibackend.md => ai.googleaibackend.md} (63%) rename docs-devsite/{vertexai.groundingattribution.md => ai.groundingattribution.md} (61%) rename docs-devsite/{vertexai.groundingmetadata.md => ai.groundingmetadata.md} (68%) rename docs-devsite/{vertexai.imagengcsimage.md => ai.imagengcsimage.md} (62%) rename docs-devsite/{vertexai.imagengenerationconfig.md => ai.imagengenerationconfig.md} (52%) rename docs-devsite/{vertexai.imagengenerationresponse.md => ai.imagengenerationresponse.md} (56%) rename docs-devsite/{vertexai.imagenimageformat.md => ai.imagenimageformat.md} (61%) rename docs-devsite/{vertexai.imageninlineimage.md => ai.imageninlineimage.md} (66%) rename docs-devsite/{vertexai.imagenmodel.md => ai.imagenmodel.md} (62%) rename docs-devsite/{vertexai.imagenmodelparams.md => ai.imagenmodelparams.md} (63%) rename docs-devsite/{vertexai.imagensafetysettings.md => ai.imagensafetysettings.md} (72%) rename docs-devsite/{vertexai.inlinedatapart.md => ai.inlinedatapart.md} (62%) rename docs-devsite/{vertexai.integerschema.md => ai.integerschema.md} (71%) create mode 100644 docs-devsite/ai.md rename docs-devsite/{vertexai.modalitytokencount.md => ai.modalitytokencount.md} (74%) rename docs-devsite/{vertexai.modelparams.md => ai.modelparams.md} (57%) rename docs-devsite/{vertexai.numberschema.md => ai.numberschema.md} (72%) rename docs-devsite/{vertexai.objectschema.md => ai.objectschema.md} (68%) rename docs-devsite/{vertexai.objectschemainterface.md => ai.objectschemainterface.md} (62%) rename docs-devsite/{vertexai.promptfeedback.md => ai.promptfeedback.md} (60%) rename docs-devsite/{vertexai.requestoptions.md => ai.requestoptions.md} (67%) rename docs-devsite/{vertexai.retrievedcontextattribution.md => ai.retrievedcontextattribution.md} (77%) create mode 100644 docs-devsite/ai.safetyrating.md create mode 100644 docs-devsite/ai.safetysetting.md rename docs-devsite/{vertexai.schema.md => ai.schema.md} (53%) rename docs-devsite/{vertexai.schemainterface.md => ai.schemainterface.md} (55%) rename docs-devsite/{vertexai.schemaparams.md => ai.schemaparams.md} (58%) rename docs-devsite/{vertexai.schemarequest.md => ai.schemarequest.md} (53%) rename docs-devsite/{vertexai.schemashared.md => ai.schemashared.md} (51%) rename docs-devsite/{vertexai.segment.md => ai.segment.md} (77%) rename docs-devsite/{vertexai.startchatparams.md => ai.startchatparams.md} (54%) rename docs-devsite/{vertexai.stringschema.md => ai.stringschema.md} (72%) rename docs-devsite/{vertexai.textpart.md => ai.textpart.md} (74%) rename docs-devsite/{vertexai.toolconfig.md => ai.toolconfig.md} (78%) rename docs-devsite/{vertexai.usagemetadata.md => ai.usagemetadata.md} (56%) rename docs-devsite/{vertexai.vertexaibackend.md => ai.vertexaibackend.md} (67%) rename docs-devsite/{vertexai.vertexaioptions.md => ai.vertexaioptions.md} (88%) rename docs-devsite/{vertexai.videometadata.md => ai.videometadata.md} (75%) rename docs-devsite/{vertexai.webattribution.md => ai.webattribution.md} (81%) delete mode 100644 docs-devsite/vertexai.ai.md delete mode 100644 docs-devsite/vertexai.counttokensresponse.md delete mode 100644 docs-devsite/vertexai.enhancedgeneratecontentresponse.md delete mode 100644 docs-devsite/vertexai.functiondeclarationstool.md delete mode 100644 docs-devsite/vertexai.generatecontentresponse.md delete mode 100644 docs-devsite/vertexai.generationconfig.md delete mode 100644 docs-devsite/vertexai.generativemodel.md delete mode 100644 docs-devsite/vertexai.md delete mode 100644 docs-devsite/vertexai.safetyrating.md delete mode 100644 docs-devsite/vertexai.safetysetting.md rename packages/{vertexai => ai}/.eslintrc.js (100%) rename packages/{vertexai => ai}/CHANGELOG.md (99%) rename packages/{vertexai => ai}/README.md (65%) rename packages/{vertexai => ai}/api-extractor.json (100%) rename packages/{vertexai => ai}/karma.conf.js (100%) rename packages/{vertexai => ai}/package.json (89%) rename packages/{vertexai => ai}/rollup.config.js (100%) rename packages/{vertexai => ai}/src/api.test.ts (100%) rename packages/{vertexai => ai}/src/api.ts (84%) rename packages/{vertexai => ai}/src/backend.test.ts (100%) rename packages/{vertexai => ai}/src/backend.ts (100%) rename packages/{vertexai => ai}/src/backwards-compatbility.test.ts (100%) rename packages/{vertexai => ai}/src/constants.ts (93%) rename packages/{vertexai => ai}/src/errors.ts (100%) rename packages/{vertexai => ai}/src/googleai-mappers.test.ts (100%) rename packages/{vertexai => ai}/src/googleai-mappers.ts (100%) rename packages/{vertexai => ai}/src/helpers.test.ts (100%) rename packages/{vertexai => ai}/src/helpers.ts (100%) rename packages/{vertexai => ai}/src/index.node.ts (100%) rename packages/{vertexai => ai}/src/index.ts (100%) rename packages/{vertexai => ai}/src/logger.ts (100%) rename packages/{vertexai => ai}/src/methods/chat-session-helpers.test.ts (100%) rename packages/{vertexai => ai}/src/methods/chat-session-helpers.ts (100%) rename packages/{vertexai => ai}/src/methods/chat-session.test.ts (100%) rename packages/{vertexai => ai}/src/methods/chat-session.ts (100%) rename packages/{vertexai => ai}/src/methods/count-tokens.test.ts (100%) rename packages/{vertexai => ai}/src/methods/count-tokens.ts (100%) rename packages/{vertexai => ai}/src/methods/generate-content.test.ts (100%) rename packages/{vertexai => ai}/src/methods/generate-content.ts (100%) rename packages/{vertexai => ai}/src/models/ai-model.test.ts (100%) rename packages/{vertexai => ai}/src/models/ai-model.ts (100%) rename packages/{vertexai => ai}/src/models/generative-model.test.ts (100%) rename packages/{vertexai => ai}/src/models/generative-model.ts (100%) rename packages/{vertexai => ai}/src/models/imagen-model.test.ts (100%) rename packages/{vertexai => ai}/src/models/imagen-model.ts (100%) rename packages/{vertexai => ai}/src/models/index.ts (100%) rename packages/{vertexai => ai}/src/public-types.ts (90%) rename packages/{vertexai => ai}/src/requests/imagen-image-format.ts (100%) rename packages/{vertexai => ai}/src/requests/request-helpers.test.ts (100%) rename packages/{vertexai => ai}/src/requests/request-helpers.ts (100%) rename packages/{vertexai => ai}/src/requests/request.test.ts (100%) rename packages/{vertexai => ai}/src/requests/request.ts (100%) rename packages/{vertexai => ai}/src/requests/response-helpers.test.ts (100%) rename packages/{vertexai => ai}/src/requests/response-helpers.ts (100%) rename packages/{vertexai => ai}/src/requests/schema-builder.test.ts (100%) rename packages/{vertexai => ai}/src/requests/schema-builder.ts (100%) rename packages/{vertexai => ai}/src/requests/stream-reader.test.ts (100%) rename packages/{vertexai => ai}/src/requests/stream-reader.ts (100%) rename packages/{vertexai => ai}/src/service.test.ts (100%) rename packages/{vertexai => ai}/src/service.ts (100%) rename packages/{vertexai => ai}/src/types/content.ts (100%) rename packages/{vertexai => ai}/src/types/enums.ts (100%) rename packages/{vertexai => ai}/src/types/error.ts (100%) rename packages/{vertexai => ai}/src/types/googleai.ts (100%) rename packages/{vertexai => ai}/src/types/imagen/index.ts (100%) rename packages/{vertexai => ai}/src/types/imagen/internal.ts (100%) rename packages/{vertexai => ai}/src/types/imagen/requests.ts (100%) rename packages/{vertexai => ai}/src/types/imagen/responses.ts (100%) rename packages/{vertexai => ai}/src/types/index.ts (100%) rename packages/{vertexai => ai}/src/types/internal.ts (100%) rename packages/{vertexai => ai}/src/types/requests.ts (100%) rename packages/{vertexai => ai}/src/types/responses.ts (100%) rename packages/{vertexai => ai}/src/types/schema.ts (100%) rename packages/{vertexai => ai}/test-utils/base64cat.ts (100%) rename packages/{vertexai => ai}/test-utils/cat.jpeg (100%) rename packages/{vertexai => ai}/test-utils/cat.png (100%) rename packages/{vertexai => ai}/test-utils/convert-mocks.ts (97%) rename packages/{vertexai => ai}/test-utils/mock-response.ts (100%) rename packages/{vertexai => ai}/tsconfig.json (100%) diff --git a/.changeset/fast-mangos-chew.md b/.changeset/fast-mangos-chew.md index c5d2e4b4d1f..4e1f7ab3121 100644 --- a/.changeset/fast-mangos-chew.md +++ b/.changeset/fast-mangos-chew.md @@ -1,5 +1,5 @@ --- -'@firebase/vertexai': patch +'@firebase/ai': patch --- Pass `GenerativeModel`'s `BaseParams` to created chat sessions. This fixes an issue where `GenerationConfig` would not be inherited from `ChatSession`. diff --git a/.changeset/perfect-camels-try.md b/.changeset/perfect-camels-try.md index 409a598531d..52292807f0a 100644 --- a/.changeset/perfect-camels-try.md +++ b/.changeset/perfect-camels-try.md @@ -1,6 +1,6 @@ --- 'firebase': minor -'@firebase/vertexai': minor +'@firebase/ai': minor --- Add support for Gemini multimodal output diff --git a/.changeset/tall-zoos-stare.md b/.changeset/tall-zoos-stare.md index 2711107986c..96d480762ea 100644 --- a/.changeset/tall-zoos-stare.md +++ b/.changeset/tall-zoos-stare.md @@ -1,6 +1,6 @@ --- 'firebase': minor -'@firebase/vertexai': minor +'@firebase/ai': minor --- Add support for the Gemini Developer API, enabling usage in a free tier, and add new `AI` API to accomodate new product naming. diff --git a/.github/workflows/canary-deploy.yml b/.github/workflows/canary-deploy.yml index 2a9a6a803a6..318db24f667 100644 --- a/.github/workflows/canary-deploy.yml +++ b/.github/workflows/canary-deploy.yml @@ -71,7 +71,7 @@ jobs: NPM_TOKEN_STORAGE: ${{secrets.NPM_TOKEN_STORAGE}} NPM_TOKEN_STORAGE_TYPES: ${{secrets.NPM_TOKEN_STORAGE_TYPES}} NPM_TOKEN_UTIL: ${{secrets.NPM_TOKEN_UTIL}} - NPM_TOKEN_VERTEXAI: ${{secrets.NPM_TOKEN_VERTEXAI}} + NPM_TOKEN_AI: ${{secrets.NPM_TOKEN_AI}} NPM_TOKEN_WEBCHANNEL_WRAPPER: ${{secrets.NPM_TOKEN_WEBCHANNEL_WRAPPER}} NPM_TOKEN_FIREBASE: ${{secrets.NPM_TOKEN_FIREBASE}} NPM_TOKEN_APP_COMPAT: ${{ secrets.NPM_TOKEN_APP_COMPAT }} diff --git a/.github/workflows/check-vertexai-responses.yml b/.github/workflows/check-vertexai-responses.yml index 5014ad44266..4eceacd61b1 100644 --- a/.github/workflows/check-vertexai-responses.yml +++ b/.github/workflows/check-vertexai-responses.yml @@ -33,7 +33,7 @@ jobs: LATEST=$(git tag --sort=v:refname | tail -n1) echo "cloned_tag=$CLONED" >> $GITHUB_ENV echo "latest_tag=$LATEST" >> $GITHUB_ENV - working-directory: packages/vertexai/test-utils/vertexai-sdk-test-data + working-directory: packages/ai/test-utils/vertexai-sdk-test-data - name: Find comment from previous run if exists # This commit represents v3.1.0 uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e diff --git a/.github/workflows/prerelease-manual-deploy.yml b/.github/workflows/prerelease-manual-deploy.yml index e5ccabdd144..d1ab281634c 100644 --- a/.github/workflows/prerelease-manual-deploy.yml +++ b/.github/workflows/prerelease-manual-deploy.yml @@ -74,7 +74,7 @@ jobs: NPM_TOKEN_STORAGE: ${{secrets.NPM_TOKEN_STORAGE}} NPM_TOKEN_STORAGE_TYPES: ${{secrets.NPM_TOKEN_STORAGE_TYPES}} NPM_TOKEN_UTIL: ${{secrets.NPM_TOKEN_UTIL}} - NPM_TOKEN_VERTEXAI: ${{secrets.NPM_TOKEN_VERTEXAI}} + NPM_TOKEN_AI: ${{secrets.NPM_TOKEN_AI}} NPM_TOKEN_WEBCHANNEL_WRAPPER: ${{secrets.NPM_TOKEN_WEBCHANNEL_WRAPPER}} NPM_TOKEN_FIREBASE: ${{secrets.NPM_TOKEN_FIREBASE}} NPM_TOKEN_APP_COMPAT: ${{ secrets.NPM_TOKEN_APP_COMPAT }} diff --git a/.github/workflows/release-prod.yml b/.github/workflows/release-prod.yml index f5a5b808629..3ae667df6be 100644 --- a/.github/workflows/release-prod.yml +++ b/.github/workflows/release-prod.yml @@ -84,7 +84,7 @@ jobs: NPM_TOKEN_STORAGE: ${{secrets.NPM_TOKEN_STORAGE}} NPM_TOKEN_STORAGE_TYPES: ${{secrets.NPM_TOKEN_STORAGE_TYPES}} NPM_TOKEN_UTIL: ${{secrets.NPM_TOKEN_UTIL}} - NPM_TOKEN_VERTEXAI: ${{secrets.NPM_TOKEN_VERTEXAI}} + NPM_TOKEN_AI: ${{secrets.NPM_TOKEN_AI}} NPM_TOKEN_WEBCHANNEL_WRAPPER: ${{secrets.NPM_TOKEN_WEBCHANNEL_WRAPPER}} NPM_TOKEN_FIREBASE: ${{secrets.NPM_TOKEN_FIREBASE}} NPM_TOKEN_APP_COMPAT: ${{ secrets.NPM_TOKEN_APP_COMPAT }} diff --git a/.github/workflows/release-staging.yml b/.github/workflows/release-staging.yml index c4adefb44a8..2f5cf5eeb38 100644 --- a/.github/workflows/release-staging.yml +++ b/.github/workflows/release-staging.yml @@ -110,7 +110,7 @@ jobs: NPM_TOKEN_STORAGE: ${{secrets.NPM_TOKEN_STORAGE}} NPM_TOKEN_STORAGE_TYPES: ${{secrets.NPM_TOKEN_STORAGE_TYPES}} NPM_TOKEN_UTIL: ${{secrets.NPM_TOKEN_UTIL}} - NPM_TOKEN_VERTEXAI: ${{secrets.NPM_TOKEN_VERTEXAI}} + NPM_TOKEN_AI: ${{secrets.NPM_TOKEN_AI}} NPM_TOKEN_WEBCHANNEL_WRAPPER: ${{secrets.NPM_TOKEN_WEBCHANNEL_WRAPPER}} NPM_TOKEN_FIREBASE: ${{secrets.NPM_TOKEN_FIREBASE}} NPM_TOKEN_APP_COMPAT: ${{ secrets.NPM_TOKEN_APP_COMPAT }} diff --git a/common/api-review/ai.api.md b/common/api-review/ai.api.md new file mode 100644 index 00000000000..d096d4c27f6 --- /dev/null +++ b/common/api-review/ai.api.md @@ -0,0 +1,955 @@ +## API Report File for "@firebase/ai" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { AppCheckTokenResult } from '@firebase/app-check-interop-types'; +import { FirebaseApp } from '@firebase/app'; +import { FirebaseAuthTokenData } from '@firebase/auth-interop-types'; +import { FirebaseError } from '@firebase/util'; + +// @public +export interface AI { + app: FirebaseApp; + backend: Backend; + // @deprecated (undocumented) + location: string; +} + +// @public +export class AIError extends FirebaseError { + constructor(code: AIErrorCode, message: string, customErrorData?: CustomErrorData | undefined); + // (undocumented) + readonly code: AIErrorCode; + // (undocumented) + readonly customErrorData?: CustomErrorData | undefined; +} + +// @public +const enum AIErrorCode { + API_NOT_ENABLED = "api-not-enabled", + ERROR = "error", + FETCH_ERROR = "fetch-error", + INVALID_CONTENT = "invalid-content", + INVALID_SCHEMA = "invalid-schema", + NO_API_KEY = "no-api-key", + NO_APP_ID = "no-app-id", + NO_MODEL = "no-model", + NO_PROJECT_ID = "no-project-id", + PARSE_FAILED = "parse-failed", + REQUEST_ERROR = "request-error", + RESPONSE_ERROR = "response-error", + UNSUPPORTED = "unsupported" +} + +export { AIErrorCode } + +export { AIErrorCode as VertexAIErrorCode } + +// @public +export abstract class AIModel { + // @internal + protected constructor(ai: AI, modelName: string); + // Warning: (ae-forgotten-export) The symbol "ApiSettings" needs to be exported by the entry point index.d.ts + // + // @internal (undocumented) + protected _apiSettings: ApiSettings; + readonly model: string; + // @internal + static normalizeModelName(modelName: string, backendType: BackendType): string; + } + +// @public +export interface AIOptions { + backend: Backend; +} + +// @public +export class ArraySchema extends Schema { + constructor(schemaParams: SchemaParams, items: TypedSchema); + // (undocumented) + items: TypedSchema; + // @internal (undocumented) + toJSON(): SchemaRequest; +} + +// @public +export abstract class Backend { + protected constructor(type: BackendType); + readonly backendType: BackendType; +} + +// @public +export const BackendType: { + readonly VERTEX_AI: "VERTEX_AI"; + readonly GOOGLE_AI: "GOOGLE_AI"; +}; + +// @public +export type BackendType = (typeof BackendType)[keyof typeof BackendType]; + +// @public +export interface BaseParams { + // (undocumented) + generationConfig?: GenerationConfig; + // (undocumented) + safetySettings?: SafetySetting[]; +} + +// @public +export enum BlockReason { + BLOCKLIST = "BLOCKLIST", + OTHER = "OTHER", + PROHIBITED_CONTENT = "PROHIBITED_CONTENT", + SAFETY = "SAFETY" +} + +// @public +export class BooleanSchema extends Schema { + constructor(schemaParams?: SchemaParams); +} + +// @public +export class ChatSession { + constructor(apiSettings: ApiSettings, model: string, params?: StartChatParams | undefined, requestOptions?: RequestOptions | undefined); + getHistory(): Promise; + // (undocumented) + model: string; + // (undocumented) + params?: StartChatParams | undefined; + // (undocumented) + requestOptions?: RequestOptions | undefined; + sendMessage(request: string | Array): Promise; + sendMessageStream(request: string | Array): Promise; + } + +// @public +export interface Citation { + // (undocumented) + endIndex?: number; + // (undocumented) + license?: string; + publicationDate?: Date_2; + // (undocumented) + startIndex?: number; + title?: string; + // (undocumented) + uri?: string; +} + +// @public +export interface CitationMetadata { + // (undocumented) + citations: Citation[]; +} + +// @public +export interface Content { + // (undocumented) + parts: Part[]; + // (undocumented) + role: Role; +} + +// @public +export interface CountTokensRequest { + // (undocumented) + contents: Content[]; + generationConfig?: GenerationConfig; + systemInstruction?: string | Part | Content; + tools?: Tool[]; +} + +// @public +export interface CountTokensResponse { + promptTokensDetails?: ModalityTokenCount[]; + totalBillableCharacters?: number; + totalTokens: number; +} + +// @public +export interface CustomErrorData { + errorDetails?: ErrorDetails[]; + response?: GenerateContentResponse; + status?: number; + statusText?: string; +} + +// @public +interface Date_2 { + // (undocumented) + day: number; + // (undocumented) + month: number; + // (undocumented) + year: number; +} + +export { Date_2 as Date } + +// @public +export interface EnhancedGenerateContentResponse extends GenerateContentResponse { + // (undocumented) + functionCalls: () => FunctionCall[] | undefined; + inlineDataParts: () => InlineDataPart[] | undefined; + text: () => string; +} + +// @public +export interface ErrorDetails { + // (undocumented) + '@type'?: string; + [key: string]: unknown; + domain?: string; + metadata?: Record; + reason?: string; +} + +// @public +export interface FileData { + // (undocumented) + fileUri: string; + // (undocumented) + mimeType: string; +} + +// @public +export interface FileDataPart { + // (undocumented) + fileData: FileData; + // (undocumented) + functionCall?: never; + // (undocumented) + functionResponse?: never; + // (undocumented) + inlineData?: never; + // (undocumented) + text?: never; +} + +// @public +export enum FinishReason { + BLOCKLIST = "BLOCKLIST", + MALFORMED_FUNCTION_CALL = "MALFORMED_FUNCTION_CALL", + MAX_TOKENS = "MAX_TOKENS", + OTHER = "OTHER", + PROHIBITED_CONTENT = "PROHIBITED_CONTENT", + RECITATION = "RECITATION", + SAFETY = "SAFETY", + SPII = "SPII", + STOP = "STOP" +} + +// @public +export interface FunctionCall { + // (undocumented) + args: object; + // (undocumented) + name: string; +} + +// @public (undocumented) +export interface FunctionCallingConfig { + // (undocumented) + allowedFunctionNames?: string[]; + // (undocumented) + mode?: FunctionCallingMode; +} + +// @public (undocumented) +export enum FunctionCallingMode { + ANY = "ANY", + AUTO = "AUTO", + NONE = "NONE" +} + +// @public +export interface FunctionCallPart { + // (undocumented) + functionCall: FunctionCall; + // (undocumented) + functionResponse?: never; + // (undocumented) + inlineData?: never; + // (undocumented) + text?: never; +} + +// @public +export interface FunctionDeclaration { + description: string; + name: string; + parameters?: ObjectSchemaInterface; +} + +// @public +export interface FunctionDeclarationsTool { + functionDeclarations?: FunctionDeclaration[]; +} + +// @public +export interface FunctionResponse { + // (undocumented) + name: string; + // (undocumented) + response: object; +} + +// @public +export interface FunctionResponsePart { + // (undocumented) + functionCall?: never; + // (undocumented) + functionResponse: FunctionResponse; + // (undocumented) + inlineData?: never; + // (undocumented) + text?: never; +} + +// @public +export interface GenerateContentCandidate { + // (undocumented) + citationMetadata?: CitationMetadata; + // (undocumented) + content: Content; + // (undocumented) + finishMessage?: string; + // (undocumented) + finishReason?: FinishReason; + // (undocumented) + groundingMetadata?: GroundingMetadata; + // (undocumented) + index: number; + // (undocumented) + safetyRatings?: SafetyRating[]; +} + +// @public +export interface GenerateContentRequest extends BaseParams { + // (undocumented) + contents: Content[]; + // (undocumented) + systemInstruction?: string | Part | Content; + // (undocumented) + toolConfig?: ToolConfig; + // (undocumented) + tools?: Tool[]; +} + +// @public +export interface GenerateContentResponse { + // (undocumented) + candidates?: GenerateContentCandidate[]; + // (undocumented) + promptFeedback?: PromptFeedback; + // (undocumented) + usageMetadata?: UsageMetadata; +} + +// @public +export interface GenerateContentResult { + // (undocumented) + response: EnhancedGenerateContentResponse; +} + +// @public +export interface GenerateContentStreamResult { + // (undocumented) + response: Promise; + // (undocumented) + stream: AsyncGenerator; +} + +// @public +export interface GenerationConfig { + // (undocumented) + candidateCount?: number; + // (undocumented) + frequencyPenalty?: number; + // (undocumented) + maxOutputTokens?: number; + // (undocumented) + presencePenalty?: number; + responseMimeType?: string; + // @beta + responseModalities?: ResponseModality[]; + responseSchema?: TypedSchema | SchemaRequest; + // (undocumented) + stopSequences?: string[]; + // (undocumented) + temperature?: number; + // (undocumented) + topK?: number; + // (undocumented) + topP?: number; +} + +// @public +export interface GenerativeContentBlob { + data: string; + // (undocumented) + mimeType: string; +} + +// @public +export class GenerativeModel extends AIModel { + constructor(ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions); + countTokens(request: CountTokensRequest | string | Array): Promise; + generateContent(request: GenerateContentRequest | string | Array): Promise; + generateContentStream(request: GenerateContentRequest | string | Array): Promise; + // (undocumented) + generationConfig: GenerationConfig; + // (undocumented) + requestOptions?: RequestOptions; + // (undocumented) + safetySettings: SafetySetting[]; + startChat(startChatParams?: StartChatParams): ChatSession; + // (undocumented) + systemInstruction?: Content; + // (undocumented) + toolConfig?: ToolConfig; + // (undocumented) + tools?: Tool[]; +} + +// @public +export function getAI(app?: FirebaseApp, options?: AIOptions): AI; + +// @public +export function getGenerativeModel(ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions): GenerativeModel; + +// @beta +export function getImagenModel(ai: AI, modelParams: ImagenModelParams, requestOptions?: RequestOptions): ImagenModel; + +// @public @deprecated (undocumented) +export function getVertexAI(app?: FirebaseApp, options?: VertexAIOptions): VertexAI; + +// @public +export class GoogleAIBackend extends Backend { + constructor(); +} + +// Warning: (ae-internal-missing-underscore) The name "GoogleAICitationMetadata" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export interface GoogleAICitationMetadata { + // (undocumented) + citationSources: Citation[]; +} + +// Warning: (ae-internal-missing-underscore) The name "GoogleAICountTokensRequest" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export interface GoogleAICountTokensRequest { + // (undocumented) + generateContentRequest: { + model: string; + contents: Content[]; + systemInstruction?: string | Part | Content; + tools?: Tool[]; + generationConfig?: GenerationConfig; + }; +} + +// Warning: (ae-internal-missing-underscore) The name "GoogleAIGenerateContentCandidate" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export interface GoogleAIGenerateContentCandidate { + // (undocumented) + citationMetadata?: GoogleAICitationMetadata; + // (undocumented) + content: Content; + // (undocumented) + finishMessage?: string; + // (undocumented) + finishReason?: FinishReason; + // (undocumented) + groundingMetadata?: GroundingMetadata; + // (undocumented) + index: number; + // (undocumented) + safetyRatings?: SafetyRating[]; +} + +// Warning: (ae-internal-missing-underscore) The name "GoogleAIGenerateContentResponse" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export interface GoogleAIGenerateContentResponse { + // (undocumented) + candidates?: GoogleAIGenerateContentCandidate[]; + // (undocumented) + promptFeedback?: PromptFeedback; + // (undocumented) + usageMetadata?: UsageMetadata; +} + +// @public @deprecated (undocumented) +export interface GroundingAttribution { + // (undocumented) + confidenceScore?: number; + // (undocumented) + retrievedContext?: RetrievedContextAttribution; + // (undocumented) + segment: Segment; + // (undocumented) + web?: WebAttribution; +} + +// @public +export interface GroundingMetadata { + // @deprecated (undocumented) + groundingAttributions: GroundingAttribution[]; + // (undocumented) + retrievalQueries?: string[]; + // (undocumented) + webSearchQueries?: string[]; +} + +// @public +export enum HarmBlockMethod { + PROBABILITY = "PROBABILITY", + SEVERITY = "SEVERITY" +} + +// @public +export enum HarmBlockThreshold { + BLOCK_LOW_AND_ABOVE = "BLOCK_LOW_AND_ABOVE", + BLOCK_MEDIUM_AND_ABOVE = "BLOCK_MEDIUM_AND_ABOVE", + BLOCK_NONE = "BLOCK_NONE", + BLOCK_ONLY_HIGH = "BLOCK_ONLY_HIGH" +} + +// @public +export enum HarmCategory { + // (undocumented) + HARM_CATEGORY_DANGEROUS_CONTENT = "HARM_CATEGORY_DANGEROUS_CONTENT", + // (undocumented) + HARM_CATEGORY_HARASSMENT = "HARM_CATEGORY_HARASSMENT", + // (undocumented) + HARM_CATEGORY_HATE_SPEECH = "HARM_CATEGORY_HATE_SPEECH", + // (undocumented) + HARM_CATEGORY_SEXUALLY_EXPLICIT = "HARM_CATEGORY_SEXUALLY_EXPLICIT" +} + +// @public +export enum HarmProbability { + HIGH = "HIGH", + LOW = "LOW", + MEDIUM = "MEDIUM", + NEGLIGIBLE = "NEGLIGIBLE" +} + +// @public +export enum HarmSeverity { + HARM_SEVERITY_HIGH = "HARM_SEVERITY_HIGH", + HARM_SEVERITY_LOW = "HARM_SEVERITY_LOW", + HARM_SEVERITY_MEDIUM = "HARM_SEVERITY_MEDIUM", + HARM_SEVERITY_NEGLIGIBLE = "HARM_SEVERITY_NEGLIGIBLE", + HARM_SEVERITY_UNSUPPORTED = "HARM_SEVERITY_UNSUPPORTED" +} + +// @beta +export enum ImagenAspectRatio { + LANDSCAPE_16x9 = "16:9", + LANDSCAPE_3x4 = "3:4", + PORTRAIT_4x3 = "4:3", + PORTRAIT_9x16 = "9:16", + SQUARE = "1:1" +} + +// @public +export interface ImagenGCSImage { + gcsURI: string; + mimeType: string; +} + +// @beta +export interface ImagenGenerationConfig { + addWatermark?: boolean; + aspectRatio?: ImagenAspectRatio; + imageFormat?: ImagenImageFormat; + negativePrompt?: string; + numberOfImages?: number; +} + +// @beta +export interface ImagenGenerationResponse { + filteredReason?: string; + images: T[]; +} + +// @beta +export class ImagenImageFormat { + compressionQuality?: number; + static jpeg(compressionQuality?: number): ImagenImageFormat; + mimeType: string; + static png(): ImagenImageFormat; +} + +// @beta +export interface ImagenInlineImage { + bytesBase64Encoded: string; + mimeType: string; +} + +// @beta +export class ImagenModel extends AIModel { + constructor(ai: AI, modelParams: ImagenModelParams, requestOptions?: RequestOptions | undefined); + generateImages(prompt: string): Promise>; + // @internal + generateImagesGCS(prompt: string, gcsURI: string): Promise>; + generationConfig?: ImagenGenerationConfig; + // (undocumented) + requestOptions?: RequestOptions | undefined; + safetySettings?: ImagenSafetySettings; +} + +// @beta +export interface ImagenModelParams { + generationConfig?: ImagenGenerationConfig; + model: string; + safetySettings?: ImagenSafetySettings; +} + +// @beta +export enum ImagenPersonFilterLevel { + ALLOW_ADULT = "allow_adult", + ALLOW_ALL = "allow_all", + BLOCK_ALL = "dont_allow" +} + +// @beta +export enum ImagenSafetyFilterLevel { + BLOCK_LOW_AND_ABOVE = "block_low_and_above", + BLOCK_MEDIUM_AND_ABOVE = "block_medium_and_above", + BLOCK_NONE = "block_none", + BLOCK_ONLY_HIGH = "block_only_high" +} + +// @beta +export interface ImagenSafetySettings { + personFilterLevel?: ImagenPersonFilterLevel; + safetyFilterLevel?: ImagenSafetyFilterLevel; +} + +// @public +export interface InlineDataPart { + // (undocumented) + functionCall?: never; + // (undocumented) + functionResponse?: never; + // (undocumented) + inlineData: GenerativeContentBlob; + // (undocumented) + text?: never; + videoMetadata?: VideoMetadata; +} + +// @public +export class IntegerSchema extends Schema { + constructor(schemaParams?: SchemaParams); +} + +// @public +export enum Modality { + AUDIO = "AUDIO", + DOCUMENT = "DOCUMENT", + IMAGE = "IMAGE", + MODALITY_UNSPECIFIED = "MODALITY_UNSPECIFIED", + TEXT = "TEXT", + VIDEO = "VIDEO" +} + +// @public +export interface ModalityTokenCount { + modality: Modality; + tokenCount: number; +} + +// @public +export interface ModelParams extends BaseParams { + // (undocumented) + model: string; + // (undocumented) + systemInstruction?: string | Part | Content; + // (undocumented) + toolConfig?: ToolConfig; + // (undocumented) + tools?: Tool[]; +} + +// @public +export class NumberSchema extends Schema { + constructor(schemaParams?: SchemaParams); +} + +// @public +export class ObjectSchema extends Schema { + constructor(schemaParams: SchemaParams, properties: { + [k: string]: TypedSchema; + }, optionalProperties?: string[]); + // (undocumented) + optionalProperties: string[]; + // (undocumented) + properties: { + [k: string]: TypedSchema; + }; + // @internal (undocumented) + toJSON(): SchemaRequest; +} + +// @public +export interface ObjectSchemaInterface extends SchemaInterface { + // (undocumented) + optionalProperties?: string[]; + // (undocumented) + type: SchemaType.OBJECT; +} + +// @public +export type Part = TextPart | InlineDataPart | FunctionCallPart | FunctionResponsePart | FileDataPart; + +// @public +export const POSSIBLE_ROLES: readonly ["user", "model", "function", "system"]; + +// @public +export interface PromptFeedback { + // (undocumented) + blockReason?: BlockReason; + blockReasonMessage?: string; + // (undocumented) + safetyRatings: SafetyRating[]; +} + +// @public +export interface RequestOptions { + baseUrl?: string; + timeout?: number; +} + +// @beta +export const ResponseModality: { + readonly TEXT: "TEXT"; + readonly IMAGE: "IMAGE"; +}; + +// @beta +export type ResponseModality = (typeof ResponseModality)[keyof typeof ResponseModality]; + +// @public (undocumented) +export interface RetrievedContextAttribution { + // (undocumented) + title: string; + // (undocumented) + uri: string; +} + +// @public +export type Role = (typeof POSSIBLE_ROLES)[number]; + +// @public +export interface SafetyRating { + // (undocumented) + blocked: boolean; + // (undocumented) + category: HarmCategory; + // (undocumented) + probability: HarmProbability; + probabilityScore: number; + severity: HarmSeverity; + severityScore: number; +} + +// @public +export interface SafetySetting { + // (undocumented) + category: HarmCategory; + method?: HarmBlockMethod; + // (undocumented) + threshold: HarmBlockThreshold; +} + +// @public +export abstract class Schema implements SchemaInterface { + constructor(schemaParams: SchemaInterface); + [key: string]: unknown; + // (undocumented) + static array(arrayParams: SchemaParams & { + items: Schema; + }): ArraySchema; + // (undocumented) + static boolean(booleanParams?: SchemaParams): BooleanSchema; + description?: string; + // (undocumented) + static enumString(stringParams: SchemaParams & { + enum: string[]; + }): StringSchema; + example?: unknown; + format?: string; + // (undocumented) + static integer(integerParams?: SchemaParams): IntegerSchema; + nullable: boolean; + // (undocumented) + static number(numberParams?: SchemaParams): NumberSchema; + // (undocumented) + static object(objectParams: SchemaParams & { + properties: { + [k: string]: Schema; + }; + optionalProperties?: string[]; + }): ObjectSchema; + // (undocumented) + static string(stringParams?: SchemaParams): StringSchema; + // @internal + toJSON(): SchemaRequest; + type: SchemaType; +} + +// @public +export interface SchemaInterface extends SchemaShared { + type: SchemaType; +} + +// @public +export interface SchemaParams extends SchemaShared { +} + +// @public +export interface SchemaRequest extends SchemaShared { + required?: string[]; + type: SchemaType; +} + +// @public +export interface SchemaShared { + // (undocumented) + [key: string]: unknown; + description?: string; + enum?: string[]; + example?: unknown; + format?: string; + items?: T; + nullable?: boolean; + properties?: { + [k: string]: T; + }; +} + +// @public +export enum SchemaType { + ARRAY = "array", + BOOLEAN = "boolean", + INTEGER = "integer", + NUMBER = "number", + OBJECT = "object", + STRING = "string" +} + +// @public (undocumented) +export interface Segment { + // (undocumented) + endIndex: number; + // (undocumented) + partIndex: number; + // (undocumented) + startIndex: number; +} + +// @public +export interface StartChatParams extends BaseParams { + // (undocumented) + history?: Content[]; + // (undocumented) + systemInstruction?: string | Part | Content; + // (undocumented) + toolConfig?: ToolConfig; + // (undocumented) + tools?: Tool[]; +} + +// @public +export class StringSchema extends Schema { + constructor(schemaParams?: SchemaParams, enumValues?: string[]); + // (undocumented) + enum?: string[]; + // @internal (undocumented) + toJSON(): SchemaRequest; +} + +// @public +export interface TextPart { + // (undocumented) + functionCall?: never; + // (undocumented) + functionResponse?: never; + // (undocumented) + inlineData?: never; + // (undocumented) + text: string; +} + +// @public +export type Tool = FunctionDeclarationsTool; + +// @public +export interface ToolConfig { + // (undocumented) + functionCallingConfig?: FunctionCallingConfig; +} + +// @public +export type TypedSchema = IntegerSchema | NumberSchema | StringSchema | BooleanSchema | ObjectSchema | ArraySchema; + +// @public +export interface UsageMetadata { + // (undocumented) + candidatesTokenCount: number; + // (undocumented) + candidatesTokensDetails?: ModalityTokenCount[]; + // (undocumented) + promptTokenCount: number; + // (undocumented) + promptTokensDetails?: ModalityTokenCount[]; + // (undocumented) + totalTokenCount: number; +} + +// @public @deprecated (undocumented) +export type VertexAI = AI; + +// @public +export class VertexAIBackend extends Backend { + constructor(location?: string); + readonly location: string; +} + +// @public @deprecated (undocumented) +export const VertexAIError: typeof AIError; + +// @public @deprecated (undocumented) +export const VertexAIModel: typeof AIModel; + +// @public +export interface VertexAIOptions { + // (undocumented) + location?: string; +} + +// @public +export interface VideoMetadata { + endOffset: string; + startOffset: string; +} + +// @public (undocumented) +export interface WebAttribution { + // (undocumented) + title: string; + // (undocumented) + uri: string; +} + + +``` diff --git a/docs-devsite/_toc.yaml b/docs-devsite/_toc.yaml index c507b44ce99..b77a6b5910e 100644 --- a/docs-devsite/_toc.yaml +++ b/docs-devsite/_toc.yaml @@ -1,6 +1,155 @@ toc: - title: firebase path: /docs/reference/js/index +- title: ai + path: /docs/reference/js/ai.md + section: + - title: AI + path: /docs/reference/js/ai.ai.md + - title: AIError + path: /docs/reference/js/ai.aierror.md + - title: AIModel + path: /docs/reference/js/ai.aimodel.md + - title: AIOptions + path: /docs/reference/js/ai.aioptions.md + - title: ArraySchema + path: /docs/reference/js/ai.arrayschema.md + - title: Backend + path: /docs/reference/js/ai.backend.md + - title: BaseParams + path: /docs/reference/js/ai.baseparams.md + - title: BooleanSchema + path: /docs/reference/js/ai.booleanschema.md + - title: ChatSession + path: /docs/reference/js/ai.chatsession.md + - title: Citation + path: /docs/reference/js/ai.citation.md + - title: CitationMetadata + path: /docs/reference/js/ai.citationmetadata.md + - title: Content + path: /docs/reference/js/ai.content.md + - title: CountTokensRequest + path: /docs/reference/js/ai.counttokensrequest.md + - title: CountTokensResponse + path: /docs/reference/js/ai.counttokensresponse.md + - title: CustomErrorData + path: /docs/reference/js/ai.customerrordata.md + - title: Date_2 + path: /docs/reference/js/ai.date_2.md + - title: EnhancedGenerateContentResponse + path: /docs/reference/js/ai.enhancedgeneratecontentresponse.md + - title: ErrorDetails + path: /docs/reference/js/ai.errordetails.md + - title: FileData + path: /docs/reference/js/ai.filedata.md + - title: FileDataPart + path: /docs/reference/js/ai.filedatapart.md + - title: FunctionCall + path: /docs/reference/js/ai.functioncall.md + - title: FunctionCallingConfig + path: /docs/reference/js/ai.functioncallingconfig.md + - title: FunctionCallPart + path: /docs/reference/js/ai.functioncallpart.md + - title: FunctionDeclaration + path: /docs/reference/js/ai.functiondeclaration.md + - title: FunctionDeclarationsTool + path: /docs/reference/js/ai.functiondeclarationstool.md + - title: FunctionResponse + path: /docs/reference/js/ai.functionresponse.md + - title: FunctionResponsePart + path: /docs/reference/js/ai.functionresponsepart.md + - title: GenerateContentCandidate + path: /docs/reference/js/ai.generatecontentcandidate.md + - title: GenerateContentRequest + path: /docs/reference/js/ai.generatecontentrequest.md + - title: GenerateContentResponse + path: /docs/reference/js/ai.generatecontentresponse.md + - title: GenerateContentResult + path: /docs/reference/js/ai.generatecontentresult.md + - title: GenerateContentStreamResult + path: /docs/reference/js/ai.generatecontentstreamresult.md + - title: GenerationConfig + path: /docs/reference/js/ai.generationconfig.md + - title: GenerativeContentBlob + path: /docs/reference/js/ai.generativecontentblob.md + - title: GenerativeModel + path: /docs/reference/js/ai.generativemodel.md + - title: GoogleAIBackend + path: /docs/reference/js/ai.googleaibackend.md + - title: GroundingAttribution + path: /docs/reference/js/ai.groundingattribution.md + - title: GroundingMetadata + path: /docs/reference/js/ai.groundingmetadata.md + - title: ImagenGCSImage + path: /docs/reference/js/ai.imagengcsimage.md + - title: ImagenGenerationConfig + path: /docs/reference/js/ai.imagengenerationconfig.md + - title: ImagenGenerationResponse + path: /docs/reference/js/ai.imagengenerationresponse.md + - title: ImagenImageFormat + path: /docs/reference/js/ai.imagenimageformat.md + - title: ImagenInlineImage + path: /docs/reference/js/ai.imageninlineimage.md + - title: ImagenModel + path: /docs/reference/js/ai.imagenmodel.md + - title: ImagenModelParams + path: /docs/reference/js/ai.imagenmodelparams.md + - title: ImagenSafetySettings + path: /docs/reference/js/ai.imagensafetysettings.md + - title: InlineDataPart + path: /docs/reference/js/ai.inlinedatapart.md + - title: IntegerSchema + path: /docs/reference/js/ai.integerschema.md + - title: ModalityTokenCount + path: /docs/reference/js/ai.modalitytokencount.md + - title: ModelParams + path: /docs/reference/js/ai.modelparams.md + - title: NumberSchema + path: /docs/reference/js/ai.numberschema.md + - title: ObjectSchema + path: /docs/reference/js/ai.objectschema.md + - title: ObjectSchemaInterface + path: /docs/reference/js/ai.objectschemainterface.md + - title: PromptFeedback + path: /docs/reference/js/ai.promptfeedback.md + - title: RequestOptions + path: /docs/reference/js/ai.requestoptions.md + - title: RetrievedContextAttribution + path: /docs/reference/js/ai.retrievedcontextattribution.md + - title: SafetyRating + path: /docs/reference/js/ai.safetyrating.md + - title: SafetySetting + path: /docs/reference/js/ai.safetysetting.md + - title: Schema + path: /docs/reference/js/ai.schema.md + - title: SchemaInterface + path: /docs/reference/js/ai.schemainterface.md + - title: SchemaParams + path: /docs/reference/js/ai.schemaparams.md + - title: SchemaRequest + path: /docs/reference/js/ai.schemarequest.md + - title: SchemaShared + path: /docs/reference/js/ai.schemashared.md + - title: Segment + path: /docs/reference/js/ai.segment.md + - title: StartChatParams + path: /docs/reference/js/ai.startchatparams.md + - title: StringSchema + path: /docs/reference/js/ai.stringschema.md + - title: TextPart + path: /docs/reference/js/ai.textpart.md + - title: ToolConfig + path: /docs/reference/js/ai.toolconfig.md + - title: UsageMetadata + path: /docs/reference/js/ai.usagemetadata.md + - title: VertexAIBackend + path: /docs/reference/js/ai.vertexaibackend.md + - title: VertexAIOptions + path: /docs/reference/js/ai.vertexaioptions.md + - title: VideoMetadata + path: /docs/reference/js/ai.videometadata.md + - title: WebAttribution + path: /docs/reference/js/ai.webattribution.md - title: analytics path: /docs/reference/js/analytics.md section: @@ -469,152 +618,3 @@ toc: path: /docs/reference/js/storage.uploadtask.md - title: UploadTaskSnapshot path: /docs/reference/js/storage.uploadtasksnapshot.md -- title: vertexai - path: /docs/reference/js/vertexai.md - section: - - title: AI - path: /docs/reference/js/vertexai.ai.md - - title: AIError - path: /docs/reference/js/vertexai.aierror.md - - title: AIModel - path: /docs/reference/js/vertexai.aimodel.md - - title: AIOptions - path: /docs/reference/js/vertexai.aioptions.md - - title: ArraySchema - path: /docs/reference/js/vertexai.arrayschema.md - - title: Backend - path: /docs/reference/js/vertexai.backend.md - - title: BaseParams - path: /docs/reference/js/vertexai.baseparams.md - - title: BooleanSchema - path: /docs/reference/js/vertexai.booleanschema.md - - title: ChatSession - path: /docs/reference/js/vertexai.chatsession.md - - title: Citation - path: /docs/reference/js/vertexai.citation.md - - title: CitationMetadata - path: /docs/reference/js/vertexai.citationmetadata.md - - title: Content - path: /docs/reference/js/vertexai.content.md - - title: CountTokensRequest - path: /docs/reference/js/vertexai.counttokensrequest.md - - title: CountTokensResponse - path: /docs/reference/js/vertexai.counttokensresponse.md - - title: CustomErrorData - path: /docs/reference/js/vertexai.customerrordata.md - - title: Date_2 - path: /docs/reference/js/vertexai.date_2.md - - title: EnhancedGenerateContentResponse - path: /docs/reference/js/vertexai.enhancedgeneratecontentresponse.md - - title: ErrorDetails - path: /docs/reference/js/vertexai.errordetails.md - - title: FileData - path: /docs/reference/js/vertexai.filedata.md - - title: FileDataPart - path: /docs/reference/js/vertexai.filedatapart.md - - title: FunctionCall - path: /docs/reference/js/vertexai.functioncall.md - - title: FunctionCallingConfig - path: /docs/reference/js/vertexai.functioncallingconfig.md - - title: FunctionCallPart - path: /docs/reference/js/vertexai.functioncallpart.md - - title: FunctionDeclaration - path: /docs/reference/js/vertexai.functiondeclaration.md - - title: FunctionDeclarationsTool - path: /docs/reference/js/vertexai.functiondeclarationstool.md - - title: FunctionResponse - path: /docs/reference/js/vertexai.functionresponse.md - - title: FunctionResponsePart - path: /docs/reference/js/vertexai.functionresponsepart.md - - title: GenerateContentCandidate - path: /docs/reference/js/vertexai.generatecontentcandidate.md - - title: GenerateContentRequest - path: /docs/reference/js/vertexai.generatecontentrequest.md - - title: GenerateContentResponse - path: /docs/reference/js/vertexai.generatecontentresponse.md - - title: GenerateContentResult - path: /docs/reference/js/vertexai.generatecontentresult.md - - title: GenerateContentStreamResult - path: /docs/reference/js/vertexai.generatecontentstreamresult.md - - title: GenerationConfig - path: /docs/reference/js/vertexai.generationconfig.md - - title: GenerativeContentBlob - path: /docs/reference/js/vertexai.generativecontentblob.md - - title: GenerativeModel - path: /docs/reference/js/vertexai.generativemodel.md - - title: GoogleAIBackend - path: /docs/reference/js/vertexai.googleaibackend.md - - title: GroundingAttribution - path: /docs/reference/js/vertexai.groundingattribution.md - - title: GroundingMetadata - path: /docs/reference/js/vertexai.groundingmetadata.md - - title: ImagenGCSImage - path: /docs/reference/js/vertexai.imagengcsimage.md - - title: ImagenGenerationConfig - path: /docs/reference/js/vertexai.imagengenerationconfig.md - - title: ImagenGenerationResponse - path: /docs/reference/js/vertexai.imagengenerationresponse.md - - title: ImagenImageFormat - path: /docs/reference/js/vertexai.imagenimageformat.md - - title: ImagenInlineImage - path: /docs/reference/js/vertexai.imageninlineimage.md - - title: ImagenModel - path: /docs/reference/js/vertexai.imagenmodel.md - - title: ImagenModelParams - path: /docs/reference/js/vertexai.imagenmodelparams.md - - title: ImagenSafetySettings - path: /docs/reference/js/vertexai.imagensafetysettings.md - - title: InlineDataPart - path: /docs/reference/js/vertexai.inlinedatapart.md - - title: IntegerSchema - path: /docs/reference/js/vertexai.integerschema.md - - title: ModalityTokenCount - path: /docs/reference/js/vertexai.modalitytokencount.md - - title: ModelParams - path: /docs/reference/js/vertexai.modelparams.md - - title: NumberSchema - path: /docs/reference/js/vertexai.numberschema.md - - title: ObjectSchema - path: /docs/reference/js/vertexai.objectschema.md - - title: ObjectSchemaInterface - path: /docs/reference/js/vertexai.objectschemainterface.md - - title: PromptFeedback - path: /docs/reference/js/vertexai.promptfeedback.md - - title: RequestOptions - path: /docs/reference/js/vertexai.requestoptions.md - - title: RetrievedContextAttribution - path: /docs/reference/js/vertexai.retrievedcontextattribution.md - - title: SafetyRating - path: /docs/reference/js/vertexai.safetyrating.md - - title: SafetySetting - path: /docs/reference/js/vertexai.safetysetting.md - - title: Schema - path: /docs/reference/js/vertexai.schema.md - - title: SchemaInterface - path: /docs/reference/js/vertexai.schemainterface.md - - title: SchemaParams - path: /docs/reference/js/vertexai.schemaparams.md - - title: SchemaRequest - path: /docs/reference/js/vertexai.schemarequest.md - - title: SchemaShared - path: /docs/reference/js/vertexai.schemashared.md - - title: Segment - path: /docs/reference/js/vertexai.segment.md - - title: StartChatParams - path: /docs/reference/js/vertexai.startchatparams.md - - title: StringSchema - path: /docs/reference/js/vertexai.stringschema.md - - title: TextPart - path: /docs/reference/js/vertexai.textpart.md - - title: ToolConfig - path: /docs/reference/js/vertexai.toolconfig.md - - title: UsageMetadata - path: /docs/reference/js/vertexai.usagemetadata.md - - title: VertexAIBackend - path: /docs/reference/js/vertexai.vertexaibackend.md - - title: VertexAIOptions - path: /docs/reference/js/vertexai.vertexaioptions.md - - title: VideoMetadata - path: /docs/reference/js/vertexai.videometadata.md - - title: WebAttribution - path: /docs/reference/js/vertexai.webattribution.md diff --git a/docs-devsite/ai.ai.md b/docs-devsite/ai.ai.md new file mode 100644 index 00000000000..d4127ffb7e8 --- /dev/null +++ b/docs-devsite/ai.ai.md @@ -0,0 +1,64 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# AI interface +An instance of the Firebase AI SDK. + +Do not create this instance directly. Instead, use [getAI()](./ai.md#getai_a94a413). + +Signature: + +```typescript +export interface AI +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [app](./ai.ai.md#aiapp) | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [AI](./ai.ai.md#ai_interface) instance is associated with. | +| [backend](./ai.ai.md#aibackend) | [Backend](./ai.backend.md#backend_class) | A [Backend](./ai.backend.md#backend_class) instance that specifies the configuration for the target backend, either the Gemini Developer API (using [GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)) or the Vertex AI Gemini API (using [VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). | +| [location](./ai.ai.md#ailocation) | string | | + +## AI.app + +The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [AI](./ai.ai.md#ai_interface) instance is associated with. + +Signature: + +```typescript +app: FirebaseApp; +``` + +## AI.backend + +A [Backend](./ai.backend.md#backend_class) instance that specifies the configuration for the target backend, either the Gemini Developer API (using [GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)) or the Vertex AI Gemini API (using [VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). + +Signature: + +```typescript +backend: Backend; +``` + +## AI.location + +> Warning: This API is now obsolete. +> +> use `AI.backend.location` instead. +> +> The location configured for this AI service instance, relevant for Vertex AI backends. +> + +Signature: + +```typescript +location: string; +``` diff --git a/docs-devsite/vertexai.aierror.md b/docs-devsite/ai.aierror.md similarity index 65% rename from docs-devsite/vertexai.aierror.md rename to docs-devsite/ai.aierror.md index dac50815b6a..cf2d7e2b4c1 100644 --- a/docs-devsite/vertexai.aierror.md +++ b/docs-devsite/ai.aierror.md @@ -23,14 +23,14 @@ export declare class AIError extends FirebaseError | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(code, message, customErrorData)](./vertexai.aierror.md#aierrorconstructor) | | Constructs a new instance of the AIError class. | +| [(constructor)(code, message, customErrorData)](./ai.aierror.md#aierrorconstructor) | | Constructs a new instance of the AIError class. | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [code](./vertexai.aierror.md#aierrorcode) | | [AIErrorCode](./vertexai.md#aierrorcode) | | -| [customErrorData](./vertexai.aierror.md#aierrorcustomerrordata) | | [CustomErrorData](./vertexai.customerrordata.md#customerrordata_interface) \| undefined | | +| [code](./ai.aierror.md#aierrorcode) | | [AIErrorCode](./ai.md#aierrorcode) | | +| [customErrorData](./ai.aierror.md#aierrorcustomerrordata) | | [CustomErrorData](./ai.customerrordata.md#customerrordata_interface) \| undefined | | ## AIError.(constructor) @@ -46,9 +46,9 @@ constructor(code: AIErrorCode, message: string, customErrorData?: CustomErrorDat | Parameter | Type | Description | | --- | --- | --- | -| code | [AIErrorCode](./vertexai.md#aierrorcode) | The error code from [AIErrorCode](./vertexai.md#aierrorcode). | +| code | [AIErrorCode](./ai.md#aierrorcode) | The error code from [AIErrorCode](./ai.md#aierrorcode). | | message | string | A human-readable message describing the error. | -| customErrorData | [CustomErrorData](./vertexai.customerrordata.md#customerrordata_interface) \| undefined | Optional error data. | +| customErrorData | [CustomErrorData](./ai.customerrordata.md#customerrordata_interface) \| undefined | Optional error data. | ## AIError.code diff --git a/docs-devsite/vertexai.aimodel.md b/docs-devsite/ai.aimodel.md similarity index 74% rename from docs-devsite/vertexai.aimodel.md rename to docs-devsite/ai.aimodel.md index 0ff091a0d03..d8910c11dc5 100644 --- a/docs-devsite/vertexai.aimodel.md +++ b/docs-devsite/ai.aimodel.md @@ -12,7 +12,7 @@ https://github.com/firebase/firebase-js-sdk # AIModel class Base class for Firebase AI model APIs. -Instances of this class are associated with a specific Firebase AI [Backend](./vertexai.backend.md#backend_class) and provide methods for interacting with the configured generative model. +Instances of this class are associated with a specific Firebase AI [Backend](./ai.backend.md#backend_class) and provide methods for interacting with the configured generative model. The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `AIModel` class. @@ -26,7 +26,7 @@ export declare abstract class AIModel | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [model](./vertexai.aimodel.md#aimodelmodel) | | string | The fully qualified model resource name to use for generating images (for example, publishers/google/models/imagen-3.0-generate-002). | +| [model](./ai.aimodel.md#aimodelmodel) | | string | The fully qualified model resource name to use for generating images (for example, publishers/google/models/imagen-3.0-generate-002). | ## AIModel.model diff --git a/docs-devsite/vertexai.aioptions.md b/docs-devsite/ai.aioptions.md similarity index 59% rename from docs-devsite/vertexai.aioptions.md rename to docs-devsite/ai.aioptions.md index 00ff0153527..a092046900b 100644 --- a/docs-devsite/vertexai.aioptions.md +++ b/docs-devsite/ai.aioptions.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # AIOptions interface -Options for initializing the AI service using [getAI()](./vertexai.md#getai_a94a413). This allows specifying which backend to use (Vertex AI Gemini API or Gemini Developer API) and configuring its specific options (like location for Vertex AI). +Options for initializing the AI service using [getAI()](./ai.md#getai_a94a413). This allows specifying which backend to use (Vertex AI Gemini API or Gemini Developer API) and configuring its specific options (like location for Vertex AI). Signature: @@ -22,7 +22,7 @@ export interface AIOptions | Property | Type | Description | | --- | --- | --- | -| [backend](./vertexai.aioptions.md#aioptionsbackend) | [Backend](./vertexai.backend.md#backend_class) | The backend configuration to use for the AI service instance. | +| [backend](./ai.aioptions.md#aioptionsbackend) | [Backend](./ai.backend.md#backend_class) | The backend configuration to use for the AI service instance. | ## AIOptions.backend diff --git a/docs-devsite/vertexai.arrayschema.md b/docs-devsite/ai.arrayschema.md similarity index 69% rename from docs-devsite/vertexai.arrayschema.md rename to docs-devsite/ai.arrayschema.md index 8f228baf9e8..ef29bbd63f5 100644 --- a/docs-devsite/vertexai.arrayschema.md +++ b/docs-devsite/ai.arrayschema.md @@ -17,19 +17,19 @@ Schema class for "array" types. The `items` param should refer to the type of it ```typescript export declare class ArraySchema extends Schema ``` -Extends: [Schema](./vertexai.schema.md#schema_class) +Extends: [Schema](./ai.schema.md#schema_class) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(schemaParams, items)](./vertexai.arrayschema.md#arrayschemaconstructor) | | Constructs a new instance of the ArraySchema class | +| [(constructor)(schemaParams, items)](./ai.arrayschema.md#arrayschemaconstructor) | | Constructs a new instance of the ArraySchema class | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [items](./vertexai.arrayschema.md#arrayschemaitems) | | [TypedSchema](./vertexai.md#typedschema) | | +| [items](./ai.arrayschema.md#arrayschemaitems) | | [TypedSchema](./ai.md#typedschema) | | ## ArraySchema.(constructor) @@ -45,8 +45,8 @@ constructor(schemaParams: SchemaParams, items: TypedSchema); | Parameter | Type | Description | | --- | --- | --- | -| schemaParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | | -| items | [TypedSchema](./vertexai.md#typedschema) | | +| schemaParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) | | +| items | [TypedSchema](./ai.md#typedschema) | | ## ArraySchema.items diff --git a/docs-devsite/vertexai.backend.md b/docs-devsite/ai.backend.md similarity index 64% rename from docs-devsite/vertexai.backend.md rename to docs-devsite/ai.backend.md index e6a2606901e..967c1de19de 100644 --- a/docs-devsite/vertexai.backend.md +++ b/docs-devsite/ai.backend.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # Backend class -Abstract base class representing the configuration for an AI service backend. This class should not be instantiated directly. Use its subclasses; [GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class) for the Gemini Developer API (via [Google AI](https://ai.google/)), and [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class) for the Vertex AI Gemini API. +Abstract base class representing the configuration for an AI service backend. This class should not be instantiated directly. Use its subclasses; [GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class) for the Gemini Developer API (via [Google AI](https://ai.google/)), and [VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class) for the Vertex AI Gemini API. Signature: @@ -22,13 +22,13 @@ export declare abstract class Backend | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(type)](./vertexai.backend.md#backendconstructor) | | Protected constructor for use by subclasses. | +| [(constructor)(type)](./ai.backend.md#backendconstructor) | | Protected constructor for use by subclasses. | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [backendType](./vertexai.backend.md#backendbackendtype) | | [BackendType](./vertexai.md#backendtype) | Specifies the backend type. | +| [backendType](./ai.backend.md#backendbackendtype) | | [BackendType](./ai.md#backendtype) | Specifies the backend type. | ## Backend.(constructor) @@ -44,7 +44,7 @@ protected constructor(type: BackendType); | Parameter | Type | Description | | --- | --- | --- | -| type | [BackendType](./vertexai.md#backendtype) | The backend type. | +| type | [BackendType](./ai.md#backendtype) | The backend type. | ## Backend.backendType diff --git a/docs-devsite/vertexai.baseparams.md b/docs-devsite/ai.baseparams.md similarity index 69% rename from docs-devsite/vertexai.baseparams.md rename to docs-devsite/ai.baseparams.md index 382ec825210..62484281f3b 100644 --- a/docs-devsite/vertexai.baseparams.md +++ b/docs-devsite/ai.baseparams.md @@ -22,8 +22,8 @@ export interface BaseParams | Property | Type | Description | | --- | --- | --- | -| [generationConfig](./vertexai.baseparams.md#baseparamsgenerationconfig) | [GenerationConfig](./vertexai.generationconfig.md#generationconfig_interface) | | -| [safetySettings](./vertexai.baseparams.md#baseparamssafetysettings) | [SafetySetting](./vertexai.safetysetting.md#safetysetting_interface)\[\] | | +| [generationConfig](./ai.baseparams.md#baseparamsgenerationconfig) | [GenerationConfig](./ai.generationconfig.md#generationconfig_interface) | | +| [safetySettings](./ai.baseparams.md#baseparamssafetysettings) | [SafetySetting](./ai.safetysetting.md#safetysetting_interface)\[\] | | ## BaseParams.generationConfig diff --git a/docs-devsite/vertexai.booleanschema.md b/docs-devsite/ai.booleanschema.md similarity index 71% rename from docs-devsite/vertexai.booleanschema.md rename to docs-devsite/ai.booleanschema.md index 89449f26142..5797d530286 100644 --- a/docs-devsite/vertexai.booleanschema.md +++ b/docs-devsite/ai.booleanschema.md @@ -17,13 +17,13 @@ Schema class for "boolean" types. ```typescript export declare class BooleanSchema extends Schema ``` -Extends: [Schema](./vertexai.schema.md#schema_class) +Extends: [Schema](./ai.schema.md#schema_class) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(schemaParams)](./vertexai.booleanschema.md#booleanschemaconstructor) | | Constructs a new instance of the BooleanSchema class | +| [(constructor)(schemaParams)](./ai.booleanschema.md#booleanschemaconstructor) | | Constructs a new instance of the BooleanSchema class | ## BooleanSchema.(constructor) @@ -39,5 +39,5 @@ constructor(schemaParams?: SchemaParams); | Parameter | Type | Description | | --- | --- | --- | -| schemaParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | | +| schemaParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) | | diff --git a/docs-devsite/vertexai.chatsession.md b/docs-devsite/ai.chatsession.md similarity index 52% rename from docs-devsite/vertexai.chatsession.md rename to docs-devsite/ai.chatsession.md index ed359f7e08c..1d6e403b6a8 100644 --- a/docs-devsite/vertexai.chatsession.md +++ b/docs-devsite/ai.chatsession.md @@ -22,23 +22,23 @@ export declare class ChatSession | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(apiSettings, model, params, requestOptions)](./vertexai.chatsession.md#chatsessionconstructor) | | Constructs a new instance of the ChatSession class | +| [(constructor)(apiSettings, model, params, requestOptions)](./ai.chatsession.md#chatsessionconstructor) | | Constructs a new instance of the ChatSession class | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [model](./vertexai.chatsession.md#chatsessionmodel) | | string | | -| [params](./vertexai.chatsession.md#chatsessionparams) | | [StartChatParams](./vertexai.startchatparams.md#startchatparams_interface) \| undefined | | -| [requestOptions](./vertexai.chatsession.md#chatsessionrequestoptions) | | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) \| undefined | | +| [model](./ai.chatsession.md#chatsessionmodel) | | string | | +| [params](./ai.chatsession.md#chatsessionparams) | | [StartChatParams](./ai.startchatparams.md#startchatparams_interface) \| undefined | | +| [requestOptions](./ai.chatsession.md#chatsessionrequestoptions) | | [RequestOptions](./ai.requestoptions.md#requestoptions_interface) \| undefined | | ## Methods | Method | Modifiers | Description | | --- | --- | --- | -| [getHistory()](./vertexai.chatsession.md#chatsessiongethistory) | | Gets the chat history so far. Blocked prompts are not added to history. Neither blocked candidates nor the prompts that generated them are added to history. | -| [sendMessage(request)](./vertexai.chatsession.md#chatsessionsendmessage) | | Sends a chat message and receives a non-streaming [GenerateContentResult](./vertexai.generatecontentresult.md#generatecontentresult_interface) | -| [sendMessageStream(request)](./vertexai.chatsession.md#chatsessionsendmessagestream) | | Sends a chat message and receives the response as a [GenerateContentStreamResult](./vertexai.generatecontentstreamresult.md#generatecontentstreamresult_interface) containing an iterable stream and a response promise. | +| [getHistory()](./ai.chatsession.md#chatsessiongethistory) | | Gets the chat history so far. Blocked prompts are not added to history. Neither blocked candidates nor the prompts that generated them are added to history. | +| [sendMessage(request)](./ai.chatsession.md#chatsessionsendmessage) | | Sends a chat message and receives a non-streaming [GenerateContentResult](./ai.generatecontentresult.md#generatecontentresult_interface) | +| [sendMessageStream(request)](./ai.chatsession.md#chatsessionsendmessagestream) | | Sends a chat message and receives the response as a [GenerateContentStreamResult](./ai.generatecontentstreamresult.md#generatecontentstreamresult_interface) containing an iterable stream and a response promise. | ## ChatSession.(constructor) @@ -56,8 +56,8 @@ constructor(apiSettings: ApiSettings, model: string, params?: StartChatParams | | --- | --- | --- | | apiSettings | ApiSettings | | | model | string | | -| params | [StartChatParams](./vertexai.startchatparams.md#startchatparams_interface) \| undefined | | -| requestOptions | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) \| undefined | | +| params | [StartChatParams](./ai.startchatparams.md#startchatparams_interface) \| undefined | | +| requestOptions | [RequestOptions](./ai.requestoptions.md#requestoptions_interface) \| undefined | | ## ChatSession.model @@ -94,11 +94,11 @@ getHistory(): Promise; ``` Returns: -Promise<[Content](./vertexai.content.md#content_interface)\[\]> +Promise<[Content](./ai.content.md#content_interface)\[\]> ## ChatSession.sendMessage() -Sends a chat message and receives a non-streaming [GenerateContentResult](./vertexai.generatecontentresult.md#generatecontentresult_interface) +Sends a chat message and receives a non-streaming [GenerateContentResult](./ai.generatecontentresult.md#generatecontentresult_interface) Signature: @@ -110,15 +110,15 @@ sendMessage(request: string | Array): Promise> | | +| request | string \| Array<string \| [Part](./ai.md#part)> | | Returns: -Promise<[GenerateContentResult](./vertexai.generatecontentresult.md#generatecontentresult_interface)> +Promise<[GenerateContentResult](./ai.generatecontentresult.md#generatecontentresult_interface)> ## ChatSession.sendMessageStream() -Sends a chat message and receives the response as a [GenerateContentStreamResult](./vertexai.generatecontentstreamresult.md#generatecontentstreamresult_interface) containing an iterable stream and a response promise. +Sends a chat message and receives the response as a [GenerateContentStreamResult](./ai.generatecontentstreamresult.md#generatecontentstreamresult_interface) containing an iterable stream and a response promise. Signature: @@ -130,9 +130,9 @@ sendMessageStream(request: string | Array): Promise> | | +| request | string \| Array<string \| [Part](./ai.md#part)> | | Returns: -Promise<[GenerateContentStreamResult](./vertexai.generatecontentstreamresult.md#generatecontentstreamresult_interface)> +Promise<[GenerateContentStreamResult](./ai.generatecontentstreamresult.md#generatecontentstreamresult_interface)> diff --git a/docs-devsite/vertexai.citation.md b/docs-devsite/ai.citation.md similarity index 55% rename from docs-devsite/vertexai.citation.md rename to docs-devsite/ai.citation.md index c694f6c2a9c..3ed9dddc70d 100644 --- a/docs-devsite/vertexai.citation.md +++ b/docs-devsite/ai.citation.md @@ -22,12 +22,12 @@ export interface Citation | Property | Type | Description | | --- | --- | --- | -| [endIndex](./vertexai.citation.md#citationendindex) | number | | -| [license](./vertexai.citation.md#citationlicense) | string | | -| [publicationDate](./vertexai.citation.md#citationpublicationdate) | Date | The publication date of the cited source, if available.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). | -| [startIndex](./vertexai.citation.md#citationstartindex) | number | | -| [title](./vertexai.citation.md#citationtitle) | string | The title of the cited source, if available.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). | -| [uri](./vertexai.citation.md#citationuri) | string | | +| [endIndex](./ai.citation.md#citationendindex) | number | | +| [license](./ai.citation.md#citationlicense) | string | | +| [publicationDate](./ai.citation.md#citationpublicationdate) | Date | The publication date of the cited source, if available.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). | +| [startIndex](./ai.citation.md#citationstartindex) | number | | +| [title](./ai.citation.md#citationtitle) | string | The title of the cited source, if available.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). | +| [uri](./ai.citation.md#citationuri) | string | | ## Citation.endIndex @@ -49,7 +49,7 @@ license?: string; The publication date of the cited source, if available. -This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). +This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). Signature: @@ -69,7 +69,7 @@ startIndex?: number; The title of the cited source, if available. -This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). +This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). Signature: diff --git a/docs-devsite/vertexai.citationmetadata.md b/docs-devsite/ai.citationmetadata.md similarity index 73% rename from docs-devsite/vertexai.citationmetadata.md rename to docs-devsite/ai.citationmetadata.md index c317160e64f..a87654de38f 100644 --- a/docs-devsite/vertexai.citationmetadata.md +++ b/docs-devsite/ai.citationmetadata.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # CitationMetadata interface -Citation metadata that may be found on a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface). +Citation metadata that may be found on a [GenerateContentCandidate](./ai.generatecontentcandidate.md#generatecontentcandidate_interface). Signature: @@ -22,7 +22,7 @@ export interface CitationMetadata | Property | Type | Description | | --- | --- | --- | -| [citations](./vertexai.citationmetadata.md#citationmetadatacitations) | [Citation](./vertexai.citation.md#citation_interface)\[\] | | +| [citations](./ai.citationmetadata.md#citationmetadatacitations) | [Citation](./ai.citation.md#citation_interface)\[\] | | ## CitationMetadata.citations diff --git a/docs-devsite/vertexai.content.md b/docs-devsite/ai.content.md similarity index 79% rename from docs-devsite/vertexai.content.md rename to docs-devsite/ai.content.md index 7a4634a62bc..299a0947924 100644 --- a/docs-devsite/vertexai.content.md +++ b/docs-devsite/ai.content.md @@ -22,8 +22,8 @@ export interface Content | Property | Type | Description | | --- | --- | --- | -| [parts](./vertexai.content.md#contentparts) | [Part](./vertexai.md#part)\[\] | | -| [role](./vertexai.content.md#contentrole) | [Role](./vertexai.md#role) | | +| [parts](./ai.content.md#contentparts) | [Part](./ai.md#part)\[\] | | +| [role](./ai.content.md#contentrole) | [Role](./ai.md#role) | | ## Content.parts diff --git a/docs-devsite/vertexai.counttokensrequest.md b/docs-devsite/ai.counttokensrequest.md similarity index 52% rename from docs-devsite/vertexai.counttokensrequest.md rename to docs-devsite/ai.counttokensrequest.md index 740ae5feed4..ddcb020378b 100644 --- a/docs-devsite/vertexai.counttokensrequest.md +++ b/docs-devsite/ai.counttokensrequest.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # CountTokensRequest interface -Params for calling [GenerativeModel.countTokens()](./vertexai.generativemodel.md#generativemodelcounttokens) +Params for calling [GenerativeModel.countTokens()](./ai.generativemodel.md#generativemodelcounttokens) Signature: @@ -22,10 +22,10 @@ export interface CountTokensRequest | Property | Type | Description | | --- | --- | --- | -| [contents](./vertexai.counttokensrequest.md#counttokensrequestcontents) | [Content](./vertexai.content.md#content_interface)\[\] | | -| [generationConfig](./vertexai.counttokensrequest.md#counttokensrequestgenerationconfig) | [GenerationConfig](./vertexai.generationconfig.md#generationconfig_interface) | Configuration options that control how the model generates a response. | -| [systemInstruction](./vertexai.counttokensrequest.md#counttokensrequestsysteminstruction) | string \| [Part](./vertexai.md#part) \| [Content](./vertexai.content.md#content_interface) | Instructions that direct the model to behave a certain way. | -| [tools](./vertexai.counttokensrequest.md#counttokensrequesttools) | [Tool](./vertexai.md#tool)\[\] | [Tool](./vertexai.md#tool) configuration. | +| [contents](./ai.counttokensrequest.md#counttokensrequestcontents) | [Content](./ai.content.md#content_interface)\[\] | | +| [generationConfig](./ai.counttokensrequest.md#counttokensrequestgenerationconfig) | [GenerationConfig](./ai.generationconfig.md#generationconfig_interface) | Configuration options that control how the model generates a response. | +| [systemInstruction](./ai.counttokensrequest.md#counttokensrequestsysteminstruction) | string \| [Part](./ai.md#part) \| [Content](./ai.content.md#content_interface) | Instructions that direct the model to behave a certain way. | +| [tools](./ai.counttokensrequest.md#counttokensrequesttools) | [Tool](./ai.md#tool)\[\] | [Tool](./ai.md#tool) configuration. | ## CountTokensRequest.contents @@ -57,7 +57,7 @@ systemInstruction?: string | Part | Content; ## CountTokensRequest.tools -[Tool](./vertexai.md#tool) configuration. +[Tool](./ai.md#tool) configuration. Signature: diff --git a/docs-devsite/ai.counttokensresponse.md b/docs-devsite/ai.counttokensresponse.md new file mode 100644 index 00000000000..71e64d885d8 --- /dev/null +++ b/docs-devsite/ai.counttokensresponse.md @@ -0,0 +1,59 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# CountTokensResponse interface +Response from calling [GenerativeModel.countTokens()](./ai.generativemodel.md#generativemodelcounttokens). + +Signature: + +```typescript +export interface CountTokensResponse +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [promptTokensDetails](./ai.counttokensresponse.md#counttokensresponseprompttokensdetails) | [ModalityTokenCount](./ai.modalitytokencount.md#modalitytokencount_interface)\[\] | The breakdown, by modality, of how many tokens are consumed by the prompt. | +| [totalBillableCharacters](./ai.counttokensresponse.md#counttokensresponsetotalbillablecharacters) | number | The total number of billable characters counted across all instances from the request.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. | +| [totalTokens](./ai.counttokensresponse.md#counttokensresponsetotaltokens) | number | The total number of tokens counted across all instances from the request. | + +## CountTokensResponse.promptTokensDetails + +The breakdown, by modality, of how many tokens are consumed by the prompt. + +Signature: + +```typescript +promptTokensDetails?: ModalityTokenCount[]; +``` + +## CountTokensResponse.totalBillableCharacters + +The total number of billable characters counted across all instances from the request. + +This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. + +Signature: + +```typescript +totalBillableCharacters?: number; +``` + +## CountTokensResponse.totalTokens + +The total number of tokens counted across all instances from the request. + +Signature: + +```typescript +totalTokens: number; +``` diff --git a/docs-devsite/vertexai.customerrordata.md b/docs-devsite/ai.customerrordata.md similarity index 55% rename from docs-devsite/vertexai.customerrordata.md rename to docs-devsite/ai.customerrordata.md index 100f4a85fd9..149ad876191 100644 --- a/docs-devsite/vertexai.customerrordata.md +++ b/docs-devsite/ai.customerrordata.md @@ -22,10 +22,10 @@ export interface CustomErrorData | Property | Type | Description | | --- | --- | --- | -| [errorDetails](./vertexai.customerrordata.md#customerrordataerrordetails) | [ErrorDetails](./vertexai.errordetails.md#errordetails_interface)\[\] | Optional additional details about the error. | -| [response](./vertexai.customerrordata.md#customerrordataresponse) | [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface) | Response from a [GenerateContentRequest](./vertexai.generatecontentrequest.md#generatecontentrequest_interface) | -| [status](./vertexai.customerrordata.md#customerrordatastatus) | number | HTTP status code of the error response. | -| [statusText](./vertexai.customerrordata.md#customerrordatastatustext) | string | HTTP status text of the error response. | +| [errorDetails](./ai.customerrordata.md#customerrordataerrordetails) | [ErrorDetails](./ai.errordetails.md#errordetails_interface)\[\] | Optional additional details about the error. | +| [response](./ai.customerrordata.md#customerrordataresponse) | [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface) | Response from a [GenerateContentRequest](./ai.generatecontentrequest.md#generatecontentrequest_interface) | +| [status](./ai.customerrordata.md#customerrordatastatus) | number | HTTP status code of the error response. | +| [statusText](./ai.customerrordata.md#customerrordatastatustext) | string | HTTP status text of the error response. | ## CustomErrorData.errorDetails @@ -39,7 +39,7 @@ errorDetails?: ErrorDetails[]; ## CustomErrorData.response -Response from a [GenerateContentRequest](./vertexai.generatecontentrequest.md#generatecontentrequest_interface) +Response from a [GenerateContentRequest](./ai.generatecontentrequest.md#generatecontentrequest_interface) Signature: diff --git a/docs-devsite/vertexai.date_2.md b/docs-devsite/ai.date_2.md similarity index 80% rename from docs-devsite/vertexai.date_2.md rename to docs-devsite/ai.date_2.md index cf073bb86fe..10b3cfc41e9 100644 --- a/docs-devsite/vertexai.date_2.md +++ b/docs-devsite/ai.date_2.md @@ -22,9 +22,9 @@ export interface Date | Property | Type | Description | | --- | --- | --- | -| [day](./vertexai.date_2.md#date_2day) | number | | -| [month](./vertexai.date_2.md#date_2month) | number | | -| [year](./vertexai.date_2.md#date_2year) | number | | +| [day](./ai.date_2.md#date_2day) | number | | +| [month](./ai.date_2.md#date_2month) | number | | +| [year](./ai.date_2.md#date_2year) | number | | ## Date\_2.day diff --git a/docs-devsite/ai.enhancedgeneratecontentresponse.md b/docs-devsite/ai.enhancedgeneratecontentresponse.md new file mode 100644 index 00000000000..330dc10f322 --- /dev/null +++ b/docs-devsite/ai.enhancedgeneratecontentresponse.md @@ -0,0 +1,56 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# EnhancedGenerateContentResponse interface +Response object wrapped with helper methods. + +Signature: + +```typescript +export interface EnhancedGenerateContentResponse extends GenerateContentResponse +``` +Extends: [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface) + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [functionCalls](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponsefunctioncalls) | () => [FunctionCall](./ai.functioncall.md#functioncall_interface)\[\] \| undefined | | +| [inlineDataParts](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponseinlinedataparts) | () => [InlineDataPart](./ai.inlinedatapart.md#inlinedatapart_interface)\[\] \| undefined | Aggregates and returns all [InlineDataPart](./ai.inlinedatapart.md#inlinedatapart_interface)s from the [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface)'s first candidate. | +| [text](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponsetext) | () => string | Returns the text string from the response, if available. Throws if the prompt or candidate was blocked. | + +## EnhancedGenerateContentResponse.functionCalls + +Signature: + +```typescript +functionCalls: () => FunctionCall[] | undefined; +``` + +## EnhancedGenerateContentResponse.inlineDataParts + +Aggregates and returns all [InlineDataPart](./ai.inlinedatapart.md#inlinedatapart_interface)s from the [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface)'s first candidate. + +Signature: + +```typescript +inlineDataParts: () => InlineDataPart[] | undefined; +``` + +## EnhancedGenerateContentResponse.text + +Returns the text string from the response, if available. Throws if the prompt or candidate was blocked. + +Signature: + +```typescript +text: () => string; +``` diff --git a/docs-devsite/vertexai.errordetails.md b/docs-devsite/ai.errordetails.md similarity index 70% rename from docs-devsite/vertexai.errordetails.md rename to docs-devsite/ai.errordetails.md index 68959343439..efa49c23021 100644 --- a/docs-devsite/vertexai.errordetails.md +++ b/docs-devsite/ai.errordetails.md @@ -22,10 +22,10 @@ export interface ErrorDetails | Property | Type | Description | | --- | --- | --- | -| ["@type"](./vertexai.errordetails.md#errordetails"@type") | string | | -| [domain](./vertexai.errordetails.md#errordetailsdomain) | string | The domain where the error occurred. | -| [metadata](./vertexai.errordetails.md#errordetailsmetadata) | Record<string, unknown> | Additional metadata about the error. | -| [reason](./vertexai.errordetails.md#errordetailsreason) | string | The reason for the error. | +| ["@type"](./ai.errordetails.md#errordetails"@type") | string | | +| [domain](./ai.errordetails.md#errordetailsdomain) | string | The domain where the error occurred. | +| [metadata](./ai.errordetails.md#errordetailsmetadata) | Record<string, unknown> | Additional metadata about the error. | +| [reason](./ai.errordetails.md#errordetailsreason) | string | The reason for the error. | ## ErrorDetails."@type" diff --git a/docs-devsite/vertexai.filedata.md b/docs-devsite/ai.filedata.md similarity index 83% rename from docs-devsite/vertexai.filedata.md rename to docs-devsite/ai.filedata.md index 7e000174692..c88ca71a8bb 100644 --- a/docs-devsite/vertexai.filedata.md +++ b/docs-devsite/ai.filedata.md @@ -22,8 +22,8 @@ export interface FileData | Property | Type | Description | | --- | --- | --- | -| [fileUri](./vertexai.filedata.md#filedatafileuri) | string | | -| [mimeType](./vertexai.filedata.md#filedatamimetype) | string | | +| [fileUri](./ai.filedata.md#filedatafileuri) | string | | +| [mimeType](./ai.filedata.md#filedatamimetype) | string | | ## FileData.fileUri diff --git a/docs-devsite/vertexai.filedatapart.md b/docs-devsite/ai.filedatapart.md similarity index 62% rename from docs-devsite/vertexai.filedatapart.md rename to docs-devsite/ai.filedatapart.md index 76162227526..65cb9dc00ef 100644 --- a/docs-devsite/vertexai.filedatapart.md +++ b/docs-devsite/ai.filedatapart.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # FileDataPart interface -Content part interface if the part represents [FileData](./vertexai.filedata.md#filedata_interface) +Content part interface if the part represents [FileData](./ai.filedata.md#filedata_interface) Signature: @@ -22,11 +22,11 @@ export interface FileDataPart | Property | Type | Description | | --- | --- | --- | -| [fileData](./vertexai.filedatapart.md#filedatapartfiledata) | [FileData](./vertexai.filedata.md#filedata_interface) | | -| [functionCall](./vertexai.filedatapart.md#filedatapartfunctioncall) | never | | -| [functionResponse](./vertexai.filedatapart.md#filedatapartfunctionresponse) | never | | -| [inlineData](./vertexai.filedatapart.md#filedatapartinlinedata) | never | | -| [text](./vertexai.filedatapart.md#filedataparttext) | never | | +| [fileData](./ai.filedatapart.md#filedatapartfiledata) | [FileData](./ai.filedata.md#filedata_interface) | | +| [functionCall](./ai.filedatapart.md#filedatapartfunctioncall) | never | | +| [functionResponse](./ai.filedatapart.md#filedatapartfunctionresponse) | never | | +| [inlineData](./ai.filedatapart.md#filedatapartinlinedata) | never | | +| [text](./ai.filedatapart.md#filedataparttext) | never | | ## FileDataPart.fileData diff --git a/docs-devsite/vertexai.functioncall.md b/docs-devsite/ai.functioncall.md similarity index 59% rename from docs-devsite/vertexai.functioncall.md rename to docs-devsite/ai.functioncall.md index 299fb7130f4..1c789784fe1 100644 --- a/docs-devsite/vertexai.functioncall.md +++ b/docs-devsite/ai.functioncall.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # FunctionCall interface -A predicted [FunctionCall](./vertexai.functioncall.md#functioncall_interface) returned from the model that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing the parameters and their values. +A predicted [FunctionCall](./ai.functioncall.md#functioncall_interface) returned from the model that contains a string representing the [FunctionDeclaration.name](./ai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing the parameters and their values. Signature: @@ -22,8 +22,8 @@ export interface FunctionCall | Property | Type | Description | | --- | --- | --- | -| [args](./vertexai.functioncall.md#functioncallargs) | object | | -| [name](./vertexai.functioncall.md#functioncallname) | string | | +| [args](./ai.functioncall.md#functioncallargs) | object | | +| [name](./ai.functioncall.md#functioncallname) | string | | ## FunctionCall.args diff --git a/docs-devsite/vertexai.functioncallingconfig.md b/docs-devsite/ai.functioncallingconfig.md similarity index 73% rename from docs-devsite/vertexai.functioncallingconfig.md rename to docs-devsite/ai.functioncallingconfig.md index 3724fec5bf7..99531b4cb1f 100644 --- a/docs-devsite/vertexai.functioncallingconfig.md +++ b/docs-devsite/ai.functioncallingconfig.md @@ -21,8 +21,8 @@ export interface FunctionCallingConfig | Property | Type | Description | | --- | --- | --- | -| [allowedFunctionNames](./vertexai.functioncallingconfig.md#functioncallingconfigallowedfunctionnames) | string\[\] | | -| [mode](./vertexai.functioncallingconfig.md#functioncallingconfigmode) | [FunctionCallingMode](./vertexai.md#functioncallingmode) | | +| [allowedFunctionNames](./ai.functioncallingconfig.md#functioncallingconfigallowedfunctionnames) | string\[\] | | +| [mode](./ai.functioncallingconfig.md#functioncallingconfigmode) | [FunctionCallingMode](./ai.md#functioncallingmode) | | ## FunctionCallingConfig.allowedFunctionNames diff --git a/docs-devsite/vertexai.functioncallpart.md b/docs-devsite/ai.functioncallpart.md similarity index 66% rename from docs-devsite/vertexai.functioncallpart.md rename to docs-devsite/ai.functioncallpart.md index 58fe0f5fa97..b16e58f80a6 100644 --- a/docs-devsite/vertexai.functioncallpart.md +++ b/docs-devsite/ai.functioncallpart.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # FunctionCallPart interface -Content part interface if the part represents a [FunctionCall](./vertexai.functioncall.md#functioncall_interface). +Content part interface if the part represents a [FunctionCall](./ai.functioncall.md#functioncall_interface). Signature: @@ -22,10 +22,10 @@ export interface FunctionCallPart | Property | Type | Description | | --- | --- | --- | -| [functionCall](./vertexai.functioncallpart.md#functioncallpartfunctioncall) | [FunctionCall](./vertexai.functioncall.md#functioncall_interface) | | -| [functionResponse](./vertexai.functioncallpart.md#functioncallpartfunctionresponse) | never | | -| [inlineData](./vertexai.functioncallpart.md#functioncallpartinlinedata) | never | | -| [text](./vertexai.functioncallpart.md#functioncallparttext) | never | | +| [functionCall](./ai.functioncallpart.md#functioncallpartfunctioncall) | [FunctionCall](./ai.functioncall.md#functioncall_interface) | | +| [functionResponse](./ai.functioncallpart.md#functioncallpartfunctionresponse) | never | | +| [inlineData](./ai.functioncallpart.md#functioncallpartinlinedata) | never | | +| [text](./ai.functioncallpart.md#functioncallparttext) | never | | ## FunctionCallPart.functionCall diff --git a/docs-devsite/vertexai.functiondeclaration.md b/docs-devsite/ai.functiondeclaration.md similarity index 65% rename from docs-devsite/vertexai.functiondeclaration.md rename to docs-devsite/ai.functiondeclaration.md index 211c1dd868d..2a87d67ed47 100644 --- a/docs-devsite/vertexai.functiondeclaration.md +++ b/docs-devsite/ai.functiondeclaration.md @@ -22,9 +22,9 @@ export declare interface FunctionDeclaration | Property | Type | Description | | --- | --- | --- | -| [description](./vertexai.functiondeclaration.md#functiondeclarationdescription) | string | Description and purpose of the function. Model uses it to decide how and whether to call the function. | -| [name](./vertexai.functiondeclaration.md#functiondeclarationname) | string | The name of the function to call. Must start with a letter or an underscore. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a max length of 64. | -| [parameters](./vertexai.functiondeclaration.md#functiondeclarationparameters) | [ObjectSchemaInterface](./vertexai.objectschemainterface.md#objectschemainterface_interface) | Optional. Describes the parameters to this function in JSON Schema Object format. Reflects the Open API 3.03 Parameter Object. Parameter names are case-sensitive. For a function with no parameters, this can be left unset. | +| [description](./ai.functiondeclaration.md#functiondeclarationdescription) | string | Description and purpose of the function. Model uses it to decide how and whether to call the function. | +| [name](./ai.functiondeclaration.md#functiondeclarationname) | string | The name of the function to call. Must start with a letter or an underscore. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a max length of 64. | +| [parameters](./ai.functiondeclaration.md#functiondeclarationparameters) | [ObjectSchemaInterface](./ai.objectschemainterface.md#objectschemainterface_interface) | Optional. Describes the parameters to this function in JSON Schema Object format. Reflects the Open API 3.03 Parameter Object. Parameter names are case-sensitive. For a function with no parameters, this can be left unset. | ## FunctionDeclaration.description diff --git a/docs-devsite/ai.functiondeclarationstool.md b/docs-devsite/ai.functiondeclarationstool.md new file mode 100644 index 00000000000..bde785d730b --- /dev/null +++ b/docs-devsite/ai.functiondeclarationstool.md @@ -0,0 +1,35 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# FunctionDeclarationsTool interface +A `FunctionDeclarationsTool` is a piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. + +Signature: + +```typescript +export declare interface FunctionDeclarationsTool +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [functionDeclarations](./ai.functiondeclarationstool.md#functiondeclarationstoolfunctiondeclarations) | [FunctionDeclaration](./ai.functiondeclaration.md#functiondeclaration_interface)\[\] | Optional. One or more function declarations to be passed to the model along with the current user query. Model may decide to call a subset of these functions by populating [FunctionCall](./ai.functioncall.md#functioncall_interface) in the response. User should provide a [FunctionResponse](./ai.functionresponse.md#functionresponse_interface) for each function call in the next turn. Based on the function responses, the model will generate the final response back to the user. Maximum 64 function declarations can be provided. | + +## FunctionDeclarationsTool.functionDeclarations + +Optional. One or more function declarations to be passed to the model along with the current user query. Model may decide to call a subset of these functions by populating [FunctionCall](./ai.functioncall.md#functioncall_interface) in the response. User should provide a [FunctionResponse](./ai.functionresponse.md#functionresponse_interface) for each function call in the next turn. Based on the function responses, the model will generate the final response back to the user. Maximum 64 function declarations can be provided. + +Signature: + +```typescript +functionDeclarations?: FunctionDeclaration[]; +``` diff --git a/docs-devsite/vertexai.functionresponse.md b/docs-devsite/ai.functionresponse.md similarity index 51% rename from docs-devsite/vertexai.functionresponse.md rename to docs-devsite/ai.functionresponse.md index 072a08b3486..e0838cf515a 100644 --- a/docs-devsite/vertexai.functionresponse.md +++ b/docs-devsite/ai.functionresponse.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # FunctionResponse interface -The result output from a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing any output from the function is used as context to the model. This should contain the result of a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) made based on model prediction. +The result output from a [FunctionCall](./ai.functioncall.md#functioncall_interface) that contains a string representing the [FunctionDeclaration.name](./ai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing any output from the function is used as context to the model. This should contain the result of a [FunctionCall](./ai.functioncall.md#functioncall_interface) made based on model prediction. Signature: @@ -22,8 +22,8 @@ export interface FunctionResponse | Property | Type | Description | | --- | --- | --- | -| [name](./vertexai.functionresponse.md#functionresponsename) | string | | -| [response](./vertexai.functionresponse.md#functionresponseresponse) | object | | +| [name](./ai.functionresponse.md#functionresponsename) | string | | +| [response](./ai.functionresponse.md#functionresponseresponse) | object | | ## FunctionResponse.name diff --git a/docs-devsite/vertexai.functionresponsepart.md b/docs-devsite/ai.functionresponsepart.md similarity index 64% rename from docs-devsite/vertexai.functionresponsepart.md rename to docs-devsite/ai.functionresponsepart.md index ffbf2ad0517..9c80258f43f 100644 --- a/docs-devsite/vertexai.functionresponsepart.md +++ b/docs-devsite/ai.functionresponsepart.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # FunctionResponsePart interface -Content part interface if the part represents [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface). +Content part interface if the part represents [FunctionResponse](./ai.functionresponse.md#functionresponse_interface). Signature: @@ -22,10 +22,10 @@ export interface FunctionResponsePart | Property | Type | Description | | --- | --- | --- | -| [functionCall](./vertexai.functionresponsepart.md#functionresponsepartfunctioncall) | never | | -| [functionResponse](./vertexai.functionresponsepart.md#functionresponsepartfunctionresponse) | [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface) | | -| [inlineData](./vertexai.functionresponsepart.md#functionresponsepartinlinedata) | never | | -| [text](./vertexai.functionresponsepart.md#functionresponseparttext) | never | | +| [functionCall](./ai.functionresponsepart.md#functionresponsepartfunctioncall) | never | | +| [functionResponse](./ai.functionresponsepart.md#functionresponsepartfunctionresponse) | [FunctionResponse](./ai.functionresponse.md#functionresponse_interface) | | +| [inlineData](./ai.functionresponsepart.md#functionresponsepartinlinedata) | never | | +| [text](./ai.functionresponsepart.md#functionresponseparttext) | never | | ## FunctionResponsePart.functionCall diff --git a/docs-devsite/vertexai.generatecontentcandidate.md b/docs-devsite/ai.generatecontentcandidate.md similarity index 51% rename from docs-devsite/vertexai.generatecontentcandidate.md rename to docs-devsite/ai.generatecontentcandidate.md index e5fd9eacbbe..ca0383549a7 100644 --- a/docs-devsite/vertexai.generatecontentcandidate.md +++ b/docs-devsite/ai.generatecontentcandidate.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # GenerateContentCandidate interface -A candidate returned as part of a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). +A candidate returned as part of a [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface). Signature: @@ -22,13 +22,13 @@ export interface GenerateContentCandidate | Property | Type | Description | | --- | --- | --- | -| [citationMetadata](./vertexai.generatecontentcandidate.md#generatecontentcandidatecitationmetadata) | [CitationMetadata](./vertexai.citationmetadata.md#citationmetadata_interface) | | -| [content](./vertexai.generatecontentcandidate.md#generatecontentcandidatecontent) | [Content](./vertexai.content.md#content_interface) | | -| [finishMessage](./vertexai.generatecontentcandidate.md#generatecontentcandidatefinishmessage) | string | | -| [finishReason](./vertexai.generatecontentcandidate.md#generatecontentcandidatefinishreason) | [FinishReason](./vertexai.md#finishreason) | | -| [groundingMetadata](./vertexai.generatecontentcandidate.md#generatecontentcandidategroundingmetadata) | [GroundingMetadata](./vertexai.groundingmetadata.md#groundingmetadata_interface) | | -| [index](./vertexai.generatecontentcandidate.md#generatecontentcandidateindex) | number | | -| [safetyRatings](./vertexai.generatecontentcandidate.md#generatecontentcandidatesafetyratings) | [SafetyRating](./vertexai.safetyrating.md#safetyrating_interface)\[\] | | +| [citationMetadata](./ai.generatecontentcandidate.md#generatecontentcandidatecitationmetadata) | [CitationMetadata](./ai.citationmetadata.md#citationmetadata_interface) | | +| [content](./ai.generatecontentcandidate.md#generatecontentcandidatecontent) | [Content](./ai.content.md#content_interface) | | +| [finishMessage](./ai.generatecontentcandidate.md#generatecontentcandidatefinishmessage) | string | | +| [finishReason](./ai.generatecontentcandidate.md#generatecontentcandidatefinishreason) | [FinishReason](./ai.md#finishreason) | | +| [groundingMetadata](./ai.generatecontentcandidate.md#generatecontentcandidategroundingmetadata) | [GroundingMetadata](./ai.groundingmetadata.md#groundingmetadata_interface) | | +| [index](./ai.generatecontentcandidate.md#generatecontentcandidateindex) | number | | +| [safetyRatings](./ai.generatecontentcandidate.md#generatecontentcandidatesafetyratings) | [SafetyRating](./ai.safetyrating.md#safetyrating_interface)\[\] | | ## GenerateContentCandidate.citationMetadata diff --git a/docs-devsite/vertexai.generatecontentrequest.md b/docs-devsite/ai.generatecontentrequest.md similarity index 53% rename from docs-devsite/vertexai.generatecontentrequest.md rename to docs-devsite/ai.generatecontentrequest.md index 31f1103a2e2..257a2d308fa 100644 --- a/docs-devsite/vertexai.generatecontentrequest.md +++ b/docs-devsite/ai.generatecontentrequest.md @@ -10,23 +10,23 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # GenerateContentRequest interface -Request sent through [GenerativeModel.generateContent()](./vertexai.generativemodel.md#generativemodelgeneratecontent) +Request sent through [GenerativeModel.generateContent()](./ai.generativemodel.md#generativemodelgeneratecontent) Signature: ```typescript export interface GenerateContentRequest extends BaseParams ``` -Extends: [BaseParams](./vertexai.baseparams.md#baseparams_interface) +Extends: [BaseParams](./ai.baseparams.md#baseparams_interface) ## Properties | Property | Type | Description | | --- | --- | --- | -| [contents](./vertexai.generatecontentrequest.md#generatecontentrequestcontents) | [Content](./vertexai.content.md#content_interface)\[\] | | -| [systemInstruction](./vertexai.generatecontentrequest.md#generatecontentrequestsysteminstruction) | string \| [Part](./vertexai.md#part) \| [Content](./vertexai.content.md#content_interface) | | -| [toolConfig](./vertexai.generatecontentrequest.md#generatecontentrequesttoolconfig) | [ToolConfig](./vertexai.toolconfig.md#toolconfig_interface) | | -| [tools](./vertexai.generatecontentrequest.md#generatecontentrequesttools) | [Tool](./vertexai.md#tool)\[\] | | +| [contents](./ai.generatecontentrequest.md#generatecontentrequestcontents) | [Content](./ai.content.md#content_interface)\[\] | | +| [systemInstruction](./ai.generatecontentrequest.md#generatecontentrequestsysteminstruction) | string \| [Part](./ai.md#part) \| [Content](./ai.content.md#content_interface) | | +| [toolConfig](./ai.generatecontentrequest.md#generatecontentrequesttoolconfig) | [ToolConfig](./ai.toolconfig.md#toolconfig_interface) | | +| [tools](./ai.generatecontentrequest.md#generatecontentrequesttools) | [Tool](./ai.md#tool)\[\] | | ## GenerateContentRequest.contents diff --git a/docs-devsite/ai.generatecontentresponse.md b/docs-devsite/ai.generatecontentresponse.md new file mode 100644 index 00000000000..7c2dc1dc86a --- /dev/null +++ b/docs-devsite/ai.generatecontentresponse.md @@ -0,0 +1,51 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# GenerateContentResponse interface +Individual response from [GenerativeModel.generateContent()](./ai.generativemodel.md#generativemodelgeneratecontent) and [GenerativeModel.generateContentStream()](./ai.generativemodel.md#generativemodelgeneratecontentstream). `generateContentStream()` will return one in each chunk until the stream is done. + +Signature: + +```typescript +export interface GenerateContentResponse +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [candidates](./ai.generatecontentresponse.md#generatecontentresponsecandidates) | [GenerateContentCandidate](./ai.generatecontentcandidate.md#generatecontentcandidate_interface)\[\] | | +| [promptFeedback](./ai.generatecontentresponse.md#generatecontentresponsepromptfeedback) | [PromptFeedback](./ai.promptfeedback.md#promptfeedback_interface) | | +| [usageMetadata](./ai.generatecontentresponse.md#generatecontentresponseusagemetadata) | [UsageMetadata](./ai.usagemetadata.md#usagemetadata_interface) | | + +## GenerateContentResponse.candidates + +Signature: + +```typescript +candidates?: GenerateContentCandidate[]; +``` + +## GenerateContentResponse.promptFeedback + +Signature: + +```typescript +promptFeedback?: PromptFeedback; +``` + +## GenerateContentResponse.usageMetadata + +Signature: + +```typescript +usageMetadata?: UsageMetadata; +``` diff --git a/docs-devsite/vertexai.generatecontentresult.md b/docs-devsite/ai.generatecontentresult.md similarity index 71% rename from docs-devsite/vertexai.generatecontentresult.md rename to docs-devsite/ai.generatecontentresult.md index 3e162f9ebb2..a59e75f8672 100644 --- a/docs-devsite/vertexai.generatecontentresult.md +++ b/docs-devsite/ai.generatecontentresult.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # GenerateContentResult interface -Result object returned from [GenerativeModel.generateContent()](./vertexai.generativemodel.md#generativemodelgeneratecontent) call. +Result object returned from [GenerativeModel.generateContent()](./ai.generativemodel.md#generativemodelgeneratecontent) call. Signature: @@ -22,7 +22,7 @@ export interface GenerateContentResult | Property | Type | Description | | --- | --- | --- | -| [response](./vertexai.generatecontentresult.md#generatecontentresultresponse) | [EnhancedGenerateContentResponse](./vertexai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponse_interface) | | +| [response](./ai.generatecontentresult.md#generatecontentresultresponse) | [EnhancedGenerateContentResponse](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponse_interface) | | ## GenerateContentResult.response diff --git a/docs-devsite/vertexai.generatecontentstreamresult.md b/docs-devsite/ai.generatecontentstreamresult.md similarity index 54% rename from docs-devsite/vertexai.generatecontentstreamresult.md rename to docs-devsite/ai.generatecontentstreamresult.md index 340abb1d0bd..fd3a06e933e 100644 --- a/docs-devsite/vertexai.generatecontentstreamresult.md +++ b/docs-devsite/ai.generatecontentstreamresult.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # GenerateContentStreamResult interface -Result object returned from [GenerativeModel.generateContentStream()](./vertexai.generativemodel.md#generativemodelgeneratecontentstream) call. Iterate over `stream` to get chunks as they come in and/or use the `response` promise to get the aggregated response when the stream is done. +Result object returned from [GenerativeModel.generateContentStream()](./ai.generativemodel.md#generativemodelgeneratecontentstream) call. Iterate over `stream` to get chunks as they come in and/or use the `response` promise to get the aggregated response when the stream is done. Signature: @@ -22,8 +22,8 @@ export interface GenerateContentStreamResult | Property | Type | Description | | --- | --- | --- | -| [response](./vertexai.generatecontentstreamresult.md#generatecontentstreamresultresponse) | Promise<[EnhancedGenerateContentResponse](./vertexai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponse_interface)> | | -| [stream](./vertexai.generatecontentstreamresult.md#generatecontentstreamresultstream) | AsyncGenerator<[EnhancedGenerateContentResponse](./vertexai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponse_interface)> | | +| [response](./ai.generatecontentstreamresult.md#generatecontentstreamresultresponse) | Promise<[EnhancedGenerateContentResponse](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponse_interface)> | | +| [stream](./ai.generatecontentstreamresult.md#generatecontentstreamresultstream) | AsyncGenerator<[EnhancedGenerateContentResponse](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponse_interface)> | | ## GenerateContentStreamResult.response diff --git a/docs-devsite/ai.generationconfig.md b/docs-devsite/ai.generationconfig.md new file mode 100644 index 00000000000..f9697a07454 --- /dev/null +++ b/docs-devsite/ai.generationconfig.md @@ -0,0 +1,134 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# GenerationConfig interface +Config options for content-related requests + +Signature: + +```typescript +export interface GenerationConfig +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [candidateCount](./ai.generationconfig.md#generationconfigcandidatecount) | number | | +| [frequencyPenalty](./ai.generationconfig.md#generationconfigfrequencypenalty) | number | | +| [maxOutputTokens](./ai.generationconfig.md#generationconfigmaxoutputtokens) | number | | +| [presencePenalty](./ai.generationconfig.md#generationconfigpresencepenalty) | number | | +| [responseMimeType](./ai.generationconfig.md#generationconfigresponsemimetype) | string | Output response MIME type of the generated candidate text. Supported MIME types are text/plain (default, text output), application/json (JSON response in the candidates), and text/x.enum. | +| [responseModalities](./ai.generationconfig.md#generationconfigresponsemodalities) | [ResponseModality](./ai.md#responsemodality)\[\] | (Public Preview) Generation modalities to be returned in generation responses. | +| [responseSchema](./ai.generationconfig.md#generationconfigresponseschema) | [TypedSchema](./ai.md#typedschema) \| [SchemaRequest](./ai.schemarequest.md#schemarequest_interface) | Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./ai.schema.md#schema_class) static method like Schema.string() or Schema.object() or it can be a plain JS object matching the [SchemaRequest](./ai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified responseMIMEType supports a schema; currently this is limited to application/json and text/x.enum. | +| [stopSequences](./ai.generationconfig.md#generationconfigstopsequences) | string\[\] | | +| [temperature](./ai.generationconfig.md#generationconfigtemperature) | number | | +| [topK](./ai.generationconfig.md#generationconfigtopk) | number | | +| [topP](./ai.generationconfig.md#generationconfigtopp) | number | | + +## GenerationConfig.candidateCount + +Signature: + +```typescript +candidateCount?: number; +``` + +## GenerationConfig.frequencyPenalty + +Signature: + +```typescript +frequencyPenalty?: number; +``` + +## GenerationConfig.maxOutputTokens + +Signature: + +```typescript +maxOutputTokens?: number; +``` + +## GenerationConfig.presencePenalty + +Signature: + +```typescript +presencePenalty?: number; +``` + +## GenerationConfig.responseMimeType + +Output response MIME type of the generated candidate text. Supported MIME types are `text/plain` (default, text output), `application/json` (JSON response in the candidates), and `text/x.enum`. + +Signature: + +```typescript +responseMimeType?: string; +``` + +## GenerationConfig.responseModalities + +> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> + +Generation modalities to be returned in generation responses. + +- Multimodal response generation is only supported by some Gemini models and versions; see [model versions](https://firebase.google.com/docs/vertex-ai/models). - Only image generation (`ResponseModality.IMAGE`) is supported. + +Signature: + +```typescript +responseModalities?: ResponseModality[]; +``` + +## GenerationConfig.responseSchema + +Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./ai.schema.md#schema_class) static method like `Schema.string()` or `Schema.object()` or it can be a plain JS object matching the [SchemaRequest](./ai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified `responseMIMEType` supports a schema; currently this is limited to `application/json` and `text/x.enum`. + +Signature: + +```typescript +responseSchema?: TypedSchema | SchemaRequest; +``` + +## GenerationConfig.stopSequences + +Signature: + +```typescript +stopSequences?: string[]; +``` + +## GenerationConfig.temperature + +Signature: + +```typescript +temperature?: number; +``` + +## GenerationConfig.topK + +Signature: + +```typescript +topK?: number; +``` + +## GenerationConfig.topP + +Signature: + +```typescript +topP?: number; +``` diff --git a/docs-devsite/vertexai.generativecontentblob.md b/docs-devsite/ai.generativecontentblob.md similarity index 78% rename from docs-devsite/vertexai.generativecontentblob.md rename to docs-devsite/ai.generativecontentblob.md index dfcd098291f..74a582aae6d 100644 --- a/docs-devsite/vertexai.generativecontentblob.md +++ b/docs-devsite/ai.generativecontentblob.md @@ -22,8 +22,8 @@ export interface GenerativeContentBlob | Property | Type | Description | | --- | --- | --- | -| [data](./vertexai.generativecontentblob.md#generativecontentblobdata) | string | Image as a base64 string. | -| [mimeType](./vertexai.generativecontentblob.md#generativecontentblobmimetype) | string | | +| [data](./ai.generativecontentblob.md#generativecontentblobdata) | string | Image as a base64 string. | +| [mimeType](./ai.generativecontentblob.md#generativecontentblobmimetype) | string | | ## GenerativeContentBlob.data diff --git a/docs-devsite/ai.generativemodel.md b/docs-devsite/ai.generativemodel.md new file mode 100644 index 00000000000..d91cf80e881 --- /dev/null +++ b/docs-devsite/ai.generativemodel.md @@ -0,0 +1,193 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# GenerativeModel class +Class for generative model APIs. + +Signature: + +```typescript +export declare class GenerativeModel extends AIModel +``` +Extends: [AIModel](./ai.aimodel.md#aimodel_class) + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(ai, modelParams, requestOptions)](./ai.generativemodel.md#generativemodelconstructor) | | Constructs a new instance of the GenerativeModel class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [generationConfig](./ai.generativemodel.md#generativemodelgenerationconfig) | | [GenerationConfig](./ai.generationconfig.md#generationconfig_interface) | | +| [requestOptions](./ai.generativemodel.md#generativemodelrequestoptions) | | [RequestOptions](./ai.requestoptions.md#requestoptions_interface) | | +| [safetySettings](./ai.generativemodel.md#generativemodelsafetysettings) | | [SafetySetting](./ai.safetysetting.md#safetysetting_interface)\[\] | | +| [systemInstruction](./ai.generativemodel.md#generativemodelsysteminstruction) | | [Content](./ai.content.md#content_interface) | | +| [toolConfig](./ai.generativemodel.md#generativemodeltoolconfig) | | [ToolConfig](./ai.toolconfig.md#toolconfig_interface) | | +| [tools](./ai.generativemodel.md#generativemodeltools) | | [Tool](./ai.md#tool)\[\] | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [countTokens(request)](./ai.generativemodel.md#generativemodelcounttokens) | | Counts the tokens in the provided request. | +| [generateContent(request)](./ai.generativemodel.md#generativemodelgeneratecontent) | | Makes a single non-streaming call to the model and returns an object containing a single [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface). | +| [generateContentStream(request)](./ai.generativemodel.md#generativemodelgeneratecontentstream) | | Makes a single streaming call to the model and returns an object containing an iterable stream that iterates over all chunks in the streaming response as well as a promise that returns the final aggregated response. | +| [startChat(startChatParams)](./ai.generativemodel.md#generativemodelstartchat) | | Gets a new [ChatSession](./ai.chatsession.md#chatsession_class) instance which can be used for multi-turn chats. | + +## GenerativeModel.(constructor) + +Constructs a new instance of the `GenerativeModel` class + +Signature: + +```typescript +constructor(ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions); +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ai | [AI](./ai.ai.md#ai_interface) | | +| modelParams | [ModelParams](./ai.modelparams.md#modelparams_interface) | | +| requestOptions | [RequestOptions](./ai.requestoptions.md#requestoptions_interface) | | + +## GenerativeModel.generationConfig + +Signature: + +```typescript +generationConfig: GenerationConfig; +``` + +## GenerativeModel.requestOptions + +Signature: + +```typescript +requestOptions?: RequestOptions; +``` + +## GenerativeModel.safetySettings + +Signature: + +```typescript +safetySettings: SafetySetting[]; +``` + +## GenerativeModel.systemInstruction + +Signature: + +```typescript +systemInstruction?: Content; +``` + +## GenerativeModel.toolConfig + +Signature: + +```typescript +toolConfig?: ToolConfig; +``` + +## GenerativeModel.tools + +Signature: + +```typescript +tools?: Tool[]; +``` + +## GenerativeModel.countTokens() + +Counts the tokens in the provided request. + +Signature: + +```typescript +countTokens(request: CountTokensRequest | string | Array): Promise; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| request | [CountTokensRequest](./ai.counttokensrequest.md#counttokensrequest_interface) \| string \| Array<string \| [Part](./ai.md#part)> | | + +Returns: + +Promise<[CountTokensResponse](./ai.counttokensresponse.md#counttokensresponse_interface)> + +## GenerativeModel.generateContent() + +Makes a single non-streaming call to the model and returns an object containing a single [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface). + +Signature: + +```typescript +generateContent(request: GenerateContentRequest | string | Array): Promise; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| request | [GenerateContentRequest](./ai.generatecontentrequest.md#generatecontentrequest_interface) \| string \| Array<string \| [Part](./ai.md#part)> | | + +Returns: + +Promise<[GenerateContentResult](./ai.generatecontentresult.md#generatecontentresult_interface)> + +## GenerativeModel.generateContentStream() + +Makes a single streaming call to the model and returns an object containing an iterable stream that iterates over all chunks in the streaming response as well as a promise that returns the final aggregated response. + +Signature: + +```typescript +generateContentStream(request: GenerateContentRequest | string | Array): Promise; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| request | [GenerateContentRequest](./ai.generatecontentrequest.md#generatecontentrequest_interface) \| string \| Array<string \| [Part](./ai.md#part)> | | + +Returns: + +Promise<[GenerateContentStreamResult](./ai.generatecontentstreamresult.md#generatecontentstreamresult_interface)> + +## GenerativeModel.startChat() + +Gets a new [ChatSession](./ai.chatsession.md#chatsession_class) instance which can be used for multi-turn chats. + +Signature: + +```typescript +startChat(startChatParams?: StartChatParams): ChatSession; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| startChatParams | [StartChatParams](./ai.startchatparams.md#startchatparams_interface) | | + +Returns: + +[ChatSession](./ai.chatsession.md#chatsession_class) + diff --git a/docs-devsite/vertexai.googleaibackend.md b/docs-devsite/ai.googleaibackend.md similarity index 63% rename from docs-devsite/vertexai.googleaibackend.md rename to docs-devsite/ai.googleaibackend.md index e302a0eba91..7ccf8834a0a 100644 --- a/docs-devsite/vertexai.googleaibackend.md +++ b/docs-devsite/ai.googleaibackend.md @@ -12,20 +12,20 @@ https://github.com/firebase/firebase-js-sdk # GoogleAIBackend class Configuration class for the Gemini Developer API. -Use this with [AIOptions](./vertexai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./vertexai.md#getai_a94a413) to specify the Gemini Developer API as the backend. +Use this with [AIOptions](./ai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./ai.md#getai_a94a413) to specify the Gemini Developer API as the backend. Signature: ```typescript export declare class GoogleAIBackend extends Backend ``` -Extends: [Backend](./vertexai.backend.md#backend_class) +Extends: [Backend](./ai.backend.md#backend_class) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)()](./vertexai.googleaibackend.md#googleaibackendconstructor) | | Creates a configuration object for the Gemini Developer API backend. | +| [(constructor)()](./ai.googleaibackend.md#googleaibackendconstructor) | | Creates a configuration object for the Gemini Developer API backend. | ## GoogleAIBackend.(constructor) diff --git a/docs-devsite/vertexai.groundingattribution.md b/docs-devsite/ai.groundingattribution.md similarity index 61% rename from docs-devsite/vertexai.groundingattribution.md rename to docs-devsite/ai.groundingattribution.md index b3a3b6257c3..a0895550bf1 100644 --- a/docs-devsite/vertexai.groundingattribution.md +++ b/docs-devsite/ai.groundingattribution.md @@ -24,10 +24,10 @@ export interface GroundingAttribution | Property | Type | Description | | --- | --- | --- | -| [confidenceScore](./vertexai.groundingattribution.md#groundingattributionconfidencescore) | number | | -| [retrievedContext](./vertexai.groundingattribution.md#groundingattributionretrievedcontext) | [RetrievedContextAttribution](./vertexai.retrievedcontextattribution.md#retrievedcontextattribution_interface) | | -| [segment](./vertexai.groundingattribution.md#groundingattributionsegment) | [Segment](./vertexai.segment.md#segment_interface) | | -| [web](./vertexai.groundingattribution.md#groundingattributionweb) | [WebAttribution](./vertexai.webattribution.md#webattribution_interface) | | +| [confidenceScore](./ai.groundingattribution.md#groundingattributionconfidencescore) | number | | +| [retrievedContext](./ai.groundingattribution.md#groundingattributionretrievedcontext) | [RetrievedContextAttribution](./ai.retrievedcontextattribution.md#retrievedcontextattribution_interface) | | +| [segment](./ai.groundingattribution.md#groundingattributionsegment) | [Segment](./ai.segment.md#segment_interface) | | +| [web](./ai.groundingattribution.md#groundingattributionweb) | [WebAttribution](./ai.webattribution.md#webattribution_interface) | | ## GroundingAttribution.confidenceScore diff --git a/docs-devsite/vertexai.groundingmetadata.md b/docs-devsite/ai.groundingmetadata.md similarity index 68% rename from docs-devsite/vertexai.groundingmetadata.md rename to docs-devsite/ai.groundingmetadata.md index 24686da39ba..90994d9c01c 100644 --- a/docs-devsite/vertexai.groundingmetadata.md +++ b/docs-devsite/ai.groundingmetadata.md @@ -22,9 +22,9 @@ export interface GroundingMetadata | Property | Type | Description | | --- | --- | --- | -| [groundingAttributions](./vertexai.groundingmetadata.md#groundingmetadatagroundingattributions) | [GroundingAttribution](./vertexai.groundingattribution.md#groundingattribution_interface)\[\] | | -| [retrievalQueries](./vertexai.groundingmetadata.md#groundingmetadataretrievalqueries) | string\[\] | | -| [webSearchQueries](./vertexai.groundingmetadata.md#groundingmetadatawebsearchqueries) | string\[\] | | +| [groundingAttributions](./ai.groundingmetadata.md#groundingmetadatagroundingattributions) | [GroundingAttribution](./ai.groundingattribution.md#groundingattribution_interface)\[\] | | +| [retrievalQueries](./ai.groundingmetadata.md#groundingmetadataretrievalqueries) | string\[\] | | +| [webSearchQueries](./ai.groundingmetadata.md#groundingmetadatawebsearchqueries) | string\[\] | | ## GroundingMetadata.groundingAttributions diff --git a/docs-devsite/vertexai.imagengcsimage.md b/docs-devsite/ai.imagengcsimage.md similarity index 62% rename from docs-devsite/vertexai.imagengcsimage.md rename to docs-devsite/ai.imagengcsimage.md index 23770192b3b..cd11d8ee354 100644 --- a/docs-devsite/vertexai.imagengcsimage.md +++ b/docs-devsite/ai.imagengcsimage.md @@ -24,8 +24,8 @@ export interface ImagenGCSImage | Property | Type | Description | | --- | --- | --- | -| [gcsURI](./vertexai.imagengcsimage.md#imagengcsimagegcsuri) | string | The URI of the file stored in a Cloud Storage for Firebase bucket. | -| [mimeType](./vertexai.imagengcsimage.md#imagengcsimagemimetype) | string | The MIME type of the image; either "image/png" or "image/jpeg".To request a different format, set the imageFormat property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). | +| [gcsURI](./ai.imagengcsimage.md#imagengcsimagegcsuri) | string | The URI of the file stored in a Cloud Storage for Firebase bucket. | +| [mimeType](./ai.imagengcsimage.md#imagengcsimagemimetype) | string | The MIME type of the image; either "image/png" or "image/jpeg".To request a different format, set the imageFormat property in your [ImagenGenerationConfig](./ai.imagengenerationconfig.md#imagengenerationconfig_interface). | ## ImagenGCSImage.gcsURI @@ -45,7 +45,7 @@ gcsURI: string; The MIME type of the image; either `"image/png"` or `"image/jpeg"`. -To request a different format, set the `imageFormat` property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). +To request a different format, set the `imageFormat` property in your [ImagenGenerationConfig](./ai.imagengenerationconfig.md#imagengenerationconfig_interface). Signature: diff --git a/docs-devsite/vertexai.imagengenerationconfig.md b/docs-devsite/ai.imagengenerationconfig.md similarity index 52% rename from docs-devsite/vertexai.imagengenerationconfig.md rename to docs-devsite/ai.imagengenerationconfig.md index 51a66b147dc..d4f32a7e5a3 100644 --- a/docs-devsite/vertexai.imagengenerationconfig.md +++ b/docs-devsite/ai.imagengenerationconfig.md @@ -27,11 +27,11 @@ export interface ImagenGenerationConfig | Property | Type | Description | | --- | --- | --- | -| [addWatermark](./vertexai.imagengenerationconfig.md#imagengenerationconfigaddwatermark) | boolean | (Public Preview) Whether to add an invisible watermark to generated images.If set to true, an invisible SynthID watermark is embedded in generated images to indicate that they are AI generated. If set to false, watermarking will be disabled.For Imagen 3 models, the default value is true; see the addWatermark documentation for more details.When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this will default to true, and cannot be turned off. | -| [aspectRatio](./vertexai.imagengenerationconfig.md#imagengenerationconfigaspectratio) | [ImagenAspectRatio](./vertexai.md#imagenaspectratio) | (Public Preview) The aspect ratio of the generated images. The default value is square 1:1. Supported aspect ratios depend on the Imagen model, see [ImagenAspectRatio](./vertexai.md#imagenaspectratio) for more details. | -| [imageFormat](./vertexai.imagengenerationconfig.md#imagengenerationconfigimageformat) | [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) | (Public Preview) The image format of the generated images. The default is PNG.See [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for more details. | -| [negativePrompt](./vertexai.imagengenerationconfig.md#imagengenerationconfignegativeprompt) | string | (Public Preview) A description of what should be omitted from the generated images.Support for negative prompts depends on the Imagen model.See the [documentation](http://firebase.google.com/docs/vertex-ai/model-parameters#imagen) for more details.This is no longer supported in the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)) in versions greater than imagen-3.0-generate-002. | -| [numberOfImages](./vertexai.imagengenerationconfig.md#imagengenerationconfignumberofimages) | number | (Public Preview) The number of images to generate. The default value is 1.The number of sample images that may be generated in each request depends on the model (typically up to 4); see the sampleCount documentation for more details. | +| [addWatermark](./ai.imagengenerationconfig.md#imagengenerationconfigaddwatermark) | boolean | (Public Preview) Whether to add an invisible watermark to generated images.If set to true, an invisible SynthID watermark is embedded in generated images to indicate that they are AI generated. If set to false, watermarking will be disabled.For Imagen 3 models, the default value is true; see the addWatermark documentation for more details.When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this will default to true, and cannot be turned off. | +| [aspectRatio](./ai.imagengenerationconfig.md#imagengenerationconfigaspectratio) | [ImagenAspectRatio](./ai.md#imagenaspectratio) | (Public Preview) The aspect ratio of the generated images. The default value is square 1:1. Supported aspect ratios depend on the Imagen model, see [ImagenAspectRatio](./ai.md#imagenaspectratio) for more details. | +| [imageFormat](./ai.imagengenerationconfig.md#imagengenerationconfigimageformat) | [ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) | (Public Preview) The image format of the generated images. The default is PNG.See [ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) for more details. | +| [negativePrompt](./ai.imagengenerationconfig.md#imagengenerationconfignegativeprompt) | string | (Public Preview) A description of what should be omitted from the generated images.Support for negative prompts depends on the Imagen model.See the [documentation](http://firebase.google.com/docs/vertex-ai/model-parameters#imagen) for more details.This is no longer supported in the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)) in versions greater than imagen-3.0-generate-002. | +| [numberOfImages](./ai.imagengenerationconfig.md#imagengenerationconfignumberofimages) | number | (Public Preview) The number of images to generate. The default value is 1.The number of sample images that may be generated in each request depends on the model (typically up to 4); see the sampleCount documentation for more details. | ## ImagenGenerationConfig.addWatermark @@ -44,7 +44,7 @@ If set to `true`, an invisible SynthID watermark is embedded in generate For Imagen 3 models, the default value is `true`; see the addWatermark documentation for more details. -When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this will default to true, and cannot be turned off. +When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this will default to true, and cannot be turned off. Signature: @@ -57,7 +57,7 @@ addWatermark?: boolean; > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > -The aspect ratio of the generated images. The default value is square 1:1. Supported aspect ratios depend on the Imagen model, see [ImagenAspectRatio](./vertexai.md#imagenaspectratio) for more details. +The aspect ratio of the generated images. The default value is square 1:1. Supported aspect ratios depend on the Imagen model, see [ImagenAspectRatio](./ai.md#imagenaspectratio) for more details. Signature: @@ -72,7 +72,7 @@ aspectRatio?: ImagenAspectRatio; The image format of the generated images. The default is PNG. -See [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for more details. +See [ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) for more details. Signature: @@ -91,7 +91,7 @@ Support for negative prompts depends on the Imagen model. See the [documentation](http://firebase.google.com/docs/vertex-ai/model-parameters#imagen) for more details. -This is no longer supported in the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)) in versions greater than `imagen-3.0-generate-002`. +This is no longer supported in the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)) in versions greater than `imagen-3.0-generate-002`. Signature: diff --git a/docs-devsite/vertexai.imagengenerationresponse.md b/docs-devsite/ai.imagengenerationresponse.md similarity index 56% rename from docs-devsite/vertexai.imagengenerationresponse.md rename to docs-devsite/ai.imagengenerationresponse.md index d8de93df3ec..54b0ac9b1a9 100644 --- a/docs-devsite/vertexai.imagengenerationresponse.md +++ b/docs-devsite/ai.imagengenerationresponse.md @@ -25,8 +25,8 @@ export interface ImagenGenerationResponse(Public Preview) The reason that images were filtered out. This property will only be defined if one or more images were filtered.Images may be filtered out due to the [ImagenSafetyFilterLevel](./vertexai.md#imagensafetyfilterlevel), [ImagenPersonFilterLevel](./vertexai.md#imagenpersonfilterlevel), or filtering included in the model. The filter levels may be adjusted in your [ImagenSafetySettings](./vertexai.imagensafetysettings.md#imagensafetysettings_interface).See the [Responsible AI and usage guidelines for Imagen](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen) for more details. | -| [images](./vertexai.imagengenerationresponse.md#imagengenerationresponseimages) | T\[\] | (Public Preview) The images generated by Imagen.The number of images generated may be fewer than the number requested if one or more were filtered out; see filteredReason. | +| [filteredReason](./ai.imagengenerationresponse.md#imagengenerationresponsefilteredreason) | string | (Public Preview) The reason that images were filtered out. This property will only be defined if one or more images were filtered.Images may be filtered out due to the [ImagenSafetyFilterLevel](./ai.md#imagensafetyfilterlevel), [ImagenPersonFilterLevel](./ai.md#imagenpersonfilterlevel), or filtering included in the model. The filter levels may be adjusted in your [ImagenSafetySettings](./ai.imagensafetysettings.md#imagensafetysettings_interface).See the [Responsible AI and usage guidelines for Imagen](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen) for more details. | +| [images](./ai.imagengenerationresponse.md#imagengenerationresponseimages) | T\[\] | (Public Preview) The images generated by Imagen.The number of images generated may be fewer than the number requested if one or more were filtered out; see filteredReason. | ## ImagenGenerationResponse.filteredReason @@ -35,7 +35,7 @@ export interface ImagenGenerationResponse, [ImagenPersonFilterLevel](./vertexai.md#imagenpersonfilterlevel), or filtering included in the model. The filter levels may be adjusted in your [ImagenSafetySettings](./vertexai.imagensafetysettings.md#imagensafetysettings_interface). +Images may be filtered out due to the [ImagenSafetyFilterLevel](./ai.md#imagensafetyfilterlevel), [ImagenPersonFilterLevel](./ai.md#imagenpersonfilterlevel), or filtering included in the model. The filter levels may be adjusted in your [ImagenSafetySettings](./ai.imagensafetysettings.md#imagensafetysettings_interface). See the [Responsible AI and usage guidelines for Imagen](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen) for more details. diff --git a/docs-devsite/vertexai.imagenimageformat.md b/docs-devsite/ai.imagenimageformat.md similarity index 61% rename from docs-devsite/vertexai.imagenimageformat.md rename to docs-devsite/ai.imagenimageformat.md index 68db8bbdae0..bd0bdf1baa7 100644 --- a/docs-devsite/vertexai.imagenimageformat.md +++ b/docs-devsite/ai.imagenimageformat.md @@ -15,7 +15,7 @@ https://github.com/firebase/firebase-js-sdk Defines the image format for images generated by Imagen. -Use this class to specify the desired format (JPEG or PNG) and compression quality for images generated by Imagen. This is typically included as part of [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface). +Use this class to specify the desired format (JPEG or PNG) and compression quality for images generated by Imagen. This is typically included as part of [ImagenModelParams](./ai.imagenmodelparams.md#imagenmodelparams_interface). Signature: @@ -27,15 +27,15 @@ export declare class ImagenImageFormat | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [compressionQuality](./vertexai.imagenimageformat.md#imagenimageformatcompressionquality) | | number | (Public Preview) The level of compression (a number between 0 and 100). | -| [mimeType](./vertexai.imagenimageformat.md#imagenimageformatmimetype) | | string | (Public Preview) The MIME type. | +| [compressionQuality](./ai.imagenimageformat.md#imagenimageformatcompressionquality) | | number | (Public Preview) The level of compression (a number between 0 and 100). | +| [mimeType](./ai.imagenimageformat.md#imagenimageformatmimetype) | | string | (Public Preview) The MIME type. | ## Methods | Method | Modifiers | Description | | --- | --- | --- | -| [jpeg(compressionQuality)](./vertexai.imagenimageformat.md#imagenimageformatjpeg) | static | (Public Preview) Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a JPEG image. | -| [png()](./vertexai.imagenimageformat.md#imagenimageformatpng) | static | (Public Preview) Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a PNG image. | +| [jpeg(compressionQuality)](./ai.imagenimageformat.md#imagenimageformatjpeg) | static | (Public Preview) Creates an [ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) for a JPEG image. | +| [png()](./ai.imagenimageformat.md#imagenimageformatpng) | static | (Public Preview) Creates an [ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) for a PNG image. | ## ImagenImageFormat.compressionQuality @@ -68,7 +68,7 @@ mimeType: string; > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > -Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a JPEG image. +Creates an [ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) for a JPEG image. Signature: @@ -84,16 +84,16 @@ static jpeg(compressionQuality?: number): ImagenImageFormat; Returns: -[ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) +[ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) -An [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) object for a JPEG image. +An [ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) object for a JPEG image. ## ImagenImageFormat.png() > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > -Creates an [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) for a PNG image. +Creates an [ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) for a PNG image. Signature: @@ -102,9 +102,9 @@ static png(): ImagenImageFormat; ``` Returns: -[ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) +[ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) -An [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) object for a PNG image. +An [ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) object for a PNG image. ### Example diff --git a/docs-devsite/vertexai.imageninlineimage.md b/docs-devsite/ai.imageninlineimage.md similarity index 66% rename from docs-devsite/vertexai.imageninlineimage.md rename to docs-devsite/ai.imageninlineimage.md index a72937b5e5d..4bb81cac55d 100644 --- a/docs-devsite/vertexai.imageninlineimage.md +++ b/docs-devsite/ai.imageninlineimage.md @@ -25,8 +25,8 @@ export interface ImagenInlineImage | Property | Type | Description | | --- | --- | --- | -| [bytesBase64Encoded](./vertexai.imageninlineimage.md#imageninlineimagebytesbase64encoded) | string | (Public Preview) The base64-encoded image data. | -| [mimeType](./vertexai.imageninlineimage.md#imageninlineimagemimetype) | string | (Public Preview) The MIME type of the image; either "image/png" or "image/jpeg".To request a different format, set the imageFormat property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). | +| [bytesBase64Encoded](./ai.imageninlineimage.md#imageninlineimagebytesbase64encoded) | string | (Public Preview) The base64-encoded image data. | +| [mimeType](./ai.imageninlineimage.md#imageninlineimagemimetype) | string | (Public Preview) The MIME type of the image; either "image/png" or "image/jpeg".To request a different format, set the imageFormat property in your [ImagenGenerationConfig](./ai.imagengenerationconfig.md#imagengenerationconfig_interface). | ## ImagenInlineImage.bytesBase64Encoded @@ -48,7 +48,7 @@ bytesBase64Encoded: string; The MIME type of the image; either `"image/png"` or `"image/jpeg"`. -To request a different format, set the `imageFormat` property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). +To request a different format, set the `imageFormat` property in your [ImagenGenerationConfig](./ai.imagengenerationconfig.md#imagengenerationconfig_interface). Signature: diff --git a/docs-devsite/vertexai.imagenmodel.md b/docs-devsite/ai.imagenmodel.md similarity index 62% rename from docs-devsite/vertexai.imagenmodel.md rename to docs-devsite/ai.imagenmodel.md index e69c49b8572..911971e0988 100644 --- a/docs-devsite/vertexai.imagenmodel.md +++ b/docs-devsite/ai.imagenmodel.md @@ -22,34 +22,34 @@ This class provides methods for generating images using the Imagen model. ```typescript export declare class ImagenModel extends AIModel ``` -Extends: [AIModel](./vertexai.aimodel.md#aimodel_class) +Extends: [AIModel](./ai.aimodel.md#aimodel_class) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(ai, modelParams, requestOptions)](./vertexai.imagenmodel.md#imagenmodelconstructor) | | (Public Preview) Constructs a new instance of the [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class. | +| [(constructor)(ai, modelParams, requestOptions)](./ai.imagenmodel.md#imagenmodelconstructor) | | (Public Preview) Constructs a new instance of the [ImagenModel](./ai.imagenmodel.md#imagenmodel_class) class. | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [generationConfig](./vertexai.imagenmodel.md#imagenmodelgenerationconfig) | | [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface) | (Public Preview) The Imagen generation configuration. | -| [requestOptions](./vertexai.imagenmodel.md#imagenmodelrequestoptions) | | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) \| undefined | (Public Preview) | -| [safetySettings](./vertexai.imagenmodel.md#imagenmodelsafetysettings) | | [ImagenSafetySettings](./vertexai.imagensafetysettings.md#imagensafetysettings_interface) | (Public Preview) Safety settings for filtering inappropriate content. | +| [generationConfig](./ai.imagenmodel.md#imagenmodelgenerationconfig) | | [ImagenGenerationConfig](./ai.imagengenerationconfig.md#imagengenerationconfig_interface) | (Public Preview) The Imagen generation configuration. | +| [requestOptions](./ai.imagenmodel.md#imagenmodelrequestoptions) | | [RequestOptions](./ai.requestoptions.md#requestoptions_interface) \| undefined | (Public Preview) | +| [safetySettings](./ai.imagenmodel.md#imagenmodelsafetysettings) | | [ImagenSafetySettings](./ai.imagensafetysettings.md#imagensafetysettings_interface) | (Public Preview) Safety settings for filtering inappropriate content. | ## Methods | Method | Modifiers | Description | | --- | --- | --- | -| [generateImages(prompt)](./vertexai.imagenmodel.md#imagenmodelgenerateimages) | | (Public Preview) Generates images using the Imagen model and returns them as base64-encoded strings. | +| [generateImages(prompt)](./ai.imagenmodel.md#imagenmodelgenerateimages) | | (Public Preview) Generates images using the Imagen model and returns them as base64-encoded strings. | ## ImagenModel.(constructor) > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > -Constructs a new instance of the [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class. +Constructs a new instance of the [ImagenModel](./ai.imagenmodel.md#imagenmodel_class) class. Signature: @@ -61,9 +61,9 @@ constructor(ai: AI, modelParams: ImagenModelParams, requestOptions?: RequestOpti | Parameter | Type | Description | | --- | --- | --- | -| ai | [AI](./vertexai.ai.md#ai_interface) | an [AI](./vertexai.ai.md#ai_interface) instance. | -| modelParams | [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface) | Parameters to use when making requests to Imagen. | -| requestOptions | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) \| undefined | Additional options to use when making requests. | +| ai | [AI](./ai.ai.md#ai_interface) | an [AI](./ai.ai.md#ai_interface) instance. | +| modelParams | [ImagenModelParams](./ai.imagenmodelparams.md#imagenmodelparams_interface) | Parameters to use when making requests to Imagen. | +| requestOptions | [RequestOptions](./ai.requestoptions.md#requestoptions_interface) \| undefined | Additional options to use when making requests. | #### Exceptions @@ -129,9 +129,9 @@ generateImages(prompt: string): PromiseReturns: -Promise<[ImagenGenerationResponse](./vertexai.imagengenerationresponse.md#imagengenerationresponse_interface)<[ImagenInlineImage](./vertexai.imageninlineimage.md#imageninlineimage_interface)>> +Promise<[ImagenGenerationResponse](./ai.imagengenerationresponse.md#imagengenerationresponse_interface)<[ImagenInlineImage](./ai.imageninlineimage.md#imageninlineimage_interface)>> -A promise that resolves to an [ImagenGenerationResponse](./vertexai.imagengenerationresponse.md#imagengenerationresponse_interface) object containing the generated images. +A promise that resolves to an [ImagenGenerationResponse](./ai.imagengenerationresponse.md#imagengenerationresponse_interface) object containing the generated images. #### Exceptions diff --git a/docs-devsite/vertexai.imagenmodelparams.md b/docs-devsite/ai.imagenmodelparams.md similarity index 63% rename from docs-devsite/vertexai.imagenmodelparams.md rename to docs-devsite/ai.imagenmodelparams.md index 5396a36e4d1..a63345b64e6 100644 --- a/docs-devsite/vertexai.imagenmodelparams.md +++ b/docs-devsite/ai.imagenmodelparams.md @@ -13,7 +13,7 @@ https://github.com/firebase/firebase-js-sdk > This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. > -Parameters for configuring an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class). +Parameters for configuring an [ImagenModel](./ai.imagenmodel.md#imagenmodel_class). Signature: @@ -25,9 +25,9 @@ export interface ImagenModelParams | Property | Type | Description | | --- | --- | --- | -| [generationConfig](./vertexai.imagenmodelparams.md#imagenmodelparamsgenerationconfig) | [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface) | (Public Preview) Configuration options for generating images with Imagen. | -| [model](./vertexai.imagenmodelparams.md#imagenmodelparamsmodel) | string | (Public Preview) The Imagen model to use for generating images. For example: imagen-3.0-generate-002.Only Imagen 3 models (named imagen-3.0-*) are supported.See [model versions](https://firebase.google.com/docs/vertex-ai/models) for a full list of supported Imagen 3 models. | -| [safetySettings](./vertexai.imagenmodelparams.md#imagenmodelparamssafetysettings) | [ImagenSafetySettings](./vertexai.imagensafetysettings.md#imagensafetysettings_interface) | (Public Preview) Safety settings for filtering potentially inappropriate content. | +| [generationConfig](./ai.imagenmodelparams.md#imagenmodelparamsgenerationconfig) | [ImagenGenerationConfig](./ai.imagengenerationconfig.md#imagengenerationconfig_interface) | (Public Preview) Configuration options for generating images with Imagen. | +| [model](./ai.imagenmodelparams.md#imagenmodelparamsmodel) | string | (Public Preview) The Imagen model to use for generating images. For example: imagen-3.0-generate-002.Only Imagen 3 models (named imagen-3.0-*) are supported.See [model versions](https://firebase.google.com/docs/vertex-ai/models) for a full list of supported Imagen 3 models. | +| [safetySettings](./ai.imagenmodelparams.md#imagenmodelparamssafetysettings) | [ImagenSafetySettings](./ai.imagensafetysettings.md#imagensafetysettings_interface) | (Public Preview) Safety settings for filtering potentially inappropriate content. | ## ImagenModelParams.generationConfig diff --git a/docs-devsite/vertexai.imagensafetysettings.md b/docs-devsite/ai.imagensafetysettings.md similarity index 72% rename from docs-devsite/vertexai.imagensafetysettings.md rename to docs-devsite/ai.imagensafetysettings.md index 3cf7931a959..366e615d243 100644 --- a/docs-devsite/vertexai.imagensafetysettings.md +++ b/docs-devsite/ai.imagensafetysettings.md @@ -27,8 +27,8 @@ export interface ImagenSafetySettings | Property | Type | Description | | --- | --- | --- | -| [personFilterLevel](./vertexai.imagensafetysettings.md#imagensafetysettingspersonfilterlevel) | [ImagenPersonFilterLevel](./vertexai.md#imagenpersonfilterlevel) | (Public Preview) A filter level controlling whether generation of images containing people or faces is allowed. | -| [safetyFilterLevel](./vertexai.imagensafetysettings.md#imagensafetysettingssafetyfilterlevel) | [ImagenSafetyFilterLevel](./vertexai.md#imagensafetyfilterlevel) | (Public Preview) A filter level controlling how aggressive to filter out sensitive content from generated images. | +| [personFilterLevel](./ai.imagensafetysettings.md#imagensafetysettingspersonfilterlevel) | [ImagenPersonFilterLevel](./ai.md#imagenpersonfilterlevel) | (Public Preview) A filter level controlling whether generation of images containing people or faces is allowed. | +| [safetyFilterLevel](./ai.imagensafetysettings.md#imagensafetysettingssafetyfilterlevel) | [ImagenSafetyFilterLevel](./ai.md#imagensafetyfilterlevel) | (Public Preview) A filter level controlling how aggressive to filter out sensitive content from generated images. | ## ImagenSafetySettings.personFilterLevel diff --git a/docs-devsite/vertexai.inlinedatapart.md b/docs-devsite/ai.inlinedatapart.md similarity index 62% rename from docs-devsite/vertexai.inlinedatapart.md rename to docs-devsite/ai.inlinedatapart.md index 0cb064fc357..0dd68edda68 100644 --- a/docs-devsite/vertexai.inlinedatapart.md +++ b/docs-devsite/ai.inlinedatapart.md @@ -22,11 +22,11 @@ export interface InlineDataPart | Property | Type | Description | | --- | --- | --- | -| [functionCall](./vertexai.inlinedatapart.md#inlinedatapartfunctioncall) | never | | -| [functionResponse](./vertexai.inlinedatapart.md#inlinedatapartfunctionresponse) | never | | -| [inlineData](./vertexai.inlinedatapart.md#inlinedatapartinlinedata) | [GenerativeContentBlob](./vertexai.generativecontentblob.md#generativecontentblob_interface) | | -| [text](./vertexai.inlinedatapart.md#inlinedataparttext) | never | | -| [videoMetadata](./vertexai.inlinedatapart.md#inlinedatapartvideometadata) | [VideoMetadata](./vertexai.videometadata.md#videometadata_interface) | Applicable if inlineData is a video. | +| [functionCall](./ai.inlinedatapart.md#inlinedatapartfunctioncall) | never | | +| [functionResponse](./ai.inlinedatapart.md#inlinedatapartfunctionresponse) | never | | +| [inlineData](./ai.inlinedatapart.md#inlinedatapartinlinedata) | [GenerativeContentBlob](./ai.generativecontentblob.md#generativecontentblob_interface) | | +| [text](./ai.inlinedatapart.md#inlinedataparttext) | never | | +| [videoMetadata](./ai.inlinedatapart.md#inlinedatapartvideometadata) | [VideoMetadata](./ai.videometadata.md#videometadata_interface) | Applicable if inlineData is a video. | ## InlineDataPart.functionCall diff --git a/docs-devsite/vertexai.integerschema.md b/docs-devsite/ai.integerschema.md similarity index 71% rename from docs-devsite/vertexai.integerschema.md rename to docs-devsite/ai.integerschema.md index 4822bdd618b..4ba96e49ca5 100644 --- a/docs-devsite/vertexai.integerschema.md +++ b/docs-devsite/ai.integerschema.md @@ -17,13 +17,13 @@ Schema class for "integer" types. ```typescript export declare class IntegerSchema extends Schema ``` -Extends: [Schema](./vertexai.schema.md#schema_class) +Extends: [Schema](./ai.schema.md#schema_class) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(schemaParams)](./vertexai.integerschema.md#integerschemaconstructor) | | Constructs a new instance of the IntegerSchema class | +| [(constructor)(schemaParams)](./ai.integerschema.md#integerschemaconstructor) | | Constructs a new instance of the IntegerSchema class | ## IntegerSchema.(constructor) @@ -39,5 +39,5 @@ constructor(schemaParams?: SchemaParams); | Parameter | Type | Description | | --- | --- | --- | -| schemaParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | | +| schemaParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) | | diff --git a/docs-devsite/ai.md b/docs-devsite/ai.md new file mode 100644 index 00000000000..c43c0391ba4 --- /dev/null +++ b/docs-devsite/ai.md @@ -0,0 +1,728 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# ai package +The Firebase AI Web SDK. + +## Functions + +| Function | Description | +| --- | --- | +| function(app, ...) | +| [getAI(app, options)](./ai.md#getai_a94a413) | Returns the default [AI](./ai.ai.md#ai_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. | +| [getVertexAI(app, options)](./ai.md#getvertexai_04094cf) | | +| function(ai, ...) | +| [getGenerativeModel(ai, modelParams, requestOptions)](./ai.md#getgenerativemodel_80bd839) | Returns a [GenerativeModel](./ai.generativemodel.md#generativemodel_class) class with methods for inference and other functionality. | +| [getImagenModel(ai, modelParams, requestOptions)](./ai.md#getimagenmodel_e1f6645) | (Public Preview) Returns an [ImagenModel](./ai.imagenmodel.md#imagenmodel_class) class with methods for using Imagen.Only Imagen 3 models (named imagen-3.0-*) are supported. | + +## Classes + +| Class | Description | +| --- | --- | +| [AIError](./ai.aierror.md#aierror_class) | Error class for the Firebase AI SDK. | +| [AIModel](./ai.aimodel.md#aimodel_class) | Base class for Firebase AI model APIs.Instances of this class are associated with a specific Firebase AI [Backend](./ai.backend.md#backend_class) and provide methods for interacting with the configured generative model. | +| [ArraySchema](./ai.arrayschema.md#arrayschema_class) | Schema class for "array" types. The items param should refer to the type of item that can be a member of the array. | +| [Backend](./ai.backend.md#backend_class) | Abstract base class representing the configuration for an AI service backend. This class should not be instantiated directly. Use its subclasses; [GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class) for the Gemini Developer API (via [Google AI](https://ai.google/)), and [VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class) for the Vertex AI Gemini API. | +| [BooleanSchema](./ai.booleanschema.md#booleanschema_class) | Schema class for "boolean" types. | +| [ChatSession](./ai.chatsession.md#chatsession_class) | ChatSession class that enables sending chat messages and stores history of sent and received messages so far. | +| [GenerativeModel](./ai.generativemodel.md#generativemodel_class) | Class for generative model APIs. | +| [GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class) | Configuration class for the Gemini Developer API.Use this with [AIOptions](./ai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./ai.md#getai_a94a413) to specify the Gemini Developer API as the backend. | +| [ImagenImageFormat](./ai.imagenimageformat.md#imagenimageformat_class) | (Public Preview) Defines the image format for images generated by Imagen.Use this class to specify the desired format (JPEG or PNG) and compression quality for images generated by Imagen. This is typically included as part of [ImagenModelParams](./ai.imagenmodelparams.md#imagenmodelparams_interface). | +| [ImagenModel](./ai.imagenmodel.md#imagenmodel_class) | (Public Preview) Class for Imagen model APIs.This class provides methods for generating images using the Imagen model. | +| [IntegerSchema](./ai.integerschema.md#integerschema_class) | Schema class for "integer" types. | +| [NumberSchema](./ai.numberschema.md#numberschema_class) | Schema class for "number" types. | +| [ObjectSchema](./ai.objectschema.md#objectschema_class) | Schema class for "object" types. The properties param must be a map of Schema objects. | +| [Schema](./ai.schema.md#schema_class) | Parent class encompassing all Schema types, with static methods that allow building specific Schema types. This class can be converted with JSON.stringify() into a JSON string accepted by Vertex AI REST endpoints. (This string conversion is automatically done when calling SDK methods.) | +| [StringSchema](./ai.stringschema.md#stringschema_class) | Schema class for "string" types. Can be used with or without enum values. | +| [VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class) | Configuration class for the Vertex AI Gemini API.Use this with [AIOptions](./ai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./ai.md#getai_a94a413) to specify the Vertex AI Gemini API as the backend. | + +## Enumerations + +| Enumeration | Description | +| --- | --- | +| [AIErrorCode](./ai.md#aierrorcode) | Standardized error codes that [AIError](./ai.aierror.md#aierror_class) can have. | +| [BlockReason](./ai.md#blockreason) | Reason that a prompt was blocked. | +| [FinishReason](./ai.md#finishreason) | Reason that a candidate finished. | +| [FunctionCallingMode](./ai.md#functioncallingmode) | | +| [HarmBlockMethod](./ai.md#harmblockmethod) | This property is not supported in the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)). | +| [HarmBlockThreshold](./ai.md#harmblockthreshold) | Threshold above which a prompt or candidate will be blocked. | +| [HarmCategory](./ai.md#harmcategory) | Harm categories that would cause prompts or candidates to be blocked. | +| [HarmProbability](./ai.md#harmprobability) | Probability that a prompt or candidate matches a harm category. | +| [HarmSeverity](./ai.md#harmseverity) | Harm severity levels. | +| [ImagenAspectRatio](./ai.md#imagenaspectratio) | (Public Preview) Aspect ratios for Imagen images.To specify an aspect ratio for generated images, set the aspectRatio property in your [ImagenGenerationConfig](./ai.imagengenerationconfig.md#imagengenerationconfig_interface).See the the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) for more details and examples of the supported aspect ratios. | +| [ImagenPersonFilterLevel](./ai.md#imagenpersonfilterlevel) | (Public Preview) A filter level controlling whether generation of images containing people or faces is allowed.See the personGeneration documentation for more details. | +| [ImagenSafetyFilterLevel](./ai.md#imagensafetyfilterlevel) | (Public Preview) A filter level controlling how aggressively to filter sensitive content.Text prompts provided as inputs and images (generated or uploaded) through Imagen on Vertex AI are assessed against a list of safety filters, which include 'harmful categories' (for example, violence, sexual, derogatory, and toxic). This filter level controls how aggressively to filter out potentially harmful content from responses. See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) and the [Responsible AI and usage guidelines](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#safety-filters) for more details. | +| [Modality](./ai.md#modality) | Content part modality. | +| [SchemaType](./ai.md#schematype) | Contains the list of OpenAPI data types as defined by the [OpenAPI specification](https://swagger.io/docs/specification/data-models/data-types/) | + +## Interfaces + +| Interface | Description | +| --- | --- | +| [AI](./ai.ai.md#ai_interface) | An instance of the Firebase AI SDK.Do not create this instance directly. Instead, use [getAI()](./ai.md#getai_a94a413). | +| [AIOptions](./ai.aioptions.md#aioptions_interface) | Options for initializing the AI service using [getAI()](./ai.md#getai_a94a413). This allows specifying which backend to use (Vertex AI Gemini API or Gemini Developer API) and configuring its specific options (like location for Vertex AI). | +| [BaseParams](./ai.baseparams.md#baseparams_interface) | Base parameters for a number of methods. | +| [Citation](./ai.citation.md#citation_interface) | A single citation. | +| [CitationMetadata](./ai.citationmetadata.md#citationmetadata_interface) | Citation metadata that may be found on a [GenerateContentCandidate](./ai.generatecontentcandidate.md#generatecontentcandidate_interface). | +| [Content](./ai.content.md#content_interface) | Content type for both prompts and response candidates. | +| [CountTokensRequest](./ai.counttokensrequest.md#counttokensrequest_interface) | Params for calling [GenerativeModel.countTokens()](./ai.generativemodel.md#generativemodelcounttokens) | +| [CountTokensResponse](./ai.counttokensresponse.md#counttokensresponse_interface) | Response from calling [GenerativeModel.countTokens()](./ai.generativemodel.md#generativemodelcounttokens). | +| [CustomErrorData](./ai.customerrordata.md#customerrordata_interface) | Details object that contains data originating from a bad HTTP response. | +| [Date\_2](./ai.date_2.md#date_2_interface) | Protobuf google.type.Date | +| [EnhancedGenerateContentResponse](./ai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponse_interface) | Response object wrapped with helper methods. | +| [ErrorDetails](./ai.errordetails.md#errordetails_interface) | Details object that may be included in an error response. | +| [FileData](./ai.filedata.md#filedata_interface) | Data pointing to a file uploaded on Google Cloud Storage. | +| [FileDataPart](./ai.filedatapart.md#filedatapart_interface) | Content part interface if the part represents [FileData](./ai.filedata.md#filedata_interface) | +| [FunctionCall](./ai.functioncall.md#functioncall_interface) | A predicted [FunctionCall](./ai.functioncall.md#functioncall_interface) returned from the model that contains a string representing the [FunctionDeclaration.name](./ai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing the parameters and their values. | +| [FunctionCallingConfig](./ai.functioncallingconfig.md#functioncallingconfig_interface) | | +| [FunctionCallPart](./ai.functioncallpart.md#functioncallpart_interface) | Content part interface if the part represents a [FunctionCall](./ai.functioncall.md#functioncall_interface). | +| [FunctionDeclaration](./ai.functiondeclaration.md#functiondeclaration_interface) | Structured representation of a function declaration as defined by the [OpenAPI 3.0 specification](https://spec.openapis.org/oas/v3.0.3). Included in this declaration are the function name and parameters. This FunctionDeclaration is a representation of a block of code that can be used as a Tool by the model and executed by the client. | +| [FunctionDeclarationsTool](./ai.functiondeclarationstool.md#functiondeclarationstool_interface) | A FunctionDeclarationsTool is a piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. | +| [FunctionResponse](./ai.functionresponse.md#functionresponse_interface) | The result output from a [FunctionCall](./ai.functioncall.md#functioncall_interface) that contains a string representing the [FunctionDeclaration.name](./ai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing any output from the function is used as context to the model. This should contain the result of a [FunctionCall](./ai.functioncall.md#functioncall_interface) made based on model prediction. | +| [FunctionResponsePart](./ai.functionresponsepart.md#functionresponsepart_interface) | Content part interface if the part represents [FunctionResponse](./ai.functionresponse.md#functionresponse_interface). | +| [GenerateContentCandidate](./ai.generatecontentcandidate.md#generatecontentcandidate_interface) | A candidate returned as part of a [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface). | +| [GenerateContentRequest](./ai.generatecontentrequest.md#generatecontentrequest_interface) | Request sent through [GenerativeModel.generateContent()](./ai.generativemodel.md#generativemodelgeneratecontent) | +| [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface) | Individual response from [GenerativeModel.generateContent()](./ai.generativemodel.md#generativemodelgeneratecontent) and [GenerativeModel.generateContentStream()](./ai.generativemodel.md#generativemodelgeneratecontentstream). generateContentStream() will return one in each chunk until the stream is done. | +| [GenerateContentResult](./ai.generatecontentresult.md#generatecontentresult_interface) | Result object returned from [GenerativeModel.generateContent()](./ai.generativemodel.md#generativemodelgeneratecontent) call. | +| [GenerateContentStreamResult](./ai.generatecontentstreamresult.md#generatecontentstreamresult_interface) | Result object returned from [GenerativeModel.generateContentStream()](./ai.generativemodel.md#generativemodelgeneratecontentstream) call. Iterate over stream to get chunks as they come in and/or use the response promise to get the aggregated response when the stream is done. | +| [GenerationConfig](./ai.generationconfig.md#generationconfig_interface) | Config options for content-related requests | +| [GenerativeContentBlob](./ai.generativecontentblob.md#generativecontentblob_interface) | Interface for sending an image. | +| [GroundingAttribution](./ai.groundingattribution.md#groundingattribution_interface) | | +| [GroundingMetadata](./ai.groundingmetadata.md#groundingmetadata_interface) | Metadata returned to client when grounding is enabled. | +| [ImagenGCSImage](./ai.imagengcsimage.md#imagengcsimage_interface) | An image generated by Imagen, stored in a Cloud Storage for Firebase bucket.This feature is not available yet. | +| [ImagenGenerationConfig](./ai.imagengenerationconfig.md#imagengenerationconfig_interface) | (Public Preview) Configuration options for generating images with Imagen.See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images-imagen) for more details. | +| [ImagenGenerationResponse](./ai.imagengenerationresponse.md#imagengenerationresponse_interface) | (Public Preview) The response from a request to generate images with Imagen. | +| [ImagenInlineImage](./ai.imageninlineimage.md#imageninlineimage_interface) | (Public Preview) An image generated by Imagen, represented as inline data. | +| [ImagenModelParams](./ai.imagenmodelparams.md#imagenmodelparams_interface) | (Public Preview) Parameters for configuring an [ImagenModel](./ai.imagenmodel.md#imagenmodel_class). | +| [ImagenSafetySettings](./ai.imagensafetysettings.md#imagensafetysettings_interface) | (Public Preview) Settings for controlling the aggressiveness of filtering out sensitive content.See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) for more details. | +| [InlineDataPart](./ai.inlinedatapart.md#inlinedatapart_interface) | Content part interface if the part represents an image. | +| [ModalityTokenCount](./ai.modalitytokencount.md#modalitytokencount_interface) | Represents token counting info for a single modality. | +| [ModelParams](./ai.modelparams.md#modelparams_interface) | Params passed to [getGenerativeModel()](./ai.md#getgenerativemodel_80bd839). | +| [ObjectSchemaInterface](./ai.objectschemainterface.md#objectschemainterface_interface) | Interface for [ObjectSchema](./ai.objectschema.md#objectschema_class) class. | +| [PromptFeedback](./ai.promptfeedback.md#promptfeedback_interface) | If the prompt was blocked, this will be populated with blockReason and the relevant safetyRatings. | +| [RequestOptions](./ai.requestoptions.md#requestoptions_interface) | Params passed to [getGenerativeModel()](./ai.md#getgenerativemodel_80bd839). | +| [RetrievedContextAttribution](./ai.retrievedcontextattribution.md#retrievedcontextattribution_interface) | | +| [SafetyRating](./ai.safetyrating.md#safetyrating_interface) | A safety rating associated with a [GenerateContentCandidate](./ai.generatecontentcandidate.md#generatecontentcandidate_interface) | +| [SafetySetting](./ai.safetysetting.md#safetysetting_interface) | Safety setting that can be sent as part of request parameters. | +| [SchemaInterface](./ai.schemainterface.md#schemainterface_interface) | Interface for [Schema](./ai.schema.md#schema_class) class. | +| [SchemaParams](./ai.schemaparams.md#schemaparams_interface) | Params passed to [Schema](./ai.schema.md#schema_class) static methods to create specific [Schema](./ai.schema.md#schema_class) classes. | +| [SchemaRequest](./ai.schemarequest.md#schemarequest_interface) | Final format for [Schema](./ai.schema.md#schema_class) params passed to backend requests. | +| [SchemaShared](./ai.schemashared.md#schemashared_interface) | Basic [Schema](./ai.schema.md#schema_class) properties shared across several Schema-related types. | +| [Segment](./ai.segment.md#segment_interface) | | +| [StartChatParams](./ai.startchatparams.md#startchatparams_interface) | Params for [GenerativeModel.startChat()](./ai.generativemodel.md#generativemodelstartchat). | +| [TextPart](./ai.textpart.md#textpart_interface) | Content part interface if the part represents a text string. | +| [ToolConfig](./ai.toolconfig.md#toolconfig_interface) | Tool config. This config is shared for all tools provided in the request. | +| [UsageMetadata](./ai.usagemetadata.md#usagemetadata_interface) | Usage metadata about a [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface). | +| [VertexAIOptions](./ai.vertexaioptions.md#vertexaioptions_interface) | Options when initializing the Firebase AI SDK. | +| [VideoMetadata](./ai.videometadata.md#videometadata_interface) | Describes the input video content. | +| [WebAttribution](./ai.webattribution.md#webattribution_interface) | | + +## Variables + +| Variable | Description | +| --- | --- | +| [BackendType](./ai.md#backendtype) | An enum-like object containing constants that represent the supported backends for the Firebase AI SDK. This determines which backend service (Vertex AI Gemini API or Gemini Developer API) the SDK will communicate with.These values are assigned to the backendType property within the specific backend configuration objects ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class) or [VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)) to identify which service to target. | +| [POSSIBLE\_ROLES](./ai.md#possible_roles) | Possible roles. | +| [ResponseModality](./ai.md#responsemodality) | (Public Preview) Generation modalities to be returned in generation responses. | +| [VertexAIError](./ai.md#vertexaierror) | | +| [VertexAIModel](./ai.md#vertexaimodel) | | + +## Type Aliases + +| Type Alias | Description | +| --- | --- | +| [BackendType](./ai.md#backendtype) | Type alias representing valid backend types. It can be either 'VERTEX_AI' or 'GOOGLE_AI'. | +| [Part](./ai.md#part) | Content part - includes text, image/video, or function call/response part types. | +| [ResponseModality](./ai.md#responsemodality) | (Public Preview) Generation modalities to be returned in generation responses. | +| [Role](./ai.md#role) | Role is the producer of the content. | +| [Tool](./ai.md#tool) | Defines a tool that model can call to access external knowledge. | +| [TypedSchema](./ai.md#typedschema) | A type that includes all specific Schema types. | +| [VertexAI](./ai.md#vertexai) | | + +## function(app, ...) + +### getAI(app, options) {:#getai_a94a413} + +Returns the default [AI](./ai.ai.md#ai_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. + +Signature: + +```typescript +export declare function getAI(app?: FirebaseApp, options?: AIOptions): AI; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| app | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) to use. | +| options | [AIOptions](./ai.aioptions.md#aioptions_interface) | [AIOptions](./ai.aioptions.md#aioptions_interface) that configure the AI instance. | + +Returns: + +[AI](./ai.ai.md#ai_interface) + +The default [AI](./ai.ai.md#ai_interface) instance for the given [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). + +### Example 1 + + +```javascript +const ai = getAI(app); + +``` + +### Example 2 + + +```javascript +// Get an AI instance configured to use the Gemini Developer API (via Google AI). +const ai = getAI(app, { backend: new GoogleAIBackend() }); + +``` + +### Example 3 + + +```javascript +// Get an AI instance configured to use the Vertex AI Gemini API. +const ai = getAI(app, { backend: new VertexAIBackend() }); + +``` + +### getVertexAI(app, options) {:#getvertexai_04094cf} + +> Warning: This API is now obsolete. +> +> Use the new [getAI()](./ai.md#getai_a94a413) instead. The Vertex AI in Firebase SDK has been replaced with the Firebase AI SDK to accommodate the evolving set of supported features and services. For migration details, see the [migration guide](https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk). +> +> Returns a [VertexAI](./ai.md#vertexai) instance for the given app, configured to use the Vertex AI Gemini API. This instance will be configured to use the Vertex AI Gemini API. +> + +Signature: + +```typescript +export declare function getVertexAI(app?: FirebaseApp, options?: VertexAIOptions): VertexAI; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| app | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) to use. | +| options | [VertexAIOptions](./ai.vertexaioptions.md#vertexaioptions_interface) | Options to configure the Vertex AI instance, including the location. | + +Returns: + +[VertexAI](./ai.md#vertexai) + +## function(ai, ...) + +### getGenerativeModel(ai, modelParams, requestOptions) {:#getgenerativemodel_80bd839} + +Returns a [GenerativeModel](./ai.generativemodel.md#generativemodel_class) class with methods for inference and other functionality. + +Signature: + +```typescript +export declare function getGenerativeModel(ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions): GenerativeModel; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ai | [AI](./ai.ai.md#ai_interface) | | +| modelParams | [ModelParams](./ai.modelparams.md#modelparams_interface) | | +| requestOptions | [RequestOptions](./ai.requestoptions.md#requestoptions_interface) | | + +Returns: + +[GenerativeModel](./ai.generativemodel.md#generativemodel_class) + +### getImagenModel(ai, modelParams, requestOptions) {:#getimagenmodel_e1f6645} + +> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> + +Returns an [ImagenModel](./ai.imagenmodel.md#imagenmodel_class) class with methods for using Imagen. + +Only Imagen 3 models (named `imagen-3.0-*`) are supported. + +Signature: + +```typescript +export declare function getImagenModel(ai: AI, modelParams: ImagenModelParams, requestOptions?: RequestOptions): ImagenModel; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ai | [AI](./ai.ai.md#ai_interface) | An [AI](./ai.ai.md#ai_interface) instance. | +| modelParams | [ImagenModelParams](./ai.imagenmodelparams.md#imagenmodelparams_interface) | Parameters to use when making Imagen requests. | +| requestOptions | [RequestOptions](./ai.requestoptions.md#requestoptions_interface) | Additional options to use when making requests. | + +Returns: + +[ImagenModel](./ai.imagenmodel.md#imagenmodel_class) + +#### Exceptions + +If the `apiKey` or `projectId` fields are missing in your Firebase config. + +## BackendType + +An enum-like object containing constants that represent the supported backends for the Firebase AI SDK. This determines which backend service (Vertex AI Gemini API or Gemini Developer API) the SDK will communicate with. + +These values are assigned to the `backendType` property within the specific backend configuration objects ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class) or [VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)) to identify which service to target. + +Signature: + +```typescript +BackendType: { + readonly VERTEX_AI: "VERTEX_AI"; + readonly GOOGLE_AI: "GOOGLE_AI"; +} +``` + +## POSSIBLE\_ROLES + +Possible roles. + +Signature: + +```typescript +POSSIBLE_ROLES: readonly ["user", "model", "function", "system"] +``` + +## ResponseModality + +> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> + +Generation modalities to be returned in generation responses. + +Signature: + +```typescript +ResponseModality: { + readonly TEXT: "TEXT"; + readonly IMAGE: "IMAGE"; +} +``` + +## VertexAIError + +> Warning: This API is now obsolete. +> +> Use the new [AIError](./ai.aierror.md#aierror_class) instead. The Vertex AI in Firebase SDK has been replaced with the Firebase AI SDK to accommodate the evolving set of supported features and services. For migration details, see the [migration guide](https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk). +> +> Error class for the Firebase AI SDK. +> + +Signature: + +```typescript +VertexAIError: typeof AIError +``` + +## VertexAIModel + +> Warning: This API is now obsolete. +> +> Use the new [AIModel](./ai.aimodel.md#aimodel_class) instead. The Vertex AI in Firebase SDK has been replaced with the Firebase AI SDK to accommodate the evolving set of supported features and services. For migration details, see the [migration guide](https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk). +> +> Base class for Firebase AI model APIs. +> + +Signature: + +```typescript +VertexAIModel: typeof AIModel +``` + +## BackendType + +Type alias representing valid backend types. It can be either `'VERTEX_AI'` or `'GOOGLE_AI'`. + +Signature: + +```typescript +export type BackendType = (typeof BackendType)[keyof typeof BackendType]; +``` + +## Part + +Content part - includes text, image/video, or function call/response part types. + +Signature: + +```typescript +export type Part = TextPart | InlineDataPart | FunctionCallPart | FunctionResponsePart | FileDataPart; +``` + +## ResponseModality + +> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> + +Generation modalities to be returned in generation responses. + +Signature: + +```typescript +export type ResponseModality = (typeof ResponseModality)[keyof typeof ResponseModality]; +``` + +## Role + +Role is the producer of the content. + +Signature: + +```typescript +export type Role = (typeof POSSIBLE_ROLES)[number]; +``` + +## Tool + +Defines a tool that model can call to access external knowledge. + +Signature: + +```typescript +export declare type Tool = FunctionDeclarationsTool; +``` + +## TypedSchema + +A type that includes all specific Schema types. + +Signature: + +```typescript +export type TypedSchema = IntegerSchema | NumberSchema | StringSchema | BooleanSchema | ObjectSchema | ArraySchema; +``` + +## VertexAI + +> Warning: This API is now obsolete. +> +> Use the new [AI](./ai.ai.md#ai_interface) instead. The Vertex AI in Firebase SDK has been replaced with the Firebase AI SDK to accommodate the evolving set of supported features and services. For migration details, see the [migration guide](https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk). +> +> An instance of the Firebase AI SDK. +> + +Signature: + +```typescript +export type VertexAI = AI; +``` + +## AIErrorCode + +Standardized error codes that [AIError](./ai.aierror.md#aierror_class) can have. + +Signature: + +```typescript +export declare const enum AIErrorCode +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| API\_NOT\_ENABLED | "api-not-enabled" | An error due to the Firebase API not being enabled in the Console. | +| ERROR | "error" | A generic error occurred. | +| FETCH\_ERROR | "fetch-error" | An error occurred while performing a fetch. | +| INVALID\_CONTENT | "invalid-content" | An error associated with a Content object. | +| INVALID\_SCHEMA | "invalid-schema" | An error due to invalid Schema input. | +| NO\_API\_KEY | "no-api-key" | An error occurred due to a missing Firebase API key. | +| NO\_APP\_ID | "no-app-id" | An error occured due to a missing Firebase app ID. | +| NO\_MODEL | "no-model" | An error occurred due to a model name not being specified during initialization. | +| NO\_PROJECT\_ID | "no-project-id" | An error occurred due to a missing project ID. | +| PARSE\_FAILED | "parse-failed" | An error occurred while parsing. | +| REQUEST\_ERROR | "request-error" | An error occurred in a request. | +| RESPONSE\_ERROR | "response-error" | An error occurred in a response. | +| UNSUPPORTED | "unsupported" | An error occured due an attempt to use an unsupported feature. | + +## BlockReason + +Reason that a prompt was blocked. + +Signature: + +```typescript +export declare enum BlockReason +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| BLOCKLIST | "BLOCKLIST" | Content was blocked because it contained terms from the terminology blocklist. | +| OTHER | "OTHER" | Content was blocked, but the reason is uncategorized. | +| PROHIBITED\_CONTENT | "PROHIBITED_CONTENT" | Content was blocked due to prohibited content. | +| SAFETY | "SAFETY" | Content was blocked by safety settings. | + +## FinishReason + +Reason that a candidate finished. + +Signature: + +```typescript +export declare enum FinishReason +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| BLOCKLIST | "BLOCKLIST" | The candidate content contained forbidden terms. | +| MALFORMED\_FUNCTION\_CALL | "MALFORMED_FUNCTION_CALL" | The function call generated by the model was invalid. | +| MAX\_TOKENS | "MAX_TOKENS" | The maximum number of tokens as specified in the request was reached. | +| OTHER | "OTHER" | Unknown reason. | +| PROHIBITED\_CONTENT | "PROHIBITED_CONTENT" | The candidate content potentially contained prohibited content. | +| RECITATION | "RECITATION" | The candidate content was flagged for recitation reasons. | +| SAFETY | "SAFETY" | The candidate content was flagged for safety reasons. | +| SPII | "SPII" | The candidate content potentially contained Sensitive Personally Identifiable Information (SPII). | +| STOP | "STOP" | Natural stop point of the model or provided stop sequence. | + +## FunctionCallingMode + + +Signature: + +```typescript +export declare enum FunctionCallingMode +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| ANY | "ANY" | Model is constrained to always predicting a function call only. If allowed_function_names is set, the predicted function call will be limited to any one of allowed_function_names, else the predicted function call will be any one of the provided function_declarations. | +| AUTO | "AUTO" | Default model behavior; model decides to predict either a function call or a natural language response. | +| NONE | "NONE" | Model will not predict any function call. Model behavior is same as when not passing any function declarations. | + +## HarmBlockMethod + +This property is not supported in the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)). + +Signature: + +```typescript +export declare enum HarmBlockMethod +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| PROBABILITY | "PROBABILITY" | The harm block method uses the probability score. | +| SEVERITY | "SEVERITY" | The harm block method uses both probability and severity scores. | + +## HarmBlockThreshold + +Threshold above which a prompt or candidate will be blocked. + +Signature: + +```typescript +export declare enum HarmBlockThreshold +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| BLOCK\_LOW\_AND\_ABOVE | "BLOCK_LOW_AND_ABOVE" | Content with NEGLIGIBLE will be allowed. | +| BLOCK\_MEDIUM\_AND\_ABOVE | "BLOCK_MEDIUM_AND_ABOVE" | Content with NEGLIGIBLE and LOW will be allowed. | +| BLOCK\_NONE | "BLOCK_NONE" | All content will be allowed. | +| BLOCK\_ONLY\_HIGH | "BLOCK_ONLY_HIGH" | Content with NEGLIGIBLE, LOW, and MEDIUM will be allowed. | + +## HarmCategory + +Harm categories that would cause prompts or candidates to be blocked. + +Signature: + +```typescript +export declare enum HarmCategory +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| HARM\_CATEGORY\_DANGEROUS\_CONTENT | "HARM_CATEGORY_DANGEROUS_CONTENT" | | +| HARM\_CATEGORY\_HARASSMENT | "HARM_CATEGORY_HARASSMENT" | | +| HARM\_CATEGORY\_HATE\_SPEECH | "HARM_CATEGORY_HATE_SPEECH" | | +| HARM\_CATEGORY\_SEXUALLY\_EXPLICIT | "HARM_CATEGORY_SEXUALLY_EXPLICIT" | | + +## HarmProbability + +Probability that a prompt or candidate matches a harm category. + +Signature: + +```typescript +export declare enum HarmProbability +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| HIGH | "HIGH" | Content has a high chance of being unsafe. | +| LOW | "LOW" | Content has a low chance of being unsafe. | +| MEDIUM | "MEDIUM" | Content has a medium chance of being unsafe. | +| NEGLIGIBLE | "NEGLIGIBLE" | Content has a negligible chance of being unsafe. | + +## HarmSeverity + +Harm severity levels. + +Signature: + +```typescript +export declare enum HarmSeverity +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| HARM\_SEVERITY\_HIGH | "HARM_SEVERITY_HIGH" | High level of harm severity. | +| HARM\_SEVERITY\_LOW | "HARM_SEVERITY_LOW" | Low level of harm severity. | +| HARM\_SEVERITY\_MEDIUM | "HARM_SEVERITY_MEDIUM" | Medium level of harm severity. | +| HARM\_SEVERITY\_NEGLIGIBLE | "HARM_SEVERITY_NEGLIGIBLE" | Negligible level of harm severity. | +| HARM\_SEVERITY\_UNSUPPORTED | "HARM_SEVERITY_UNSUPPORTED" | Harm severity is not supported. | + +## ImagenAspectRatio + +> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> + +Aspect ratios for Imagen images. + +To specify an aspect ratio for generated images, set the `aspectRatio` property in your [ImagenGenerationConfig](./ai.imagengenerationconfig.md#imagengenerationconfig_interface). + +See the the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) for more details and examples of the supported aspect ratios. + +Signature: + +```typescript +export declare enum ImagenAspectRatio +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| LANDSCAPE\_16x9 | "16:9" | (Public Preview) Landscape (16:9) aspect ratio. | +| LANDSCAPE\_3x4 | "3:4" | (Public Preview) Landscape (3:4) aspect ratio. | +| PORTRAIT\_4x3 | "4:3" | (Public Preview) Portrait (4:3) aspect ratio. | +| PORTRAIT\_9x16 | "9:16" | (Public Preview) Portrait (9:16) aspect ratio. | +| SQUARE | "1:1" | (Public Preview) Square (1:1) aspect ratio. | + +## ImagenPersonFilterLevel + +> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> + +A filter level controlling whether generation of images containing people or faces is allowed. + +See the personGeneration documentation for more details. + +Signature: + +```typescript +export declare enum ImagenPersonFilterLevel +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| ALLOW\_ADULT | "allow_adult" | (Public Preview) Allow generation of images containing adults only; images of children are filtered out.Generation of images containing people or faces may require your use case to be reviewed and approved by Cloud support; see the [Responsible AI and usage guidelines](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#person-face-gen) for more details. | +| ALLOW\_ALL | "allow_all" | (Public Preview) Allow generation of images containing adults only; images of children are filtered out.Generation of images containing people or faces may require your use case to be reviewed and approved by Cloud support; see the [Responsible AI and usage guidelines](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#person-face-gen) for more details. | +| BLOCK\_ALL | "dont_allow" | (Public Preview) Disallow generation of images containing people or faces; images of people are filtered out. | + +## ImagenSafetyFilterLevel + +> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> + +A filter level controlling how aggressively to filter sensitive content. + +Text prompts provided as inputs and images (generated or uploaded) through Imagen on Vertex AI are assessed against a list of safety filters, which include 'harmful categories' (for example, `violence`, `sexual`, `derogatory`, and `toxic`). This filter level controls how aggressively to filter out potentially harmful content from responses. See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) and the [Responsible AI and usage guidelines](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#safety-filters) for more details. + +Signature: + +```typescript +export declare enum ImagenSafetyFilterLevel +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| BLOCK\_LOW\_AND\_ABOVE | "block_low_and_above" | (Public Preview) The most aggressive filtering level; most strict blocking. | +| BLOCK\_MEDIUM\_AND\_ABOVE | "block_medium_and_above" | (Public Preview) Blocks some sensitive prompts and responses. | +| BLOCK\_NONE | "block_none" | (Public Preview) The least aggressive filtering level; blocks very few sensitive prompts and responses.Access to this feature is restricted and may require your case to be reviewed and approved by Cloud support. | +| BLOCK\_ONLY\_HIGH | "block_only_high" | (Public Preview) Blocks few sensitive prompts and responses. | + +## Modality + +Content part modality. + +Signature: + +```typescript +export declare enum Modality +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| AUDIO | "AUDIO" | Audio. | +| DOCUMENT | "DOCUMENT" | Document (for example, PDF). | +| IMAGE | "IMAGE" | Image. | +| MODALITY\_UNSPECIFIED | "MODALITY_UNSPECIFIED" | Unspecified modality. | +| TEXT | "TEXT" | Plain text. | +| VIDEO | "VIDEO" | Video. | + +## SchemaType + +Contains the list of OpenAPI data types as defined by the [OpenAPI specification](https://swagger.io/docs/specification/data-models/data-types/) + +Signature: + +```typescript +export declare enum SchemaType +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| ARRAY | "array" | Array type. | +| BOOLEAN | "boolean" | Boolean type. | +| INTEGER | "integer" | Integer type. | +| NUMBER | "number" | Number type. | +| OBJECT | "object" | Object type. | +| STRING | "string" | String type. | + diff --git a/docs-devsite/vertexai.modalitytokencount.md b/docs-devsite/ai.modalitytokencount.md similarity index 74% rename from docs-devsite/vertexai.modalitytokencount.md rename to docs-devsite/ai.modalitytokencount.md index d710b51fba6..b1a504dddbc 100644 --- a/docs-devsite/vertexai.modalitytokencount.md +++ b/docs-devsite/ai.modalitytokencount.md @@ -22,8 +22,8 @@ export interface ModalityTokenCount | Property | Type | Description | | --- | --- | --- | -| [modality](./vertexai.modalitytokencount.md#modalitytokencountmodality) | [Modality](./vertexai.md#modality) | The modality associated with this token count. | -| [tokenCount](./vertexai.modalitytokencount.md#modalitytokencounttokencount) | number | The number of tokens counted. | +| [modality](./ai.modalitytokencount.md#modalitytokencountmodality) | [Modality](./ai.md#modality) | The modality associated with this token count. | +| [tokenCount](./ai.modalitytokencount.md#modalitytokencounttokencount) | number | The number of tokens counted. | ## ModalityTokenCount.modality diff --git a/docs-devsite/vertexai.modelparams.md b/docs-devsite/ai.modelparams.md similarity index 57% rename from docs-devsite/vertexai.modelparams.md rename to docs-devsite/ai.modelparams.md index bb8a87d5fb2..a92b2e9035d 100644 --- a/docs-devsite/vertexai.modelparams.md +++ b/docs-devsite/ai.modelparams.md @@ -10,23 +10,23 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # ModelParams interface -Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_80bd839). +Params passed to [getGenerativeModel()](./ai.md#getgenerativemodel_80bd839). Signature: ```typescript export interface ModelParams extends BaseParams ``` -Extends: [BaseParams](./vertexai.baseparams.md#baseparams_interface) +Extends: [BaseParams](./ai.baseparams.md#baseparams_interface) ## Properties | Property | Type | Description | | --- | --- | --- | -| [model](./vertexai.modelparams.md#modelparamsmodel) | string | | -| [systemInstruction](./vertexai.modelparams.md#modelparamssysteminstruction) | string \| [Part](./vertexai.md#part) \| [Content](./vertexai.content.md#content_interface) | | -| [toolConfig](./vertexai.modelparams.md#modelparamstoolconfig) | [ToolConfig](./vertexai.toolconfig.md#toolconfig_interface) | | -| [tools](./vertexai.modelparams.md#modelparamstools) | [Tool](./vertexai.md#tool)\[\] | | +| [model](./ai.modelparams.md#modelparamsmodel) | string | | +| [systemInstruction](./ai.modelparams.md#modelparamssysteminstruction) | string \| [Part](./ai.md#part) \| [Content](./ai.content.md#content_interface) | | +| [toolConfig](./ai.modelparams.md#modelparamstoolconfig) | [ToolConfig](./ai.toolconfig.md#toolconfig_interface) | | +| [tools](./ai.modelparams.md#modelparamstools) | [Tool](./ai.md#tool)\[\] | | ## ModelParams.model diff --git a/docs-devsite/vertexai.numberschema.md b/docs-devsite/ai.numberschema.md similarity index 72% rename from docs-devsite/vertexai.numberschema.md rename to docs-devsite/ai.numberschema.md index 8fdd9374652..4e7800cfb21 100644 --- a/docs-devsite/vertexai.numberschema.md +++ b/docs-devsite/ai.numberschema.md @@ -17,13 +17,13 @@ Schema class for "number" types. ```typescript export declare class NumberSchema extends Schema ``` -Extends: [Schema](./vertexai.schema.md#schema_class) +Extends: [Schema](./ai.schema.md#schema_class) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(schemaParams)](./vertexai.numberschema.md#numberschemaconstructor) | | Constructs a new instance of the NumberSchema class | +| [(constructor)(schemaParams)](./ai.numberschema.md#numberschemaconstructor) | | Constructs a new instance of the NumberSchema class | ## NumberSchema.(constructor) @@ -39,5 +39,5 @@ constructor(schemaParams?: SchemaParams); | Parameter | Type | Description | | --- | --- | --- | -| schemaParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | | +| schemaParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) | | diff --git a/docs-devsite/vertexai.objectschema.md b/docs-devsite/ai.objectschema.md similarity index 68% rename from docs-devsite/vertexai.objectschema.md rename to docs-devsite/ai.objectschema.md index 8731960b220..2ba358c6b07 100644 --- a/docs-devsite/vertexai.objectschema.md +++ b/docs-devsite/ai.objectschema.md @@ -17,20 +17,20 @@ Schema class for "object" types. The `properties` param must be a map of `Schema ```typescript export declare class ObjectSchema extends Schema ``` -Extends: [Schema](./vertexai.schema.md#schema_class) +Extends: [Schema](./ai.schema.md#schema_class) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(schemaParams, properties, optionalProperties)](./vertexai.objectschema.md#objectschemaconstructor) | | Constructs a new instance of the ObjectSchema class | +| [(constructor)(schemaParams, properties, optionalProperties)](./ai.objectschema.md#objectschemaconstructor) | | Constructs a new instance of the ObjectSchema class | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [optionalProperties](./vertexai.objectschema.md#objectschemaoptionalproperties) | | string\[\] | | -| [properties](./vertexai.objectschema.md#objectschemaproperties) | | { \[k: string\]: [TypedSchema](./vertexai.md#typedschema); } | | +| [optionalProperties](./ai.objectschema.md#objectschemaoptionalproperties) | | string\[\] | | +| [properties](./ai.objectschema.md#objectschemaproperties) | | { \[k: string\]: [TypedSchema](./ai.md#typedschema); } | | ## ObjectSchema.(constructor) @@ -48,8 +48,8 @@ constructor(schemaParams: SchemaParams, properties: { | Parameter | Type | Description | | --- | --- | --- | -| schemaParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | | -| properties | { \[k: string\]: [TypedSchema](./vertexai.md#typedschema); } | | +| schemaParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) | | +| properties | { \[k: string\]: [TypedSchema](./ai.md#typedschema); } | | | optionalProperties | string\[\] | | ## ObjectSchema.optionalProperties diff --git a/docs-devsite/vertexai.objectschemainterface.md b/docs-devsite/ai.objectschemainterface.md similarity index 62% rename from docs-devsite/vertexai.objectschemainterface.md rename to docs-devsite/ai.objectschemainterface.md index 4eb7a5d80e7..15b1a97f40d 100644 --- a/docs-devsite/vertexai.objectschemainterface.md +++ b/docs-devsite/ai.objectschemainterface.md @@ -10,21 +10,21 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # ObjectSchemaInterface interface -Interface for [ObjectSchema](./vertexai.objectschema.md#objectschema_class) class. +Interface for [ObjectSchema](./ai.objectschema.md#objectschema_class) class. Signature: ```typescript export interface ObjectSchemaInterface extends SchemaInterface ``` -Extends: [SchemaInterface](./vertexai.schemainterface.md#schemainterface_interface) +Extends: [SchemaInterface](./ai.schemainterface.md#schemainterface_interface) ## Properties | Property | Type | Description | | --- | --- | --- | -| [optionalProperties](./vertexai.objectschemainterface.md#objectschemainterfaceoptionalproperties) | string\[\] | | -| [type](./vertexai.objectschemainterface.md#objectschemainterfacetype) | [SchemaType.OBJECT](./vertexai.md#schematypeobject_enummember) | | +| [optionalProperties](./ai.objectschemainterface.md#objectschemainterfaceoptionalproperties) | string\[\] | | +| [type](./ai.objectschemainterface.md#objectschemainterfacetype) | [SchemaType.OBJECT](./ai.md#schematypeobject_enummember) | | ## ObjectSchemaInterface.optionalProperties diff --git a/docs-devsite/vertexai.promptfeedback.md b/docs-devsite/ai.promptfeedback.md similarity index 60% rename from docs-devsite/vertexai.promptfeedback.md rename to docs-devsite/ai.promptfeedback.md index 08ea4aaf4cf..b874c0e70b1 100644 --- a/docs-devsite/vertexai.promptfeedback.md +++ b/docs-devsite/ai.promptfeedback.md @@ -22,9 +22,9 @@ export interface PromptFeedback | Property | Type | Description | | --- | --- | --- | -| [blockReason](./vertexai.promptfeedback.md#promptfeedbackblockreason) | [BlockReason](./vertexai.md#blockreason) | | -| [blockReasonMessage](./vertexai.promptfeedback.md#promptfeedbackblockreasonmessage) | string | A human-readable description of the blockReason.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). | -| [safetyRatings](./vertexai.promptfeedback.md#promptfeedbacksafetyratings) | [SafetyRating](./vertexai.safetyrating.md#safetyrating_interface)\[\] | | +| [blockReason](./ai.promptfeedback.md#promptfeedbackblockreason) | [BlockReason](./ai.md#blockreason) | | +| [blockReasonMessage](./ai.promptfeedback.md#promptfeedbackblockreasonmessage) | string | A human-readable description of the blockReason.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). | +| [safetyRatings](./ai.promptfeedback.md#promptfeedbacksafetyratings) | [SafetyRating](./ai.safetyrating.md#safetyrating_interface)\[\] | | ## PromptFeedback.blockReason @@ -38,7 +38,7 @@ blockReason?: BlockReason; A human-readable description of the `blockReason`. -This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). +This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). Signature: diff --git a/docs-devsite/vertexai.requestoptions.md b/docs-devsite/ai.requestoptions.md similarity index 67% rename from docs-devsite/vertexai.requestoptions.md rename to docs-devsite/ai.requestoptions.md index 3c233d72b90..73aa03c1d25 100644 --- a/docs-devsite/vertexai.requestoptions.md +++ b/docs-devsite/ai.requestoptions.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # RequestOptions interface -Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_80bd839). +Params passed to [getGenerativeModel()](./ai.md#getgenerativemodel_80bd839). Signature: @@ -22,8 +22,8 @@ export interface RequestOptions | Property | Type | Description | | --- | --- | --- | -| [baseUrl](./vertexai.requestoptions.md#requestoptionsbaseurl) | string | Base url for endpoint. Defaults to https://firebasevertexai.googleapis.com | -| [timeout](./vertexai.requestoptions.md#requestoptionstimeout) | number | Request timeout in milliseconds. Defaults to 180 seconds (180000ms). | +| [baseUrl](./ai.requestoptions.md#requestoptionsbaseurl) | string | Base url for endpoint. Defaults to https://firebasevertexai.googleapis.com | +| [timeout](./ai.requestoptions.md#requestoptionstimeout) | number | Request timeout in milliseconds. Defaults to 180 seconds (180000ms). | ## RequestOptions.baseUrl diff --git a/docs-devsite/vertexai.retrievedcontextattribution.md b/docs-devsite/ai.retrievedcontextattribution.md similarity index 77% rename from docs-devsite/vertexai.retrievedcontextattribution.md rename to docs-devsite/ai.retrievedcontextattribution.md index e36bfacb3ec..21b12b79c18 100644 --- a/docs-devsite/vertexai.retrievedcontextattribution.md +++ b/docs-devsite/ai.retrievedcontextattribution.md @@ -21,8 +21,8 @@ export interface RetrievedContextAttribution | Property | Type | Description | | --- | --- | --- | -| [title](./vertexai.retrievedcontextattribution.md#retrievedcontextattributiontitle) | string | | -| [uri](./vertexai.retrievedcontextattribution.md#retrievedcontextattributionuri) | string | | +| [title](./ai.retrievedcontextattribution.md#retrievedcontextattributiontitle) | string | | +| [uri](./ai.retrievedcontextattribution.md#retrievedcontextattributionuri) | string | | ## RetrievedContextAttribution.title diff --git a/docs-devsite/ai.safetyrating.md b/docs-devsite/ai.safetyrating.md new file mode 100644 index 00000000000..86b1549569e --- /dev/null +++ b/docs-devsite/ai.safetyrating.md @@ -0,0 +1,90 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# SafetyRating interface +A safety rating associated with a [GenerateContentCandidate](./ai.generatecontentcandidate.md#generatecontentcandidate_interface) + +Signature: + +```typescript +export interface SafetyRating +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [blocked](./ai.safetyrating.md#safetyratingblocked) | boolean | | +| [category](./ai.safetyrating.md#safetyratingcategory) | [HarmCategory](./ai.md#harmcategory) | | +| [probability](./ai.safetyrating.md#safetyratingprobability) | [HarmProbability](./ai.md#harmprobability) | | +| [probabilityScore](./ai.safetyrating.md#safetyratingprobabilityscore) | number | The probability score of the harm category.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. | +| [severity](./ai.safetyrating.md#safetyratingseverity) | [HarmSeverity](./ai.md#harmseverity) | The harm severity level.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to HarmSeverity.UNSUPPORTED. | +| [severityScore](./ai.safetyrating.md#safetyratingseverityscore) | number | The severity score of the harm category.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. | + +## SafetyRating.blocked + +Signature: + +```typescript +blocked: boolean; +``` + +## SafetyRating.category + +Signature: + +```typescript +category: HarmCategory; +``` + +## SafetyRating.probability + +Signature: + +```typescript +probability: HarmProbability; +``` + +## SafetyRating.probabilityScore + +The probability score of the harm category. + +This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. + +Signature: + +```typescript +probabilityScore: number; +``` + +## SafetyRating.severity + +The harm severity level. + +This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to `HarmSeverity.UNSUPPORTED`. + +Signature: + +```typescript +severity: HarmSeverity; +``` + +## SafetyRating.severityScore + +The severity score of the harm category. + +This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. + +Signature: + +```typescript +severityScore: number; +``` diff --git a/docs-devsite/ai.safetysetting.md b/docs-devsite/ai.safetysetting.md new file mode 100644 index 00000000000..b6c770a5f60 --- /dev/null +++ b/docs-devsite/ai.safetysetting.md @@ -0,0 +1,55 @@ +Project: /docs/reference/js/_project.yaml +Book: /docs/reference/_book.yaml +page_type: reference + +{% comment %} +DO NOT EDIT THIS FILE! +This is generated by the JS SDK team, and any local changes will be +overwritten. Changes should be made in the source code at +https://github.com/firebase/firebase-js-sdk +{% endcomment %} + +# SafetySetting interface +Safety setting that can be sent as part of request parameters. + +Signature: + +```typescript +export interface SafetySetting +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [category](./ai.safetysetting.md#safetysettingcategory) | [HarmCategory](./ai.md#harmcategory) | | +| [method](./ai.safetysetting.md#safetysettingmethod) | [HarmBlockMethod](./ai.md#harmblockmethod) | The harm block method.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), an [AIError](./ai.aierror.md#aierror_class) will be thrown if this property is defined. | +| [threshold](./ai.safetysetting.md#safetysettingthreshold) | [HarmBlockThreshold](./ai.md#harmblockthreshold) | | + +## SafetySetting.category + +Signature: + +```typescript +category: HarmCategory; +``` + +## SafetySetting.method + +The harm block method. + +This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), an [AIError](./ai.aierror.md#aierror_class) will be thrown if this property is defined. + +Signature: + +```typescript +method?: HarmBlockMethod; +``` + +## SafetySetting.threshold + +Signature: + +```typescript +threshold: HarmBlockThreshold; +``` diff --git a/docs-devsite/vertexai.schema.md b/docs-devsite/ai.schema.md similarity index 53% rename from docs-devsite/vertexai.schema.md rename to docs-devsite/ai.schema.md index f4a36c3c506..b0681b0cdf3 100644 --- a/docs-devsite/vertexai.schema.md +++ b/docs-devsite/ai.schema.md @@ -17,35 +17,35 @@ Parent class encompassing all Schema types, with static methods that allow build ```typescript export declare abstract class Schema implements SchemaInterface ``` -Implements: [SchemaInterface](./vertexai.schemainterface.md#schemainterface_interface) +Implements: [SchemaInterface](./ai.schemainterface.md#schemainterface_interface) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(schemaParams)](./vertexai.schema.md#schemaconstructor) | | Constructs a new instance of the Schema class | +| [(constructor)(schemaParams)](./ai.schema.md#schemaconstructor) | | Constructs a new instance of the Schema class | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [description](./vertexai.schema.md#schemadescription) | | string | Optional. The description of the property. | -| [example](./vertexai.schema.md#schemaexample) | | unknown | Optional. The example of the property. | -| [format](./vertexai.schema.md#schemaformat) | | string | Optional. The format of the property. Supported formats:
  • for NUMBER type: "float", "double"
  • for INTEGER type: "int32", "int64"
  • for STRING type: "email", "byte", etc
| -| [nullable](./vertexai.schema.md#schemanullable) | | boolean | Optional. Whether the property is nullable. Defaults to false. | -| [type](./vertexai.schema.md#schematype) | | [SchemaType](./vertexai.md#schematype) | Optional. The type of the property. [SchemaType](./vertexai.md#schematype). | +| [description](./ai.schema.md#schemadescription) | | string | Optional. The description of the property. | +| [example](./ai.schema.md#schemaexample) | | unknown | Optional. The example of the property. | +| [format](./ai.schema.md#schemaformat) | | string | Optional. The format of the property. Supported formats:
  • for NUMBER type: "float", "double"
  • for INTEGER type: "int32", "int64"
  • for STRING type: "email", "byte", etc
| +| [nullable](./ai.schema.md#schemanullable) | | boolean | Optional. Whether the property is nullable. Defaults to false. | +| [type](./ai.schema.md#schematype) | | [SchemaType](./ai.md#schematype) | Optional. The type of the property. [SchemaType](./ai.md#schematype). | ## Methods | Method | Modifiers | Description | | --- | --- | --- | -| [array(arrayParams)](./vertexai.schema.md#schemaarray) | static | | -| [boolean(booleanParams)](./vertexai.schema.md#schemaboolean) | static | | -| [enumString(stringParams)](./vertexai.schema.md#schemaenumstring) | static | | -| [integer(integerParams)](./vertexai.schema.md#schemainteger) | static | | -| [number(numberParams)](./vertexai.schema.md#schemanumber) | static | | -| [object(objectParams)](./vertexai.schema.md#schemaobject) | static | | -| [string(stringParams)](./vertexai.schema.md#schemastring) | static | | +| [array(arrayParams)](./ai.schema.md#schemaarray) | static | | +| [boolean(booleanParams)](./ai.schema.md#schemaboolean) | static | | +| [enumString(stringParams)](./ai.schema.md#schemaenumstring) | static | | +| [integer(integerParams)](./ai.schema.md#schemainteger) | static | | +| [number(numberParams)](./ai.schema.md#schemanumber) | static | | +| [object(objectParams)](./ai.schema.md#schemaobject) | static | | +| [string(stringParams)](./ai.schema.md#schemastring) | static | | ## Schema.(constructor) @@ -61,7 +61,7 @@ constructor(schemaParams: SchemaInterface); | Parameter | Type | Description | | --- | --- | --- | -| schemaParams | [SchemaInterface](./vertexai.schemainterface.md#schemainterface_interface) | | +| schemaParams | [SchemaInterface](./ai.schemainterface.md#schemainterface_interface) | | ## Schema.description @@ -105,7 +105,7 @@ nullable: boolean; ## Schema.type -Optional. The type of the property. [SchemaType](./vertexai.md#schematype). +Optional. The type of the property. [SchemaType](./ai.md#schematype). Signature: @@ -127,11 +127,11 @@ static array(arrayParams: SchemaParams & { | Parameter | Type | Description | | --- | --- | --- | -| arrayParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) & { items: [Schema](./vertexai.schema.md#schema_class); } | | +| arrayParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) & { items: [Schema](./ai.schema.md#schema_class); } | | Returns: -[ArraySchema](./vertexai.arrayschema.md#arrayschema_class) +[ArraySchema](./ai.arrayschema.md#arrayschema_class) ## Schema.boolean() @@ -145,11 +145,11 @@ static boolean(booleanParams?: SchemaParams): BooleanSchema; | Parameter | Type | Description | | --- | --- | --- | -| booleanParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | | +| booleanParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) | | Returns: -[BooleanSchema](./vertexai.booleanschema.md#booleanschema_class) +[BooleanSchema](./ai.booleanschema.md#booleanschema_class) ## Schema.enumString() @@ -165,11 +165,11 @@ static enumString(stringParams: SchemaParams & { | Parameter | Type | Description | | --- | --- | --- | -| stringParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) & { enum: string\[\]; } | | +| stringParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) & { enum: string\[\]; } | | Returns: -[StringSchema](./vertexai.stringschema.md#stringschema_class) +[StringSchema](./ai.stringschema.md#stringschema_class) ## Schema.integer() @@ -183,11 +183,11 @@ static integer(integerParams?: SchemaParams): IntegerSchema; | Parameter | Type | Description | | --- | --- | --- | -| integerParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | | +| integerParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) | | Returns: -[IntegerSchema](./vertexai.integerschema.md#integerschema_class) +[IntegerSchema](./ai.integerschema.md#integerschema_class) ## Schema.number() @@ -201,11 +201,11 @@ static number(numberParams?: SchemaParams): NumberSchema; | Parameter | Type | Description | | --- | --- | --- | -| numberParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | | +| numberParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) | | Returns: -[NumberSchema](./vertexai.numberschema.md#numberschema_class) +[NumberSchema](./ai.numberschema.md#numberschema_class) ## Schema.object() @@ -224,11 +224,11 @@ static object(objectParams: SchemaParams & { | Parameter | Type | Description | | --- | --- | --- | -| objectParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) & { properties: { \[k: string\]: [Schema](./vertexai.schema.md#schema_class); }; optionalProperties?: string\[\]; } | | +| objectParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) & { properties: { \[k: string\]: [Schema](./ai.schema.md#schema_class); }; optionalProperties?: string\[\]; } | | Returns: -[ObjectSchema](./vertexai.objectschema.md#objectschema_class) +[ObjectSchema](./ai.objectschema.md#objectschema_class) ## Schema.string() @@ -242,9 +242,9 @@ static string(stringParams?: SchemaParams): StringSchema; | Parameter | Type | Description | | --- | --- | --- | -| stringParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | | +| stringParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) | | Returns: -[StringSchema](./vertexai.stringschema.md#stringschema_class) +[StringSchema](./ai.stringschema.md#stringschema_class) diff --git a/docs-devsite/vertexai.schemainterface.md b/docs-devsite/ai.schemainterface.md similarity index 55% rename from docs-devsite/vertexai.schemainterface.md rename to docs-devsite/ai.schemainterface.md index c14b561193b..6dd33e69e18 100644 --- a/docs-devsite/vertexai.schemainterface.md +++ b/docs-devsite/ai.schemainterface.md @@ -10,24 +10,24 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # SchemaInterface interface -Interface for [Schema](./vertexai.schema.md#schema_class) class. +Interface for [Schema](./ai.schema.md#schema_class) class. Signature: ```typescript export interface SchemaInterface extends SchemaShared ``` -Extends: [SchemaShared](./vertexai.schemashared.md#schemashared_interface)<[SchemaInterface](./vertexai.schemainterface.md#schemainterface_interface)> +Extends: [SchemaShared](./ai.schemashared.md#schemashared_interface)<[SchemaInterface](./ai.schemainterface.md#schemainterface_interface)> ## Properties | Property | Type | Description | | --- | --- | --- | -| [type](./vertexai.schemainterface.md#schemainterfacetype) | [SchemaType](./vertexai.md#schematype) | The type of the property. [SchemaType](./vertexai.md#schematype). | +| [type](./ai.schemainterface.md#schemainterfacetype) | [SchemaType](./ai.md#schematype) | The type of the property. [SchemaType](./ai.md#schematype). | ## SchemaInterface.type -The type of the property. [SchemaType](./vertexai.md#schematype). +The type of the property. [SchemaType](./ai.md#schematype). Signature: diff --git a/docs-devsite/vertexai.schemaparams.md b/docs-devsite/ai.schemaparams.md similarity index 58% rename from docs-devsite/vertexai.schemaparams.md rename to docs-devsite/ai.schemaparams.md index 8e4a41f6bdc..3f9626306c2 100644 --- a/docs-devsite/vertexai.schemaparams.md +++ b/docs-devsite/ai.schemaparams.md @@ -10,12 +10,12 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # SchemaParams interface -Params passed to [Schema](./vertexai.schema.md#schema_class) static methods to create specific [Schema](./vertexai.schema.md#schema_class) classes. +Params passed to [Schema](./ai.schema.md#schema_class) static methods to create specific [Schema](./ai.schema.md#schema_class) classes. Signature: ```typescript export interface SchemaParams extends SchemaShared ``` -Extends: [SchemaShared](./vertexai.schemashared.md#schemashared_interface)<[SchemaInterface](./vertexai.schemainterface.md#schemainterface_interface)> +Extends: [SchemaShared](./ai.schemashared.md#schemashared_interface)<[SchemaInterface](./ai.schemainterface.md#schemainterface_interface)> diff --git a/docs-devsite/vertexai.schemarequest.md b/docs-devsite/ai.schemarequest.md similarity index 53% rename from docs-devsite/vertexai.schemarequest.md rename to docs-devsite/ai.schemarequest.md index c382c2a6297..e71d24a6b1a 100644 --- a/docs-devsite/vertexai.schemarequest.md +++ b/docs-devsite/ai.schemarequest.md @@ -10,21 +10,21 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # SchemaRequest interface -Final format for [Schema](./vertexai.schema.md#schema_class) params passed to backend requests. +Final format for [Schema](./ai.schema.md#schema_class) params passed to backend requests. Signature: ```typescript export interface SchemaRequest extends SchemaShared ``` -Extends: [SchemaShared](./vertexai.schemashared.md#schemashared_interface)<[SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface)> +Extends: [SchemaShared](./ai.schemashared.md#schemashared_interface)<[SchemaRequest](./ai.schemarequest.md#schemarequest_interface)> ## Properties | Property | Type | Description | | --- | --- | --- | -| [required](./vertexai.schemarequest.md#schemarequestrequired) | string\[\] | Optional. Array of required property. | -| [type](./vertexai.schemarequest.md#schemarequesttype) | [SchemaType](./vertexai.md#schematype) | The type of the property. [SchemaType](./vertexai.md#schematype). | +| [required](./ai.schemarequest.md#schemarequestrequired) | string\[\] | Optional. Array of required property. | +| [type](./ai.schemarequest.md#schemarequesttype) | [SchemaType](./ai.md#schematype) | The type of the property. [SchemaType](./ai.md#schematype). | ## SchemaRequest.required @@ -38,7 +38,7 @@ required?: string[]; ## SchemaRequest.type -The type of the property. [SchemaType](./vertexai.md#schematype). +The type of the property. [SchemaType](./ai.md#schematype). Signature: diff --git a/docs-devsite/vertexai.schemashared.md b/docs-devsite/ai.schemashared.md similarity index 51% rename from docs-devsite/vertexai.schemashared.md rename to docs-devsite/ai.schemashared.md index 4fdf8941438..eba57f82935 100644 --- a/docs-devsite/vertexai.schemashared.md +++ b/docs-devsite/ai.schemashared.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # SchemaShared interface -Basic [Schema](./vertexai.schema.md#schema_class) properties shared across several Schema-related types. +Basic [Schema](./ai.schema.md#schema_class) properties shared across several Schema-related types. Signature: @@ -22,13 +22,13 @@ export interface SchemaShared | Property | Type | Description | | --- | --- | --- | -| [description](./vertexai.schemashared.md#schemashareddescription) | string | Optional. The description of the property. | -| [enum](./vertexai.schemashared.md#schemasharedenum) | string\[\] | Optional. The enum of the property. | -| [example](./vertexai.schemashared.md#schemasharedexample) | unknown | Optional. The example of the property. | -| [format](./vertexai.schemashared.md#schemasharedformat) | string | Optional. The format of the property. When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this must be either 'enum' or 'date-time', otherwise requests will fail. | -| [items](./vertexai.schemashared.md#schemashareditems) | T | Optional. The items of the property. | -| [nullable](./vertexai.schemashared.md#schemasharednullable) | boolean | Optional. Whether the property is nullable. | -| [properties](./vertexai.schemashared.md#schemasharedproperties) | { \[k: string\]: T; } | Optional. Map of Schema objects. | +| [description](./ai.schemashared.md#schemashareddescription) | string | Optional. The description of the property. | +| [enum](./ai.schemashared.md#schemasharedenum) | string\[\] | Optional. The enum of the property. | +| [example](./ai.schemashared.md#schemasharedexample) | unknown | Optional. The example of the property. | +| [format](./ai.schemashared.md#schemasharedformat) | string | Optional. The format of the property. When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this must be either 'enum' or 'date-time', otherwise requests will fail. | +| [items](./ai.schemashared.md#schemashareditems) | T | Optional. The items of the property. | +| [nullable](./ai.schemashared.md#schemasharednullable) | boolean | Optional. Whether the property is nullable. | +| [properties](./ai.schemashared.md#schemasharedproperties) | { \[k: string\]: T; } | Optional. Map of Schema objects. | ## SchemaShared.description @@ -62,7 +62,7 @@ example?: unknown; ## SchemaShared.format -Optional. The format of the property. When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this must be either `'enum'` or `'date-time'`, otherwise requests will fail. +Optional. The format of the property. When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this must be either `'enum'` or `'date-time'`, otherwise requests will fail. Signature: diff --git a/docs-devsite/vertexai.segment.md b/docs-devsite/ai.segment.md similarity index 77% rename from docs-devsite/vertexai.segment.md rename to docs-devsite/ai.segment.md index db61f00a149..69f4aaf8407 100644 --- a/docs-devsite/vertexai.segment.md +++ b/docs-devsite/ai.segment.md @@ -21,9 +21,9 @@ export interface Segment | Property | Type | Description | | --- | --- | --- | -| [endIndex](./vertexai.segment.md#segmentendindex) | number | | -| [partIndex](./vertexai.segment.md#segmentpartindex) | number | | -| [startIndex](./vertexai.segment.md#segmentstartindex) | number | | +| [endIndex](./ai.segment.md#segmentendindex) | number | | +| [partIndex](./ai.segment.md#segmentpartindex) | number | | +| [startIndex](./ai.segment.md#segmentstartindex) | number | | ## Segment.endIndex diff --git a/docs-devsite/vertexai.startchatparams.md b/docs-devsite/ai.startchatparams.md similarity index 54% rename from docs-devsite/vertexai.startchatparams.md rename to docs-devsite/ai.startchatparams.md index e07bbd91d82..2d039bbe868 100644 --- a/docs-devsite/vertexai.startchatparams.md +++ b/docs-devsite/ai.startchatparams.md @@ -10,23 +10,23 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # StartChatParams interface -Params for [GenerativeModel.startChat()](./vertexai.generativemodel.md#generativemodelstartchat). +Params for [GenerativeModel.startChat()](./ai.generativemodel.md#generativemodelstartchat). Signature: ```typescript export interface StartChatParams extends BaseParams ``` -Extends: [BaseParams](./vertexai.baseparams.md#baseparams_interface) +Extends: [BaseParams](./ai.baseparams.md#baseparams_interface) ## Properties | Property | Type | Description | | --- | --- | --- | -| [history](./vertexai.startchatparams.md#startchatparamshistory) | [Content](./vertexai.content.md#content_interface)\[\] | | -| [systemInstruction](./vertexai.startchatparams.md#startchatparamssysteminstruction) | string \| [Part](./vertexai.md#part) \| [Content](./vertexai.content.md#content_interface) | | -| [toolConfig](./vertexai.startchatparams.md#startchatparamstoolconfig) | [ToolConfig](./vertexai.toolconfig.md#toolconfig_interface) | | -| [tools](./vertexai.startchatparams.md#startchatparamstools) | [Tool](./vertexai.md#tool)\[\] | | +| [history](./ai.startchatparams.md#startchatparamshistory) | [Content](./ai.content.md#content_interface)\[\] | | +| [systemInstruction](./ai.startchatparams.md#startchatparamssysteminstruction) | string \| [Part](./ai.md#part) \| [Content](./ai.content.md#content_interface) | | +| [toolConfig](./ai.startchatparams.md#startchatparamstoolconfig) | [ToolConfig](./ai.toolconfig.md#toolconfig_interface) | | +| [tools](./ai.startchatparams.md#startchatparamstools) | [Tool](./ai.md#tool)\[\] | | ## StartChatParams.history diff --git a/docs-devsite/vertexai.stringschema.md b/docs-devsite/ai.stringschema.md similarity index 72% rename from docs-devsite/vertexai.stringschema.md rename to docs-devsite/ai.stringschema.md index bfafe0fe9df..c3ab8f13a6f 100644 --- a/docs-devsite/vertexai.stringschema.md +++ b/docs-devsite/ai.stringschema.md @@ -17,19 +17,19 @@ Schema class for "string" types. Can be used with or without enum values. ```typescript export declare class StringSchema extends Schema ``` -Extends: [Schema](./vertexai.schema.md#schema_class) +Extends: [Schema](./ai.schema.md#schema_class) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(schemaParams, enumValues)](./vertexai.stringschema.md#stringschemaconstructor) | | Constructs a new instance of the StringSchema class | +| [(constructor)(schemaParams, enumValues)](./ai.stringschema.md#stringschemaconstructor) | | Constructs a new instance of the StringSchema class | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [enum](./vertexai.stringschema.md#stringschemaenum) | | string\[\] | | +| [enum](./ai.stringschema.md#stringschemaenum) | | string\[\] | | ## StringSchema.(constructor) @@ -45,7 +45,7 @@ constructor(schemaParams?: SchemaParams, enumValues?: string[]); | Parameter | Type | Description | | --- | --- | --- | -| schemaParams | [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | | +| schemaParams | [SchemaParams](./ai.schemaparams.md#schemaparams_interface) | | | enumValues | string\[\] | | ## StringSchema.enum diff --git a/docs-devsite/vertexai.textpart.md b/docs-devsite/ai.textpart.md similarity index 74% rename from docs-devsite/vertexai.textpart.md rename to docs-devsite/ai.textpart.md index afee40a369d..2057d95d32e 100644 --- a/docs-devsite/vertexai.textpart.md +++ b/docs-devsite/ai.textpart.md @@ -22,10 +22,10 @@ export interface TextPart | Property | Type | Description | | --- | --- | --- | -| [functionCall](./vertexai.textpart.md#textpartfunctioncall) | never | | -| [functionResponse](./vertexai.textpart.md#textpartfunctionresponse) | never | | -| [inlineData](./vertexai.textpart.md#textpartinlinedata) | never | | -| [text](./vertexai.textpart.md#textparttext) | string | | +| [functionCall](./ai.textpart.md#textpartfunctioncall) | never | | +| [functionResponse](./ai.textpart.md#textpartfunctionresponse) | never | | +| [inlineData](./ai.textpart.md#textpartinlinedata) | never | | +| [text](./ai.textpart.md#textparttext) | string | | ## TextPart.functionCall diff --git a/docs-devsite/vertexai.toolconfig.md b/docs-devsite/ai.toolconfig.md similarity index 78% rename from docs-devsite/vertexai.toolconfig.md rename to docs-devsite/ai.toolconfig.md index 30c62c17c01..81bd6ccd8fc 100644 --- a/docs-devsite/vertexai.toolconfig.md +++ b/docs-devsite/ai.toolconfig.md @@ -22,7 +22,7 @@ export interface ToolConfig | Property | Type | Description | | --- | --- | --- | -| [functionCallingConfig](./vertexai.toolconfig.md#toolconfigfunctioncallingconfig) | [FunctionCallingConfig](./vertexai.functioncallingconfig.md#functioncallingconfig_interface) | | +| [functionCallingConfig](./ai.toolconfig.md#toolconfigfunctioncallingconfig) | [FunctionCallingConfig](./ai.functioncallingconfig.md#functioncallingconfig_interface) | | ## ToolConfig.functionCallingConfig diff --git a/docs-devsite/vertexai.usagemetadata.md b/docs-devsite/ai.usagemetadata.md similarity index 56% rename from docs-devsite/vertexai.usagemetadata.md rename to docs-devsite/ai.usagemetadata.md index 176878235d5..4211fea72b4 100644 --- a/docs-devsite/vertexai.usagemetadata.md +++ b/docs-devsite/ai.usagemetadata.md @@ -10,7 +10,7 @@ https://github.com/firebase/firebase-js-sdk {% endcomment %} # UsageMetadata interface -Usage metadata about a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). +Usage metadata about a [GenerateContentResponse](./ai.generatecontentresponse.md#generatecontentresponse_interface). Signature: @@ -22,11 +22,11 @@ export interface UsageMetadata | Property | Type | Description | | --- | --- | --- | -| [candidatesTokenCount](./vertexai.usagemetadata.md#usagemetadatacandidatestokencount) | number | | -| [candidatesTokensDetails](./vertexai.usagemetadata.md#usagemetadatacandidatestokensdetails) | [ModalityTokenCount](./vertexai.modalitytokencount.md#modalitytokencount_interface)\[\] | | -| [promptTokenCount](./vertexai.usagemetadata.md#usagemetadataprompttokencount) | number | | -| [promptTokensDetails](./vertexai.usagemetadata.md#usagemetadataprompttokensdetails) | [ModalityTokenCount](./vertexai.modalitytokencount.md#modalitytokencount_interface)\[\] | | -| [totalTokenCount](./vertexai.usagemetadata.md#usagemetadatatotaltokencount) | number | | +| [candidatesTokenCount](./ai.usagemetadata.md#usagemetadatacandidatestokencount) | number | | +| [candidatesTokensDetails](./ai.usagemetadata.md#usagemetadatacandidatestokensdetails) | [ModalityTokenCount](./ai.modalitytokencount.md#modalitytokencount_interface)\[\] | | +| [promptTokenCount](./ai.usagemetadata.md#usagemetadataprompttokencount) | number | | +| [promptTokensDetails](./ai.usagemetadata.md#usagemetadataprompttokensdetails) | [ModalityTokenCount](./ai.modalitytokencount.md#modalitytokencount_interface)\[\] | | +| [totalTokenCount](./ai.usagemetadata.md#usagemetadatatotaltokencount) | number | | ## UsageMetadata.candidatesTokenCount diff --git a/docs-devsite/vertexai.vertexaibackend.md b/docs-devsite/ai.vertexaibackend.md similarity index 67% rename from docs-devsite/vertexai.vertexaibackend.md rename to docs-devsite/ai.vertexaibackend.md index ba82c775ca8..88424b75c45 100644 --- a/docs-devsite/vertexai.vertexaibackend.md +++ b/docs-devsite/ai.vertexaibackend.md @@ -12,26 +12,26 @@ https://github.com/firebase/firebase-js-sdk # VertexAIBackend class Configuration class for the Vertex AI Gemini API. -Use this with [AIOptions](./vertexai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./vertexai.md#getai_a94a413) to specify the Vertex AI Gemini API as the backend. +Use this with [AIOptions](./ai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./ai.md#getai_a94a413) to specify the Vertex AI Gemini API as the backend. Signature: ```typescript export declare class VertexAIBackend extends Backend ``` -Extends: [Backend](./vertexai.backend.md#backend_class) +Extends: [Backend](./ai.backend.md#backend_class) ## Constructors | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(location)](./vertexai.vertexaibackend.md#vertexaibackendconstructor) | | Creates a configuration object for the Vertex AI backend. | +| [(constructor)(location)](./ai.vertexaibackend.md#vertexaibackendconstructor) | | Creates a configuration object for the Vertex AI backend. | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [location](./vertexai.vertexaibackend.md#vertexaibackendlocation) | | string | The region identifier. See [Vertex AI locations](https://firebase.google.com/docs/vertex-ai/locations#available-locations) for a list of supported locations. | +| [location](./ai.vertexaibackend.md#vertexaibackendlocation) | | string | The region identifier. See [Vertex AI locations](https://firebase.google.com/docs/vertex-ai/locations#available-locations) for a list of supported locations. | ## VertexAIBackend.(constructor) diff --git a/docs-devsite/vertexai.vertexaioptions.md b/docs-devsite/ai.vertexaioptions.md similarity index 88% rename from docs-devsite/vertexai.vertexaioptions.md rename to docs-devsite/ai.vertexaioptions.md index 776dfd29374..311fa4785f7 100644 --- a/docs-devsite/vertexai.vertexaioptions.md +++ b/docs-devsite/ai.vertexaioptions.md @@ -22,7 +22,7 @@ export interface VertexAIOptions | Property | Type | Description | | --- | --- | --- | -| [location](./vertexai.vertexaioptions.md#vertexaioptionslocation) | string | | +| [location](./ai.vertexaioptions.md#vertexaioptionslocation) | string | | ## VertexAIOptions.location diff --git a/docs-devsite/vertexai.videometadata.md b/docs-devsite/ai.videometadata.md similarity index 75% rename from docs-devsite/vertexai.videometadata.md rename to docs-devsite/ai.videometadata.md index b4f872c4e3d..d63447837be 100644 --- a/docs-devsite/vertexai.videometadata.md +++ b/docs-devsite/ai.videometadata.md @@ -22,8 +22,8 @@ export interface VideoMetadata | Property | Type | Description | | --- | --- | --- | -| [endOffset](./vertexai.videometadata.md#videometadataendoffset) | string | The end offset of the video in protobuf [Duration](https://cloud.google.com/ruby/docs/reference/google-cloud-workflows-v1/latest/Google-Protobuf-Duration#json-mapping) format. | -| [startOffset](./vertexai.videometadata.md#videometadatastartoffset) | string | The start offset of the video in protobuf [Duration](https://cloud.google.com/ruby/docs/reference/google-cloud-workflows-v1/latest/Google-Protobuf-Duration#json-mapping) format. | +| [endOffset](./ai.videometadata.md#videometadataendoffset) | string | The end offset of the video in protobuf [Duration](https://cloud.google.com/ruby/docs/reference/google-cloud-workflows-v1/latest/Google-Protobuf-Duration#json-mapping) format. | +| [startOffset](./ai.videometadata.md#videometadatastartoffset) | string | The start offset of the video in protobuf [Duration](https://cloud.google.com/ruby/docs/reference/google-cloud-workflows-v1/latest/Google-Protobuf-Duration#json-mapping) format. | ## VideoMetadata.endOffset diff --git a/docs-devsite/vertexai.webattribution.md b/docs-devsite/ai.webattribution.md similarity index 81% rename from docs-devsite/vertexai.webattribution.md rename to docs-devsite/ai.webattribution.md index bb4fecf874d..2040d9eb82a 100644 --- a/docs-devsite/vertexai.webattribution.md +++ b/docs-devsite/ai.webattribution.md @@ -21,8 +21,8 @@ export interface WebAttribution | Property | Type | Description | | --- | --- | --- | -| [title](./vertexai.webattribution.md#webattributiontitle) | string | | -| [uri](./vertexai.webattribution.md#webattributionuri) | string | | +| [title](./ai.webattribution.md#webattributiontitle) | string | | +| [uri](./ai.webattribution.md#webattributionuri) | string | | ## WebAttribution.title diff --git a/docs-devsite/index.md b/docs-devsite/index.md index af34d0d0250..47ec0be16ba 100644 --- a/docs-devsite/index.md +++ b/docs-devsite/index.md @@ -15,6 +15,7 @@ https://github.com/firebase/firebase-js-sdk | Package | Description | | --- | --- | +| [@firebase/ai](./ai.md#ai_package) | The Firebase AI Web SDK. | | [@firebase/analytics](./analytics.md#analytics_package) | The Firebase Analytics Web SDK. This SDK does not work in a Node.js environment. | | [@firebase/app](./app.md#app_package) | Firebase App | | [@firebase/app-check](./app-check.md#app-check_package) | The Firebase App Check Web SDK. | @@ -27,5 +28,4 @@ https://github.com/firebase/firebase-js-sdk | [@firebase/performance](./performance.md#performance_package) | The Firebase Performance Monitoring Web SDK. This SDK does not work in a Node.js environment. | | [@firebase/remote-config](./remote-config.md#remote-config_package) | The Firebase Remote Config Web SDK. This SDK does not work in a Node.js environment. | | [@firebase/storage](./storage.md#storage_package) | Cloud Storage for Firebase | -| [@firebase/vertexai](./vertexai.md#vertexai_package) | The Firebase AI Web SDK. | diff --git a/docs-devsite/vertexai.ai.md b/docs-devsite/vertexai.ai.md deleted file mode 100644 index 661bf0b4fe3..00000000000 --- a/docs-devsite/vertexai.ai.md +++ /dev/null @@ -1,64 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# AI interface -An instance of the Firebase AI SDK. - -Do not create this instance directly. Instead, use [getAI()](./vertexai.md#getai_a94a413). - -Signature: - -```typescript -export interface AI -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [app](./vertexai.ai.md#aiapp) | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [AI](./vertexai.ai.md#ai_interface) instance is associated with. | -| [backend](./vertexai.ai.md#aibackend) | [Backend](./vertexai.backend.md#backend_class) | A [Backend](./vertexai.backend.md#backend_class) instance that specifies the configuration for the target backend, either the Gemini Developer API (using [GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)) or the Vertex AI Gemini API (using [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). | -| [location](./vertexai.ai.md#ailocation) | string | The location configured for this AI service instance, relevant for Vertex AI backends. | - -## AI.app - -The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) this [AI](./vertexai.ai.md#ai_interface) instance is associated with. - -Signature: - -```typescript -app: FirebaseApp; -``` - -## AI.backend - -A [Backend](./vertexai.backend.md#backend_class) instance that specifies the configuration for the target backend, either the Gemini Developer API (using [GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)) or the Vertex AI Gemini API (using [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). - -Signature: - -```typescript -backend: Backend; -``` - -## AI.location - -> Warning: This API is now obsolete. -> -> use `AI.backend.location` instead. -> - -The location configured for this AI service instance, relevant for Vertex AI backends. - -Signature: - -```typescript -location: string; -``` diff --git a/docs-devsite/vertexai.counttokensresponse.md b/docs-devsite/vertexai.counttokensresponse.md deleted file mode 100644 index b304ccb82a0..00000000000 --- a/docs-devsite/vertexai.counttokensresponse.md +++ /dev/null @@ -1,59 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# CountTokensResponse interface -Response from calling [GenerativeModel.countTokens()](./vertexai.generativemodel.md#generativemodelcounttokens). - -Signature: - -```typescript -export interface CountTokensResponse -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [promptTokensDetails](./vertexai.counttokensresponse.md#counttokensresponseprompttokensdetails) | [ModalityTokenCount](./vertexai.modalitytokencount.md#modalitytokencount_interface)\[\] | The breakdown, by modality, of how many tokens are consumed by the prompt. | -| [totalBillableCharacters](./vertexai.counttokensresponse.md#counttokensresponsetotalbillablecharacters) | number | The total number of billable characters counted across all instances from the request.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. | -| [totalTokens](./vertexai.counttokensresponse.md#counttokensresponsetotaltokens) | number | The total number of tokens counted across all instances from the request. | - -## CountTokensResponse.promptTokensDetails - -The breakdown, by modality, of how many tokens are consumed by the prompt. - -Signature: - -```typescript -promptTokensDetails?: ModalityTokenCount[]; -``` - -## CountTokensResponse.totalBillableCharacters - -The total number of billable characters counted across all instances from the request. - -This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. - -Signature: - -```typescript -totalBillableCharacters?: number; -``` - -## CountTokensResponse.totalTokens - -The total number of tokens counted across all instances from the request. - -Signature: - -```typescript -totalTokens: number; -``` diff --git a/docs-devsite/vertexai.enhancedgeneratecontentresponse.md b/docs-devsite/vertexai.enhancedgeneratecontentresponse.md deleted file mode 100644 index b557219bf84..00000000000 --- a/docs-devsite/vertexai.enhancedgeneratecontentresponse.md +++ /dev/null @@ -1,56 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# EnhancedGenerateContentResponse interface -Response object wrapped with helper methods. - -Signature: - -```typescript -export interface EnhancedGenerateContentResponse extends GenerateContentResponse -``` -Extends: [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface) - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [functionCalls](./vertexai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponsefunctioncalls) | () => [FunctionCall](./vertexai.functioncall.md#functioncall_interface)\[\] \| undefined | | -| [inlineDataParts](./vertexai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponseinlinedataparts) | () => [InlineDataPart](./vertexai.inlinedatapart.md#inlinedatapart_interface)\[\] \| undefined | Aggregates and returns all [InlineDataPart](./vertexai.inlinedatapart.md#inlinedatapart_interface)s from the [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface)'s first candidate. | -| [text](./vertexai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponsetext) | () => string | Returns the text string from the response, if available. Throws if the prompt or candidate was blocked. | - -## EnhancedGenerateContentResponse.functionCalls - -Signature: - -```typescript -functionCalls: () => FunctionCall[] | undefined; -``` - -## EnhancedGenerateContentResponse.inlineDataParts - -Aggregates and returns all [InlineDataPart](./vertexai.inlinedatapart.md#inlinedatapart_interface)s from the [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface)'s first candidate. - -Signature: - -```typescript -inlineDataParts: () => InlineDataPart[] | undefined; -``` - -## EnhancedGenerateContentResponse.text - -Returns the text string from the response, if available. Throws if the prompt or candidate was blocked. - -Signature: - -```typescript -text: () => string; -``` diff --git a/docs-devsite/vertexai.functiondeclarationstool.md b/docs-devsite/vertexai.functiondeclarationstool.md deleted file mode 100644 index 2eff3138d8d..00000000000 --- a/docs-devsite/vertexai.functiondeclarationstool.md +++ /dev/null @@ -1,35 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# FunctionDeclarationsTool interface -A `FunctionDeclarationsTool` is a piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. - -Signature: - -```typescript -export declare interface FunctionDeclarationsTool -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [functionDeclarations](./vertexai.functiondeclarationstool.md#functiondeclarationstoolfunctiondeclarations) | [FunctionDeclaration](./vertexai.functiondeclaration.md#functiondeclaration_interface)\[\] | Optional. One or more function declarations to be passed to the model along with the current user query. Model may decide to call a subset of these functions by populating [FunctionCall](./vertexai.functioncall.md#functioncall_interface) in the response. User should provide a [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface) for each function call in the next turn. Based on the function responses, the model will generate the final response back to the user. Maximum 64 function declarations can be provided. | - -## FunctionDeclarationsTool.functionDeclarations - -Optional. One or more function declarations to be passed to the model along with the current user query. Model may decide to call a subset of these functions by populating [FunctionCall](./vertexai.functioncall.md#functioncall_interface) in the response. User should provide a [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface) for each function call in the next turn. Based on the function responses, the model will generate the final response back to the user. Maximum 64 function declarations can be provided. - -Signature: - -```typescript -functionDeclarations?: FunctionDeclaration[]; -``` diff --git a/docs-devsite/vertexai.generatecontentresponse.md b/docs-devsite/vertexai.generatecontentresponse.md deleted file mode 100644 index 304674c9b6f..00000000000 --- a/docs-devsite/vertexai.generatecontentresponse.md +++ /dev/null @@ -1,51 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# GenerateContentResponse interface -Individual response from [GenerativeModel.generateContent()](./vertexai.generativemodel.md#generativemodelgeneratecontent) and [GenerativeModel.generateContentStream()](./vertexai.generativemodel.md#generativemodelgeneratecontentstream). `generateContentStream()` will return one in each chunk until the stream is done. - -Signature: - -```typescript -export interface GenerateContentResponse -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [candidates](./vertexai.generatecontentresponse.md#generatecontentresponsecandidates) | [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface)\[\] | | -| [promptFeedback](./vertexai.generatecontentresponse.md#generatecontentresponsepromptfeedback) | [PromptFeedback](./vertexai.promptfeedback.md#promptfeedback_interface) | | -| [usageMetadata](./vertexai.generatecontentresponse.md#generatecontentresponseusagemetadata) | [UsageMetadata](./vertexai.usagemetadata.md#usagemetadata_interface) | | - -## GenerateContentResponse.candidates - -Signature: - -```typescript -candidates?: GenerateContentCandidate[]; -``` - -## GenerateContentResponse.promptFeedback - -Signature: - -```typescript -promptFeedback?: PromptFeedback; -``` - -## GenerateContentResponse.usageMetadata - -Signature: - -```typescript -usageMetadata?: UsageMetadata; -``` diff --git a/docs-devsite/vertexai.generationconfig.md b/docs-devsite/vertexai.generationconfig.md deleted file mode 100644 index 360ef709419..00000000000 --- a/docs-devsite/vertexai.generationconfig.md +++ /dev/null @@ -1,134 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# GenerationConfig interface -Config options for content-related requests - -Signature: - -```typescript -export interface GenerationConfig -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [candidateCount](./vertexai.generationconfig.md#generationconfigcandidatecount) | number | | -| [frequencyPenalty](./vertexai.generationconfig.md#generationconfigfrequencypenalty) | number | | -| [maxOutputTokens](./vertexai.generationconfig.md#generationconfigmaxoutputtokens) | number | | -| [presencePenalty](./vertexai.generationconfig.md#generationconfigpresencepenalty) | number | | -| [responseMimeType](./vertexai.generationconfig.md#generationconfigresponsemimetype) | string | Output response MIME type of the generated candidate text. Supported MIME types are text/plain (default, text output), application/json (JSON response in the candidates), and text/x.enum. | -| [responseModalities](./vertexai.generationconfig.md#generationconfigresponsemodalities) | [ResponseModality](./vertexai.md#responsemodality)\[\] | (Public Preview) Generation modalities to be returned in generation responses. | -| [responseSchema](./vertexai.generationconfig.md#generationconfigresponseschema) | [TypedSchema](./vertexai.md#typedschema) \| [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) | Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./vertexai.schema.md#schema_class) static method like Schema.string() or Schema.object() or it can be a plain JS object matching the [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified responseMIMEType supports a schema; currently this is limited to application/json and text/x.enum. | -| [stopSequences](./vertexai.generationconfig.md#generationconfigstopsequences) | string\[\] | | -| [temperature](./vertexai.generationconfig.md#generationconfigtemperature) | number | | -| [topK](./vertexai.generationconfig.md#generationconfigtopk) | number | | -| [topP](./vertexai.generationconfig.md#generationconfigtopp) | number | | - -## GenerationConfig.candidateCount - -Signature: - -```typescript -candidateCount?: number; -``` - -## GenerationConfig.frequencyPenalty - -Signature: - -```typescript -frequencyPenalty?: number; -``` - -## GenerationConfig.maxOutputTokens - -Signature: - -```typescript -maxOutputTokens?: number; -``` - -## GenerationConfig.presencePenalty - -Signature: - -```typescript -presencePenalty?: number; -``` - -## GenerationConfig.responseMimeType - -Output response MIME type of the generated candidate text. Supported MIME types are `text/plain` (default, text output), `application/json` (JSON response in the candidates), and `text/x.enum`. - -Signature: - -```typescript -responseMimeType?: string; -``` - -## GenerationConfig.responseModalities - -> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. -> - -Generation modalities to be returned in generation responses. - -- Multimodal response generation is only supported by some Gemini models and versions; see [model versions](https://firebase.google.com/docs/vertex-ai/models). - Only image generation (`ResponseModality.IMAGE`) is supported. - -Signature: - -```typescript -responseModalities?: ResponseModality[]; -``` - -## GenerationConfig.responseSchema - -Output response schema of the generated candidate text. This value can be a class generated with a [Schema](./vertexai.schema.md#schema_class) static method like `Schema.string()` or `Schema.object()` or it can be a plain JS object matching the [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) interface.
Note: This only applies when the specified `responseMIMEType` supports a schema; currently this is limited to `application/json` and `text/x.enum`. - -Signature: - -```typescript -responseSchema?: TypedSchema | SchemaRequest; -``` - -## GenerationConfig.stopSequences - -Signature: - -```typescript -stopSequences?: string[]; -``` - -## GenerationConfig.temperature - -Signature: - -```typescript -temperature?: number; -``` - -## GenerationConfig.topK - -Signature: - -```typescript -topK?: number; -``` - -## GenerationConfig.topP - -Signature: - -```typescript -topP?: number; -``` diff --git a/docs-devsite/vertexai.generativemodel.md b/docs-devsite/vertexai.generativemodel.md deleted file mode 100644 index ba82b65aceb..00000000000 --- a/docs-devsite/vertexai.generativemodel.md +++ /dev/null @@ -1,193 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# GenerativeModel class -Class for generative model APIs. - -Signature: - -```typescript -export declare class GenerativeModel extends AIModel -``` -Extends: [AIModel](./vertexai.aimodel.md#aimodel_class) - -## Constructors - -| Constructor | Modifiers | Description | -| --- | --- | --- | -| [(constructor)(ai, modelParams, requestOptions)](./vertexai.generativemodel.md#generativemodelconstructor) | | Constructs a new instance of the GenerativeModel class | - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [generationConfig](./vertexai.generativemodel.md#generativemodelgenerationconfig) | | [GenerationConfig](./vertexai.generationconfig.md#generationconfig_interface) | | -| [requestOptions](./vertexai.generativemodel.md#generativemodelrequestoptions) | | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | | -| [safetySettings](./vertexai.generativemodel.md#generativemodelsafetysettings) | | [SafetySetting](./vertexai.safetysetting.md#safetysetting_interface)\[\] | | -| [systemInstruction](./vertexai.generativemodel.md#generativemodelsysteminstruction) | | [Content](./vertexai.content.md#content_interface) | | -| [toolConfig](./vertexai.generativemodel.md#generativemodeltoolconfig) | | [ToolConfig](./vertexai.toolconfig.md#toolconfig_interface) | | -| [tools](./vertexai.generativemodel.md#generativemodeltools) | | [Tool](./vertexai.md#tool)\[\] | | - -## Methods - -| Method | Modifiers | Description | -| --- | --- | --- | -| [countTokens(request)](./vertexai.generativemodel.md#generativemodelcounttokens) | | Counts the tokens in the provided request. | -| [generateContent(request)](./vertexai.generativemodel.md#generativemodelgeneratecontent) | | Makes a single non-streaming call to the model and returns an object containing a single [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). | -| [generateContentStream(request)](./vertexai.generativemodel.md#generativemodelgeneratecontentstream) | | Makes a single streaming call to the model and returns an object containing an iterable stream that iterates over all chunks in the streaming response as well as a promise that returns the final aggregated response. | -| [startChat(startChatParams)](./vertexai.generativemodel.md#generativemodelstartchat) | | Gets a new [ChatSession](./vertexai.chatsession.md#chatsession_class) instance which can be used for multi-turn chats. | - -## GenerativeModel.(constructor) - -Constructs a new instance of the `GenerativeModel` class - -Signature: - -```typescript -constructor(ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions); -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| ai | [AI](./vertexai.ai.md#ai_interface) | | -| modelParams | [ModelParams](./vertexai.modelparams.md#modelparams_interface) | | -| requestOptions | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | | - -## GenerativeModel.generationConfig - -Signature: - -```typescript -generationConfig: GenerationConfig; -``` - -## GenerativeModel.requestOptions - -Signature: - -```typescript -requestOptions?: RequestOptions; -``` - -## GenerativeModel.safetySettings - -Signature: - -```typescript -safetySettings: SafetySetting[]; -``` - -## GenerativeModel.systemInstruction - -Signature: - -```typescript -systemInstruction?: Content; -``` - -## GenerativeModel.toolConfig - -Signature: - -```typescript -toolConfig?: ToolConfig; -``` - -## GenerativeModel.tools - -Signature: - -```typescript -tools?: Tool[]; -``` - -## GenerativeModel.countTokens() - -Counts the tokens in the provided request. - -Signature: - -```typescript -countTokens(request: CountTokensRequest | string | Array): Promise; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| request | [CountTokensRequest](./vertexai.counttokensrequest.md#counttokensrequest_interface) \| string \| Array<string \| [Part](./vertexai.md#part)> | | - -Returns: - -Promise<[CountTokensResponse](./vertexai.counttokensresponse.md#counttokensresponse_interface)> - -## GenerativeModel.generateContent() - -Makes a single non-streaming call to the model and returns an object containing a single [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). - -Signature: - -```typescript -generateContent(request: GenerateContentRequest | string | Array): Promise; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| request | [GenerateContentRequest](./vertexai.generatecontentrequest.md#generatecontentrequest_interface) \| string \| Array<string \| [Part](./vertexai.md#part)> | | - -Returns: - -Promise<[GenerateContentResult](./vertexai.generatecontentresult.md#generatecontentresult_interface)> - -## GenerativeModel.generateContentStream() - -Makes a single streaming call to the model and returns an object containing an iterable stream that iterates over all chunks in the streaming response as well as a promise that returns the final aggregated response. - -Signature: - -```typescript -generateContentStream(request: GenerateContentRequest | string | Array): Promise; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| request | [GenerateContentRequest](./vertexai.generatecontentrequest.md#generatecontentrequest_interface) \| string \| Array<string \| [Part](./vertexai.md#part)> | | - -Returns: - -Promise<[GenerateContentStreamResult](./vertexai.generatecontentstreamresult.md#generatecontentstreamresult_interface)> - -## GenerativeModel.startChat() - -Gets a new [ChatSession](./vertexai.chatsession.md#chatsession_class) instance which can be used for multi-turn chats. - -Signature: - -```typescript -startChat(startChatParams?: StartChatParams): ChatSession; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| startChatParams | [StartChatParams](./vertexai.startchatparams.md#startchatparams_interface) | | - -Returns: - -[ChatSession](./vertexai.chatsession.md#chatsession_class) - diff --git a/docs-devsite/vertexai.md b/docs-devsite/vertexai.md deleted file mode 100644 index 7f56e0b373c..00000000000 --- a/docs-devsite/vertexai.md +++ /dev/null @@ -1,716 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# vertexai package -The Firebase AI Web SDK. - -## Functions - -| Function | Description | -| --- | --- | -| function(app, ...) | -| [getAI(app, options)](./vertexai.md#getai_a94a413) | Returns the default [AI](./vertexai.ai.md#ai_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. | -| [getVertexAI(app, options)](./vertexai.md#getvertexai_04094cf) | It is recommended to use the new [getAI()](./vertexai.md#getai_a94a413).Returns a [VertexAI](./vertexai.md#vertexai) instance for the given app, configured to use the Vertex AI Gemini API. This instance will be configured to use the Vertex AI Gemini API. | -| function(ai, ...) | -| [getGenerativeModel(ai, modelParams, requestOptions)](./vertexai.md#getgenerativemodel_80bd839) | Returns a [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) class with methods for inference and other functionality. | -| [getImagenModel(ai, modelParams, requestOptions)](./vertexai.md#getimagenmodel_e1f6645) | (Public Preview) Returns an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class with methods for using Imagen.Only Imagen 3 models (named imagen-3.0-*) are supported. | - -## Classes - -| Class | Description | -| --- | --- | -| [AIError](./vertexai.aierror.md#aierror_class) | Error class for the Firebase AI SDK. | -| [AIModel](./vertexai.aimodel.md#aimodel_class) | Base class for Firebase AI model APIs.Instances of this class are associated with a specific Firebase AI [Backend](./vertexai.backend.md#backend_class) and provide methods for interacting with the configured generative model. | -| [ArraySchema](./vertexai.arrayschema.md#arrayschema_class) | Schema class for "array" types. The items param should refer to the type of item that can be a member of the array. | -| [Backend](./vertexai.backend.md#backend_class) | Abstract base class representing the configuration for an AI service backend. This class should not be instantiated directly. Use its subclasses; [GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class) for the Gemini Developer API (via [Google AI](https://ai.google/)), and [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class) for the Vertex AI Gemini API. | -| [BooleanSchema](./vertexai.booleanschema.md#booleanschema_class) | Schema class for "boolean" types. | -| [ChatSession](./vertexai.chatsession.md#chatsession_class) | ChatSession class that enables sending chat messages and stores history of sent and received messages so far. | -| [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) | Class for generative model APIs. | -| [GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class) | Configuration class for the Gemini Developer API.Use this with [AIOptions](./vertexai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./vertexai.md#getai_a94a413) to specify the Gemini Developer API as the backend. | -| [ImagenImageFormat](./vertexai.imagenimageformat.md#imagenimageformat_class) | (Public Preview) Defines the image format for images generated by Imagen.Use this class to specify the desired format (JPEG or PNG) and compression quality for images generated by Imagen. This is typically included as part of [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface). | -| [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) | (Public Preview) Class for Imagen model APIs.This class provides methods for generating images using the Imagen model. | -| [IntegerSchema](./vertexai.integerschema.md#integerschema_class) | Schema class for "integer" types. | -| [NumberSchema](./vertexai.numberschema.md#numberschema_class) | Schema class for "number" types. | -| [ObjectSchema](./vertexai.objectschema.md#objectschema_class) | Schema class for "object" types. The properties param must be a map of Schema objects. | -| [Schema](./vertexai.schema.md#schema_class) | Parent class encompassing all Schema types, with static methods that allow building specific Schema types. This class can be converted with JSON.stringify() into a JSON string accepted by Vertex AI REST endpoints. (This string conversion is automatically done when calling SDK methods.) | -| [StringSchema](./vertexai.stringschema.md#stringschema_class) | Schema class for "string" types. Can be used with or without enum values. | -| [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class) | Configuration class for the Vertex AI Gemini API.Use this with [AIOptions](./vertexai.aioptions.md#aioptions_interface) when initializing the AI service via [getAI()](./vertexai.md#getai_a94a413) to specify the Vertex AI Gemini API as the backend. | - -## Enumerations - -| Enumeration | Description | -| --- | --- | -| [AIErrorCode](./vertexai.md#aierrorcode) | Standardized error codes that [AIError](./vertexai.aierror.md#aierror_class) can have. | -| [BlockReason](./vertexai.md#blockreason) | Reason that a prompt was blocked. | -| [FinishReason](./vertexai.md#finishreason) | Reason that a candidate finished. | -| [FunctionCallingMode](./vertexai.md#functioncallingmode) | | -| [HarmBlockMethod](./vertexai.md#harmblockmethod) | This property is not supported in the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)). | -| [HarmBlockThreshold](./vertexai.md#harmblockthreshold) | Threshold above which a prompt or candidate will be blocked. | -| [HarmCategory](./vertexai.md#harmcategory) | Harm categories that would cause prompts or candidates to be blocked. | -| [HarmProbability](./vertexai.md#harmprobability) | Probability that a prompt or candidate matches a harm category. | -| [HarmSeverity](./vertexai.md#harmseverity) | Harm severity levels. | -| [ImagenAspectRatio](./vertexai.md#imagenaspectratio) | (Public Preview) Aspect ratios for Imagen images.To specify an aspect ratio for generated images, set the aspectRatio property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface).See the the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) for more details and examples of the supported aspect ratios. | -| [ImagenPersonFilterLevel](./vertexai.md#imagenpersonfilterlevel) | (Public Preview) A filter level controlling whether generation of images containing people or faces is allowed.See the personGeneration documentation for more details. | -| [ImagenSafetyFilterLevel](./vertexai.md#imagensafetyfilterlevel) | (Public Preview) A filter level controlling how aggressively to filter sensitive content.Text prompts provided as inputs and images (generated or uploaded) through Imagen on Vertex AI are assessed against a list of safety filters, which include 'harmful categories' (for example, violence, sexual, derogatory, and toxic). This filter level controls how aggressively to filter out potentially harmful content from responses. See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) and the [Responsible AI and usage guidelines](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#safety-filters) for more details. | -| [Modality](./vertexai.md#modality) | Content part modality. | -| [SchemaType](./vertexai.md#schematype) | Contains the list of OpenAPI data types as defined by the [OpenAPI specification](https://swagger.io/docs/specification/data-models/data-types/) | - -## Interfaces - -| Interface | Description | -| --- | --- | -| [AI](./vertexai.ai.md#ai_interface) | An instance of the Firebase AI SDK.Do not create this instance directly. Instead, use [getAI()](./vertexai.md#getai_a94a413). | -| [AIOptions](./vertexai.aioptions.md#aioptions_interface) | Options for initializing the AI service using [getAI()](./vertexai.md#getai_a94a413). This allows specifying which backend to use (Vertex AI Gemini API or Gemini Developer API) and configuring its specific options (like location for Vertex AI). | -| [BaseParams](./vertexai.baseparams.md#baseparams_interface) | Base parameters for a number of methods. | -| [Citation](./vertexai.citation.md#citation_interface) | A single citation. | -| [CitationMetadata](./vertexai.citationmetadata.md#citationmetadata_interface) | Citation metadata that may be found on a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface). | -| [Content](./vertexai.content.md#content_interface) | Content type for both prompts and response candidates. | -| [CountTokensRequest](./vertexai.counttokensrequest.md#counttokensrequest_interface) | Params for calling [GenerativeModel.countTokens()](./vertexai.generativemodel.md#generativemodelcounttokens) | -| [CountTokensResponse](./vertexai.counttokensresponse.md#counttokensresponse_interface) | Response from calling [GenerativeModel.countTokens()](./vertexai.generativemodel.md#generativemodelcounttokens). | -| [CustomErrorData](./vertexai.customerrordata.md#customerrordata_interface) | Details object that contains data originating from a bad HTTP response. | -| [Date\_2](./vertexai.date_2.md#date_2_interface) | Protobuf google.type.Date | -| [EnhancedGenerateContentResponse](./vertexai.enhancedgeneratecontentresponse.md#enhancedgeneratecontentresponse_interface) | Response object wrapped with helper methods. | -| [ErrorDetails](./vertexai.errordetails.md#errordetails_interface) | Details object that may be included in an error response. | -| [FileData](./vertexai.filedata.md#filedata_interface) | Data pointing to a file uploaded on Google Cloud Storage. | -| [FileDataPart](./vertexai.filedatapart.md#filedatapart_interface) | Content part interface if the part represents [FileData](./vertexai.filedata.md#filedata_interface) | -| [FunctionCall](./vertexai.functioncall.md#functioncall_interface) | A predicted [FunctionCall](./vertexai.functioncall.md#functioncall_interface) returned from the model that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing the parameters and their values. | -| [FunctionCallingConfig](./vertexai.functioncallingconfig.md#functioncallingconfig_interface) | | -| [FunctionCallPart](./vertexai.functioncallpart.md#functioncallpart_interface) | Content part interface if the part represents a [FunctionCall](./vertexai.functioncall.md#functioncall_interface). | -| [FunctionDeclaration](./vertexai.functiondeclaration.md#functiondeclaration_interface) | Structured representation of a function declaration as defined by the [OpenAPI 3.0 specification](https://spec.openapis.org/oas/v3.0.3). Included in this declaration are the function name and parameters. This FunctionDeclaration is a representation of a block of code that can be used as a Tool by the model and executed by the client. | -| [FunctionDeclarationsTool](./vertexai.functiondeclarationstool.md#functiondeclarationstool_interface) | A FunctionDeclarationsTool is a piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. | -| [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface) | The result output from a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) that contains a string representing the [FunctionDeclaration.name](./vertexai.functiondeclaration.md#functiondeclarationname) and a structured JSON object containing any output from the function is used as context to the model. This should contain the result of a [FunctionCall](./vertexai.functioncall.md#functioncall_interface) made based on model prediction. | -| [FunctionResponsePart](./vertexai.functionresponsepart.md#functionresponsepart_interface) | Content part interface if the part represents [FunctionResponse](./vertexai.functionresponse.md#functionresponse_interface). | -| [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface) | A candidate returned as part of a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). | -| [GenerateContentRequest](./vertexai.generatecontentrequest.md#generatecontentrequest_interface) | Request sent through [GenerativeModel.generateContent()](./vertexai.generativemodel.md#generativemodelgeneratecontent) | -| [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface) | Individual response from [GenerativeModel.generateContent()](./vertexai.generativemodel.md#generativemodelgeneratecontent) and [GenerativeModel.generateContentStream()](./vertexai.generativemodel.md#generativemodelgeneratecontentstream). generateContentStream() will return one in each chunk until the stream is done. | -| [GenerateContentResult](./vertexai.generatecontentresult.md#generatecontentresult_interface) | Result object returned from [GenerativeModel.generateContent()](./vertexai.generativemodel.md#generativemodelgeneratecontent) call. | -| [GenerateContentStreamResult](./vertexai.generatecontentstreamresult.md#generatecontentstreamresult_interface) | Result object returned from [GenerativeModel.generateContentStream()](./vertexai.generativemodel.md#generativemodelgeneratecontentstream) call. Iterate over stream to get chunks as they come in and/or use the response promise to get the aggregated response when the stream is done. | -| [GenerationConfig](./vertexai.generationconfig.md#generationconfig_interface) | Config options for content-related requests | -| [GenerativeContentBlob](./vertexai.generativecontentblob.md#generativecontentblob_interface) | Interface for sending an image. | -| [GroundingAttribution](./vertexai.groundingattribution.md#groundingattribution_interface) | | -| [GroundingMetadata](./vertexai.groundingmetadata.md#groundingmetadata_interface) | Metadata returned to client when grounding is enabled. | -| [ImagenGCSImage](./vertexai.imagengcsimage.md#imagengcsimage_interface) | An image generated by Imagen, stored in a Cloud Storage for Firebase bucket.This feature is not available yet. | -| [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface) | (Public Preview) Configuration options for generating images with Imagen.See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images-imagen) for more details. | -| [ImagenGenerationResponse](./vertexai.imagengenerationresponse.md#imagengenerationresponse_interface) | (Public Preview) The response from a request to generate images with Imagen. | -| [ImagenInlineImage](./vertexai.imageninlineimage.md#imageninlineimage_interface) | (Public Preview) An image generated by Imagen, represented as inline data. | -| [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface) | (Public Preview) Parameters for configuring an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class). | -| [ImagenSafetySettings](./vertexai.imagensafetysettings.md#imagensafetysettings_interface) | (Public Preview) Settings for controlling the aggressiveness of filtering out sensitive content.See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) for more details. | -| [InlineDataPart](./vertexai.inlinedatapart.md#inlinedatapart_interface) | Content part interface if the part represents an image. | -| [ModalityTokenCount](./vertexai.modalitytokencount.md#modalitytokencount_interface) | Represents token counting info for a single modality. | -| [ModelParams](./vertexai.modelparams.md#modelparams_interface) | Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_80bd839). | -| [ObjectSchemaInterface](./vertexai.objectschemainterface.md#objectschemainterface_interface) | Interface for [ObjectSchema](./vertexai.objectschema.md#objectschema_class) class. | -| [PromptFeedback](./vertexai.promptfeedback.md#promptfeedback_interface) | If the prompt was blocked, this will be populated with blockReason and the relevant safetyRatings. | -| [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | Params passed to [getGenerativeModel()](./vertexai.md#getgenerativemodel_80bd839). | -| [RetrievedContextAttribution](./vertexai.retrievedcontextattribution.md#retrievedcontextattribution_interface) | | -| [SafetyRating](./vertexai.safetyrating.md#safetyrating_interface) | A safety rating associated with a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface) | -| [SafetySetting](./vertexai.safetysetting.md#safetysetting_interface) | Safety setting that can be sent as part of request parameters. | -| [SchemaInterface](./vertexai.schemainterface.md#schemainterface_interface) | Interface for [Schema](./vertexai.schema.md#schema_class) class. | -| [SchemaParams](./vertexai.schemaparams.md#schemaparams_interface) | Params passed to [Schema](./vertexai.schema.md#schema_class) static methods to create specific [Schema](./vertexai.schema.md#schema_class) classes. | -| [SchemaRequest](./vertexai.schemarequest.md#schemarequest_interface) | Final format for [Schema](./vertexai.schema.md#schema_class) params passed to backend requests. | -| [SchemaShared](./vertexai.schemashared.md#schemashared_interface) | Basic [Schema](./vertexai.schema.md#schema_class) properties shared across several Schema-related types. | -| [Segment](./vertexai.segment.md#segment_interface) | | -| [StartChatParams](./vertexai.startchatparams.md#startchatparams_interface) | Params for [GenerativeModel.startChat()](./vertexai.generativemodel.md#generativemodelstartchat). | -| [TextPart](./vertexai.textpart.md#textpart_interface) | Content part interface if the part represents a text string. | -| [ToolConfig](./vertexai.toolconfig.md#toolconfig_interface) | Tool config. This config is shared for all tools provided in the request. | -| [UsageMetadata](./vertexai.usagemetadata.md#usagemetadata_interface) | Usage metadata about a [GenerateContentResponse](./vertexai.generatecontentresponse.md#generatecontentresponse_interface). | -| [VertexAIOptions](./vertexai.vertexaioptions.md#vertexaioptions_interface) | Options when initializing the Firebase AI SDK. | -| [VideoMetadata](./vertexai.videometadata.md#videometadata_interface) | Describes the input video content. | -| [WebAttribution](./vertexai.webattribution.md#webattribution_interface) | | - -## Variables - -| Variable | Description | -| --- | --- | -| [BackendType](./vertexai.md#backendtype) | An enum-like object containing constants that represent the supported backends for the Firebase AI SDK. This determines which backend service (Vertex AI Gemini API or Gemini Developer API) the SDK will communicate with.These values are assigned to the backendType property within the specific backend configuration objects ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class) or [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)) to identify which service to target. | -| [POSSIBLE\_ROLES](./vertexai.md#possible_roles) | Possible roles. | -| [ResponseModality](./vertexai.md#responsemodality) | (Public Preview) Generation modalities to be returned in generation responses. | -| [VertexAIError](./vertexai.md#vertexaierror) | Error class for the Firebase AI SDK.For more information, refer to the documentation for the new [AIError](./vertexai.aierror.md#aierror_class). | -| [VertexAIModel](./vertexai.md#vertexaimodel) | Base class for Firebase AI model APIs.For more information, refer to the documentation for the new [AIModel](./vertexai.aimodel.md#aimodel_class). | - -## Type Aliases - -| Type Alias | Description | -| --- | --- | -| [BackendType](./vertexai.md#backendtype) | Type alias representing valid backend types. It can be either 'VERTEX_AI' or 'GOOGLE_AI'. | -| [Part](./vertexai.md#part) | Content part - includes text, image/video, or function call/response part types. | -| [ResponseModality](./vertexai.md#responsemodality) | (Public Preview) Generation modalities to be returned in generation responses. | -| [Role](./vertexai.md#role) | Role is the producer of the content. | -| [Tool](./vertexai.md#tool) | Defines a tool that model can call to access external knowledge. | -| [TypedSchema](./vertexai.md#typedschema) | A type that includes all specific Schema types. | -| [VertexAI](./vertexai.md#vertexai) | An instance of the Firebase AI SDK.For more information, refer to the documentation for the new [AI](./vertexai.ai.md#ai_interface) interface. | - -## function(app, ...) - -### getAI(app, options) {:#getai_a94a413} - -Returns the default [AI](./vertexai.ai.md#ai_interface) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with the default settings. - -Signature: - -```typescript -export declare function getAI(app?: FirebaseApp, options?: AIOptions): AI; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| app | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) to use. | -| options | [AIOptions](./vertexai.aioptions.md#aioptions_interface) | [AIOptions](./vertexai.aioptions.md#aioptions_interface) that configure the AI instance. | - -Returns: - -[AI](./vertexai.ai.md#ai_interface) - -The default [AI](./vertexai.ai.md#ai_interface) instance for the given [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). - -### Example 1 - - -```javascript -const ai = getAI(app); - -``` - -### Example 2 - - -```javascript -// Get an AI instance configured to use the Gemini Developer API (via Google AI). -const ai = getAI(app, { backend: new GoogleAIBackend() }); - -``` - -### Example 3 - - -```javascript -// Get an AI instance configured to use the Vertex AI Gemini API. -const ai = getAI(app, { backend: new VertexAIBackend() }); - -``` - -### getVertexAI(app, options) {:#getvertexai_04094cf} - -It is recommended to use the new [getAI()](./vertexai.md#getai_a94a413). - -Returns a [VertexAI](./vertexai.md#vertexai) instance for the given app, configured to use the Vertex AI Gemini API. This instance will be configured to use the Vertex AI Gemini API. - -Signature: - -```typescript -export declare function getVertexAI(app?: FirebaseApp, options?: VertexAIOptions): VertexAI; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| app | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) to use. | -| options | [VertexAIOptions](./vertexai.vertexaioptions.md#vertexaioptions_interface) | Options to configure the Vertex AI instance, including the location. | - -Returns: - -[VertexAI](./vertexai.md#vertexai) - -## function(ai, ...) - -### getGenerativeModel(ai, modelParams, requestOptions) {:#getgenerativemodel_80bd839} - -Returns a [GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) class with methods for inference and other functionality. - -Signature: - -```typescript -export declare function getGenerativeModel(ai: AI, modelParams: ModelParams, requestOptions?: RequestOptions): GenerativeModel; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| ai | [AI](./vertexai.ai.md#ai_interface) | | -| modelParams | [ModelParams](./vertexai.modelparams.md#modelparams_interface) | | -| requestOptions | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | | - -Returns: - -[GenerativeModel](./vertexai.generativemodel.md#generativemodel_class) - -### getImagenModel(ai, modelParams, requestOptions) {:#getimagenmodel_e1f6645} - -> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. -> - -Returns an [ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) class with methods for using Imagen. - -Only Imagen 3 models (named `imagen-3.0-*`) are supported. - -Signature: - -```typescript -export declare function getImagenModel(ai: AI, modelParams: ImagenModelParams, requestOptions?: RequestOptions): ImagenModel; -``` - -#### Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| ai | [AI](./vertexai.ai.md#ai_interface) | An [AI](./vertexai.ai.md#ai_interface) instance. | -| modelParams | [ImagenModelParams](./vertexai.imagenmodelparams.md#imagenmodelparams_interface) | Parameters to use when making Imagen requests. | -| requestOptions | [RequestOptions](./vertexai.requestoptions.md#requestoptions_interface) | Additional options to use when making requests. | - -Returns: - -[ImagenModel](./vertexai.imagenmodel.md#imagenmodel_class) - -#### Exceptions - -If the `apiKey` or `projectId` fields are missing in your Firebase config. - -## BackendType - -An enum-like object containing constants that represent the supported backends for the Firebase AI SDK. This determines which backend service (Vertex AI Gemini API or Gemini Developer API) the SDK will communicate with. - -These values are assigned to the `backendType` property within the specific backend configuration objects ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class) or [VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)) to identify which service to target. - -Signature: - -```typescript -BackendType: { - readonly VERTEX_AI: "VERTEX_AI"; - readonly GOOGLE_AI: "GOOGLE_AI"; -} -``` - -## POSSIBLE\_ROLES - -Possible roles. - -Signature: - -```typescript -POSSIBLE_ROLES: readonly ["user", "model", "function", "system"] -``` - -## ResponseModality - -> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. -> - -Generation modalities to be returned in generation responses. - -Signature: - -```typescript -ResponseModality: { - readonly TEXT: "TEXT"; - readonly IMAGE: "IMAGE"; -} -``` - -## VertexAIError - -Error class for the Firebase AI SDK. - -For more information, refer to the documentation for the new [AIError](./vertexai.aierror.md#aierror_class). - -Signature: - -```typescript -VertexAIError: typeof AIError -``` - -## VertexAIModel - -Base class for Firebase AI model APIs. - -For more information, refer to the documentation for the new [AIModel](./vertexai.aimodel.md#aimodel_class). - -Signature: - -```typescript -VertexAIModel: typeof AIModel -``` - -## BackendType - -Type alias representing valid backend types. It can be either `'VERTEX_AI'` or `'GOOGLE_AI'`. - -Signature: - -```typescript -export type BackendType = (typeof BackendType)[keyof typeof BackendType]; -``` - -## Part - -Content part - includes text, image/video, or function call/response part types. - -Signature: - -```typescript -export type Part = TextPart | InlineDataPart | FunctionCallPart | FunctionResponsePart | FileDataPart; -``` - -## ResponseModality - -> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. -> - -Generation modalities to be returned in generation responses. - -Signature: - -```typescript -export type ResponseModality = (typeof ResponseModality)[keyof typeof ResponseModality]; -``` - -## Role - -Role is the producer of the content. - -Signature: - -```typescript -export type Role = (typeof POSSIBLE_ROLES)[number]; -``` - -## Tool - -Defines a tool that model can call to access external knowledge. - -Signature: - -```typescript -export declare type Tool = FunctionDeclarationsTool; -``` - -## TypedSchema - -A type that includes all specific Schema types. - -Signature: - -```typescript -export type TypedSchema = IntegerSchema | NumberSchema | StringSchema | BooleanSchema | ObjectSchema | ArraySchema; -``` - -## VertexAI - -An instance of the Firebase AI SDK. - -For more information, refer to the documentation for the new [AI](./vertexai.ai.md#ai_interface) interface. - -Signature: - -```typescript -export type VertexAI = AI; -``` - -## AIErrorCode - -Standardized error codes that [AIError](./vertexai.aierror.md#aierror_class) can have. - -Signature: - -```typescript -export declare const enum AIErrorCode -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| API\_NOT\_ENABLED | "api-not-enabled" | An error due to the Firebase API not being enabled in the Console. | -| ERROR | "error" | A generic error occurred. | -| FETCH\_ERROR | "fetch-error" | An error occurred while performing a fetch. | -| INVALID\_CONTENT | "invalid-content" | An error associated with a Content object. | -| INVALID\_SCHEMA | "invalid-schema" | An error due to invalid Schema input. | -| NO\_API\_KEY | "no-api-key" | An error occurred due to a missing Firebase API key. | -| NO\_APP\_ID | "no-app-id" | An error occured due to a missing Firebase app ID. | -| NO\_MODEL | "no-model" | An error occurred due to a model name not being specified during initialization. | -| NO\_PROJECT\_ID | "no-project-id" | An error occurred due to a missing project ID. | -| PARSE\_FAILED | "parse-failed" | An error occurred while parsing. | -| REQUEST\_ERROR | "request-error" | An error occurred in a request. | -| RESPONSE\_ERROR | "response-error" | An error occurred in a response. | -| UNSUPPORTED | "unsupported" | An error occured due an attempt to use an unsupported feature. | - -## BlockReason - -Reason that a prompt was blocked. - -Signature: - -```typescript -export declare enum BlockReason -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| BLOCKLIST | "BLOCKLIST" | Content was blocked because it contained terms from the terminology blocklist. | -| OTHER | "OTHER" | Content was blocked, but the reason is uncategorized. | -| PROHIBITED\_CONTENT | "PROHIBITED_CONTENT" | Content was blocked due to prohibited content. | -| SAFETY | "SAFETY" | Content was blocked by safety settings. | - -## FinishReason - -Reason that a candidate finished. - -Signature: - -```typescript -export declare enum FinishReason -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| BLOCKLIST | "BLOCKLIST" | The candidate content contained forbidden terms. | -| MALFORMED\_FUNCTION\_CALL | "MALFORMED_FUNCTION_CALL" | The function call generated by the model was invalid. | -| MAX\_TOKENS | "MAX_TOKENS" | The maximum number of tokens as specified in the request was reached. | -| OTHER | "OTHER" | Unknown reason. | -| PROHIBITED\_CONTENT | "PROHIBITED_CONTENT" | The candidate content potentially contained prohibited content. | -| RECITATION | "RECITATION" | The candidate content was flagged for recitation reasons. | -| SAFETY | "SAFETY" | The candidate content was flagged for safety reasons. | -| SPII | "SPII" | The candidate content potentially contained Sensitive Personally Identifiable Information (SPII). | -| STOP | "STOP" | Natural stop point of the model or provided stop sequence. | - -## FunctionCallingMode - - -Signature: - -```typescript -export declare enum FunctionCallingMode -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| ANY | "ANY" | Model is constrained to always predicting a function call only. If allowed_function_names is set, the predicted function call will be limited to any one of allowed_function_names, else the predicted function call will be any one of the provided function_declarations. | -| AUTO | "AUTO" | Default model behavior; model decides to predict either a function call or a natural language response. | -| NONE | "NONE" | Model will not predict any function call. Model behavior is same as when not passing any function declarations. | - -## HarmBlockMethod - -This property is not supported in the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)). - -Signature: - -```typescript -export declare enum HarmBlockMethod -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| PROBABILITY | "PROBABILITY" | The harm block method uses the probability score. | -| SEVERITY | "SEVERITY" | The harm block method uses both probability and severity scores. | - -## HarmBlockThreshold - -Threshold above which a prompt or candidate will be blocked. - -Signature: - -```typescript -export declare enum HarmBlockThreshold -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| BLOCK\_LOW\_AND\_ABOVE | "BLOCK_LOW_AND_ABOVE" | Content with NEGLIGIBLE will be allowed. | -| BLOCK\_MEDIUM\_AND\_ABOVE | "BLOCK_MEDIUM_AND_ABOVE" | Content with NEGLIGIBLE and LOW will be allowed. | -| BLOCK\_NONE | "BLOCK_NONE" | All content will be allowed. | -| BLOCK\_ONLY\_HIGH | "BLOCK_ONLY_HIGH" | Content with NEGLIGIBLE, LOW, and MEDIUM will be allowed. | - -## HarmCategory - -Harm categories that would cause prompts or candidates to be blocked. - -Signature: - -```typescript -export declare enum HarmCategory -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| HARM\_CATEGORY\_DANGEROUS\_CONTENT | "HARM_CATEGORY_DANGEROUS_CONTENT" | | -| HARM\_CATEGORY\_HARASSMENT | "HARM_CATEGORY_HARASSMENT" | | -| HARM\_CATEGORY\_HATE\_SPEECH | "HARM_CATEGORY_HATE_SPEECH" | | -| HARM\_CATEGORY\_SEXUALLY\_EXPLICIT | "HARM_CATEGORY_SEXUALLY_EXPLICIT" | | - -## HarmProbability - -Probability that a prompt or candidate matches a harm category. - -Signature: - -```typescript -export declare enum HarmProbability -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| HIGH | "HIGH" | Content has a high chance of being unsafe. | -| LOW | "LOW" | Content has a low chance of being unsafe. | -| MEDIUM | "MEDIUM" | Content has a medium chance of being unsafe. | -| NEGLIGIBLE | "NEGLIGIBLE" | Content has a negligible chance of being unsafe. | - -## HarmSeverity - -Harm severity levels. - -Signature: - -```typescript -export declare enum HarmSeverity -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| HARM\_SEVERITY\_HIGH | "HARM_SEVERITY_HIGH" | High level of harm severity. | -| HARM\_SEVERITY\_LOW | "HARM_SEVERITY_LOW" | Low level of harm severity. | -| HARM\_SEVERITY\_MEDIUM | "HARM_SEVERITY_MEDIUM" | Medium level of harm severity. | -| HARM\_SEVERITY\_NEGLIGIBLE | "HARM_SEVERITY_NEGLIGIBLE" | Negligible level of harm severity. | -| HARM\_SEVERITY\_UNSUPPORTED | "HARM_SEVERITY_UNSUPPORTED" | Harm severity is not supported. | - -## ImagenAspectRatio - -> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. -> - -Aspect ratios for Imagen images. - -To specify an aspect ratio for generated images, set the `aspectRatio` property in your [ImagenGenerationConfig](./vertexai.imagengenerationconfig.md#imagengenerationconfig_interface). - -See the the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) for more details and examples of the supported aspect ratios. - -Signature: - -```typescript -export declare enum ImagenAspectRatio -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| LANDSCAPE\_16x9 | "16:9" | (Public Preview) Landscape (16:9) aspect ratio. | -| LANDSCAPE\_3x4 | "3:4" | (Public Preview) Landscape (3:4) aspect ratio. | -| PORTRAIT\_4x3 | "4:3" | (Public Preview) Portrait (4:3) aspect ratio. | -| PORTRAIT\_9x16 | "9:16" | (Public Preview) Portrait (9:16) aspect ratio. | -| SQUARE | "1:1" | (Public Preview) Square (1:1) aspect ratio. | - -## ImagenPersonFilterLevel - -> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. -> - -A filter level controlling whether generation of images containing people or faces is allowed. - -See the personGeneration documentation for more details. - -Signature: - -```typescript -export declare enum ImagenPersonFilterLevel -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| ALLOW\_ADULT | "allow_adult" | (Public Preview) Allow generation of images containing adults only; images of children are filtered out.Generation of images containing people or faces may require your use case to be reviewed and approved by Cloud support; see the [Responsible AI and usage guidelines](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#person-face-gen) for more details. | -| ALLOW\_ALL | "allow_all" | (Public Preview) Allow generation of images containing adults only; images of children are filtered out.Generation of images containing people or faces may require your use case to be reviewed and approved by Cloud support; see the [Responsible AI and usage guidelines](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#person-face-gen) for more details. | -| BLOCK\_ALL | "dont_allow" | (Public Preview) Disallow generation of images containing people or faces; images of people are filtered out. | - -## ImagenSafetyFilterLevel - -> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. -> - -A filter level controlling how aggressively to filter sensitive content. - -Text prompts provided as inputs and images (generated or uploaded) through Imagen on Vertex AI are assessed against a list of safety filters, which include 'harmful categories' (for example, `violence`, `sexual`, `derogatory`, and `toxic`). This filter level controls how aggressively to filter out potentially harmful content from responses. See the [documentation](http://firebase.google.com/docs/vertex-ai/generate-images) and the [Responsible AI and usage guidelines](https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#safety-filters) for more details. - -Signature: - -```typescript -export declare enum ImagenSafetyFilterLevel -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| BLOCK\_LOW\_AND\_ABOVE | "block_low_and_above" | (Public Preview) The most aggressive filtering level; most strict blocking. | -| BLOCK\_MEDIUM\_AND\_ABOVE | "block_medium_and_above" | (Public Preview) Blocks some sensitive prompts and responses. | -| BLOCK\_NONE | "block_none" | (Public Preview) The least aggressive filtering level; blocks very few sensitive prompts and responses.Access to this feature is restricted and may require your case to be reviewed and approved by Cloud support. | -| BLOCK\_ONLY\_HIGH | "block_only_high" | (Public Preview) Blocks few sensitive prompts and responses. | - -## Modality - -Content part modality. - -Signature: - -```typescript -export declare enum Modality -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| AUDIO | "AUDIO" | Audio. | -| DOCUMENT | "DOCUMENT" | Document (for example, PDF). | -| IMAGE | "IMAGE" | Image. | -| MODALITY\_UNSPECIFIED | "MODALITY_UNSPECIFIED" | Unspecified modality. | -| TEXT | "TEXT" | Plain text. | -| VIDEO | "VIDEO" | Video. | - -## SchemaType - -Contains the list of OpenAPI data types as defined by the [OpenAPI specification](https://swagger.io/docs/specification/data-models/data-types/) - -Signature: - -```typescript -export declare enum SchemaType -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| ARRAY | "array" | Array type. | -| BOOLEAN | "boolean" | Boolean type. | -| INTEGER | "integer" | Integer type. | -| NUMBER | "number" | Number type. | -| OBJECT | "object" | Object type. | -| STRING | "string" | String type. | - diff --git a/docs-devsite/vertexai.safetyrating.md b/docs-devsite/vertexai.safetyrating.md deleted file mode 100644 index ebe5003c662..00000000000 --- a/docs-devsite/vertexai.safetyrating.md +++ /dev/null @@ -1,90 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# SafetyRating interface -A safety rating associated with a [GenerateContentCandidate](./vertexai.generatecontentcandidate.md#generatecontentcandidate_interface) - -Signature: - -```typescript -export interface SafetyRating -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [blocked](./vertexai.safetyrating.md#safetyratingblocked) | boolean | | -| [category](./vertexai.safetyrating.md#safetyratingcategory) | [HarmCategory](./vertexai.md#harmcategory) | | -| [probability](./vertexai.safetyrating.md#safetyratingprobability) | [HarmProbability](./vertexai.md#harmprobability) | | -| [probabilityScore](./vertexai.safetyrating.md#safetyratingprobabilityscore) | number | The probability score of the harm category.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. | -| [severity](./vertexai.safetyrating.md#safetyratingseverity) | [HarmSeverity](./vertexai.md#harmseverity) | The harm severity level.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to HarmSeverity.UNSUPPORTED. | -| [severityScore](./vertexai.safetyrating.md#safetyratingseverityscore) | number | The severity score of the harm category.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. | - -## SafetyRating.blocked - -Signature: - -```typescript -blocked: boolean; -``` - -## SafetyRating.category - -Signature: - -```typescript -category: HarmCategory; -``` - -## SafetyRating.probability - -Signature: - -```typescript -probability: HarmProbability; -``` - -## SafetyRating.probabilityScore - -The probability score of the harm category. - -This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. - -Signature: - -```typescript -probabilityScore: number; -``` - -## SafetyRating.severity - -The harm severity level. - -This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to `HarmSeverity.UNSUPPORTED`. - -Signature: - -```typescript -severity: HarmSeverity; -``` - -## SafetyRating.severityScore - -The severity score of the harm category. - -This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. - -Signature: - -```typescript -severityScore: number; -``` diff --git a/docs-devsite/vertexai.safetysetting.md b/docs-devsite/vertexai.safetysetting.md deleted file mode 100644 index a91843faaa5..00000000000 --- a/docs-devsite/vertexai.safetysetting.md +++ /dev/null @@ -1,55 +0,0 @@ -Project: /docs/reference/js/_project.yaml -Book: /docs/reference/_book.yaml -page_type: reference - -{% comment %} -DO NOT EDIT THIS FILE! -This is generated by the JS SDK team, and any local changes will be -overwritten. Changes should be made in the source code at -https://github.com/firebase/firebase-js-sdk -{% endcomment %} - -# SafetySetting interface -Safety setting that can be sent as part of request parameters. - -Signature: - -```typescript -export interface SafetySetting -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [category](./vertexai.safetysetting.md#safetysettingcategory) | [HarmCategory](./vertexai.md#harmcategory) | | -| [method](./vertexai.safetysetting.md#safetysettingmethod) | [HarmBlockMethod](./vertexai.md#harmblockmethod) | The harm block method.This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), an [AIError](./vertexai.aierror.md#aierror_class) will be thrown if this property is defined. | -| [threshold](./vertexai.safetysetting.md#safetysettingthreshold) | [HarmBlockThreshold](./vertexai.md#harmblockthreshold) | | - -## SafetySetting.category - -Signature: - -```typescript -category: HarmCategory; -``` - -## SafetySetting.method - -The harm block method. - -This property is only supported in the Vertex AI Gemini API ([VertexAIBackend](./vertexai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./vertexai.googleaibackend.md#googleaibackend_class)), an [AIError](./vertexai.aierror.md#aierror_class) will be thrown if this property is defined. - -Signature: - -```typescript -method?: HarmBlockMethod; -``` - -## SafetySetting.threshold - -Signature: - -```typescript -threshold: HarmBlockThreshold; -``` diff --git a/packages/vertexai/.eslintrc.js b/packages/ai/.eslintrc.js similarity index 100% rename from packages/vertexai/.eslintrc.js rename to packages/ai/.eslintrc.js diff --git a/packages/vertexai/CHANGELOG.md b/packages/ai/CHANGELOG.md similarity index 99% rename from packages/vertexai/CHANGELOG.md rename to packages/ai/CHANGELOG.md index 8fa9e9d4b4a..a49b5dacbd4 100644 --- a/packages/vertexai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -1,4 +1,4 @@ -# @firebase/vertexai +# @firebase/ai ## 1.2.2 diff --git a/packages/vertexai/README.md b/packages/ai/README.md similarity index 65% rename from packages/vertexai/README.md rename to packages/ai/README.md index b559a1e739e..94c95e50b25 100644 --- a/packages/vertexai/README.md +++ b/packages/ai/README.md @@ -1,5 +1,5 @@ -# @firebase/vertexai +# @firebase/ai -This is the Firebase Vertex AI component of the Firebase JS SDK. +This is the Firebase AI component of the Firebase JS SDK. **This package is not intended for direct usage, and should only be used via the officially supported [firebase](https://www.npmjs.com/package/firebase) package.** diff --git a/packages/vertexai/api-extractor.json b/packages/ai/api-extractor.json similarity index 100% rename from packages/vertexai/api-extractor.json rename to packages/ai/api-extractor.json diff --git a/packages/vertexai/karma.conf.js b/packages/ai/karma.conf.js similarity index 100% rename from packages/vertexai/karma.conf.js rename to packages/ai/karma.conf.js diff --git a/packages/vertexai/package.json b/packages/ai/package.json similarity index 89% rename from packages/vertexai/package.json rename to packages/ai/package.json index 0c6b24fc495..d159793b206 100644 --- a/packages/vertexai/package.json +++ b/packages/ai/package.json @@ -1,7 +1,7 @@ { - "name": "@firebase/vertexai", + "name": "@firebase/ai", "version": "1.2.2", - "description": "A Firebase SDK for VertexAI", + "description": "The Firebase AI SDK", "author": "Firebase (https://firebase.google.com/)", "engines": { "node": ">=18.0.0" @@ -11,7 +11,7 @@ "module": "dist/esm/index.esm2017.js", "exports": { ".": { - "types": "./dist/vertexai-public.d.ts", + "types": "./dist/ai-public.d.ts", "node": { "require": "./dist/index.node.cjs.js", "import": "./dist/index.node.mjs" @@ -31,7 +31,7 @@ "lint": "eslint -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'", "lint:fix": "eslint --fix -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'", "build": "rollup -c && yarn api-report", - "build:deps": "lerna run --scope @firebase/vertexai --include-dependencies build", + "build:deps": "lerna run --scope @firebase/ai --include-dependencies build", "dev": "rollup -c -w", "update-responses": "../../scripts/update_vertexai_responses.sh", "testsetup": "yarn update-responses && yarn ts-node ./test-utils/convert-mocks.ts", @@ -40,7 +40,7 @@ "test:skip-clone": "karma start", "test:browser": "yarn testsetup && karma start", "api-report": "api-extractor run --local --verbose", - "typings:public": "node ../../scripts/build/use_typings.js ./dist/vertexai-public.d.ts", + "typings:public": "node ../../scripts/build/use_typings.js ./dist/ai-public.d.ts", "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "peerDependencies": { @@ -64,7 +64,7 @@ "typescript": "5.5.4" }, "repository": { - "directory": "packages/vertexai", + "directory": "packages/ai", "type": "git", "url": "git+https://github.com/firebase/firebase-js-sdk.git" }, diff --git a/packages/vertexai/rollup.config.js b/packages/ai/rollup.config.js similarity index 100% rename from packages/vertexai/rollup.config.js rename to packages/ai/rollup.config.js diff --git a/packages/vertexai/src/api.test.ts b/packages/ai/src/api.test.ts similarity index 100% rename from packages/vertexai/src/api.test.ts rename to packages/ai/src/api.test.ts diff --git a/packages/vertexai/src/api.ts b/packages/ai/src/api.ts similarity index 84% rename from packages/vertexai/src/api.ts rename to packages/ai/src/api.ts index 06bd747746a..d2229c067fc 100644 --- a/packages/vertexai/src/api.ts +++ b/packages/ai/src/api.ts @@ -41,18 +41,22 @@ export { Backend, VertexAIBackend, GoogleAIBackend } from './backend'; export { AIErrorCode as VertexAIErrorCode }; /** - * Base class for Firebase AI model APIs. + * @deprecated Use the new {@link AIModel} instead. The Vertex AI in Firebase SDK has been + * replaced with the Firebase AI SDK to accommodate the evolving set of supported features and + * services. For migration details, see the {@link https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk | migration guide}. * - * For more information, refer to the documentation for the new {@link AIModel}. + * Base class for Firebase AI model APIs. * * @public */ export const VertexAIModel = AIModel; /** - * Error class for the Firebase AI SDK. + * @deprecated Use the new {@link AIError} instead. The Vertex AI in Firebase SDK has been + * replaced with the Firebase AI SDK to accommodate the evolving set of supported features and + * services. For migration details, see the {@link https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk | migration guide}. * - * For more information, refer to the documentation for the new {@link AIError}. + * Error class for the Firebase AI SDK. * * @public */ @@ -65,7 +69,9 @@ declare module '@firebase/component' { } /** - * It is recommended to use the new {@link getAI | getAI()}. + * @deprecated Use the new {@link getAI | getAI()} instead. The Vertex AI in Firebase SDK has been + * replaced with the Firebase AI SDK to accommodate the evolving set of supported features and + * services. For migration details, see the {@link https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk | migration guide}. * * Returns a {@link VertexAI} instance for the given app, configured to use the * Vertex AI Gemini API. This instance will be diff --git a/packages/vertexai/src/backend.test.ts b/packages/ai/src/backend.test.ts similarity index 100% rename from packages/vertexai/src/backend.test.ts rename to packages/ai/src/backend.test.ts diff --git a/packages/vertexai/src/backend.ts b/packages/ai/src/backend.ts similarity index 100% rename from packages/vertexai/src/backend.ts rename to packages/ai/src/backend.ts diff --git a/packages/vertexai/src/backwards-compatbility.test.ts b/packages/ai/src/backwards-compatbility.test.ts similarity index 100% rename from packages/vertexai/src/backwards-compatbility.test.ts rename to packages/ai/src/backwards-compatbility.test.ts diff --git a/packages/vertexai/src/constants.ts b/packages/ai/src/constants.ts similarity index 93% rename from packages/vertexai/src/constants.ts rename to packages/ai/src/constants.ts index 6339ce63017..cb54567735a 100644 --- a/packages/vertexai/src/constants.ts +++ b/packages/ai/src/constants.ts @@ -17,9 +17,6 @@ import { version } from '../package.json'; -// TODO (v12): Remove this -export const VERTEX_TYPE = 'vertexAI'; - export const AI_TYPE = 'AI'; export const DEFAULT_LOCATION = 'us-central1'; diff --git a/packages/vertexai/src/errors.ts b/packages/ai/src/errors.ts similarity index 100% rename from packages/vertexai/src/errors.ts rename to packages/ai/src/errors.ts diff --git a/packages/vertexai/src/googleai-mappers.test.ts b/packages/ai/src/googleai-mappers.test.ts similarity index 100% rename from packages/vertexai/src/googleai-mappers.test.ts rename to packages/ai/src/googleai-mappers.test.ts diff --git a/packages/vertexai/src/googleai-mappers.ts b/packages/ai/src/googleai-mappers.ts similarity index 100% rename from packages/vertexai/src/googleai-mappers.ts rename to packages/ai/src/googleai-mappers.ts diff --git a/packages/vertexai/src/helpers.test.ts b/packages/ai/src/helpers.test.ts similarity index 100% rename from packages/vertexai/src/helpers.test.ts rename to packages/ai/src/helpers.test.ts diff --git a/packages/vertexai/src/helpers.ts b/packages/ai/src/helpers.ts similarity index 100% rename from packages/vertexai/src/helpers.ts rename to packages/ai/src/helpers.ts diff --git a/packages/vertexai/src/index.node.ts b/packages/ai/src/index.node.ts similarity index 100% rename from packages/vertexai/src/index.node.ts rename to packages/ai/src/index.node.ts diff --git a/packages/vertexai/src/index.ts b/packages/ai/src/index.ts similarity index 100% rename from packages/vertexai/src/index.ts rename to packages/ai/src/index.ts diff --git a/packages/vertexai/src/logger.ts b/packages/ai/src/logger.ts similarity index 100% rename from packages/vertexai/src/logger.ts rename to packages/ai/src/logger.ts diff --git a/packages/vertexai/src/methods/chat-session-helpers.test.ts b/packages/ai/src/methods/chat-session-helpers.test.ts similarity index 100% rename from packages/vertexai/src/methods/chat-session-helpers.test.ts rename to packages/ai/src/methods/chat-session-helpers.test.ts diff --git a/packages/vertexai/src/methods/chat-session-helpers.ts b/packages/ai/src/methods/chat-session-helpers.ts similarity index 100% rename from packages/vertexai/src/methods/chat-session-helpers.ts rename to packages/ai/src/methods/chat-session-helpers.ts diff --git a/packages/vertexai/src/methods/chat-session.test.ts b/packages/ai/src/methods/chat-session.test.ts similarity index 100% rename from packages/vertexai/src/methods/chat-session.test.ts rename to packages/ai/src/methods/chat-session.test.ts diff --git a/packages/vertexai/src/methods/chat-session.ts b/packages/ai/src/methods/chat-session.ts similarity index 100% rename from packages/vertexai/src/methods/chat-session.ts rename to packages/ai/src/methods/chat-session.ts diff --git a/packages/vertexai/src/methods/count-tokens.test.ts b/packages/ai/src/methods/count-tokens.test.ts similarity index 100% rename from packages/vertexai/src/methods/count-tokens.test.ts rename to packages/ai/src/methods/count-tokens.test.ts diff --git a/packages/vertexai/src/methods/count-tokens.ts b/packages/ai/src/methods/count-tokens.ts similarity index 100% rename from packages/vertexai/src/methods/count-tokens.ts rename to packages/ai/src/methods/count-tokens.ts diff --git a/packages/vertexai/src/methods/generate-content.test.ts b/packages/ai/src/methods/generate-content.test.ts similarity index 100% rename from packages/vertexai/src/methods/generate-content.test.ts rename to packages/ai/src/methods/generate-content.test.ts diff --git a/packages/vertexai/src/methods/generate-content.ts b/packages/ai/src/methods/generate-content.ts similarity index 100% rename from packages/vertexai/src/methods/generate-content.ts rename to packages/ai/src/methods/generate-content.ts diff --git a/packages/vertexai/src/models/ai-model.test.ts b/packages/ai/src/models/ai-model.test.ts similarity index 100% rename from packages/vertexai/src/models/ai-model.test.ts rename to packages/ai/src/models/ai-model.test.ts diff --git a/packages/vertexai/src/models/ai-model.ts b/packages/ai/src/models/ai-model.ts similarity index 100% rename from packages/vertexai/src/models/ai-model.ts rename to packages/ai/src/models/ai-model.ts diff --git a/packages/vertexai/src/models/generative-model.test.ts b/packages/ai/src/models/generative-model.test.ts similarity index 100% rename from packages/vertexai/src/models/generative-model.test.ts rename to packages/ai/src/models/generative-model.test.ts diff --git a/packages/vertexai/src/models/generative-model.ts b/packages/ai/src/models/generative-model.ts similarity index 100% rename from packages/vertexai/src/models/generative-model.ts rename to packages/ai/src/models/generative-model.ts diff --git a/packages/vertexai/src/models/imagen-model.test.ts b/packages/ai/src/models/imagen-model.test.ts similarity index 100% rename from packages/vertexai/src/models/imagen-model.test.ts rename to packages/ai/src/models/imagen-model.test.ts diff --git a/packages/vertexai/src/models/imagen-model.ts b/packages/ai/src/models/imagen-model.ts similarity index 100% rename from packages/vertexai/src/models/imagen-model.ts rename to packages/ai/src/models/imagen-model.ts diff --git a/packages/vertexai/src/models/index.ts b/packages/ai/src/models/index.ts similarity index 100% rename from packages/vertexai/src/models/index.ts rename to packages/ai/src/models/index.ts diff --git a/packages/vertexai/src/public-types.ts b/packages/ai/src/public-types.ts similarity index 90% rename from packages/vertexai/src/public-types.ts rename to packages/ai/src/public-types.ts index b12615a81ed..7a5b7dc3997 100644 --- a/packages/vertexai/src/public-types.ts +++ b/packages/ai/src/public-types.ts @@ -21,9 +21,11 @@ import { Backend } from './backend'; export * from './types'; /** - * An instance of the Firebase AI SDK. + * @deprecated Use the new {@link AI | AI} instead. The Vertex AI in Firebase SDK has been + * replaced with the Firebase AI SDK to accommodate the evolving set of supported features and + * services. For migration details, see the {@link https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk | migration guide}. * - * For more information, refer to the documentation for the new {@link AI} interface. + * An instance of the Firebase AI SDK. * * @public */ @@ -57,9 +59,9 @@ export interface AI { */ backend: Backend; /** - * The location configured for this AI service instance, relevant for Vertex AI backends. - * * @deprecated use `AI.backend.location` instead. + * + * The location configured for this AI service instance, relevant for Vertex AI backends. */ location: string; } diff --git a/packages/vertexai/src/requests/imagen-image-format.ts b/packages/ai/src/requests/imagen-image-format.ts similarity index 100% rename from packages/vertexai/src/requests/imagen-image-format.ts rename to packages/ai/src/requests/imagen-image-format.ts diff --git a/packages/vertexai/src/requests/request-helpers.test.ts b/packages/ai/src/requests/request-helpers.test.ts similarity index 100% rename from packages/vertexai/src/requests/request-helpers.test.ts rename to packages/ai/src/requests/request-helpers.test.ts diff --git a/packages/vertexai/src/requests/request-helpers.ts b/packages/ai/src/requests/request-helpers.ts similarity index 100% rename from packages/vertexai/src/requests/request-helpers.ts rename to packages/ai/src/requests/request-helpers.ts diff --git a/packages/vertexai/src/requests/request.test.ts b/packages/ai/src/requests/request.test.ts similarity index 100% rename from packages/vertexai/src/requests/request.test.ts rename to packages/ai/src/requests/request.test.ts diff --git a/packages/vertexai/src/requests/request.ts b/packages/ai/src/requests/request.ts similarity index 100% rename from packages/vertexai/src/requests/request.ts rename to packages/ai/src/requests/request.ts diff --git a/packages/vertexai/src/requests/response-helpers.test.ts b/packages/ai/src/requests/response-helpers.test.ts similarity index 100% rename from packages/vertexai/src/requests/response-helpers.test.ts rename to packages/ai/src/requests/response-helpers.test.ts diff --git a/packages/vertexai/src/requests/response-helpers.ts b/packages/ai/src/requests/response-helpers.ts similarity index 100% rename from packages/vertexai/src/requests/response-helpers.ts rename to packages/ai/src/requests/response-helpers.ts diff --git a/packages/vertexai/src/requests/schema-builder.test.ts b/packages/ai/src/requests/schema-builder.test.ts similarity index 100% rename from packages/vertexai/src/requests/schema-builder.test.ts rename to packages/ai/src/requests/schema-builder.test.ts diff --git a/packages/vertexai/src/requests/schema-builder.ts b/packages/ai/src/requests/schema-builder.ts similarity index 100% rename from packages/vertexai/src/requests/schema-builder.ts rename to packages/ai/src/requests/schema-builder.ts diff --git a/packages/vertexai/src/requests/stream-reader.test.ts b/packages/ai/src/requests/stream-reader.test.ts similarity index 100% rename from packages/vertexai/src/requests/stream-reader.test.ts rename to packages/ai/src/requests/stream-reader.test.ts diff --git a/packages/vertexai/src/requests/stream-reader.ts b/packages/ai/src/requests/stream-reader.ts similarity index 100% rename from packages/vertexai/src/requests/stream-reader.ts rename to packages/ai/src/requests/stream-reader.ts diff --git a/packages/vertexai/src/service.test.ts b/packages/ai/src/service.test.ts similarity index 100% rename from packages/vertexai/src/service.test.ts rename to packages/ai/src/service.test.ts diff --git a/packages/vertexai/src/service.ts b/packages/ai/src/service.ts similarity index 100% rename from packages/vertexai/src/service.ts rename to packages/ai/src/service.ts diff --git a/packages/vertexai/src/types/content.ts b/packages/ai/src/types/content.ts similarity index 100% rename from packages/vertexai/src/types/content.ts rename to packages/ai/src/types/content.ts diff --git a/packages/vertexai/src/types/enums.ts b/packages/ai/src/types/enums.ts similarity index 100% rename from packages/vertexai/src/types/enums.ts rename to packages/ai/src/types/enums.ts diff --git a/packages/vertexai/src/types/error.ts b/packages/ai/src/types/error.ts similarity index 100% rename from packages/vertexai/src/types/error.ts rename to packages/ai/src/types/error.ts diff --git a/packages/vertexai/src/types/googleai.ts b/packages/ai/src/types/googleai.ts similarity index 100% rename from packages/vertexai/src/types/googleai.ts rename to packages/ai/src/types/googleai.ts diff --git a/packages/vertexai/src/types/imagen/index.ts b/packages/ai/src/types/imagen/index.ts similarity index 100% rename from packages/vertexai/src/types/imagen/index.ts rename to packages/ai/src/types/imagen/index.ts diff --git a/packages/vertexai/src/types/imagen/internal.ts b/packages/ai/src/types/imagen/internal.ts similarity index 100% rename from packages/vertexai/src/types/imagen/internal.ts rename to packages/ai/src/types/imagen/internal.ts diff --git a/packages/vertexai/src/types/imagen/requests.ts b/packages/ai/src/types/imagen/requests.ts similarity index 100% rename from packages/vertexai/src/types/imagen/requests.ts rename to packages/ai/src/types/imagen/requests.ts diff --git a/packages/vertexai/src/types/imagen/responses.ts b/packages/ai/src/types/imagen/responses.ts similarity index 100% rename from packages/vertexai/src/types/imagen/responses.ts rename to packages/ai/src/types/imagen/responses.ts diff --git a/packages/vertexai/src/types/index.ts b/packages/ai/src/types/index.ts similarity index 100% rename from packages/vertexai/src/types/index.ts rename to packages/ai/src/types/index.ts diff --git a/packages/vertexai/src/types/internal.ts b/packages/ai/src/types/internal.ts similarity index 100% rename from packages/vertexai/src/types/internal.ts rename to packages/ai/src/types/internal.ts diff --git a/packages/vertexai/src/types/requests.ts b/packages/ai/src/types/requests.ts similarity index 100% rename from packages/vertexai/src/types/requests.ts rename to packages/ai/src/types/requests.ts diff --git a/packages/vertexai/src/types/responses.ts b/packages/ai/src/types/responses.ts similarity index 100% rename from packages/vertexai/src/types/responses.ts rename to packages/ai/src/types/responses.ts diff --git a/packages/vertexai/src/types/schema.ts b/packages/ai/src/types/schema.ts similarity index 100% rename from packages/vertexai/src/types/schema.ts rename to packages/ai/src/types/schema.ts diff --git a/packages/vertexai/test-utils/base64cat.ts b/packages/ai/test-utils/base64cat.ts similarity index 100% rename from packages/vertexai/test-utils/base64cat.ts rename to packages/ai/test-utils/base64cat.ts diff --git a/packages/vertexai/test-utils/cat.jpeg b/packages/ai/test-utils/cat.jpeg similarity index 100% rename from packages/vertexai/test-utils/cat.jpeg rename to packages/ai/test-utils/cat.jpeg diff --git a/packages/vertexai/test-utils/cat.png b/packages/ai/test-utils/cat.png similarity index 100% rename from packages/vertexai/test-utils/cat.png rename to packages/ai/test-utils/cat.png diff --git a/packages/vertexai/test-utils/convert-mocks.ts b/packages/ai/test-utils/convert-mocks.ts similarity index 97% rename from packages/vertexai/test-utils/convert-mocks.ts rename to packages/ai/test-utils/convert-mocks.ts index 8690af4ac72..4bac70d1d10 100644 --- a/packages/vertexai/test-utils/convert-mocks.ts +++ b/packages/ai/test-utils/convert-mocks.ts @@ -47,7 +47,7 @@ function generateMockLookupFile(): void { const fileText = ` /** - * DO NOT EDIT - This file was generated by the packages/vertexai/test-utils/convert-mocks.ts script. + * DO NOT EDIT - This file was generated by the packages/ai/test-utils/convert-mocks.ts script. * * These objects map mock response filenames to their JSON contents. * diff --git a/packages/vertexai/test-utils/mock-response.ts b/packages/ai/test-utils/mock-response.ts similarity index 100% rename from packages/vertexai/test-utils/mock-response.ts rename to packages/ai/test-utils/mock-response.ts diff --git a/packages/vertexai/tsconfig.json b/packages/ai/tsconfig.json similarity index 100% rename from packages/vertexai/tsconfig.json rename to packages/ai/tsconfig.json diff --git a/packages/app/src/constants.ts b/packages/app/src/constants.ts index 8ef4eada39c..21ded00529a 100644 --- a/packages/app/src/constants.ts +++ b/packages/app/src/constants.ts @@ -39,7 +39,7 @@ import { name as remoteConfigCompatName } from '../../../packages/remote-config- import { name as storageName } from '../../../packages/storage/package.json'; import { name as storageCompatName } from '../../../packages/storage-compat/package.json'; import { name as firestoreName } from '../../../packages/firestore/package.json'; -import { name as vertexName } from '../../../packages/vertexai/package.json'; +import { name as aiName } from '../../../packages/ai/package.json'; import { name as firestoreCompatName } from '../../../packages/firestore-compat/package.json'; import { name as packageName } from '../../../packages/firebase/package.json'; @@ -76,7 +76,7 @@ export const PLATFORM_LOG_STRING = { [storageCompatName]: 'fire-gcs-compat', [firestoreName]: 'fire-fst', [firestoreCompatName]: 'fire-fst-compat', - [vertexName]: 'fire-vertex', + [aiName]: 'fire-vertex', 'fire-js': 'fire-js', // Platform identifier for JS SDK. [packageName]: 'fire-js-all' } as const; diff --git a/packages/firebase/ai/index.ts b/packages/firebase/ai/index.ts index 2645fd3004f..ea092d0f6e9 100644 --- a/packages/firebase/ai/index.ts +++ b/packages/firebase/ai/index.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,4 +15,4 @@ * limitations under the License. */ -export * from '@firebase/vertexai'; +export * from '@firebase/ai'; diff --git a/packages/firebase/ai/package.json b/packages/firebase/ai/package.json index 75405002478..932f0f8d2a1 100644 --- a/packages/firebase/ai/package.json +++ b/packages/firebase/ai/package.json @@ -3,5 +3,5 @@ "main": "dist/index.cjs.js", "browser": "dist/esm/index.esm.js", "module": "dist/esm/index.esm.js", - "typings": "dist/vertexai/index.d.ts" -} \ No newline at end of file + "typings": "dist/ai/index.d.ts" +} diff --git a/packages/firebase/package.json b/packages/firebase/package.json index c61bc701426..caaba0fd528 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -228,28 +228,28 @@ "default": "./storage/dist/esm/index.esm.js" }, "./ai": { - "types": "./vertexai/dist/vertexai/index.d.ts", + "types": "./ai/dist/ai/index.d.ts", "node": { - "require": "./vertexai/dist/index.cjs.js", - "import": "./vertexai/dist/index.mjs" + "require": "./ai/dist/index.cjs.js", + "import": "./ai/dist/index.mjs" }, "browser": { - "require": "./vertexai/dist/index.cjs.js", - "import": "./vertexai/dist/esm/index.esm.js" + "require": "./ai/dist/index.cjs.js", + "import": "./ai/dist/esm/index.esm.js" }, - "default": "./vertexai/dist/esm/index.esm.js" + "default": "./ai/dist/esm/index.esm.js" }, "./vertexai": { - "types": "./vertexai/dist/vertexai/index.d.ts", + "types": "./ai/dist/ai/index.d.ts", "node": { - "require": "./vertexai/dist/index.cjs.js", - "import": "./vertexai/dist/index.mjs" + "require": "./ai/dist/index.cjs.js", + "import": "./ai/dist/index.mjs" }, "browser": { - "require": "./vertexai/dist/index.cjs.js", - "import": "./vertexai/dist/esm/index.esm.js" + "require": "./ai/dist/index.cjs.js", + "import": "./ai/dist/esm/index.esm.js" }, - "default": "./vertexai/dist/esm/index.esm.js" + "default": "./ai/dist/esm/index.esm.js" }, "./compat/analytics": { "types": "./compat/analytics/dist/compat/analytics/index.d.ts", @@ -411,6 +411,7 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { + "@firebase/ai": "1.2.2", "@firebase/app": "0.12.1", "@firebase/app-compat": "0.3.1", "@firebase/app-types": "0.9.3", @@ -437,8 +438,7 @@ "@firebase/analytics-compat": "0.2.19", "@firebase/app-check": "0.9.1", "@firebase/app-check-compat": "0.3.22", - "@firebase/util": "1.11.1", - "@firebase/vertexai": "1.2.2" + "@firebase/util": "1.11.1" }, "devDependencies": { "rollup": "2.79.2", diff --git a/packages/firebase/vertexai/index.ts b/packages/firebase/vertexai/index.ts index 2645fd3004f..530f99162ed 100644 --- a/packages/firebase/vertexai/index.ts +++ b/packages/firebase/vertexai/index.ts @@ -15,4 +15,4 @@ * limitations under the License. */ -export * from '@firebase/vertexai'; +export * from '@firebase/ai'; diff --git a/packages/firebase/vertexai/package.json b/packages/firebase/vertexai/package.json index 20e04a3bbb5..3da541949dc 100644 --- a/packages/firebase/vertexai/package.json +++ b/packages/firebase/vertexai/package.json @@ -3,5 +3,5 @@ "main": "dist/index.cjs.js", "browser": "dist/esm/index.esm.js", "module": "dist/esm/index.esm.js", - "typings": "dist/vertexai/index.d.ts" -} \ No newline at end of file + "typings": "dist/ai/index.d.ts" +} diff --git a/scripts/docgen/docgen.ts b/scripts/docgen/docgen.ts index 113e4a6b339..811570decd1 100644 --- a/scripts/docgen/docgen.ts +++ b/scripts/docgen/docgen.ts @@ -60,7 +60,7 @@ const PREFERRED_PARAMS = [ 'performance', 'remoteConfig', 'storage', - 'vertexAI' + 'ai' ]; let authApiReportOriginal: string; diff --git a/scripts/update_vertexai_responses.sh b/scripts/update_vertexai_responses.sh index d80959febce..680a71ff776 100755 --- a/scripts/update_vertexai_responses.sh +++ b/scripts/update_vertexai_responses.sh @@ -21,7 +21,7 @@ RESPONSES_VERSION='v11.*' # The major version of mock responses to use REPO_NAME="vertexai-sdk-test-data" REPO_LINK="https://github.com/FirebaseExtended/$REPO_NAME.git" -cd "$(dirname "$0")/../packages/vertexai/test-utils" || exit +cd "$(dirname "$0")/../packages/ai/test-utils" || exit rm -rf "$REPO_NAME" git clone "$REPO_LINK" --quiet || exit cd "$REPO_NAME" || exit From 8593fa05bd884c2f1f6f3b4ae062efa48af93d24 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 13 May 2025 13:45:07 -0700 Subject: [PATCH 37/77] Add Emulator Overlay (#8977) (#9031) Co-authored-by: Maneesh Tewani --- .changeset/three-singers-wonder.md | 10 ++ common/api-review/util.api.md | 3 + packages/auth/src/core/auth/emulator.ts | 16 +- packages/auth/src/platform_browser/index.ts | 8 +- packages/database/src/api/Database.ts | 8 +- packages/firestore/src/lite-api/database.ts | 11 +- packages/functions/src/api.ts | 4 +- packages/functions/src/service.ts | 7 +- packages/storage/src/api.ts | 4 +- packages/storage/src/service.ts | 4 +- packages/util/src/emulator.ts | 178 ++++++++++++++++++++ 11 files changed, 238 insertions(+), 15 deletions(-) create mode 100644 .changeset/three-singers-wonder.md diff --git a/.changeset/three-singers-wonder.md b/.changeset/three-singers-wonder.md new file mode 100644 index 00000000000..72dea9d7aa5 --- /dev/null +++ b/.changeset/three-singers-wonder.md @@ -0,0 +1,10 @@ +--- +"@firebase/auth": patch +"@firebase/firestore": patch +"@firebase/util": patch +"@firebase/database": patch +"@firebase/storage": patch +"@firebase/functions": patch +--- + +Add Emulator Overlay diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index 0f8fc13cd3a..98f8657fd5d 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -482,6 +482,9 @@ export interface Subscribe { // @public (undocumented) export type Unsubscribe = () => void; +// @public +export function updateEmulatorBanner(name: string, isRunningEmulator: boolean): void; + // Warning: (ae-missing-release-tag) "validateArgCount" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public diff --git a/packages/auth/src/core/auth/emulator.ts b/packages/auth/src/core/auth/emulator.ts index 8547f7bad6c..42fbda3f095 100644 --- a/packages/auth/src/core/auth/emulator.ts +++ b/packages/auth/src/core/auth/emulator.ts @@ -18,7 +18,12 @@ import { Auth } from '../../model/public_types'; import { AuthErrorCode } from '../errors'; import { _assert } from '../util/assert'; import { _castAuth } from './auth_impl'; -import { deepEqual, isCloudWorkstation, pingServer } from '@firebase/util'; +import { + deepEqual, + isCloudWorkstation, + pingServer, + updateEmulatorBanner +} from '@firebase/util'; /** * Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production @@ -97,13 +102,12 @@ export function connectAuthEmulator( authInternal.emulatorConfig = emulatorConfig; authInternal.settings.appVerificationDisabledForTesting = true; - if (!disableWarnings) { - emitEmulatorWarning(); - } - - // Workaround to get cookies in Firebase Studio if (isCloudWorkstation(host)) { + updateEmulatorBanner('Auth', true); + // Workaround to get cookies in Firebase Studio void pingServer(`${protocol}//${host}${portStr}`); + } else if (!disableWarnings) { + emitEmulatorWarning(); } } diff --git a/packages/auth/src/platform_browser/index.ts b/packages/auth/src/platform_browser/index.ts index f94525bfeb7..99ab834cbdb 100644 --- a/packages/auth/src/platform_browser/index.ts +++ b/packages/auth/src/platform_browser/index.ts @@ -30,7 +30,11 @@ import { browserSessionPersistence } from './persistence/session_storage'; import { indexedDBLocalPersistence } from './persistence/indexed_db'; import { browserPopupRedirectResolver } from './popup_redirect'; import { Auth, User } from '../model/public_types'; -import { getDefaultEmulatorHost, getExperimentalSetting } from '@firebase/util'; +import { + getDefaultEmulatorHost, + getExperimentalSetting, + updateEmulatorBanner +} from '@firebase/util'; import { _setExternalJSProvider } from './load_js'; import { _createError } from '../core/util/assert'; import { AuthErrorCode } from '../core/errors'; @@ -110,6 +114,8 @@ export function getAuth(app: FirebaseApp = getApp()): Auth { const authEmulatorHost = getDefaultEmulatorHost('auth'); if (authEmulatorHost) { connectAuthEmulator(auth, `http://${authEmulatorHost}`); + } else { + updateEmulatorBanner('Auth', false); } return auth; diff --git a/packages/database/src/api/Database.ts b/packages/database/src/api/Database.ts index 515e278b5c5..a94b04518d7 100644 --- a/packages/database/src/api/Database.ts +++ b/packages/database/src/api/Database.ts @@ -31,7 +31,8 @@ import { EmulatorMockTokenOptions, getDefaultEmulatorHostnameAndPort, isCloudWorkstation, - pingServer + pingServer, + updateEmulatorBanner } from '@firebase/util'; import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider'; @@ -257,6 +258,10 @@ export class Database implements _FirebaseService { this.app.options['databaseAuthVariableOverride'] ); this._instanceStarted = true; + updateEmulatorBanner( + 'Database', + this._repo.repoInfo_.emulatorOptions !== null + ); } return this._repoInternal; } @@ -393,6 +398,7 @@ export function connectDatabaseEmulator( // Workaround to get cookies in Firebase Studio if (isCloudWorkstation(host)) { void pingServer(host); + updateEmulatorBanner('Database', true); } // Modify the repo to apply emulator settings diff --git a/packages/firestore/src/lite-api/database.ts b/packages/firestore/src/lite-api/database.ts index 8e7fdb27e90..fce6d5843b7 100644 --- a/packages/firestore/src/lite-api/database.ts +++ b/packages/firestore/src/lite-api/database.ts @@ -28,6 +28,7 @@ import { EmulatorMockTokenOptions, getDefaultEmulatorHostnameAndPort, isCloudWorkstation, + updateEmulatorBanner, pingServer } from '@firebase/util'; @@ -142,6 +143,7 @@ export class Firestore implements FirestoreService { _freezeSettings(): FirestoreSettingsImpl { this._settingsFrozen = true; + updateEmulatorBanner('Firestore', this._settings.isUsingEmulator); return this._settings; } @@ -334,9 +336,7 @@ export function connectFirestoreEmulator( emulatorOptions: firestore._getEmulatorOptions() }; const newHostSetting = `${host}:${port}`; - if (useSsl) { - void pingServer(`https://${newHostSetting}`); - } + if (settings.host !== DEFAULT_HOST && settings.host !== newHostSetting) { logWarn( 'Host has been set in both settings() and connectFirestoreEmulator(), emulator host ' + @@ -357,6 +357,11 @@ export function connectFirestoreEmulator( firestore._setSettings(newConfig); + if (useSsl) { + void pingServer(`https://${newHostSetting}`); + updateEmulatorBanner('Firestore', true); + } + if (options.mockUserToken) { let token: string; let user: User; diff --git a/packages/functions/src/api.ts b/packages/functions/src/api.ts index 7f92cba8343..cb987035145 100644 --- a/packages/functions/src/api.ts +++ b/packages/functions/src/api.ts @@ -29,7 +29,8 @@ import { } from './service'; import { getModularInstance, - getDefaultEmulatorHostnameAndPort + getDefaultEmulatorHostnameAndPort, + updateEmulatorBanner } from '@firebase/util'; export { FunctionsError } from './error'; @@ -47,6 +48,7 @@ export function getFunctions( app: FirebaseApp = getApp(), regionOrCustomDomain: string = DEFAULT_REGION ): Functions { + updateEmulatorBanner('Functions', false); // Dependencies const functionsProvider: Provider<'functions'> = _getProvider( getModularInstance(app), diff --git a/packages/functions/src/service.ts b/packages/functions/src/service.ts index af9d8898d2e..57504a4c7a4 100644 --- a/packages/functions/src/service.ts +++ b/packages/functions/src/service.ts @@ -30,7 +30,11 @@ import { Provider } from '@firebase/component'; import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; import { MessagingInternalComponentName } from '@firebase/messaging-interop-types'; import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types'; -import { isCloudWorkstation, pingServer } from '@firebase/util'; +import { + isCloudWorkstation, + pingServer, + updateEmulatorBanner +} from '@firebase/util'; export const DEFAULT_REGION = 'us-central1'; @@ -182,6 +186,7 @@ export function connectFunctionsEmulator( // Workaround to get cookies in Firebase Studio if (useSsl) { void pingServer(functionsInstance.emulatorOrigin); + updateEmulatorBanner('Functions', true); } } diff --git a/packages/storage/src/api.ts b/packages/storage/src/api.ts index 84c77ea0c8c..b164a1324c3 100644 --- a/packages/storage/src/api.ts +++ b/packages/storage/src/api.ts @@ -53,7 +53,8 @@ import { STORAGE_TYPE } from './constants'; import { EmulatorMockTokenOptions, getModularInstance, - getDefaultEmulatorHostnameAndPort + getDefaultEmulatorHostnameAndPort, + updateEmulatorBanner } from '@firebase/util'; import { StringFormat } from './implementation/string'; @@ -332,6 +333,7 @@ export function getStorage( bucketUrl?: string ): FirebaseStorage { app = getModularInstance(app); + updateEmulatorBanner('Storage', false); const storageProvider: Provider<'storage'> = _getProvider(app, STORAGE_TYPE); const storageInstance = storageProvider.getImmediate({ identifier: bucketUrl diff --git a/packages/storage/src/service.ts b/packages/storage/src/service.ts index 741dd6eaa1a..97d1407bb52 100644 --- a/packages/storage/src/service.ts +++ b/packages/storage/src/service.ts @@ -46,7 +46,8 @@ import { createMockUserToken, EmulatorMockTokenOptions, isCloudWorkstation, - pingServer + pingServer, + updateEmulatorBanner } from '@firebase/util'; import { Connection, ConnectionType } from './implementation/connection'; @@ -150,6 +151,7 @@ export function connectStorageEmulator( // Workaround to get cookies in Firebase Studio if (useSsl) { void pingServer(`https://${storage.host}`); + updateEmulatorBanner('Storage', true); } storage._isUsingEmulator = true; storage._protocol = useSsl ? 'https' : 'http'; diff --git a/packages/util/src/emulator.ts b/packages/util/src/emulator.ts index 2850b5be378..ff09940d88f 100644 --- a/packages/util/src/emulator.ts +++ b/packages/util/src/emulator.ts @@ -16,6 +16,7 @@ */ import { base64urlEncodeWithoutPadding } from './crypt'; +import { isCloudWorkstation } from './url'; // Firebase Auth tokens contain snake_case claims following the JWT standard / convention. /* eslint-disable camelcase */ @@ -140,3 +141,180 @@ export function createMockUserToken( signature ].join('.'); } + +interface EmulatorStatusMap { + [name: string]: boolean; +} +const emulatorStatus: EmulatorStatusMap = {}; + +interface EmulatorSummary { + prod: string[]; + emulator: string[]; +} + +// Checks whether any products are running on an emulator +function getEmulatorSummary(): EmulatorSummary { + const summary: EmulatorSummary = { + prod: [], + emulator: [] + }; + for (const key of Object.keys(emulatorStatus)) { + if (emulatorStatus[key]) { + summary.emulator.push(key); + } else { + summary.prod.push(key); + } + } + return summary; +} + +function getOrCreateEl(id: string): { created: boolean; element: HTMLElement } { + let parentDiv = document.getElementById(id); + let created = false; + if (!parentDiv) { + parentDiv = document.createElement('div'); + parentDiv.setAttribute('id', id); + created = true; + } + return { created, element: parentDiv }; +} + +let previouslyDismissed = false; +/** + * Updates Emulator Banner. Primarily used for Firebase Studio + * @param name + * @param isRunningEmulator + * @public + */ +export function updateEmulatorBanner( + name: string, + isRunningEmulator: boolean +): void { + if ( + typeof window === 'undefined' || + typeof document === 'undefined' || + !isCloudWorkstation(window.location.host) || + emulatorStatus[name] === isRunningEmulator || + emulatorStatus[name] || // If already set to use emulator, can't go back to prod. + previouslyDismissed + ) { + return; + } + + emulatorStatus[name] = isRunningEmulator; + + function prefixedId(id: string): string { + return `__firebase__banner__${id}`; + } + const bannerId = '__firebase__banner'; + const summary = getEmulatorSummary(); + const showError = summary.prod.length > 0; + + function tearDown(): void { + const element = document.getElementById(bannerId); + if (element) { + element.remove(); + } + } + + function setupBannerStyles(bannerEl: HTMLElement): void { + bannerEl.style.display = 'flex'; + bannerEl.style.background = '#7faaf0'; + bannerEl.style.position = 'absolute'; + bannerEl.style.bottom = '5px'; + bannerEl.style.left = '5px'; + bannerEl.style.padding = '.5em'; + bannerEl.style.borderRadius = '5px'; + bannerEl.style.alignItems = 'center'; + } + + function setupIconStyles(prependIcon: SVGElement, iconId: string): void { + prependIcon.setAttribute('width', '24'); + prependIcon.setAttribute('id', iconId); + prependIcon.setAttribute('height', '24'); + prependIcon.setAttribute('viewBox', '0 0 24 24'); + prependIcon.setAttribute('fill', 'none'); + prependIcon.style.marginLeft = '-6px'; + } + + function setupCloseBtn(): HTMLSpanElement { + const closeBtn = document.createElement('span'); + closeBtn.style.cursor = 'pointer'; + closeBtn.style.marginLeft = '16px'; + closeBtn.style.fontSize = '24px'; + closeBtn.innerHTML = ' ×'; + closeBtn.onclick = () => { + previouslyDismissed = true; + tearDown(); + }; + return closeBtn; + } + + function setupLinkStyles( + learnMoreLink: HTMLAnchorElement, + learnMoreId: string + ): void { + learnMoreLink.setAttribute('id', learnMoreId); + learnMoreLink.innerText = 'Learn more'; + learnMoreLink.href = + 'https://firebase.google.com/docs/studio/preview-apps#preview-backend'; + learnMoreLink.setAttribute('target', '__blank'); + learnMoreLink.style.paddingLeft = '5px'; + learnMoreLink.style.textDecoration = 'underline'; + } + + function setupDom(): void { + const banner = getOrCreateEl(bannerId); + const firebaseTextId = prefixedId('text'); + const firebaseText: HTMLSpanElement = + document.getElementById(firebaseTextId) || document.createElement('span'); + const learnMoreId = prefixedId('learnmore'); + const learnMoreLink: HTMLAnchorElement = + (document.getElementById(learnMoreId) as HTMLAnchorElement) || + document.createElement('a'); + const prependIconId = prefixedId('preprendIcon'); + const prependIcon: SVGElement = + (document.getElementById( + prependIconId + ) as HTMLOrSVGElement as SVGElement) || + document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + if (banner.created) { + // update styles + const bannerEl = banner.element; + setupBannerStyles(bannerEl); + setupLinkStyles(learnMoreLink, learnMoreId); + const closeBtn = setupCloseBtn(); + setupIconStyles(prependIcon, prependIconId); + bannerEl.append(prependIcon, firebaseText, learnMoreLink, closeBtn); + document.body.appendChild(bannerEl); + } + + if (showError) { + firebaseText.innerText = `Preview backend disconnected.`; + prependIcon.innerHTML = ` + + + + + + +`; + } else { + prependIcon.innerHTML = ` + + + + + + +`; + firebaseText.innerText = 'Preview backend running in this workspace.'; + } + firebaseText.setAttribute('id', firebaseTextId); + } + if (document.readyState === 'loading') { + window.addEventListener('DOMContentLoaded', setupDom); + } else { + setupDom(); + } +} From bf8c3d18abe5d3707b4b623b78fcf4f200cf62fb Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 13 May 2025 13:52:02 -0700 Subject: [PATCH 38/77] add temporary release branch (#9032) --- .github/workflows/release-staging.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-staging.yml b/.github/workflows/release-staging.yml index c4adefb44a8..a23cdeb0941 100644 --- a/.github/workflows/release-staging.yml +++ b/.github/workflows/release-staging.yml @@ -30,6 +30,7 @@ on: options: - main - v8 + - at-11-7-1 verbose: description: 'Enable verbose logging' type: boolean From f92069a214f430bcd8d1056e3605fc9eb6e053ed Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 13 May 2025 13:56:30 -0700 Subject: [PATCH 39/77] Allow custom branch for release (#9033) * add temporary release branch --- .github/workflows/release-staging.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-staging.yml b/.github/workflows/release-staging.yml index 2f5cf5eeb38..16d358424a2 100644 --- a/.github/workflows/release-staging.yml +++ b/.github/workflows/release-staging.yml @@ -30,6 +30,7 @@ on: options: - main - v8 + - at-11-7-1 verbose: description: 'Enable verbose logging' type: boolean From 799de5997077b0a277af5f1377083ad407bfcd6c Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 13 May 2025 14:23:52 -0700 Subject: [PATCH 40/77] restore Vertex token for temp publish (#9036) --- .github/workflows/canary-deploy.yml | 1 + .github/workflows/prerelease-manual-deploy.yml | 1 + .github/workflows/release-prod.yml | 1 + .github/workflows/release-staging.yml | 1 + 4 files changed, 4 insertions(+) diff --git a/.github/workflows/canary-deploy.yml b/.github/workflows/canary-deploy.yml index 318db24f667..4b4cce63761 100644 --- a/.github/workflows/canary-deploy.yml +++ b/.github/workflows/canary-deploy.yml @@ -72,6 +72,7 @@ jobs: NPM_TOKEN_STORAGE_TYPES: ${{secrets.NPM_TOKEN_STORAGE_TYPES}} NPM_TOKEN_UTIL: ${{secrets.NPM_TOKEN_UTIL}} NPM_TOKEN_AI: ${{secrets.NPM_TOKEN_AI}} + NPM_TOKEN_VERTEXAI: ${{secrets.NPM_TOKEN_VERTEXAI}} NPM_TOKEN_WEBCHANNEL_WRAPPER: ${{secrets.NPM_TOKEN_WEBCHANNEL_WRAPPER}} NPM_TOKEN_FIREBASE: ${{secrets.NPM_TOKEN_FIREBASE}} NPM_TOKEN_APP_COMPAT: ${{ secrets.NPM_TOKEN_APP_COMPAT }} diff --git a/.github/workflows/prerelease-manual-deploy.yml b/.github/workflows/prerelease-manual-deploy.yml index d1ab281634c..73e82f11943 100644 --- a/.github/workflows/prerelease-manual-deploy.yml +++ b/.github/workflows/prerelease-manual-deploy.yml @@ -75,6 +75,7 @@ jobs: NPM_TOKEN_STORAGE_TYPES: ${{secrets.NPM_TOKEN_STORAGE_TYPES}} NPM_TOKEN_UTIL: ${{secrets.NPM_TOKEN_UTIL}} NPM_TOKEN_AI: ${{secrets.NPM_TOKEN_AI}} + NPM_TOKEN_VERTEXAI: ${{secrets.NPM_TOKEN_VERTEXAI}} NPM_TOKEN_WEBCHANNEL_WRAPPER: ${{secrets.NPM_TOKEN_WEBCHANNEL_WRAPPER}} NPM_TOKEN_FIREBASE: ${{secrets.NPM_TOKEN_FIREBASE}} NPM_TOKEN_APP_COMPAT: ${{ secrets.NPM_TOKEN_APP_COMPAT }} diff --git a/.github/workflows/release-prod.yml b/.github/workflows/release-prod.yml index 3ae667df6be..c89c7934db6 100644 --- a/.github/workflows/release-prod.yml +++ b/.github/workflows/release-prod.yml @@ -85,6 +85,7 @@ jobs: NPM_TOKEN_STORAGE_TYPES: ${{secrets.NPM_TOKEN_STORAGE_TYPES}} NPM_TOKEN_UTIL: ${{secrets.NPM_TOKEN_UTIL}} NPM_TOKEN_AI: ${{secrets.NPM_TOKEN_AI}} + NPM_TOKEN_VERTEXAI: ${{secrets.NPM_TOKEN_VERTEXAI}} NPM_TOKEN_WEBCHANNEL_WRAPPER: ${{secrets.NPM_TOKEN_WEBCHANNEL_WRAPPER}} NPM_TOKEN_FIREBASE: ${{secrets.NPM_TOKEN_FIREBASE}} NPM_TOKEN_APP_COMPAT: ${{ secrets.NPM_TOKEN_APP_COMPAT }} diff --git a/.github/workflows/release-staging.yml b/.github/workflows/release-staging.yml index 16d358424a2..e75ee4e703d 100644 --- a/.github/workflows/release-staging.yml +++ b/.github/workflows/release-staging.yml @@ -112,6 +112,7 @@ jobs: NPM_TOKEN_STORAGE_TYPES: ${{secrets.NPM_TOKEN_STORAGE_TYPES}} NPM_TOKEN_UTIL: ${{secrets.NPM_TOKEN_UTIL}} NPM_TOKEN_AI: ${{secrets.NPM_TOKEN_AI}} + NPM_TOKEN_VERTEXAI: ${{secrets.NPM_TOKEN_VERTEXAI}} NPM_TOKEN_WEBCHANNEL_WRAPPER: ${{secrets.NPM_TOKEN_WEBCHANNEL_WRAPPER}} NPM_TOKEN_FIREBASE: ${{secrets.NPM_TOKEN_FIREBASE}} NPM_TOKEN_APP_COMPAT: ${{ secrets.NPM_TOKEN_APP_COMPAT }} From 9338d9d160cc2d9eddeb9ff4bb58e895f8cb9ae9 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Wed, 14 May 2025 12:08:58 -0700 Subject: [PATCH 41/77] Version Packages (manual PR) (#9034) --- .changeset/three-singers-wonder.md | 10 ---- integration/compat-interop/package.json | 28 +++++------ integration/firestore/package.json | 4 +- integration/messaging/package.json | 2 +- packages/analytics-compat/CHANGELOG.md | 9 ++++ packages/analytics-compat/package.json | 10 ++-- packages/analytics/CHANGELOG.md | 9 ++++ packages/analytics/package.json | 10 ++-- packages/app-check-compat/CHANGELOG.md | 9 ++++ packages/app-check-compat/package.json | 10 ++-- packages/app-check/CHANGELOG.md | 8 ++++ packages/app-check/package.json | 8 ++-- packages/app-compat/CHANGELOG.md | 9 ++++ packages/app-compat/package.json | 8 ++-- packages/app/CHANGELOG.md | 8 ++++ packages/app/package.json | 6 +-- packages/auth-compat/CHANGELOG.md | 9 ++++ packages/auth-compat/package.json | 10 ++-- packages/auth/CHANGELOG.md | 10 ++++ packages/auth/package.json | 8 ++-- packages/component/CHANGELOG.md | 7 +++ packages/component/package.json | 4 +- packages/data-connect/CHANGELOG.md | 8 ++++ packages/data-connect/package.json | 8 ++-- packages/database-compat/CHANGELOG.md | 10 ++++ packages/database-compat/package.json | 12 ++--- packages/database-types/CHANGELOG.md | 7 +++ packages/database-types/package.json | 4 +- packages/database/CHANGELOG.md | 10 ++++ packages/database/package.json | 8 ++-- packages/firebase/CHANGELOG.md | 33 +++++++++++++ packages/firebase/package.json | 56 +++++++++++----------- packages/firestore-compat/CHANGELOG.md | 9 ++++ packages/firestore-compat/package.json | 10 ++-- packages/firestore/CHANGELOG.md | 10 ++++ packages/firestore/package.json | 12 ++--- packages/functions-compat/CHANGELOG.md | 9 ++++ packages/functions-compat/package.json | 10 ++-- packages/functions/CHANGELOG.md | 10 ++++ packages/functions/package.json | 8 ++-- packages/installations-compat/CHANGELOG.md | 9 ++++ packages/installations-compat/package.json | 10 ++-- packages/installations/CHANGELOG.md | 8 ++++ packages/installations/package.json | 8 ++-- packages/messaging-compat/CHANGELOG.md | 9 ++++ packages/messaging-compat/package.json | 10 ++-- packages/messaging/CHANGELOG.md | 9 ++++ packages/messaging/package.json | 10 ++-- packages/performance-compat/CHANGELOG.md | 9 ++++ packages/performance-compat/package.json | 10 ++-- packages/performance/CHANGELOG.md | 9 ++++ packages/performance/package.json | 10 ++-- packages/remote-config-compat/CHANGELOG.md | 9 ++++ packages/remote-config-compat/package.json | 10 ++-- packages/remote-config/CHANGELOG.md | 9 ++++ packages/remote-config/package.json | 10 ++-- packages/storage-compat/CHANGELOG.md | 9 ++++ packages/storage-compat/package.json | 12 ++--- packages/storage/CHANGELOG.md | 10 ++++ packages/storage/package.json | 10 ++-- packages/template/package.json | 2 +- packages/util/CHANGELOG.md | 6 +++ packages/util/package.json | 2 +- packages/vertexai/CHANGELOG.md | 8 ++++ packages/vertexai/package.json | 8 ++-- repo-scripts/size-analysis/package.json | 4 +- 66 files changed, 464 insertions(+), 186 deletions(-) delete mode 100644 .changeset/three-singers-wonder.md diff --git a/.changeset/three-singers-wonder.md b/.changeset/three-singers-wonder.md deleted file mode 100644 index 72dea9d7aa5..00000000000 --- a/.changeset/three-singers-wonder.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@firebase/auth": patch -"@firebase/firestore": patch -"@firebase/util": patch -"@firebase/database": patch -"@firebase/storage": patch -"@firebase/functions": patch ---- - -Add Emulator Overlay diff --git a/integration/compat-interop/package.json b/integration/compat-interop/package.json index c28006dd7d3..ddaf85b0762 100644 --- a/integration/compat-interop/package.json +++ b/integration/compat-interop/package.json @@ -8,20 +8,20 @@ "test:debug": "karma start --browsers Chrome --auto-watch" }, "dependencies": { - "@firebase/app": "0.12.1", - "@firebase/app-compat": "0.3.1", - "@firebase/analytics": "0.10.13", - "@firebase/analytics-compat": "0.2.19", - "@firebase/auth": "1.10.2", - "@firebase/auth-compat": "0.5.22", - "@firebase/functions": "0.12.4", - "@firebase/functions-compat": "0.3.21", - "@firebase/messaging": "0.12.18", - "@firebase/messaging-compat": "0.2.18", - "@firebase/performance": "0.7.3", - "@firebase/performance-compat": "0.2.16", - "@firebase/remote-config": "0.6.1", - "@firebase/remote-config-compat": "0.2.14" + "@firebase/app": "0.12.2", + "@firebase/app-compat": "0.3.2", + "@firebase/analytics": "0.10.14", + "@firebase/analytics-compat": "0.2.20", + "@firebase/auth": "1.10.3", + "@firebase/auth-compat": "0.5.23", + "@firebase/functions": "0.12.5", + "@firebase/functions-compat": "0.3.22", + "@firebase/messaging": "0.12.19", + "@firebase/messaging-compat": "0.2.19", + "@firebase/performance": "0.7.4", + "@firebase/performance-compat": "0.2.17", + "@firebase/remote-config": "0.6.2", + "@firebase/remote-config-compat": "0.2.15" }, "devDependencies": { "typescript": "5.5.4" diff --git a/integration/firestore/package.json b/integration/firestore/package.json index ee49ae9734f..8fc6d1c6597 100644 --- a/integration/firestore/package.json +++ b/integration/firestore/package.json @@ -14,8 +14,8 @@ "test:memory:debug": "yarn build:memory; karma start --auto-watch --browsers Chrome" }, "dependencies": { - "@firebase/app": "0.12.1", - "@firebase/firestore": "4.7.12" + "@firebase/app": "0.12.2", + "@firebase/firestore": "4.7.13" }, "devDependencies": { "@types/mocha": "9.1.1", diff --git a/integration/messaging/package.json b/integration/messaging/package.json index a8f0fd1cf7a..2d8b5431375 100644 --- a/integration/messaging/package.json +++ b/integration/messaging/package.json @@ -9,7 +9,7 @@ "test:manual": "mocha --exit" }, "devDependencies": { - "firebase": "11.7.1", + "firebase": "11.7.2", "chai": "4.5.0", "chromedriver": "119.0.1", "express": "4.21.2", diff --git a/packages/analytics-compat/CHANGELOG.md b/packages/analytics-compat/CHANGELOG.md index 0a349c8d806..1c577c35f85 100644 --- a/packages/analytics-compat/CHANGELOG.md +++ b/packages/analytics-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/analytics-compat +## 0.2.20 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/analytics@0.10.14 + - @firebase/component@0.6.15 + ## 0.2.19 ### Patch Changes diff --git a/packages/analytics-compat/package.json b/packages/analytics-compat/package.json index dc614771cda..ec23086ca6f 100644 --- a/packages/analytics-compat/package.json +++ b/packages/analytics-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/analytics-compat", - "version": "0.2.19", + "version": "0.2.20", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -22,7 +22,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.3.1", + "@firebase/app-compat": "0.3.2", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -52,10 +52,10 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.14", - "@firebase/analytics": "0.10.13", + "@firebase/component": "0.6.15", + "@firebase/analytics": "0.10.14", "@firebase/analytics-types": "0.8.3", - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/analytics/CHANGELOG.md b/packages/analytics/CHANGELOG.md index 964d404dc9c..75441edf909 100644 --- a/packages/analytics/CHANGELOG.md +++ b/packages/analytics/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/analytics +## 0.10.14 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + - @firebase/installations@0.6.15 + ## 0.10.13 ### Patch Changes diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 3878c34658e..82c989ebdd6 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/analytics", - "version": "0.10.13", + "version": "0.10.14", "description": "A analytics package for new firebase packages", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -39,15 +39,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.14", + "@firebase/installations": "0.6.15", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check-compat/CHANGELOG.md b/packages/app-check-compat/CHANGELOG.md index db793dea557..f23eb3b7cfc 100644 --- a/packages/app-check-compat/CHANGELOG.md +++ b/packages/app-check-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/app-check-compat +## 0.3.23 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/app-check@0.9.2 + - @firebase/component@0.6.15 + ## 0.3.22 ### Patch Changes diff --git a/packages/app-check-compat/package.json b/packages/app-check-compat/package.json index 05be510bdf8..abaee8bf502 100644 --- a/packages/app-check-compat/package.json +++ b/packages/app-check-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-check-compat", - "version": "0.3.22", + "version": "0.3.23", "description": "A compat App Check package for new firebase packages", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -34,16 +34,16 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/app-check": "0.9.1", + "@firebase/app-check": "0.9.2", "@firebase/app-check-types": "0.5.3", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.3.1", + "@firebase/app-compat": "0.3.2", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check/CHANGELOG.md b/packages/app-check/CHANGELOG.md index a3afb0864cb..eaad91dc099 100644 --- a/packages/app-check/CHANGELOG.md +++ b/packages/app-check/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/app-check +## 0.9.2 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 0.9.1 ### Patch Changes diff --git a/packages/app-check/package.json b/packages/app-check/package.json index 7dc2a2bb139..589adcd1880 100644 --- a/packages/app-check/package.json +++ b/packages/app-check/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-check", - "version": "0.9.1", + "version": "0.9.2", "description": "The App Check component of the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,14 +37,14 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "@firebase/logger": "0.4.4", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-compat/CHANGELOG.md b/packages/app-compat/CHANGELOG.md index be47b8e2e3b..35d1b7adc88 100644 --- a/packages/app-compat/CHANGELOG.md +++ b/packages/app-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/app-compat +## 0.3.2 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/app@0.12.2 + - @firebase/component@0.6.15 + ## 0.3.1 ### Patch Changes diff --git a/packages/app-compat/package.json b/packages/app-compat/package.json index 725c2fa99b1..fbc7cf5c395 100644 --- a/packages/app-compat/package.json +++ b/packages/app-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-compat", - "version": "0.3.1", + "version": "0.3.2", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,10 +37,10 @@ }, "license": "Apache-2.0", "dependencies": { - "@firebase/app": "0.12.1", - "@firebase/util": "1.11.1", + "@firebase/app": "0.12.2", + "@firebase/util": "1.11.2", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.14", + "@firebase/component": "0.6.15", "tslib": "^2.1.0" }, "devDependencies": { diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index c8950255f24..9620acb9518 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/app +## 0.12.2 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 0.12.1 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 24d32c0a686..4f78c2c6699 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app", - "version": "0.12.1", + "version": "0.12.2", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,9 +37,9 @@ "typings:internal": "node ../../scripts/build/use_typings.js ./dist/app.d.ts" }, "dependencies": { - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.14", + "@firebase/component": "0.6.15", "idb": "7.1.1", "tslib": "^2.1.0" }, diff --git a/packages/auth-compat/CHANGELOG.md b/packages/auth-compat/CHANGELOG.md index c3d3e3e3983..d0fd0f12faa 100644 --- a/packages/auth-compat/CHANGELOG.md +++ b/packages/auth-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/auth-compat +## 0.5.23 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/auth@1.10.3 + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 0.5.22 ### Patch Changes diff --git a/packages/auth-compat/package.json b/packages/auth-compat/package.json index d061a5f17a0..7310e55a2e7 100644 --- a/packages/auth-compat/package.json +++ b/packages/auth-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth-compat", - "version": "0.5.22", + "version": "0.5.23", "description": "FirebaseAuth compatibility package that uses API style compatible with Firebase@8 and prior versions", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,15 +49,15 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/auth": "1.10.2", + "@firebase/auth": "1.10.3", "@firebase/auth-types": "0.13.0", - "@firebase/component": "0.6.14", - "@firebase/util": "1.11.1", + "@firebase/component": "0.6.15", + "@firebase/util": "1.11.2", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.3.1", + "@firebase/app-compat": "0.3.2", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md index 74d290fead1..2eb6f477f78 100644 --- a/packages/auth/CHANGELOG.md +++ b/packages/auth/CHANGELOG.md @@ -1,5 +1,15 @@ # @firebase/auth +## 1.10.3 + +### Patch Changes + +- [`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24) [#9031](https://github.com/firebase/firebase-js-sdk/pull/9031) - Add Emulator Overlay + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 1.10.2 ### Patch Changes diff --git a/packages/auth/package.json b/packages/auth/package.json index a58608538db..26f8a4c73db 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth", - "version": "1.10.2", + "version": "1.10.3", "description": "The Firebase Authenticaton component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/node/index.js", @@ -124,14 +124,14 @@ } }, "dependencies": { - "@firebase/component": "0.6.14", + "@firebase/component": "0.6.15", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-strip": "2.1.0", "@types/express": "4.17.21", diff --git a/packages/component/CHANGELOG.md b/packages/component/CHANGELOG.md index 6213c4204ae..b711d33fbdd 100644 --- a/packages/component/CHANGELOG.md +++ b/packages/component/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/component +## 0.6.15 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + ## 0.6.14 ### Patch Changes diff --git a/packages/component/package.json b/packages/component/package.json index f3338c83fdc..2297a386eb1 100644 --- a/packages/component/package.json +++ b/packages/component/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/component", - "version": "0.6.14", + "version": "0.6.15", "description": "Firebase Component Platform", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -31,7 +31,7 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "tslib": "^2.1.0" }, "license": "Apache-2.0", diff --git a/packages/data-connect/CHANGELOG.md b/packages/data-connect/CHANGELOG.md index c222e97117a..7dca51b9ddd 100644 --- a/packages/data-connect/CHANGELOG.md +++ b/packages/data-connect/CHANGELOG.md @@ -1,5 +1,13 @@ ## Unreleased +## 0.3.6 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 0.3.5 ### Patch Changes diff --git a/packages/data-connect/package.json b/packages/data-connect/package.json index 2c0c454ff55..3982438f5b8 100644 --- a/packages/data-connect/package.json +++ b/packages/data-connect/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/data-connect", - "version": "0.3.5", + "version": "0.3.6", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,13 +49,13 @@ }, "dependencies": { "@firebase/auth-interop-types": "0.2.4", - "@firebase/component": "0.6.14", + "@firebase/component": "0.6.15", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/database-compat/CHANGELOG.md b/packages/database-compat/CHANGELOG.md index 9fde0eac95c..02d06d6f5c4 100644 --- a/packages/database-compat/CHANGELOG.md +++ b/packages/database-compat/CHANGELOG.md @@ -1,5 +1,15 @@ # @firebase/database-compat +## 2.0.7 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/database@1.0.16 + - @firebase/component@0.6.15 + - @firebase/database-types@1.0.12 + ## 2.0.6 ### Patch Changes diff --git a/packages/database-compat/package.json b/packages/database-compat/package.json index ec0e6994468..6b552d78128 100644 --- a/packages/database-compat/package.json +++ b/packages/database-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database-compat", - "version": "2.0.6", + "version": "2.0.7", "description": "The Realtime Database component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.js", @@ -49,15 +49,15 @@ "add-compat-overloads": "ts-node-script ../../scripts/build/create-overloads.ts -i ../database/dist/public.d.ts -o dist/database-compat/src/index.d.ts -a -r Database:types.FirebaseDatabase -r Query:types.Query -r DatabaseReference:types.Reference -r FirebaseApp:FirebaseAppCompat --moduleToEnhance @firebase/database" }, "dependencies": { - "@firebase/database": "1.0.15", - "@firebase/database-types": "1.0.11", + "@firebase/database": "1.0.16", + "@firebase/database-types": "1.0.12", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.1", + "@firebase/app-compat": "0.3.2", "typescript": "5.5.4" }, "repository": { diff --git a/packages/database-types/CHANGELOG.md b/packages/database-types/CHANGELOG.md index e24ab9364a6..9cef5bc8ef4 100644 --- a/packages/database-types/CHANGELOG.md +++ b/packages/database-types/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/database-types +## 1.0.12 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + ## 1.0.11 ### Patch Changes diff --git a/packages/database-types/package.json b/packages/database-types/package.json index 20565d5c28a..b93ba52dd38 100644 --- a/packages/database-types/package.json +++ b/packages/database-types/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database-types", - "version": "1.0.11", + "version": "1.0.12", "description": "@firebase/database Types", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -13,7 +13,7 @@ ], "dependencies": { "@firebase/app-types": "0.9.3", - "@firebase/util": "1.11.1" + "@firebase/util": "1.11.2" }, "repository": { "directory": "packages/database-types", diff --git a/packages/database/CHANGELOG.md b/packages/database/CHANGELOG.md index 1cfa56fd78e..7fdf8b2ee08 100644 --- a/packages/database/CHANGELOG.md +++ b/packages/database/CHANGELOG.md @@ -1,5 +1,15 @@ # Unreleased +## 1.0.16 + +### Patch Changes + +- [`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24) [#9031](https://github.com/firebase/firebase-js-sdk/pull/9031) - Add Emulator Overlay + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 1.0.15 ### Patch Changes diff --git a/packages/database/package.json b/packages/database/package.json index e68132857d4..a1fb1e8bd1c 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database", - "version": "1.0.15", + "version": "1.0.16", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,15 +49,15 @@ "peerDependencies": {}, "dependencies": { "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "@firebase/app-check-interop-types": "0.3.3", "@firebase/auth-interop-types": "0.2.4", "faye-websocket": "0.11.4", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/firebase/CHANGELOG.md b/packages/firebase/CHANGELOG.md index 3ecab3e54f6..ecb0b1ac653 100644 --- a/packages/firebase/CHANGELOG.md +++ b/packages/firebase/CHANGELOG.md @@ -1,5 +1,38 @@ # firebase +## 11.7.2 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/auth@1.10.3 + - @firebase/firestore@4.7.13 + - @firebase/util@1.11.2 + - @firebase/database@1.0.16 + - @firebase/storage@0.13.9 + - @firebase/functions@0.12.5 + - @firebase/auth-compat@0.5.23 + - @firebase/firestore-compat@0.3.48 + - @firebase/analytics@0.10.14 + - @firebase/analytics-compat@0.2.20 + - @firebase/app@0.12.2 + - @firebase/app-check@0.9.2 + - @firebase/app-check-compat@0.3.23 + - @firebase/app-compat@0.3.2 + - @firebase/data-connect@0.3.6 + - @firebase/database-compat@2.0.7 + - @firebase/functions-compat@0.3.22 + - @firebase/installations@0.6.15 + - @firebase/installations-compat@0.2.15 + - @firebase/messaging@0.12.19 + - @firebase/messaging-compat@0.2.19 + - @firebase/performance@0.7.4 + - @firebase/performance-compat@0.2.17 + - @firebase/remote-config@0.6.2 + - @firebase/remote-config-compat@0.2.15 + - @firebase/storage-compat@0.3.19 + - @firebase/vertexai@1.2.3 + ## 11.7.1 ### Patch Changes diff --git a/packages/firebase/package.json b/packages/firebase/package.json index f4fe3bb2d50..b4edcedca3a 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -1,6 +1,6 @@ { "name": "firebase", - "version": "11.7.1", + "version": "11.7.2", "description": "Firebase JavaScript library for web and Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -399,34 +399,34 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { - "@firebase/app": "0.12.1", - "@firebase/app-compat": "0.3.1", + "@firebase/app": "0.12.2", + "@firebase/app-compat": "0.3.2", "@firebase/app-types": "0.9.3", - "@firebase/auth": "1.10.2", - "@firebase/auth-compat": "0.5.22", - "@firebase/data-connect": "0.3.5", - "@firebase/database": "1.0.15", - "@firebase/database-compat": "2.0.6", - "@firebase/firestore": "4.7.12", - "@firebase/firestore-compat": "0.3.47", - "@firebase/functions": "0.12.4", - "@firebase/functions-compat": "0.3.21", - "@firebase/installations": "0.6.14", - "@firebase/installations-compat": "0.2.14", - "@firebase/messaging": "0.12.18", - "@firebase/messaging-compat": "0.2.18", - "@firebase/storage": "0.13.8", - "@firebase/storage-compat": "0.3.18", - "@firebase/performance": "0.7.3", - "@firebase/performance-compat": "0.2.16", - "@firebase/remote-config": "0.6.1", - "@firebase/remote-config-compat": "0.2.14", - "@firebase/analytics": "0.10.13", - "@firebase/analytics-compat": "0.2.19", - "@firebase/app-check": "0.9.1", - "@firebase/app-check-compat": "0.3.22", - "@firebase/util": "1.11.1", - "@firebase/vertexai": "1.2.2" + "@firebase/auth": "1.10.3", + "@firebase/auth-compat": "0.5.23", + "@firebase/data-connect": "0.3.6", + "@firebase/database": "1.0.16", + "@firebase/database-compat": "2.0.7", + "@firebase/firestore": "4.7.13", + "@firebase/firestore-compat": "0.3.48", + "@firebase/functions": "0.12.5", + "@firebase/functions-compat": "0.3.22", + "@firebase/installations": "0.6.15", + "@firebase/installations-compat": "0.2.15", + "@firebase/messaging": "0.12.19", + "@firebase/messaging-compat": "0.2.19", + "@firebase/storage": "0.13.9", + "@firebase/storage-compat": "0.3.19", + "@firebase/performance": "0.7.4", + "@firebase/performance-compat": "0.2.17", + "@firebase/remote-config": "0.6.2", + "@firebase/remote-config-compat": "0.2.15", + "@firebase/analytics": "0.10.14", + "@firebase/analytics-compat": "0.2.20", + "@firebase/app-check": "0.9.2", + "@firebase/app-check-compat": "0.3.23", + "@firebase/util": "1.11.2", + "@firebase/vertexai": "1.2.3" }, "devDependencies": { "rollup": "2.79.2", diff --git a/packages/firestore-compat/CHANGELOG.md b/packages/firestore-compat/CHANGELOG.md index c721b0b7b20..4b05f22a243 100644 --- a/packages/firestore-compat/CHANGELOG.md +++ b/packages/firestore-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/firestore-compat +## 0.3.48 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/firestore@4.7.13 + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 0.3.47 ### Patch Changes diff --git a/packages/firestore-compat/package.json b/packages/firestore-compat/package.json index 648cf8ee019..29d57083e1a 100644 --- a/packages/firestore-compat/package.json +++ b/packages/firestore-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore-compat", - "version": "0.3.47", + "version": "0.3.48", "description": "The Cloud Firestore component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -46,14 +46,14 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/component": "0.6.14", - "@firebase/firestore": "4.7.12", - "@firebase/util": "1.11.1", + "@firebase/component": "0.6.15", + "@firebase/firestore": "4.7.13", + "@firebase/util": "1.11.2", "@firebase/firestore-types": "3.0.3", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.1", + "@firebase/app-compat": "0.3.2", "@types/eslint": "7.29.0", "rollup": "2.79.2", "rollup-plugin-sourcemaps": "0.6.3", diff --git a/packages/firestore/CHANGELOG.md b/packages/firestore/CHANGELOG.md index 0fd288b01c8..84bbccd8b8b 100644 --- a/packages/firestore/CHANGELOG.md +++ b/packages/firestore/CHANGELOG.md @@ -1,5 +1,15 @@ # @firebase/firestore +## 4.7.13 + +### Patch Changes + +- [`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24) [#9031](https://github.com/firebase/firebase-js-sdk/pull/9031) - Add Emulator Overlay + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 4.7.12 ### Patch Changes diff --git a/packages/firestore/package.json b/packages/firestore/package.json index 38eea5b1505..5ff890803a3 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore", - "version": "4.7.12", + "version": "4.7.13", "engines": { "node": ">=18.0.0" }, @@ -98,9 +98,9 @@ "lite/package.json" ], "dependencies": { - "@firebase/component": "0.6.14", + "@firebase/component": "0.6.15", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "@firebase/webchannel-wrapper": "1.0.3", "@grpc/grpc-js": "~1.9.0", "@grpc/proto-loader": "^0.7.8", @@ -110,9 +110,9 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.1", - "@firebase/app-compat": "0.3.1", - "@firebase/auth": "1.10.2", + "@firebase/app": "0.12.2", + "@firebase/app-compat": "0.3.2", + "@firebase/auth": "1.10.3", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", "@types/eslint": "7.29.0", diff --git a/packages/functions-compat/CHANGELOG.md b/packages/functions-compat/CHANGELOG.md index 28a5efc295d..60a92238883 100644 --- a/packages/functions-compat/CHANGELOG.md +++ b/packages/functions-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/functions-compat +## 0.3.22 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/functions@0.12.5 + - @firebase/component@0.6.15 + ## 0.3.21 ### Patch Changes diff --git a/packages/functions-compat/package.json b/packages/functions-compat/package.json index 02725731af4..75238014aed 100644 --- a/packages/functions-compat/package.json +++ b/packages/functions-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/functions-compat", - "version": "0.3.21", + "version": "0.3.22", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -29,7 +29,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.3.1", + "@firebase/app-compat": "0.3.2", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -62,10 +62,10 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.14", - "@firebase/functions": "0.12.4", + "@firebase/component": "0.6.15", + "@firebase/functions": "0.12.5", "@firebase/functions-types": "0.6.3", - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/functions/CHANGELOG.md b/packages/functions/CHANGELOG.md index c0a54ac71b0..89a0f1cd396 100644 --- a/packages/functions/CHANGELOG.md +++ b/packages/functions/CHANGELOG.md @@ -1,5 +1,15 @@ # @firebase/functions +## 0.12.5 + +### Patch Changes + +- [`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24) [#9031](https://github.com/firebase/firebase-js-sdk/pull/9031) - Add Emulator Overlay + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 0.12.4 ### Patch Changes diff --git a/packages/functions/package.json b/packages/functions/package.json index e2e6d9f99fc..560327628a8 100644 --- a/packages/functions/package.json +++ b/packages/functions/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/functions", - "version": "0.12.4", + "version": "0.12.5", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -49,7 +49,7 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -65,11 +65,11 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.14", + "@firebase/component": "0.6.15", "@firebase/messaging-interop-types": "0.2.3", "@firebase/auth-interop-types": "0.2.4", "@firebase/app-check-interop-types": "0.3.3", - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/installations-compat/CHANGELOG.md b/packages/installations-compat/CHANGELOG.md index e34cf0add2a..1e63861c661 100644 --- a/packages/installations-compat/CHANGELOG.md +++ b/packages/installations-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/installations-compat +## 0.2.15 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + - @firebase/installations@0.6.15 + ## 0.2.14 ### Patch Changes diff --git a/packages/installations-compat/package.json b/packages/installations-compat/package.json index ce75757d337..c485f5d1ff2 100644 --- a/packages/installations-compat/package.json +++ b/packages/installations-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/installations-compat", - "version": "0.2.14", + "version": "0.2.15", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", "module": "dist/esm/index.esm2017.js", @@ -44,7 +44,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app-compat": "0.3.1", + "@firebase/app-compat": "0.3.2", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", @@ -57,10 +57,10 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.14", + "@firebase/installations": "0.6.15", "@firebase/installations-types": "0.5.3", - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "tslib": "^2.1.0" } } diff --git a/packages/installations/CHANGELOG.md b/packages/installations/CHANGELOG.md index 71a9e9757cd..79c23c4ba6f 100644 --- a/packages/installations/CHANGELOG.md +++ b/packages/installations/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/installations +## 0.6.15 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 0.6.14 ### Patch Changes diff --git a/packages/installations/package.json b/packages/installations/package.json index 32285b7fb1c..f1b21b10b96 100644 --- a/packages/installations/package.json +++ b/packages/installations/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/installations", - "version": "0.6.14", + "version": "0.6.15", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", "module": "dist/esm/index.esm2017.js", @@ -49,7 +49,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", @@ -62,8 +62,8 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "idb": "7.1.1", "tslib": "^2.1.0" } diff --git a/packages/messaging-compat/CHANGELOG.md b/packages/messaging-compat/CHANGELOG.md index 61da0c87ed3..e0a157271dc 100644 --- a/packages/messaging-compat/CHANGELOG.md +++ b/packages/messaging-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/messaging-compat +## 0.2.19 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + - @firebase/messaging@0.12.19 + ## 0.2.18 ### Patch Changes diff --git a/packages/messaging-compat/package.json b/packages/messaging-compat/package.json index 1021d98c515..cc134aaf97a 100644 --- a/packages/messaging-compat/package.json +++ b/packages/messaging-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/messaging-compat", - "version": "0.2.18", + "version": "0.2.19", "license": "Apache-2.0", "description": "", "author": "Firebase (https://firebase.google.com/)", @@ -38,13 +38,13 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/messaging": "0.12.18", - "@firebase/component": "0.6.14", - "@firebase/util": "1.11.1", + "@firebase/messaging": "0.12.19", + "@firebase/component": "0.6.15", + "@firebase/util": "1.11.2", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.1", + "@firebase/app-compat": "0.3.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", "ts-essentials": "9.4.2", diff --git a/packages/messaging/CHANGELOG.md b/packages/messaging/CHANGELOG.md index d0a684dcb8e..9b8472b8520 100644 --- a/packages/messaging/CHANGELOG.md +++ b/packages/messaging/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/messaging +## 0.12.19 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + - @firebase/installations@0.6.15 + ## 0.12.18 ### Patch Changes diff --git a/packages/messaging/package.json b/packages/messaging/package.json index 3d2e70ebfb4..b3b81cf0836 100644 --- a/packages/messaging/package.json +++ b/packages/messaging/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/messaging", - "version": "0.12.18", + "version": "0.12.19", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -52,15 +52,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.14", + "@firebase/installations": "0.6.15", "@firebase/messaging-interop-types": "0.2.3", - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "idb": "7.1.1", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/performance-compat/CHANGELOG.md b/packages/performance-compat/CHANGELOG.md index 56d5b805eef..e0b57fd0f43 100644 --- a/packages/performance-compat/CHANGELOG.md +++ b/packages/performance-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/performance-compat +## 0.2.17 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + - @firebase/performance@0.7.4 + ## 0.2.16 ### Patch Changes diff --git a/packages/performance-compat/package.json b/packages/performance-compat/package.json index 37b49ad2455..741ff2f83d0 100644 --- a/packages/performance-compat/package.json +++ b/packages/performance-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/performance-compat", - "version": "0.2.16", + "version": "0.2.17", "description": "The compatibility package of Firebase Performance", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -38,11 +38,11 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/performance": "0.7.3", + "@firebase/performance": "0.7.4", "@firebase/performance-types": "0.2.3", - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.14", + "@firebase/component": "0.6.15", "tslib": "^2.1.0" }, "devDependencies": { @@ -51,7 +51,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.3.1" + "@firebase/app-compat": "0.3.2" }, "repository": { "directory": "packages/performance-compat", diff --git a/packages/performance/CHANGELOG.md b/packages/performance/CHANGELOG.md index b03ece7ed0c..6e3bdfb842f 100644 --- a/packages/performance/CHANGELOG.md +++ b/packages/performance/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/performance +## 0.7.4 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + - @firebase/installations@0.6.15 + ## 0.7.3 ### Patch Changes diff --git a/packages/performance/package.json b/packages/performance/package.json index a6256c12f39..c676fd7ad96 100644 --- a/packages/performance/package.json +++ b/packages/performance/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/performance", - "version": "0.7.3", + "version": "0.7.4", "description": "Firebase performance for web", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -39,15 +39,15 @@ }, "dependencies": { "@firebase/logger": "0.4.4", - "@firebase/installations": "0.6.14", - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/installations": "0.6.15", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "tslib": "^2.1.0", "web-vitals": "^4.2.4" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/remote-config-compat/CHANGELOG.md b/packages/remote-config-compat/CHANGELOG.md index 01ed6bc0cef..ec5e443efe8 100644 --- a/packages/remote-config-compat/CHANGELOG.md +++ b/packages/remote-config-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/remote-config-compat +## 0.2.15 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + - @firebase/remote-config@0.6.2 + ## 0.2.14 ### Patch Changes diff --git a/packages/remote-config-compat/package.json b/packages/remote-config-compat/package.json index 9839182086c..3d8e22e6dbf 100644 --- a/packages/remote-config-compat/package.json +++ b/packages/remote-config-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/remote-config-compat", - "version": "0.2.14", + "version": "0.2.15", "description": "The compatibility package of Remote Config", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,11 +37,11 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/remote-config": "0.6.1", + "@firebase/remote-config": "0.6.2", "@firebase/remote-config-types": "0.4.0", - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.14", + "@firebase/component": "0.6.15", "tslib": "^2.1.0" }, "devDependencies": { @@ -50,7 +50,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.3.1" + "@firebase/app-compat": "0.3.2" }, "repository": { "directory": "packages/remote-config-compat", diff --git a/packages/remote-config/CHANGELOG.md b/packages/remote-config/CHANGELOG.md index 4a209ee5e38..640f6c34f4d 100644 --- a/packages/remote-config/CHANGELOG.md +++ b/packages/remote-config/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/remote-config +## 0.6.2 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + - @firebase/installations@0.6.15 + ## 0.6.1 ### Patch Changes diff --git a/packages/remote-config/package.json b/packages/remote-config/package.json index 1b068256178..4f1fe85e9f8 100644 --- a/packages/remote-config/package.json +++ b/packages/remote-config/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/remote-config", - "version": "0.6.1", + "version": "0.6.2", "description": "The Remote Config package of the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -40,15 +40,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.14", + "@firebase/installations": "0.6.15", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/storage-compat/CHANGELOG.md b/packages/storage-compat/CHANGELOG.md index 309cd864964..31689803f0a 100644 --- a/packages/storage-compat/CHANGELOG.md +++ b/packages/storage-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/storage-compat +## 0.3.19 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/storage@0.13.9 + - @firebase/component@0.6.15 + ## 0.3.18 ### Patch Changes diff --git a/packages/storage-compat/package.json b/packages/storage-compat/package.json index fe7b9772ec0..445b13d5045 100644 --- a/packages/storage-compat/package.json +++ b/packages/storage-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage-compat", - "version": "0.3.18", + "version": "0.3.19", "description": "The Firebase Firestore compatibility package", "author": "Firebase (https://firebase.google.com/)", "main": "./dist/index.cjs.js", @@ -37,15 +37,15 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/storage": "0.13.8", + "@firebase/storage": "0.13.9", "@firebase/storage-types": "0.8.3", - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.1", - "@firebase/auth-compat": "0.5.22", + "@firebase/app-compat": "0.3.2", + "@firebase/auth-compat": "0.5.23", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/storage/CHANGELOG.md b/packages/storage/CHANGELOG.md index 7636c13260d..e18aa9af7b3 100644 --- a/packages/storage/CHANGELOG.md +++ b/packages/storage/CHANGELOG.md @@ -1,5 +1,15 @@ #Unreleased +## 0.13.9 + +### Patch Changes + +- [`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24) [#9031](https://github.com/firebase/firebase-js-sdk/pull/9031) - Add Emulator Overlay + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 0.13.8 ### Patch Changes diff --git a/packages/storage/package.json b/packages/storage/package.json index 963ad3e86d6..5b3e9bb871d 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage", - "version": "0.13.8", + "version": "0.13.9", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -46,16 +46,16 @@ }, "license": "Apache-2.0", "dependencies": { - "@firebase/util": "1.11.1", - "@firebase/component": "0.6.14", + "@firebase/util": "1.11.2", + "@firebase/component": "0.6.15", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.1", - "@firebase/auth": "1.10.2", + "@firebase/app": "0.12.2", + "@firebase/auth": "1.10.3", "rollup": "2.79.2", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", diff --git a/packages/template/package.json b/packages/template/package.json index 7f5bf9cb1bd..d8d49446172 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -48,7 +48,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/util/CHANGELOG.md b/packages/util/CHANGELOG.md index 5a33cbad42b..79857db4665 100644 --- a/packages/util/CHANGELOG.md +++ b/packages/util/CHANGELOG.md @@ -1,5 +1,11 @@ # @firebase/util +## 1.11.2 + +### Patch Changes + +- [`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24) [#9031](https://github.com/firebase/firebase-js-sdk/pull/9031) - Add Emulator Overlay + ## 1.11.1 ### Patch Changes diff --git a/packages/util/package.json b/packages/util/package.json index 8d8c85b3f2e..61ea91f5a6c 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/util", - "version": "1.11.1", + "version": "1.11.2", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", diff --git a/packages/vertexai/CHANGELOG.md b/packages/vertexai/CHANGELOG.md index 8fa9e9d4b4a..168841c958c 100644 --- a/packages/vertexai/CHANGELOG.md +++ b/packages/vertexai/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/vertexai +## 1.2.3 + +### Patch Changes + +- Updated dependencies [[`8593fa0`](https://github.com/firebase/firebase-js-sdk/commit/8593fa05bd884c2f1f6f3b4ae062efa48af93d24)]: + - @firebase/util@1.11.2 + - @firebase/component@0.6.15 + ## 1.2.2 ### Patch Changes diff --git a/packages/vertexai/package.json b/packages/vertexai/package.json index 0c6b24fc495..e8a7e4e6d09 100644 --- a/packages/vertexai/package.json +++ b/packages/vertexai/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/vertexai", - "version": "1.2.2", + "version": "1.2.3", "description": "A Firebase SDK for VertexAI", "author": "Firebase (https://firebase.google.com/)", "engines": { @@ -49,14 +49,14 @@ }, "dependencies": { "@firebase/app-check-interop-types": "0.3.3", - "@firebase/component": "0.6.14", + "@firebase/component": "0.6.15", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/repo-scripts/size-analysis/package.json b/repo-scripts/size-analysis/package.json index 85590c3c77d..fa4b0eca4fd 100644 --- a/repo-scripts/size-analysis/package.json +++ b/repo-scripts/size-analysis/package.json @@ -20,9 +20,9 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.1", + "@firebase/app": "0.12.2", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", + "@firebase/util": "1.11.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-node-resolve": "16.0.0", From 9bcd1ea9b8cc5b55692765d40df000da8ddef02b Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Wed, 14 May 2025 15:09:28 -0700 Subject: [PATCH 42/77] Fixed scroll behavior (#9043) --- .changeset/two-donkeys-cry.md | 5 +++++ packages/util/src/emulator.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/two-donkeys-cry.md diff --git a/.changeset/two-donkeys-cry.md b/.changeset/two-donkeys-cry.md new file mode 100644 index 00000000000..5462fdcc941 --- /dev/null +++ b/.changeset/two-donkeys-cry.md @@ -0,0 +1,5 @@ +--- +"@firebase/util": patch +--- + +Fixed emulator overlay behavior on scroll diff --git a/packages/util/src/emulator.ts b/packages/util/src/emulator.ts index ff09940d88f..1c4d4ae7a7d 100644 --- a/packages/util/src/emulator.ts +++ b/packages/util/src/emulator.ts @@ -220,7 +220,7 @@ export function updateEmulatorBanner( function setupBannerStyles(bannerEl: HTMLElement): void { bannerEl.style.display = 'flex'; bannerEl.style.background = '#7faaf0'; - bannerEl.style.position = 'absolute'; + bannerEl.style.position = 'fixed'; bannerEl.style.bottom = '5px'; bannerEl.style.left = '5px'; bannerEl.style.padding = '.5em'; From 8601ecb6e847343ded621353a4ad6dc43912c9a9 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Wed, 14 May 2025 15:58:47 -0700 Subject: [PATCH 43/77] Version Packages (manual PR) (#9044) --- .changeset/two-donkeys-cry.md | 5 -- integration/compat-interop/package.json | 28 +++++------ integration/firestore/package.json | 4 +- integration/messaging/package.json | 2 +- packages/analytics-compat/CHANGELOG.md | 9 ++++ packages/analytics-compat/package.json | 10 ++-- packages/analytics/CHANGELOG.md | 9 ++++ packages/analytics/package.json | 10 ++-- packages/app-check-compat/CHANGELOG.md | 9 ++++ packages/app-check-compat/package.json | 10 ++-- packages/app-check/CHANGELOG.md | 8 ++++ packages/app-check/package.json | 8 ++-- packages/app-compat/CHANGELOG.md | 9 ++++ packages/app-compat/package.json | 8 ++-- packages/app/CHANGELOG.md | 8 ++++ packages/app/package.json | 6 +-- packages/auth-compat/CHANGELOG.md | 9 ++++ packages/auth-compat/package.json | 10 ++-- packages/auth/CHANGELOG.md | 8 ++++ packages/auth/package.json | 8 ++-- packages/component/CHANGELOG.md | 7 +++ packages/component/package.json | 4 +- packages/data-connect/CHANGELOG.md | 8 ++++ packages/data-connect/package.json | 8 ++-- packages/database-compat/CHANGELOG.md | 10 ++++ packages/database-compat/package.json | 12 ++--- packages/database-types/CHANGELOG.md | 7 +++ packages/database-types/package.json | 4 +- packages/database/CHANGELOG.md | 8 ++++ packages/database/package.json | 8 ++-- packages/firebase/CHANGELOG.md | 33 +++++++++++++ packages/firebase/package.json | 56 +++++++++++----------- packages/firestore-compat/CHANGELOG.md | 9 ++++ packages/firestore-compat/package.json | 10 ++-- packages/firestore/CHANGELOG.md | 8 ++++ packages/firestore/package.json | 12 ++--- packages/functions-compat/CHANGELOG.md | 9 ++++ packages/functions-compat/package.json | 10 ++-- packages/functions/CHANGELOG.md | 8 ++++ packages/functions/package.json | 8 ++-- packages/installations-compat/CHANGELOG.md | 9 ++++ packages/installations-compat/package.json | 10 ++-- packages/installations/CHANGELOG.md | 8 ++++ packages/installations/package.json | 8 ++-- packages/messaging-compat/CHANGELOG.md | 9 ++++ packages/messaging-compat/package.json | 10 ++-- packages/messaging/CHANGELOG.md | 9 ++++ packages/messaging/package.json | 10 ++-- packages/performance-compat/CHANGELOG.md | 9 ++++ packages/performance-compat/package.json | 10 ++-- packages/performance/CHANGELOG.md | 9 ++++ packages/performance/package.json | 10 ++-- packages/remote-config-compat/CHANGELOG.md | 9 ++++ packages/remote-config-compat/package.json | 10 ++-- packages/remote-config/CHANGELOG.md | 9 ++++ packages/remote-config/package.json | 10 ++-- packages/storage-compat/CHANGELOG.md | 9 ++++ packages/storage-compat/package.json | 12 ++--- packages/storage/CHANGELOG.md | 8 ++++ packages/storage/package.json | 10 ++-- packages/template/package.json | 2 +- packages/util/CHANGELOG.md | 6 +++ packages/util/package.json | 2 +- packages/vertexai/CHANGELOG.md | 8 ++++ packages/vertexai/package.json | 8 ++-- repo-scripts/size-analysis/package.json | 4 +- 66 files changed, 454 insertions(+), 181 deletions(-) delete mode 100644 .changeset/two-donkeys-cry.md diff --git a/.changeset/two-donkeys-cry.md b/.changeset/two-donkeys-cry.md deleted file mode 100644 index 5462fdcc941..00000000000 --- a/.changeset/two-donkeys-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@firebase/util": patch ---- - -Fixed emulator overlay behavior on scroll diff --git a/integration/compat-interop/package.json b/integration/compat-interop/package.json index ddaf85b0762..7b97998233e 100644 --- a/integration/compat-interop/package.json +++ b/integration/compat-interop/package.json @@ -8,20 +8,20 @@ "test:debug": "karma start --browsers Chrome --auto-watch" }, "dependencies": { - "@firebase/app": "0.12.2", - "@firebase/app-compat": "0.3.2", - "@firebase/analytics": "0.10.14", - "@firebase/analytics-compat": "0.2.20", - "@firebase/auth": "1.10.3", - "@firebase/auth-compat": "0.5.23", - "@firebase/functions": "0.12.5", - "@firebase/functions-compat": "0.3.22", - "@firebase/messaging": "0.12.19", - "@firebase/messaging-compat": "0.2.19", - "@firebase/performance": "0.7.4", - "@firebase/performance-compat": "0.2.17", - "@firebase/remote-config": "0.6.2", - "@firebase/remote-config-compat": "0.2.15" + "@firebase/app": "0.12.3", + "@firebase/app-compat": "0.3.3", + "@firebase/analytics": "0.10.15", + "@firebase/analytics-compat": "0.2.21", + "@firebase/auth": "1.10.4", + "@firebase/auth-compat": "0.5.24", + "@firebase/functions": "0.12.6", + "@firebase/functions-compat": "0.3.23", + "@firebase/messaging": "0.12.20", + "@firebase/messaging-compat": "0.2.20", + "@firebase/performance": "0.7.5", + "@firebase/performance-compat": "0.2.18", + "@firebase/remote-config": "0.6.3", + "@firebase/remote-config-compat": "0.2.16" }, "devDependencies": { "typescript": "5.5.4" diff --git a/integration/firestore/package.json b/integration/firestore/package.json index 8fc6d1c6597..dac850a09b3 100644 --- a/integration/firestore/package.json +++ b/integration/firestore/package.json @@ -14,8 +14,8 @@ "test:memory:debug": "yarn build:memory; karma start --auto-watch --browsers Chrome" }, "dependencies": { - "@firebase/app": "0.12.2", - "@firebase/firestore": "4.7.13" + "@firebase/app": "0.12.3", + "@firebase/firestore": "4.7.14" }, "devDependencies": { "@types/mocha": "9.1.1", diff --git a/integration/messaging/package.json b/integration/messaging/package.json index 2d8b5431375..f883f54f098 100644 --- a/integration/messaging/package.json +++ b/integration/messaging/package.json @@ -9,7 +9,7 @@ "test:manual": "mocha --exit" }, "devDependencies": { - "firebase": "11.7.2", + "firebase": "11.7.3", "chai": "4.5.0", "chromedriver": "119.0.1", "express": "4.21.2", diff --git a/packages/analytics-compat/CHANGELOG.md b/packages/analytics-compat/CHANGELOG.md index 1c577c35f85..e950b6ad5eb 100644 --- a/packages/analytics-compat/CHANGELOG.md +++ b/packages/analytics-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/analytics-compat +## 0.2.21 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/analytics@0.10.15 + - @firebase/component@0.6.16 + ## 0.2.20 ### Patch Changes diff --git a/packages/analytics-compat/package.json b/packages/analytics-compat/package.json index ec23086ca6f..fc86eff94ff 100644 --- a/packages/analytics-compat/package.json +++ b/packages/analytics-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/analytics-compat", - "version": "0.2.20", + "version": "0.2.21", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -22,7 +22,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.3.2", + "@firebase/app-compat": "0.3.3", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -52,10 +52,10 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.15", - "@firebase/analytics": "0.10.14", + "@firebase/component": "0.6.16", + "@firebase/analytics": "0.10.15", "@firebase/analytics-types": "0.8.3", - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/analytics/CHANGELOG.md b/packages/analytics/CHANGELOG.md index 75441edf909..06548ecd2cd 100644 --- a/packages/analytics/CHANGELOG.md +++ b/packages/analytics/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/analytics +## 0.10.15 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/installations@0.6.16 + ## 0.10.14 ### Patch Changes diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 82c989ebdd6..2ffd405cc81 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/analytics", - "version": "0.10.14", + "version": "0.10.15", "description": "A analytics package for new firebase packages", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -39,15 +39,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.15", + "@firebase/installations": "0.6.16", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check-compat/CHANGELOG.md b/packages/app-check-compat/CHANGELOG.md index f23eb3b7cfc..825cf013e08 100644 --- a/packages/app-check-compat/CHANGELOG.md +++ b/packages/app-check-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/app-check-compat +## 0.3.24 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/app-check@0.9.3 + - @firebase/component@0.6.16 + ## 0.3.23 ### Patch Changes diff --git a/packages/app-check-compat/package.json b/packages/app-check-compat/package.json index abaee8bf502..e2e214c2d24 100644 --- a/packages/app-check-compat/package.json +++ b/packages/app-check-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-check-compat", - "version": "0.3.23", + "version": "0.3.24", "description": "A compat App Check package for new firebase packages", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -34,16 +34,16 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/app-check": "0.9.2", + "@firebase/app-check": "0.9.3", "@firebase/app-check-types": "0.5.3", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.3.2", + "@firebase/app-compat": "0.3.3", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check/CHANGELOG.md b/packages/app-check/CHANGELOG.md index eaad91dc099..d7e6fdf6446 100644 --- a/packages/app-check/CHANGELOG.md +++ b/packages/app-check/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/app-check +## 0.9.3 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + ## 0.9.2 ### Patch Changes diff --git a/packages/app-check/package.json b/packages/app-check/package.json index 589adcd1880..25b27f90ca4 100644 --- a/packages/app-check/package.json +++ b/packages/app-check/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-check", - "version": "0.9.2", + "version": "0.9.3", "description": "The App Check component of the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,14 +37,14 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "@firebase/logger": "0.4.4", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-compat/CHANGELOG.md b/packages/app-compat/CHANGELOG.md index 35d1b7adc88..38ae80099ea 100644 --- a/packages/app-compat/CHANGELOG.md +++ b/packages/app-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/app-compat +## 0.3.3 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/app@0.12.3 + - @firebase/component@0.6.16 + ## 0.3.2 ### Patch Changes diff --git a/packages/app-compat/package.json b/packages/app-compat/package.json index fbc7cf5c395..a0e0b89a9fc 100644 --- a/packages/app-compat/package.json +++ b/packages/app-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-compat", - "version": "0.3.2", + "version": "0.3.3", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,10 +37,10 @@ }, "license": "Apache-2.0", "dependencies": { - "@firebase/app": "0.12.2", - "@firebase/util": "1.11.2", + "@firebase/app": "0.12.3", + "@firebase/util": "1.11.3", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.15", + "@firebase/component": "0.6.16", "tslib": "^2.1.0" }, "devDependencies": { diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 9620acb9518..10a196ca0fa 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/app +## 0.12.3 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + ## 0.12.2 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 4f78c2c6699..07e70ba14ec 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app", - "version": "0.12.2", + "version": "0.12.3", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,9 +37,9 @@ "typings:internal": "node ../../scripts/build/use_typings.js ./dist/app.d.ts" }, "dependencies": { - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.15", + "@firebase/component": "0.6.16", "idb": "7.1.1", "tslib": "^2.1.0" }, diff --git a/packages/auth-compat/CHANGELOG.md b/packages/auth-compat/CHANGELOG.md index d0fd0f12faa..97ec10f7a77 100644 --- a/packages/auth-compat/CHANGELOG.md +++ b/packages/auth-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/auth-compat +## 0.5.24 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/auth@1.10.4 + - @firebase/component@0.6.16 + ## 0.5.23 ### Patch Changes diff --git a/packages/auth-compat/package.json b/packages/auth-compat/package.json index 7310e55a2e7..c244b8e3f7a 100644 --- a/packages/auth-compat/package.json +++ b/packages/auth-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth-compat", - "version": "0.5.23", + "version": "0.5.24", "description": "FirebaseAuth compatibility package that uses API style compatible with Firebase@8 and prior versions", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,15 +49,15 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/auth": "1.10.3", + "@firebase/auth": "1.10.4", "@firebase/auth-types": "0.13.0", - "@firebase/component": "0.6.15", - "@firebase/util": "1.11.2", + "@firebase/component": "0.6.16", + "@firebase/util": "1.11.3", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.3.2", + "@firebase/app-compat": "0.3.3", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md index 2eb6f477f78..9999db32429 100644 --- a/packages/auth/CHANGELOG.md +++ b/packages/auth/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/auth +## 1.10.4 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + ## 1.10.3 ### Patch Changes diff --git a/packages/auth/package.json b/packages/auth/package.json index 26f8a4c73db..deb2006f9b9 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth", - "version": "1.10.3", + "version": "1.10.4", "description": "The Firebase Authenticaton component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/node/index.js", @@ -124,14 +124,14 @@ } }, "dependencies": { - "@firebase/component": "0.6.15", + "@firebase/component": "0.6.16", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-strip": "2.1.0", "@types/express": "4.17.21", diff --git a/packages/component/CHANGELOG.md b/packages/component/CHANGELOG.md index b711d33fbdd..5dca079098c 100644 --- a/packages/component/CHANGELOG.md +++ b/packages/component/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/component +## 0.6.16 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + ## 0.6.15 ### Patch Changes diff --git a/packages/component/package.json b/packages/component/package.json index 2297a386eb1..cb0e6a336d2 100644 --- a/packages/component/package.json +++ b/packages/component/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/component", - "version": "0.6.15", + "version": "0.6.16", "description": "Firebase Component Platform", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -31,7 +31,7 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "tslib": "^2.1.0" }, "license": "Apache-2.0", diff --git a/packages/data-connect/CHANGELOG.md b/packages/data-connect/CHANGELOG.md index 7dca51b9ddd..5e9eb3532bb 100644 --- a/packages/data-connect/CHANGELOG.md +++ b/packages/data-connect/CHANGELOG.md @@ -1,5 +1,13 @@ ## Unreleased +## 0.3.7 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + ## 0.3.6 ### Patch Changes diff --git a/packages/data-connect/package.json b/packages/data-connect/package.json index 3982438f5b8..f4dfa7e0863 100644 --- a/packages/data-connect/package.json +++ b/packages/data-connect/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/data-connect", - "version": "0.3.6", + "version": "0.3.7", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,13 +49,13 @@ }, "dependencies": { "@firebase/auth-interop-types": "0.2.4", - "@firebase/component": "0.6.15", + "@firebase/component": "0.6.16", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/database-compat/CHANGELOG.md b/packages/database-compat/CHANGELOG.md index 02d06d6f5c4..1dab50a408f 100644 --- a/packages/database-compat/CHANGELOG.md +++ b/packages/database-compat/CHANGELOG.md @@ -1,5 +1,15 @@ # @firebase/database-compat +## 2.0.8 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/database@1.0.17 + - @firebase/database-types@1.0.13 + ## 2.0.7 ### Patch Changes diff --git a/packages/database-compat/package.json b/packages/database-compat/package.json index 6b552d78128..d24167a9965 100644 --- a/packages/database-compat/package.json +++ b/packages/database-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database-compat", - "version": "2.0.7", + "version": "2.0.8", "description": "The Realtime Database component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.js", @@ -49,15 +49,15 @@ "add-compat-overloads": "ts-node-script ../../scripts/build/create-overloads.ts -i ../database/dist/public.d.ts -o dist/database-compat/src/index.d.ts -a -r Database:types.FirebaseDatabase -r Query:types.Query -r DatabaseReference:types.Reference -r FirebaseApp:FirebaseAppCompat --moduleToEnhance @firebase/database" }, "dependencies": { - "@firebase/database": "1.0.16", - "@firebase/database-types": "1.0.12", + "@firebase/database": "1.0.17", + "@firebase/database-types": "1.0.13", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.2", + "@firebase/app-compat": "0.3.3", "typescript": "5.5.4" }, "repository": { diff --git a/packages/database-types/CHANGELOG.md b/packages/database-types/CHANGELOG.md index 9cef5bc8ef4..b2be70c2efc 100644 --- a/packages/database-types/CHANGELOG.md +++ b/packages/database-types/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/database-types +## 1.0.13 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + ## 1.0.12 ### Patch Changes diff --git a/packages/database-types/package.json b/packages/database-types/package.json index b93ba52dd38..f6cbb176699 100644 --- a/packages/database-types/package.json +++ b/packages/database-types/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database-types", - "version": "1.0.12", + "version": "1.0.13", "description": "@firebase/database Types", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -13,7 +13,7 @@ ], "dependencies": { "@firebase/app-types": "0.9.3", - "@firebase/util": "1.11.2" + "@firebase/util": "1.11.3" }, "repository": { "directory": "packages/database-types", diff --git a/packages/database/CHANGELOG.md b/packages/database/CHANGELOG.md index 7fdf8b2ee08..ea27ec478d8 100644 --- a/packages/database/CHANGELOG.md +++ b/packages/database/CHANGELOG.md @@ -1,5 +1,13 @@ # Unreleased +## 1.0.17 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + ## 1.0.16 ### Patch Changes diff --git a/packages/database/package.json b/packages/database/package.json index a1fb1e8bd1c..719469d5cca 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database", - "version": "1.0.16", + "version": "1.0.17", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,15 +49,15 @@ "peerDependencies": {}, "dependencies": { "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "@firebase/app-check-interop-types": "0.3.3", "@firebase/auth-interop-types": "0.2.4", "faye-websocket": "0.11.4", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/firebase/CHANGELOG.md b/packages/firebase/CHANGELOG.md index ecb0b1ac653..ed42e9ee853 100644 --- a/packages/firebase/CHANGELOG.md +++ b/packages/firebase/CHANGELOG.md @@ -1,5 +1,38 @@ # firebase +## 11.7.3 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/analytics@0.10.15 + - @firebase/analytics-compat@0.2.21 + - @firebase/app@0.12.3 + - @firebase/app-check@0.9.3 + - @firebase/app-check-compat@0.3.24 + - @firebase/app-compat@0.3.3 + - @firebase/auth@1.10.4 + - @firebase/auth-compat@0.5.24 + - @firebase/data-connect@0.3.7 + - @firebase/database@1.0.17 + - @firebase/database-compat@2.0.8 + - @firebase/firestore@4.7.14 + - @firebase/firestore-compat@0.3.49 + - @firebase/functions@0.12.6 + - @firebase/functions-compat@0.3.23 + - @firebase/installations@0.6.16 + - @firebase/installations-compat@0.2.16 + - @firebase/messaging@0.12.20 + - @firebase/messaging-compat@0.2.20 + - @firebase/performance@0.7.5 + - @firebase/performance-compat@0.2.18 + - @firebase/remote-config@0.6.3 + - @firebase/remote-config-compat@0.2.16 + - @firebase/storage@0.13.10 + - @firebase/storage-compat@0.3.20 + - @firebase/vertexai@1.2.4 + ## 11.7.2 ### Patch Changes diff --git a/packages/firebase/package.json b/packages/firebase/package.json index b4edcedca3a..da489e837ea 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -1,6 +1,6 @@ { "name": "firebase", - "version": "11.7.2", + "version": "11.7.3", "description": "Firebase JavaScript library for web and Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -399,34 +399,34 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { - "@firebase/app": "0.12.2", - "@firebase/app-compat": "0.3.2", + "@firebase/app": "0.12.3", + "@firebase/app-compat": "0.3.3", "@firebase/app-types": "0.9.3", - "@firebase/auth": "1.10.3", - "@firebase/auth-compat": "0.5.23", - "@firebase/data-connect": "0.3.6", - "@firebase/database": "1.0.16", - "@firebase/database-compat": "2.0.7", - "@firebase/firestore": "4.7.13", - "@firebase/firestore-compat": "0.3.48", - "@firebase/functions": "0.12.5", - "@firebase/functions-compat": "0.3.22", - "@firebase/installations": "0.6.15", - "@firebase/installations-compat": "0.2.15", - "@firebase/messaging": "0.12.19", - "@firebase/messaging-compat": "0.2.19", - "@firebase/storage": "0.13.9", - "@firebase/storage-compat": "0.3.19", - "@firebase/performance": "0.7.4", - "@firebase/performance-compat": "0.2.17", - "@firebase/remote-config": "0.6.2", - "@firebase/remote-config-compat": "0.2.15", - "@firebase/analytics": "0.10.14", - "@firebase/analytics-compat": "0.2.20", - "@firebase/app-check": "0.9.2", - "@firebase/app-check-compat": "0.3.23", - "@firebase/util": "1.11.2", - "@firebase/vertexai": "1.2.3" + "@firebase/auth": "1.10.4", + "@firebase/auth-compat": "0.5.24", + "@firebase/data-connect": "0.3.7", + "@firebase/database": "1.0.17", + "@firebase/database-compat": "2.0.8", + "@firebase/firestore": "4.7.14", + "@firebase/firestore-compat": "0.3.49", + "@firebase/functions": "0.12.6", + "@firebase/functions-compat": "0.3.23", + "@firebase/installations": "0.6.16", + "@firebase/installations-compat": "0.2.16", + "@firebase/messaging": "0.12.20", + "@firebase/messaging-compat": "0.2.20", + "@firebase/storage": "0.13.10", + "@firebase/storage-compat": "0.3.20", + "@firebase/performance": "0.7.5", + "@firebase/performance-compat": "0.2.18", + "@firebase/remote-config": "0.6.3", + "@firebase/remote-config-compat": "0.2.16", + "@firebase/analytics": "0.10.15", + "@firebase/analytics-compat": "0.2.21", + "@firebase/app-check": "0.9.3", + "@firebase/app-check-compat": "0.3.24", + "@firebase/util": "1.11.3", + "@firebase/vertexai": "1.2.4" }, "devDependencies": { "rollup": "2.79.2", diff --git a/packages/firestore-compat/CHANGELOG.md b/packages/firestore-compat/CHANGELOG.md index 4b05f22a243..f22abcaf049 100644 --- a/packages/firestore-compat/CHANGELOG.md +++ b/packages/firestore-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/firestore-compat +## 0.3.49 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/firestore@4.7.14 + ## 0.3.48 ### Patch Changes diff --git a/packages/firestore-compat/package.json b/packages/firestore-compat/package.json index 29d57083e1a..e2243ccba9f 100644 --- a/packages/firestore-compat/package.json +++ b/packages/firestore-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore-compat", - "version": "0.3.48", + "version": "0.3.49", "description": "The Cloud Firestore component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -46,14 +46,14 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/component": "0.6.15", - "@firebase/firestore": "4.7.13", - "@firebase/util": "1.11.2", + "@firebase/component": "0.6.16", + "@firebase/firestore": "4.7.14", + "@firebase/util": "1.11.3", "@firebase/firestore-types": "3.0.3", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.2", + "@firebase/app-compat": "0.3.3", "@types/eslint": "7.29.0", "rollup": "2.79.2", "rollup-plugin-sourcemaps": "0.6.3", diff --git a/packages/firestore/CHANGELOG.md b/packages/firestore/CHANGELOG.md index 84bbccd8b8b..19144f16afd 100644 --- a/packages/firestore/CHANGELOG.md +++ b/packages/firestore/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/firestore +## 4.7.14 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + ## 4.7.13 ### Patch Changes diff --git a/packages/firestore/package.json b/packages/firestore/package.json index 5ff890803a3..7c4a27a291b 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore", - "version": "4.7.13", + "version": "4.7.14", "engines": { "node": ">=18.0.0" }, @@ -98,9 +98,9 @@ "lite/package.json" ], "dependencies": { - "@firebase/component": "0.6.15", + "@firebase/component": "0.6.16", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "@firebase/webchannel-wrapper": "1.0.3", "@grpc/grpc-js": "~1.9.0", "@grpc/proto-loader": "^0.7.8", @@ -110,9 +110,9 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.2", - "@firebase/app-compat": "0.3.2", - "@firebase/auth": "1.10.3", + "@firebase/app": "0.12.3", + "@firebase/app-compat": "0.3.3", + "@firebase/auth": "1.10.4", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", "@types/eslint": "7.29.0", diff --git a/packages/functions-compat/CHANGELOG.md b/packages/functions-compat/CHANGELOG.md index 60a92238883..991f41c0868 100644 --- a/packages/functions-compat/CHANGELOG.md +++ b/packages/functions-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/functions-compat +## 0.3.23 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/functions@0.12.6 + ## 0.3.22 ### Patch Changes diff --git a/packages/functions-compat/package.json b/packages/functions-compat/package.json index 75238014aed..60d1ffacfbc 100644 --- a/packages/functions-compat/package.json +++ b/packages/functions-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/functions-compat", - "version": "0.3.22", + "version": "0.3.23", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -29,7 +29,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.3.2", + "@firebase/app-compat": "0.3.3", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -62,10 +62,10 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.15", - "@firebase/functions": "0.12.5", + "@firebase/component": "0.6.16", + "@firebase/functions": "0.12.6", "@firebase/functions-types": "0.6.3", - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/functions/CHANGELOG.md b/packages/functions/CHANGELOG.md index 89a0f1cd396..a63cd968b70 100644 --- a/packages/functions/CHANGELOG.md +++ b/packages/functions/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/functions +## 0.12.6 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + ## 0.12.5 ### Patch Changes diff --git a/packages/functions/package.json b/packages/functions/package.json index 560327628a8..6fde3987e75 100644 --- a/packages/functions/package.json +++ b/packages/functions/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/functions", - "version": "0.12.5", + "version": "0.12.6", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -49,7 +49,7 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -65,11 +65,11 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.15", + "@firebase/component": "0.6.16", "@firebase/messaging-interop-types": "0.2.3", "@firebase/auth-interop-types": "0.2.4", "@firebase/app-check-interop-types": "0.3.3", - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/installations-compat/CHANGELOG.md b/packages/installations-compat/CHANGELOG.md index 1e63861c661..c46d91d70ab 100644 --- a/packages/installations-compat/CHANGELOG.md +++ b/packages/installations-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/installations-compat +## 0.2.16 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/installations@0.6.16 + ## 0.2.15 ### Patch Changes diff --git a/packages/installations-compat/package.json b/packages/installations-compat/package.json index c485f5d1ff2..73b318577ce 100644 --- a/packages/installations-compat/package.json +++ b/packages/installations-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/installations-compat", - "version": "0.2.15", + "version": "0.2.16", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", "module": "dist/esm/index.esm2017.js", @@ -44,7 +44,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app-compat": "0.3.2", + "@firebase/app-compat": "0.3.3", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", @@ -57,10 +57,10 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.15", + "@firebase/installations": "0.6.16", "@firebase/installations-types": "0.5.3", - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "tslib": "^2.1.0" } } diff --git a/packages/installations/CHANGELOG.md b/packages/installations/CHANGELOG.md index 79c23c4ba6f..035655cf96f 100644 --- a/packages/installations/CHANGELOG.md +++ b/packages/installations/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/installations +## 0.6.16 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + ## 0.6.15 ### Patch Changes diff --git a/packages/installations/package.json b/packages/installations/package.json index f1b21b10b96..bd35c91a7ee 100644 --- a/packages/installations/package.json +++ b/packages/installations/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/installations", - "version": "0.6.15", + "version": "0.6.16", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", "module": "dist/esm/index.esm2017.js", @@ -49,7 +49,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", @@ -62,8 +62,8 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "idb": "7.1.1", "tslib": "^2.1.0" } diff --git a/packages/messaging-compat/CHANGELOG.md b/packages/messaging-compat/CHANGELOG.md index e0a157271dc..cafca2b6ec5 100644 --- a/packages/messaging-compat/CHANGELOG.md +++ b/packages/messaging-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/messaging-compat +## 0.2.20 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/messaging@0.12.20 + ## 0.2.19 ### Patch Changes diff --git a/packages/messaging-compat/package.json b/packages/messaging-compat/package.json index cc134aaf97a..4f36674d8ef 100644 --- a/packages/messaging-compat/package.json +++ b/packages/messaging-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/messaging-compat", - "version": "0.2.19", + "version": "0.2.20", "license": "Apache-2.0", "description": "", "author": "Firebase (https://firebase.google.com/)", @@ -38,13 +38,13 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/messaging": "0.12.19", - "@firebase/component": "0.6.15", - "@firebase/util": "1.11.2", + "@firebase/messaging": "0.12.20", + "@firebase/component": "0.6.16", + "@firebase/util": "1.11.3", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.2", + "@firebase/app-compat": "0.3.3", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", "ts-essentials": "9.4.2", diff --git a/packages/messaging/CHANGELOG.md b/packages/messaging/CHANGELOG.md index 9b8472b8520..c8b10a8e5d1 100644 --- a/packages/messaging/CHANGELOG.md +++ b/packages/messaging/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/messaging +## 0.12.20 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/installations@0.6.16 + ## 0.12.19 ### Patch Changes diff --git a/packages/messaging/package.json b/packages/messaging/package.json index b3b81cf0836..c412ba5eecc 100644 --- a/packages/messaging/package.json +++ b/packages/messaging/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/messaging", - "version": "0.12.19", + "version": "0.12.20", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -52,15 +52,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.15", + "@firebase/installations": "0.6.16", "@firebase/messaging-interop-types": "0.2.3", - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "idb": "7.1.1", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/performance-compat/CHANGELOG.md b/packages/performance-compat/CHANGELOG.md index e0b57fd0f43..ee1ed073fdb 100644 --- a/packages/performance-compat/CHANGELOG.md +++ b/packages/performance-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/performance-compat +## 0.2.18 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/performance@0.7.5 + ## 0.2.17 ### Patch Changes diff --git a/packages/performance-compat/package.json b/packages/performance-compat/package.json index 741ff2f83d0..71286577acc 100644 --- a/packages/performance-compat/package.json +++ b/packages/performance-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/performance-compat", - "version": "0.2.17", + "version": "0.2.18", "description": "The compatibility package of Firebase Performance", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -38,11 +38,11 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/performance": "0.7.4", + "@firebase/performance": "0.7.5", "@firebase/performance-types": "0.2.3", - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.15", + "@firebase/component": "0.6.16", "tslib": "^2.1.0" }, "devDependencies": { @@ -51,7 +51,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.3.2" + "@firebase/app-compat": "0.3.3" }, "repository": { "directory": "packages/performance-compat", diff --git a/packages/performance/CHANGELOG.md b/packages/performance/CHANGELOG.md index 6e3bdfb842f..fe169a3ad39 100644 --- a/packages/performance/CHANGELOG.md +++ b/packages/performance/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/performance +## 0.7.5 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/installations@0.6.16 + ## 0.7.4 ### Patch Changes diff --git a/packages/performance/package.json b/packages/performance/package.json index c676fd7ad96..e7a9fed6020 100644 --- a/packages/performance/package.json +++ b/packages/performance/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/performance", - "version": "0.7.4", + "version": "0.7.5", "description": "Firebase performance for web", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -39,15 +39,15 @@ }, "dependencies": { "@firebase/logger": "0.4.4", - "@firebase/installations": "0.6.15", - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/installations": "0.6.16", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "tslib": "^2.1.0", "web-vitals": "^4.2.4" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/remote-config-compat/CHANGELOG.md b/packages/remote-config-compat/CHANGELOG.md index ec5e443efe8..d2bdfb1fac2 100644 --- a/packages/remote-config-compat/CHANGELOG.md +++ b/packages/remote-config-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/remote-config-compat +## 0.2.16 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/remote-config@0.6.3 + ## 0.2.15 ### Patch Changes diff --git a/packages/remote-config-compat/package.json b/packages/remote-config-compat/package.json index 3d8e22e6dbf..7d2e5483efe 100644 --- a/packages/remote-config-compat/package.json +++ b/packages/remote-config-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/remote-config-compat", - "version": "0.2.15", + "version": "0.2.16", "description": "The compatibility package of Remote Config", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,11 +37,11 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/remote-config": "0.6.2", + "@firebase/remote-config": "0.6.3", "@firebase/remote-config-types": "0.4.0", - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.15", + "@firebase/component": "0.6.16", "tslib": "^2.1.0" }, "devDependencies": { @@ -50,7 +50,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.3.2" + "@firebase/app-compat": "0.3.3" }, "repository": { "directory": "packages/remote-config-compat", diff --git a/packages/remote-config/CHANGELOG.md b/packages/remote-config/CHANGELOG.md index 640f6c34f4d..07fe5cc9605 100644 --- a/packages/remote-config/CHANGELOG.md +++ b/packages/remote-config/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/remote-config +## 0.6.3 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/installations@0.6.16 + ## 0.6.2 ### Patch Changes diff --git a/packages/remote-config/package.json b/packages/remote-config/package.json index 4f1fe85e9f8..ffa93164d3a 100644 --- a/packages/remote-config/package.json +++ b/packages/remote-config/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/remote-config", - "version": "0.6.2", + "version": "0.6.3", "description": "The Remote Config package of the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -40,15 +40,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.15", + "@firebase/installations": "0.6.16", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/storage-compat/CHANGELOG.md b/packages/storage-compat/CHANGELOG.md index 31689803f0a..a9a3f9e1621 100644 --- a/packages/storage-compat/CHANGELOG.md +++ b/packages/storage-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/storage-compat +## 0.3.20 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + - @firebase/storage@0.13.10 + ## 0.3.19 ### Patch Changes diff --git a/packages/storage-compat/package.json b/packages/storage-compat/package.json index 445b13d5045..046b0a79563 100644 --- a/packages/storage-compat/package.json +++ b/packages/storage-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage-compat", - "version": "0.3.19", + "version": "0.3.20", "description": "The Firebase Firestore compatibility package", "author": "Firebase (https://firebase.google.com/)", "main": "./dist/index.cjs.js", @@ -37,15 +37,15 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/storage": "0.13.9", + "@firebase/storage": "0.13.10", "@firebase/storage-types": "0.8.3", - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.2", - "@firebase/auth-compat": "0.5.23", + "@firebase/app-compat": "0.3.3", + "@firebase/auth-compat": "0.5.24", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/storage/CHANGELOG.md b/packages/storage/CHANGELOG.md index e18aa9af7b3..2872e5a792b 100644 --- a/packages/storage/CHANGELOG.md +++ b/packages/storage/CHANGELOG.md @@ -1,5 +1,13 @@ #Unreleased +## 0.13.10 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + ## 0.13.9 ### Patch Changes diff --git a/packages/storage/package.json b/packages/storage/package.json index 5b3e9bb871d..9c5a738bee1 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage", - "version": "0.13.9", + "version": "0.13.10", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -46,16 +46,16 @@ }, "license": "Apache-2.0", "dependencies": { - "@firebase/util": "1.11.2", - "@firebase/component": "0.6.15", + "@firebase/util": "1.11.3", + "@firebase/component": "0.6.16", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.2", - "@firebase/auth": "1.10.3", + "@firebase/app": "0.12.3", + "@firebase/auth": "1.10.4", "rollup": "2.79.2", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", diff --git a/packages/template/package.json b/packages/template/package.json index d8d49446172..5d8c84d3566 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -48,7 +48,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/util/CHANGELOG.md b/packages/util/CHANGELOG.md index 79857db4665..9eaaa3415e4 100644 --- a/packages/util/CHANGELOG.md +++ b/packages/util/CHANGELOG.md @@ -1,5 +1,11 @@ # @firebase/util +## 1.11.3 + +### Patch Changes + +- [`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b) [#9043](https://github.com/firebase/firebase-js-sdk/pull/9043) - Fixed emulator overlay behavior on scroll + ## 1.11.2 ### Patch Changes diff --git a/packages/util/package.json b/packages/util/package.json index 61ea91f5a6c..4e50552bb2f 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/util", - "version": "1.11.2", + "version": "1.11.3", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", diff --git a/packages/vertexai/CHANGELOG.md b/packages/vertexai/CHANGELOG.md index 168841c958c..d1f30760110 100644 --- a/packages/vertexai/CHANGELOG.md +++ b/packages/vertexai/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/vertexai +## 1.2.4 + +### Patch Changes + +- Updated dependencies [[`9bcd1ea`](https://github.com/firebase/firebase-js-sdk/commit/9bcd1ea9b8cc5b55692765d40df000da8ddef02b)]: + - @firebase/util@1.11.3 + - @firebase/component@0.6.16 + ## 1.2.3 ### Patch Changes diff --git a/packages/vertexai/package.json b/packages/vertexai/package.json index e8a7e4e6d09..54e928d0f77 100644 --- a/packages/vertexai/package.json +++ b/packages/vertexai/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/vertexai", - "version": "1.2.3", + "version": "1.2.4", "description": "A Firebase SDK for VertexAI", "author": "Firebase (https://firebase.google.com/)", "engines": { @@ -49,14 +49,14 @@ }, "dependencies": { "@firebase/app-check-interop-types": "0.3.3", - "@firebase/component": "0.6.15", + "@firebase/component": "0.6.16", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/repo-scripts/size-analysis/package.json b/repo-scripts/size-analysis/package.json index fa4b0eca4fd..09b6cc14ca6 100644 --- a/repo-scripts/size-analysis/package.json +++ b/repo-scripts/size-analysis/package.json @@ -20,9 +20,9 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.2", + "@firebase/app": "0.12.3", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.2", + "@firebase/util": "1.11.3", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-node-resolve": "16.0.0", From 5b539058e150a3a1fea72850fe967b3bb60ec69f Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Thu, 15 May 2025 09:26:28 -0700 Subject: [PATCH 44/77] Remove redundant changeset --- .changeset/three-singers-wonder.md | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .changeset/three-singers-wonder.md diff --git a/.changeset/three-singers-wonder.md b/.changeset/three-singers-wonder.md deleted file mode 100644 index 72dea9d7aa5..00000000000 --- a/.changeset/three-singers-wonder.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@firebase/auth": patch -"@firebase/firestore": patch -"@firebase/util": patch -"@firebase/database": patch -"@firebase/storage": patch -"@firebase/functions": patch ---- - -Add Emulator Overlay From 880110bbac7868419fad447fc191d8f1d7ada38d Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Thu, 15 May 2025 10:13:03 -0700 Subject: [PATCH 45/77] Update e2e tests to reflect AI package and new API (#9017) --- e2e/package.json | 2 +- e2e/sample-apps/modular.js | 16 +- e2e/tests/modular.test.ts | 15 +- e2e/yarn.lock | 2189 ++++++++++++++++++------------------ 4 files changed, 1136 insertions(+), 1086 deletions(-) diff --git a/e2e/package.json b/e2e/package.json index 189479017e4..ca33e7f696f 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -17,7 +17,7 @@ "author": "", "license": "ISC", "dependencies": { - "firebase": "11.3.0" + "firebase": "^11.8.0-20250512211235" }, "devDependencies": { "@babel/core": "7.26.8", diff --git a/e2e/sample-apps/modular.js b/e2e/sample-apps/modular.js index 9e943e04494..d01b5b0139d 100644 --- a/e2e/sample-apps/modular.js +++ b/e2e/sample-apps/modular.js @@ -58,7 +58,7 @@ import { onValue, off } from 'firebase/database'; -import { getGenerativeModel, getVertexAI, VertexAI } from 'firebase/vertexai'; +import { getGenerativeModel, getAI, VertexAIBackend } from 'firebase/ai'; import { getDataConnect, DataConnect } from 'firebase/data-connect'; /** @@ -307,15 +307,15 @@ function callPerformance(app) { } /** - * VertexAI smoke test. + * AI smoke test. * Just make sure some functions can be called without obvious errors. */ -async function callVertexAI(app) { - console.log('[VERTEXAI] start'); - const vertexAI = getVertexAI(app); - const model = getGenerativeModel(vertexAI, { model: 'gemini-1.5-flash' }); +async function callAI(app) { + console.log('[AI] start'); + const ai = getAI(app, { backend: new VertexAIBackend() }); + const model = getGenerativeModel(ai, { model: 'gemini-1.5-flash' }); const result = await model.countTokens('abcdefg'); - console.log(`[VERTEXAI] counted tokens: ${result.totalTokens}`); + console.log(`[AI] counted tokens: ${result.totalTokens}`); } /** @@ -350,7 +350,7 @@ async function main() { callAnalytics(app); callPerformance(app); await callFunctions(app); - await callVertexAI(app); + await callAI(app); callDataConnect(app); await authLogout(app); console.log('DONE'); diff --git a/e2e/tests/modular.test.ts b/e2e/tests/modular.test.ts index 005fadef19a..c6cd0b88c8e 100644 --- a/e2e/tests/modular.test.ts +++ b/e2e/tests/modular.test.ts @@ -86,7 +86,12 @@ import { StorageReference, deleteObject } from 'firebase/storage'; -import { getGenerativeModel, getVertexAI, VertexAI } from 'firebase/vertexai'; +import { + getGenerativeModel, + getAI, + AI, + VertexAIBackend +} from 'firebase/vertexai'; import { getDataConnect, DataConnect } from 'firebase/data-connect'; import { config, testAccount } from '../firebase-config'; import 'jest'; @@ -307,13 +312,13 @@ describe('MODULAR', () => { }); }); - describe('VERTEXAI', () => { - let vertexAI: VertexAI; + describe('AI', () => { + let ai: AI; it('getVertexAI()', () => { - vertexAI = getVertexAI(app); + ai = getAI(app, { backend: new VertexAIBackend() }); }); it('getGenerativeModel() and countTokens()', async () => { - const model = getGenerativeModel(vertexAI, { model: 'gemini-1.5-flash' }); + const model = getGenerativeModel(ai, { model: 'gemini-1.5-flash' }); expect(model.model).toMatch(/gemini-1.5-flash$/); const result = await model.countTokens('abcdefg'); expect(result.totalTokens).toBeTruthy; diff --git a/e2e/yarn.lock b/e2e/yarn.lock index 08a958955b7..c20459aecdd 100644 --- a/e2e/yarn.lock +++ b/e2e/yarn.lock @@ -4,7 +4,7 @@ "@ampproject/remapping@^2.2.0": version "2.3.0" - resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz" integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== dependencies: "@jridgewell/gen-mapping" "^0.3.5" @@ -12,16 +12,25 @@ "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.26.2": version "7.26.2" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== dependencies: "@babel/helper-validator-identifier" "^7.25.9" js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0": +"@babel/code-frame@^7.25.9": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz#9374b5cd068d128dac0b94ff482594273b1c2815" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz" + integrity sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/code-frame@^7.26.0": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz" integrity sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g== dependencies: "@babel/helper-validator-identifier" "^7.25.9" @@ -30,17 +39,17 @@ "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz#f02ba6d34e88fadd5e8861e8b38902f43cc1c819" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz" integrity sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA== "@babel/compat-data@^7.26.5", "@babel/compat-data@^7.26.8": version "7.26.8" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz" integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== -"@babel/core@7.26.8": +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.8.0", "@babel/core@7.26.8": version "7.26.8" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.8.tgz#7742f11c75acea6b08a8e24c5c0c8c89e89bf53e" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.8.tgz" integrity sha512-l+lkXCHS6tQEc5oUpK28xBOZ6+HwaH7YwoYQbLFiYb4nS2/l1tKnZEtEWkD0GuiYdvArf9qBS0XlQGXzPMsNqQ== dependencies: "@ampproject/remapping" "^2.2.0" @@ -60,9 +69,51 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": +"@babel/core@^7.11.6": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz" + integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.0" + "@babel/generator" "^7.26.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.0" + "@babel/parser" "^7.26.0" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.26.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/core@^7.12.3": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz" + integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.0" + "@babel/generator" "^7.26.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.0" + "@babel/parser" "^7.26.0" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.26.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/core@^7.23.9": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz" integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== dependencies: "@ampproject/remapping" "^2.2.0" @@ -83,7 +134,7 @@ "@babel/generator@^7.25.9", "@babel/generator@^7.26.0": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz#505cc7c90d92513f458a477e5ef0703e7c91b8d7" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz" integrity sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w== dependencies: "@babel/parser" "^7.26.0" @@ -94,7 +145,7 @@ "@babel/generator@^7.26.8", "@babel/generator@^7.26.9": version "7.26.9" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz" integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg== dependencies: "@babel/parser" "^7.26.9" @@ -105,7 +156,7 @@ "@babel/generator@^7.7.2": version "7.26.3" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz#ab8d4360544a425c90c248df7059881f4b2ce019" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz" integrity sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ== dependencies: "@babel/parser" "^7.26.3" @@ -116,14 +167,14 @@ "@babel/helper-annotate-as-pure@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz" integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== dependencies: "@babel/types" "^7.25.9" "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz" integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== dependencies: "@babel/compat-data" "^7.25.9" @@ -134,7 +185,7 @@ "@babel/helper-compilation-targets@^7.26.5": version "7.26.5" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz" integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== dependencies: "@babel/compat-data" "^7.26.5" @@ -145,7 +196,7 @@ "@babel/helper-create-class-features-plugin@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz#7644147706bb90ff613297d49ed5266bde729f83" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz" integrity sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ== dependencies: "@babel/helper-annotate-as-pure" "^7.25.9" @@ -158,7 +209,7 @@ "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz#3e8999db94728ad2b2458d7a470e7770b7764e26" + resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz" integrity sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw== dependencies: "@babel/helper-annotate-as-pure" "^7.25.9" @@ -167,7 +218,7 @@ "@babel/helper-define-polyfill-provider@^0.6.2": version "0.6.2" - resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz#18594f789c3594acb24cfdb4a7f7b7d2e8bd912d" + resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz" integrity sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== dependencies: "@babel/helper-compilation-targets" "^7.22.6" @@ -178,7 +229,7 @@ "@babel/helper-define-polyfill-provider@^0.6.3": version "0.6.3" - resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz#f4f2792fae2ef382074bc2d713522cf24e6ddb21" + resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz" integrity sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg== dependencies: "@babel/helper-compilation-targets" "^7.22.6" @@ -189,7 +240,7 @@ "@babel/helper-member-expression-to-functions@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz" integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== dependencies: "@babel/traverse" "^7.25.9" @@ -197,7 +248,7 @@ "@babel/helper-module-imports@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz" integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== dependencies: "@babel/traverse" "^7.25.9" @@ -205,7 +256,7 @@ "@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz" integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== dependencies: "@babel/helper-module-imports" "^7.25.9" @@ -214,24 +265,24 @@ "@babel/helper-optimise-call-expression@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz" integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== dependencies: "@babel/types" "^7.25.9" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.8.0": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz" integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== "@babel/helper-plugin-utils@^7.26.5": version "7.26.5" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz" integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== "@babel/helper-remap-async-to-generator@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz#e53956ab3d5b9fb88be04b3e2f31b523afd34b92" + resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz" integrity sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw== dependencies: "@babel/helper-annotate-as-pure" "^7.25.9" @@ -240,7 +291,7 @@ "@babel/helper-replace-supers@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz#ba447224798c3da3f8713fc272b145e33da6a5c5" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz" integrity sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ== dependencies: "@babel/helper-member-expression-to-functions" "^7.25.9" @@ -249,7 +300,7 @@ "@babel/helper-simple-access@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz#6d51783299884a2c74618d6ef0f86820ec2e7739" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz" integrity sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q== dependencies: "@babel/traverse" "^7.25.9" @@ -257,7 +308,7 @@ "@babel/helper-skip-transparent-expression-wrappers@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz" integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== dependencies: "@babel/traverse" "^7.25.9" @@ -265,22 +316,22 @@ "@babel/helper-string-parser@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz" integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== "@babel/helper-validator-identifier@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz" integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== "@babel/helper-validator-option@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz" integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== "@babel/helper-wrap-function@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz#d99dfd595312e6c894bd7d237470025c85eea9d0" + resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz" integrity sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g== dependencies: "@babel/template" "^7.25.9" @@ -289,7 +340,7 @@ "@babel/helpers@^7.26.0": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz" integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== dependencies: "@babel/template" "^7.25.9" @@ -297,36 +348,50 @@ "@babel/helpers@^7.26.7": version "7.26.9" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz#28f3fb45252fc88ef2dc547c8a911c255fc9fef6" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz" integrity sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA== dependencies: "@babel/template" "^7.26.9" "@babel/types" "^7.26.9" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.26.3": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9": version "7.26.3" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz" integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== dependencies: "@babel/types" "^7.26.3" -"@babel/parser@^7.25.9", "@babel/parser@^7.26.0": +"@babel/parser@^7.25.9": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.0.tgz#710a75a7d805a8f72753154e451474e9795b121c" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.0.tgz" integrity sha512-aP8x5pIw3xvYr/sXT+SEUwyhrXT8rUJRZltK/qN3Db80dcKpTett8cJxHyjk+xYSVXvNnl2SfcJVjbwxpOSscA== dependencies: "@babel/types" "^7.26.0" +"@babel/parser@^7.26.0": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.0.tgz" + integrity sha512-aP8x5pIw3xvYr/sXT+SEUwyhrXT8rUJRZltK/qN3Db80dcKpTett8cJxHyjk+xYSVXvNnl2SfcJVjbwxpOSscA== + dependencies: + "@babel/types" "^7.26.0" + +"@babel/parser@^7.26.3": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz" + integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== + dependencies: + "@babel/types" "^7.26.3" + "@babel/parser@^7.26.8", "@babel/parser@^7.26.9": version "7.26.9" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz" integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== dependencies: "@babel/types" "^7.26.9" "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz" integrity sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -334,21 +399,21 @@ "@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz#af9e4fb63ccb8abcb92375b2fcfe36b60c774d30" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz" integrity sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz#e8dc26fcd616e6c5bf2bd0d5a2c151d4f92a9137" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz" integrity sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz#807a667f9158acac6f6164b4beb85ad9ebc9e1d1" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz" integrity sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -357,7 +422,7 @@ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz#de7093f1e7deaf68eadd7cc6b07f2ab82543269e" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz" integrity sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -365,138 +430,138 @@ "@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": version "7.21.0-placeholder-for-preset-env.2" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-class-static-block@^7.14.5": version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz" integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-import-assertions@^7.26.0": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz" integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-syntax-import-attributes@^7.24.7", "@babel/plugin-syntax-import-attributes@^7.26.0": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz" integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-jsx@^7.25.9", "@babel/plugin-syntax-jsx@^7.7.2": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz" integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-private-property-in-object@^7.14.5": version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz" integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.25.9", "@babel/plugin-syntax-typescript@^7.7.2": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz" integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz" integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.18.6" @@ -504,14 +569,14 @@ "@babel/plugin-transform-arrow-functions@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845" + resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz" integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-async-generator-functions@^7.26.8": version "7.26.8" - resolved "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz#5e3991135e3b9c6eaaf5eff56d1ae5a11df45ff8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz" integrity sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg== dependencies: "@babel/helper-plugin-utils" "^7.26.5" @@ -520,7 +585,7 @@ "@babel/plugin-transform-async-to-generator@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz#c80008dacae51482793e5a9c08b39a5be7e12d71" + resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz" integrity sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ== dependencies: "@babel/helper-module-imports" "^7.25.9" @@ -529,21 +594,21 @@ "@babel/plugin-transform-block-scoped-functions@^7.26.5": version "7.26.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz#3dc4405d31ad1cbe45293aa57205a6e3b009d53e" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz" integrity sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ== dependencies: "@babel/helper-plugin-utils" "^7.26.5" "@babel/plugin-transform-block-scoping@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz#c33665e46b06759c93687ca0f84395b80c0473a1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz" integrity sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-class-properties@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f" + resolved "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz" integrity sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q== dependencies: "@babel/helper-create-class-features-plugin" "^7.25.9" @@ -551,7 +616,7 @@ "@babel/plugin-transform-class-static-block@^7.26.0": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz#6c8da219f4eb15cae9834ec4348ff8e9e09664a0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz" integrity sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ== dependencies: "@babel/helper-create-class-features-plugin" "^7.25.9" @@ -559,7 +624,7 @@ "@babel/plugin-transform-classes@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" + resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz" integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== dependencies: "@babel/helper-annotate-as-pure" "^7.25.9" @@ -571,7 +636,7 @@ "@babel/plugin-transform-computed-properties@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b" + resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz" integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -579,14 +644,14 @@ "@babel/plugin-transform-destructuring@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz" integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-dotall-regex@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz#bad7945dd07734ca52fe3ad4e872b40ed09bb09a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz" integrity sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -594,14 +659,14 @@ "@babel/plugin-transform-duplicate-keys@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz#8850ddf57dce2aebb4394bb434a7598031059e6d" + resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz" integrity sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz#6f7259b4de127721a08f1e5165b852fcaa696d31" + resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz" integrity sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -609,28 +674,28 @@ "@babel/plugin-transform-dynamic-import@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz#23e917de63ed23c6600c5dd06d94669dce79f7b8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz" integrity sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-exponentiation-operator@^7.26.3": version "7.26.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz#e29f01b6de302c7c2c794277a48f04a9ca7f03bc" + resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz" integrity sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-export-namespace-from@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz#90745fe55053394f554e40584cda81f2c8a402a2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz" integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-for-of@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz#4bdc7d42a213397905d89f02350c5267866d5755" + resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz" integrity sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -638,7 +703,7 @@ "@babel/plugin-transform-function-name@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97" + resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz" integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA== dependencies: "@babel/helper-compilation-targets" "^7.25.9" @@ -647,35 +712,35 @@ "@babel/plugin-transform-json-strings@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz#c86db407cb827cded902a90c707d2781aaa89660" + resolved "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz" integrity sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-literals@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de" + resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz" integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-logical-assignment-operators@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz#b19441a8c39a2fda0902900b306ea05ae1055db7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz" integrity sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-member-expression-literals@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de" + resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz" integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-modules-amd@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz#49ba478f2295101544abd794486cd3088dddb6c5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz" integrity sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw== dependencies: "@babel/helper-module-transforms" "^7.25.9" @@ -683,7 +748,7 @@ "@babel/plugin-transform-modules-commonjs@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz#d165c8c569a080baf5467bda88df6425fc060686" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz" integrity sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg== dependencies: "@babel/helper-module-transforms" "^7.25.9" @@ -692,7 +757,7 @@ "@babel/plugin-transform-modules-commonjs@^7.26.3": version "7.26.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz" integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== dependencies: "@babel/helper-module-transforms" "^7.26.0" @@ -700,7 +765,7 @@ "@babel/plugin-transform-modules-systemjs@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz#8bd1b43836269e3d33307151a114bcf3ba6793f8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz" integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA== dependencies: "@babel/helper-module-transforms" "^7.25.9" @@ -710,7 +775,7 @@ "@babel/plugin-transform-modules-umd@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz#6710079cdd7c694db36529a1e8411e49fcbf14c9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz" integrity sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw== dependencies: "@babel/helper-module-transforms" "^7.25.9" @@ -718,7 +783,7 @@ "@babel/plugin-transform-named-capturing-groups-regex@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz#454990ae6cc22fd2a0fa60b3a2c6f63a38064e6a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz" integrity sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -726,28 +791,28 @@ "@babel/plugin-transform-new-target@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz#42e61711294b105c248336dcb04b77054ea8becd" + resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz" integrity sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-nullish-coalescing-operator@^7.26.6": version "7.26.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz#fbf6b3c92cb509e7b319ee46e3da89c5bedd31fe" + resolved "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz" integrity sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw== dependencies: "@babel/helper-plugin-utils" "^7.26.5" "@babel/plugin-transform-numeric-separator@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz#bfed75866261a8b643468b0ccfd275f2033214a1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz" integrity sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-object-rest-spread@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz#0203725025074164808bcf1a2cfa90c652c99f18" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz" integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg== dependencies: "@babel/helper-compilation-targets" "^7.25.9" @@ -756,7 +821,7 @@ "@babel/plugin-transform-object-super@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz" integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -764,14 +829,14 @@ "@babel/plugin-transform-optional-catch-binding@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz#10e70d96d52bb1f10c5caaac59ac545ea2ba7ff3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz" integrity sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-optional-chaining@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd" + resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz" integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -779,14 +844,14 @@ "@babel/plugin-transform-parameters@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257" + resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz" integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-private-methods@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" + resolved "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz" integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== dependencies: "@babel/helper-create-class-features-plugin" "^7.25.9" @@ -794,7 +859,7 @@ "@babel/plugin-transform-private-property-in-object@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz#9c8b73e64e6cc3cbb2743633885a7dd2c385fe33" + resolved "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz" integrity sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw== dependencies: "@babel/helper-annotate-as-pure" "^7.25.9" @@ -803,14 +868,14 @@ "@babel/plugin-transform-property-literals@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f" + resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz" integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-regenerator@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz#03a8a4670d6cebae95305ac6defac81ece77740b" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz" integrity sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -818,7 +883,7 @@ "@babel/plugin-transform-regexp-modifiers@^7.26.0": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz#2f5837a5b5cd3842a919d8147e9903cc7455b850" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz" integrity sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -826,21 +891,21 @@ "@babel/plugin-transform-reserved-words@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz#0398aed2f1f10ba3f78a93db219b27ef417fb9ce" + resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz" integrity sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-shorthand-properties@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz" integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-spread@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz" integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -848,28 +913,28 @@ "@babel/plugin-transform-sticky-regex@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz#c7f02b944e986a417817b20ba2c504dfc1453d32" + resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz" integrity sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-template-literals@^7.26.8": version "7.26.8" - resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz#966b15d153a991172a540a69ad5e1845ced990b5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz" integrity sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q== dependencies: "@babel/helper-plugin-utils" "^7.26.5" "@babel/plugin-transform-typeof-symbol@^7.26.7": version "7.26.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz#d0e33acd9223744c1e857dbd6fa17bd0a3786937" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz" integrity sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw== dependencies: "@babel/helper-plugin-utils" "^7.26.5" "@babel/plugin-transform-typescript@^7.25.9": version "7.26.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.3.tgz#3d6add9c78735623317387ee26d5ada540eee3fd" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.3.tgz" integrity sha512-6+5hpdr6mETwSKjmJUdYw0EIkATiQhnELWlE3kJFBwSg/BGIVwVaVbX+gOXBCdc7Ln1RXZxyWGecIXhUfnl7oA== dependencies: "@babel/helper-annotate-as-pure" "^7.25.9" @@ -880,14 +945,14 @@ "@babel/plugin-transform-unicode-escapes@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz#a75ef3947ce15363fccaa38e2dd9bc70b2788b82" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz" integrity sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-unicode-property-regex@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz#a901e96f2c1d071b0d1bb5dc0d3c880ce8f53dd3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz" integrity sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -895,7 +960,7 @@ "@babel/plugin-transform-unicode-regex@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz#5eae747fe39eacf13a8bd006a4fb0b5d1fa5e9b1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz" integrity sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -903,7 +968,7 @@ "@babel/plugin-transform-unicode-sets-regex@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz#65114c17b4ffc20fa5b163c63c70c0d25621fabe" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz" integrity sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -911,7 +976,7 @@ "@babel/preset-env@7.26.8": version "7.26.8" - resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.8.tgz#7af0090829b606d2046db99679004731e1dc364d" + resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.8.tgz" integrity sha512-um7Sy+2THd697S4zJEfv/U5MHGJzkN2xhtsR3T/SWRbVSic62nbISh51VVfU9JiO/L/Z97QczHTaFVkOU8IzNg== dependencies: "@babel/compat-data" "^7.26.8" @@ -986,7 +1051,7 @@ "@babel/preset-modules@0.1.6-no-external-plugins": version "0.1.6-no-external-plugins" - resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz" integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -995,7 +1060,7 @@ "@babel/preset-typescript@7.26.0": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz#4a570f1b8d104a242d923957ffa1eaff142a106d" + resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz" integrity sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -1006,14 +1071,14 @@ "@babel/runtime@^7.8.4": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz" integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== dependencies: regenerator-runtime "^0.14.0" "@babel/template@^7.25.9", "@babel/template@^7.3.3": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz" integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== dependencies: "@babel/code-frame" "^7.25.9" @@ -1022,7 +1087,7 @@ "@babel/template@^7.26.8", "@babel/template@^7.26.9": version "7.26.9" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz" integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== dependencies: "@babel/code-frame" "^7.26.2" @@ -1031,7 +1096,7 @@ "@babel/traverse@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz" integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw== dependencies: "@babel/code-frame" "^7.25.9" @@ -1044,7 +1109,7 @@ "@babel/traverse@^7.26.8": version "7.26.9" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz" integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg== dependencies: "@babel/code-frame" "^7.26.2" @@ -1055,9 +1120,17 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.26.3", "@babel/types@^7.3.3": +"@babel/types@^7.0.0": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz" + integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + +"@babel/types@^7.20.7": version "7.26.3" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz" integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== dependencies: "@babel/helper-string-parser" "^7.25.9" @@ -1065,15 +1138,23 @@ "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.4.4": version "7.26.0" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz" integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== dependencies: "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" +"@babel/types@^7.26.3", "@babel/types@^7.3.3": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz" + integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/types@^7.26.8", "@babel/types@^7.26.9": version "7.26.9" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz" integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== dependencies: "@babel/helper-string-parser" "^7.25.9" @@ -1081,415 +1162,376 @@ "@bcoe/v8-coverage@^0.2.3": version "0.2.3" - resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" - resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: "@jridgewell/trace-mapping" "0.3.9" "@discoveryjs/json-ext@^0.5.0": version "0.5.7" - resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@firebase/analytics-compat@0.2.17": - version "0.2.17" - resolved "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.17.tgz#c3cfc8ffb863d574ec26d86f9c8344d752832995" - integrity sha512-SJNVOeTvzdqZQvXFzj7yAirXnYcLDxh57wBFROfeowq/kRN1AqOw1tG6U4OiFOEhqi7s3xLze/LMkZatk2IEww== +"@firebase/ai@1.3.0-20250512211235": + version "1.3.0-20250512211235" + resolved "https://registry.npmjs.org/@firebase/ai/-/ai-1.3.0-20250512211235.tgz" + integrity sha512-L/3vFDzzmAScgRVcZXiqyQHLXTjyBvfC4bVHvsHGlUyBYjlS+xcPUFdNHFowM84r6qyp/X94pO58ERgRO/CUCg== dependencies: - "@firebase/analytics" "0.10.11" + "@firebase/app-check-interop-types" "0.3.3" + "@firebase/component" "0.6.14" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.11.1" + tslib "^2.1.0" + +"@firebase/analytics-compat@0.2.20-20250512211235": + version "0.2.20-20250512211235" + dependencies: + "@firebase/analytics" "0.10.14-20250512211235" "@firebase/analytics-types" "0.8.3" - "@firebase/component" "0.6.12" - "@firebase/util" "1.10.3" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" "@firebase/analytics-types@0.8.3": version "0.8.3" - resolved "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz#d08cd39a6209693ca2039ba7a81570dfa6c1518f" + resolved "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz" integrity sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg== -"@firebase/analytics@0.10.11": - version "0.10.11" - resolved "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.11.tgz#6896413e92613573af775c45050af889a43676da" - integrity sha512-zwuPiRE0+hgcS95JZbJ6DFQN4xYFO8IyGxpeePTV51YJMwCf3lkBa6FnZ/iXIqDKcBPMgMuuEZozI0BJWaLEYg== +"@firebase/analytics@0.10.14-20250512211235": + version "0.10.14-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/installations" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/installations" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" -"@firebase/app-check-compat@0.3.18": - version "0.3.18" - resolved "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.18.tgz#abe63858fca86b61ea431e0d9e58ccb8bac1b275" - integrity sha512-qjozwnwYmAIdrsVGrJk+hnF1WBois54IhZR6gO0wtZQoTvWL/GtiA2F31TIgAhF0ayUiZhztOv1RfC7YyrZGDQ== +"@firebase/app-check-compat@0.3.23-20250512211235": + version "0.3.23-20250512211235" dependencies: - "@firebase/app-check" "0.8.11" + "@firebase/app-check" "0.10.0-20250512211235" "@firebase/app-check-types" "0.5.3" - "@firebase/component" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" "@firebase/app-check-interop-types@0.3.3": version "0.3.3" - resolved "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz#ed9c4a4f48d1395ef378f007476db3940aa5351a" + resolved "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz" integrity sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A== "@firebase/app-check-types@0.5.3": version "0.5.3" - resolved "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz#38ba954acf4bffe451581a32fffa20337f11d8e5" + resolved "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz" integrity sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng== -"@firebase/app-check@0.8.11": - version "0.8.11" - resolved "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.11.tgz#3c67148046fea0a0a9a1eecf1a17fdc31a76eda7" - integrity sha512-42zIfRI08/7bQqczAy7sY2JqZYEv3a1eNa4fLFdtJ54vNevbBIRSEA3fZgRqWFNHalh5ohsBXdrYgFqaRIuCcQ== +"@firebase/app-check@0.10.0-20250512211235": + version "0.10.0-20250512211235" dependencies: - "@firebase/component" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" -"@firebase/app-compat@0.2.49": - version "0.2.49" - resolved "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.49.tgz#715a519ce8566d37c44db399c02a4785359b7b22" - integrity sha512-vf838b9WrHs2GH6NfsvA27a3ngDzCnR7oxmc5LJHaJ7mWSCuce1iDRJ2B6raJ6SH9592XXvtW+kzRcPYhC/LoA== +"@firebase/app-compat@0.4.0-20250512211235": + version "0.4.0-20250512211235" dependencies: - "@firebase/app" "0.11.0" - "@firebase/component" "0.6.12" + "@firebase/app" "0.13.0-20250512211235" + "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" -"@firebase/app-types@0.9.3": +"@firebase/app-types@0.9.3", "@firebase/app-types@0.x": version "0.9.3" - resolved "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz#8408219eae9b1fb74f86c24e7150a148460414ad" + resolved "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz" integrity sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw== -"@firebase/app@0.11.0": - version "0.11.0" - resolved "https://registry.npmjs.org/@firebase/app/-/app-0.11.0.tgz#9f6333fe352283a2ecba9bddc0d66fa637cbf251" - integrity sha512-FaPl2RB2iClQK4IIAN4ruhzyGNRcvAwXk0Ltqdt55RiTmQ4aM2EAJicgI8QNQd2JIkeCT1K8JKsEba3T1/J7FA== +"@firebase/app@0.13.0-20250512211235", "@firebase/app@0.x": + version "0.13.0-20250512211235" dependencies: - "@firebase/component" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" idb "7.1.1" tslib "^2.1.0" -"@firebase/auth-compat@0.5.18": - version "0.5.18" - resolved "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.18.tgz#ba1674287e6df4f95675866d6f960a9fc4a9abfc" - integrity sha512-dFBev8AMNb2AgIt9afwf/Ku4/0Wq9R9OFSeBB/xjyJt+RfQ9PnNWqU2oFphews23byLg6jle8twRA7iOYfRGRw== +"@firebase/auth-compat@0.5.23-20250512211235": + version "0.5.23-20250512211235" dependencies: - "@firebase/auth" "1.9.0" + "@firebase/auth" "1.10.3-20250512211235" "@firebase/auth-types" "0.13.0" - "@firebase/component" "0.6.12" - "@firebase/util" "1.10.3" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" "@firebase/auth-interop-types@0.2.4": version "0.2.4" - resolved "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz#176a08686b0685596ff03d7879b7e4115af53de0" + resolved "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz" integrity sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA== "@firebase/auth-types@0.13.0": version "0.13.0" - resolved "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz#ae6e0015e3bd4bfe18edd0942b48a0a118a098d9" + resolved "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz" integrity sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg== -"@firebase/auth@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@firebase/auth/-/auth-1.9.0.tgz#eea1ab78fd3d68db3cdef69a0d7fba3663a940c5" - integrity sha512-Xz2mbEYauF689qXG/4HppS2+/yGo9R7B6eNUBh3H2+XpAZTGdx8d8TFsW/BMTAK9Q95NB0pb1Bbvfx0lwofq8Q== +"@firebase/auth@1.10.3-20250512211235": + version "1.10.3-20250512211235" dependencies: - "@firebase/component" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" -"@firebase/component@0.6.12": - version "0.6.12" - resolved "https://registry.npmjs.org/@firebase/component/-/component-0.6.12.tgz#08905a534e9b769164e7e1b1e80f6e7611eb67f3" - integrity sha512-YnxqjtohLbnb7raXt2YuA44cC1wA9GiehM/cmxrsoxKlFxBLy2V0OkRSj9gpngAE0UoJ421Wlav9ycO7lTPAUw== +"@firebase/component@0.6.14": + version "0.6.14" + resolved "https://registry.npmjs.org/@firebase/component/-/component-0.6.14.tgz" + integrity sha512-kf/zAT8GQJ9nYoHuj0mv7twp1QzifKYrO+GsmsVHHM+Hi9KkmI7E3B3J0CtihHpb34vinl4gbJrYJ2p2wfvc9A== dependencies: - "@firebase/util" "1.10.3" + "@firebase/util" "1.11.1" tslib "^2.1.0" -"@firebase/data-connect@0.3.0": - version "0.3.0" - resolved "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.0.tgz#5602986c28e2ac94df2499a7cf68ad622957089e" - integrity sha512-inbLq0JyQD/d02Al3Lso0Hc8z1BVpB3dYSMFcQkeKhYyjn5bspLczLdasPbCOEUp8MOkLblLZhJuRs7Q/spFnw== +"@firebase/component@0.6.15-20250512211235": + version "0.6.15-20250512211235" + dependencies: + "@firebase/util" "1.12.0-20250512211235" + tslib "^2.1.0" + +"@firebase/data-connect@0.3.6-20250512211235": + version "0.3.6-20250512211235" dependencies: "@firebase/auth-interop-types" "0.2.4" - "@firebase/component" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" -"@firebase/database-compat@2.0.3": - version "2.0.3" - resolved "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.3.tgz#87f18e814c06d62fea4bfb10d3b833f4259345ca" - integrity sha512-uHGQrSUeJvsDfA+IyHW5O4vdRPsCksEzv4T4Jins+bmQgYy20ZESU4x01xrQCn/nzqKHuQMEW99CoCO7D+5NiQ== +"@firebase/database-compat@2.0.7-20250512211235": + version "2.0.7-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/database" "1.0.12" - "@firebase/database-types" "1.0.8" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/database" "1.0.16-20250512211235" + "@firebase/database-types" "1.0.12-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" -"@firebase/database-types@1.0.8": - version "1.0.8" - resolved "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.8.tgz#eddcce594be118bf9aebb043b5a6d51cfb6de620" - integrity sha512-6lPWIGeufhUq1heofZULyVvWFhD01TUrkkB9vyhmksjZ4XF7NaivQp9rICMk7QNhqwa+uDCaj4j+Q8qqcSVZ9g== +"@firebase/database-types@1.0.12-20250512211235": + version "1.0.12-20250512211235" dependencies: "@firebase/app-types" "0.9.3" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" -"@firebase/database@1.0.12": - version "1.0.12" - resolved "https://registry.npmjs.org/@firebase/database/-/database-1.0.12.tgz#4e1807b82dc734df8596eac44d7766ff96c2de24" - integrity sha512-psFl5t6rSFHq3i3fnU1QQlc4BB9Hnhh8TgEqvQlPPm8kDLw8gYxvjqYw3c5CZW0+zKR837nwT6im/wtJUivMKw== +"@firebase/database@1.0.16-20250512211235": + version "1.0.16-20250512211235" dependencies: "@firebase/app-check-interop-types" "0.3.3" "@firebase/auth-interop-types" "0.2.4" - "@firebase/component" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" faye-websocket "0.11.4" tslib "^2.1.0" -"@firebase/firestore-compat@0.3.42": - version "0.3.42" - resolved "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.42.tgz#0acad6d6e05df9764a86b0125886afdffedb08f2" - integrity sha512-L/JqnVw7Bf+2jcCmW1nCiknkIVVM5RIR4rHE1UrtInAvP9vo8pUhFEZVzbwX71SuCoHOwjiaPDvVSeOFachokg== +"@firebase/firestore-compat@0.3.48-20250512211235": + version "0.3.48-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/firestore" "4.7.7" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/firestore" "4.7.13-20250512211235" "@firebase/firestore-types" "3.0.3" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" "@firebase/firestore-types@3.0.3": version "3.0.3" - resolved "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz#7d0c3dd8850c0193d8f5ee0cc8f11961407742c1" + resolved "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz" integrity sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q== -"@firebase/firestore@4.7.7": - version "4.7.7" - resolved "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.7.tgz#f9cf680e7402a833cf24a13d4f866ca1971e2c1b" - integrity sha512-DDYBjqSyd2vD3SjfRqI2Q9Ua1N0URP+1P0/SnNdVSp0/S5mkbaklIX/eU+199ze0XXnC61RYLqi6KYTYtGoz2A== +"@firebase/firestore@4.7.13-20250512211235": + version "4.7.13-20250512211235" dependencies: - "@firebase/component" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" "@firebase/webchannel-wrapper" "1.0.3" "@grpc/grpc-js" "~1.9.0" "@grpc/proto-loader" "^0.7.8" tslib "^2.1.0" -"@firebase/functions-compat@0.3.19": - version "0.3.19" - resolved "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.19.tgz#f1d1ce51674a6ee8d5449b721374d35243dc3002" - integrity sha512-uw4tR8NcJCDu86UD63Za8A8SgFgmAVFb1XsGlkuBY7gpLyZWEFavWnwRkZ/8cUwpqUhp/SptXFZ1WFJSnOokLw== +"@firebase/functions-compat@0.3.22-20250512211235": + version "0.3.22-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/functions" "0.12.2" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/functions" "0.12.5-20250512211235" "@firebase/functions-types" "0.6.3" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" "@firebase/functions-types@0.6.3": version "0.6.3" - resolved "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz#f5faf770248b13f45d256f614230da6a11bfb654" + resolved "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz" integrity sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg== -"@firebase/functions@0.12.2": - version "0.12.2" - resolved "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.2.tgz#bea33b35437278228be563dfc02520d8623d43f4" - integrity sha512-iKpFDoCYk/Qm+Qwv5ynRb9/yq64QOt0A0+t9NuekyAZnSoV56kSNq/PmsVmBauar5SlmEjhHk6QKdMBP9S0gXA== +"@firebase/functions@0.12.5-20250512211235": + version "0.12.5-20250512211235" dependencies: "@firebase/app-check-interop-types" "0.3.3" "@firebase/auth-interop-types" "0.2.4" - "@firebase/component" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" "@firebase/messaging-interop-types" "0.2.3" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" -"@firebase/installations-compat@0.2.12": - version "0.2.12" - resolved "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.12.tgz#ee6396f3cc787c0dd4fc5dd87fec1db9dbb40c97" - integrity sha512-RhcGknkxmFu92F6Jb3rXxv6a4sytPjJGifRZj8MSURPuv2Xu+/AispCXEfY1ZraobhEHTG5HLGsP6R4l9qB5aA== +"@firebase/installations-compat@0.2.15-20250512211235": + version "0.2.15-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/installations" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/installations" "0.6.15-20250512211235" "@firebase/installations-types" "0.5.3" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" "@firebase/installations-types@0.5.3": version "0.5.3" - resolved "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz#cac8a14dd49f09174da9df8ae453f9b359c3ef2f" + resolved "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz" integrity sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA== -"@firebase/installations@0.6.12": - version "0.6.12" - resolved "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.12.tgz#6d9ad14e60caa8fae4ec0120c0e46ceb9d6fbdae" - integrity sha512-ES/WpuAV2k2YtBTvdaknEo7IY8vaGjIjS3zhnHSAIvY9KwTR8XZFXOJoZ3nSkjN1A5R4MtEh+07drnzPDg9vaw== +"@firebase/installations@0.6.15-20250512211235": + version "0.6.15-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/util" "1.10.3" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/util" "1.12.0-20250512211235" idb "7.1.1" tslib "^2.1.0" "@firebase/logger@0.4.4": version "0.4.4" - resolved "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz#29e8379d20fd1149349a195ee6deee4573a86f48" + resolved "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz" integrity sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g== dependencies: tslib "^2.1.0" -"@firebase/messaging-compat@0.2.16": - version "0.2.16" - resolved "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.16.tgz#533af4542a54b932146d175d5687aedd428be972" - integrity sha512-9HZZ88Ig3zQ0ok/Pwt4gQcNsOhoEy8hDHoGsV1am6ulgMuGuDVD2gl11Lere2ksL+msM12Lddi2x/7TCqmODZw== +"@firebase/messaging-compat@0.2.19-20250512211235": + version "0.2.19-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/messaging" "0.12.16" - "@firebase/util" "1.10.3" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/messaging" "0.12.19-20250512211235" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" "@firebase/messaging-interop-types@0.2.3": version "0.2.3" - resolved "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz#e647c9cd1beecfe6a6e82018a6eec37555e4da3e" + resolved "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz" integrity sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q== -"@firebase/messaging@0.12.16": - version "0.12.16" - resolved "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.16.tgz#bd8a768274bdc4368396bd9eaa356bffb998bef2" - integrity sha512-VJ8sCEIeP3+XkfbJA7410WhYGHdloYFZXoHe/vt+vNVDGw8JQPTQSVTRvjrUprEf5I4Tbcnpr2H34lS6zhCHSA== +"@firebase/messaging@0.12.19-20250512211235": + version "0.12.19-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/installations" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/installations" "0.6.15-20250512211235" "@firebase/messaging-interop-types" "0.2.3" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" idb "7.1.1" tslib "^2.1.0" -"@firebase/performance-compat@0.2.13": - version "0.2.13" - resolved "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.13.tgz#29bb94909c10553b40ca97e7f7d0e163bad8a77d" - integrity sha512-pB0SMQj2TLQ6roDcX0YQDWvUnVgsVOl0VnUvyT/VBdCUuQYDHobZsPEuQsoEqmPA44KS/Gl0oyKqf+I8UPtRgw== +"@firebase/performance-compat@0.2.17-20250512211235": + version "0.2.17-20250512211235" dependencies: - "@firebase/component" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/performance" "0.7.0" + "@firebase/performance" "0.7.4-20250512211235" "@firebase/performance-types" "0.2.3" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" "@firebase/performance-types@0.2.3": version "0.2.3" - resolved "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz#5ce64e90fa20ab5561f8b62a305010cf9fab86fb" + resolved "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz" integrity sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ== -"@firebase/performance@0.7.0": - version "0.7.0" - resolved "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.0.tgz#1cd82039f7e06e0f059287dfa21705c68ec9a691" - integrity sha512-L91PwYuiJdKXKSRqsWNicvTppAJVzKjye03UlegeD6TkpKjb93T8AmJ9B0Mt0bcWHCNtnnRBCdSCvD2U9GZDjw== +"@firebase/performance@0.7.4-20250512211235": + version "0.7.4-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/installations" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/installations" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" web-vitals "^4.2.4" -"@firebase/remote-config-compat@0.2.12": - version "0.2.12" - resolved "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.12.tgz#ae0b597b3228deef0e3c6b2c6e631f19213eca4c" - integrity sha512-91jLWPtubIuPBngg9SzwvNCWzhMLcyBccmt7TNZP+y1cuYFNOWWHKUXQ3IrxCLB7WwLqQaEu7fTDAjHsTyBsSw== +"@firebase/remote-config-compat@0.2.15-20250512211235": + version "0.2.15-20250512211235" dependencies: - "@firebase/component" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/remote-config" "0.5.0" + "@firebase/remote-config" "0.6.2-20250512211235" "@firebase/remote-config-types" "0.4.0" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" "@firebase/remote-config-types@0.4.0": version "0.4.0" - resolved "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz#91b9a836d5ca30ced68c1516163b281fbb544537" + resolved "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz" integrity sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg== -"@firebase/remote-config@0.5.0": - version "0.5.0" - resolved "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.5.0.tgz#30212fa77adba8a62fc6408eb32122147ae80790" - integrity sha512-weiEbpBp5PBJTHUWR4GwI7ZacaAg68BKha5QnZ8Go65W4oQjEWqCW/rfskABI/OkrGijlL3CUmCB/SA6mVo0qA== +"@firebase/remote-config@0.6.2-20250512211235": + version "0.6.2-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/installations" "0.6.12" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/installations" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" -"@firebase/storage-compat@0.3.16": - version "0.3.16" - resolved "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.16.tgz#49ab9c572bb172e6335c099d95a48bee0f17cc98" - integrity sha512-EeMuok/s0r938lEomia8XILEqSYULm7HcYZ/GTZLDWur0kMf2ktuPVZiTdRiwEV3Iki7FtQO5txrQ/0pLRVLAw== +"@firebase/storage-compat@0.3.19-20250512211235": + version "0.3.19-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/storage" "0.13.6" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/storage" "0.13.9-20250512211235" "@firebase/storage-types" "0.8.3" - "@firebase/util" "1.10.3" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" "@firebase/storage-types@0.8.3": version "0.8.3" - resolved "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz#2531ef593a3452fc12c59117195d6485c6632d3d" + resolved "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz" integrity sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg== -"@firebase/storage@0.13.6": - version "0.13.6" - resolved "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.6.tgz#322def6cda335df991ce9787aa5ef5650db901bd" - integrity sha512-BEJLYQzVgAoglRl5VRIRZ91RRBZgS/O37/PSGQJBYNuoLmFZUrtwrlLTOAwG776NlO9VQR+K2j15/36Lr2EqHA== +"@firebase/storage@0.13.9-20250512211235": + version "0.13.9-20250512211235" dependencies: - "@firebase/component" "0.6.12" - "@firebase/util" "1.10.3" + "@firebase/component" "0.6.15-20250512211235" + "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" -"@firebase/util@1.10.3": - version "1.10.3" - resolved "https://registry.npmjs.org/@firebase/util/-/util-1.10.3.tgz#63fc5fea7b36236219c4875731597494416678d1" - integrity sha512-wfoF5LTy0m2ufUapV0ZnpcGQvuavTbJ5Qr1Ze9OJGL70cSMvhDyjS4w2121XdA3lGZSTOsDOyGhpoDtYwck85A== +"@firebase/util@1.11.1", "@firebase/util@1.x": + version "1.11.1" + resolved "https://registry.npmjs.org/@firebase/util/-/util-1.11.1.tgz" + integrity sha512-RXg4WE8C2LUrvoV/TMGRTu223zZf9Dq9MR8yHZio9nF9TpLnpCPURw9VWWB2WATDl6HfIdWfl2x2SJYtHkN4hw== dependencies: tslib "^2.1.0" -"@firebase/vertexai@1.0.4": - version "1.0.4" - resolved "https://registry.npmjs.org/@firebase/vertexai/-/vertexai-1.0.4.tgz#1966ddfb32492d004f595f639e57162d488c84ba" - integrity sha512-Nkf/r4u166b4Id6zrrW0Qtg1KyZpQvvYchtkebamnHtIfY+Qnt51I/sx4Saos/WrmO8SnrSU850LfmJ7pehYXg== +"@firebase/util@1.12.0-20250512211235": + version "1.12.0-20250512211235" + resolved "https://registry.npmjs.org/@firebase/util/-/util-1.12.0-20250512211235.tgz" + integrity sha512-o7CkpIAymb6irDYCoK0wKZiEMMgHX4oGdsXEXvsIPBWjoZsUavF6HH4iYVGslL4KkasEEjJiLNiomxxGbJPqKg== dependencies: - "@firebase/app-check-interop-types" "0.3.3" - "@firebase/component" "0.6.12" - "@firebase/logger" "0.4.4" - "@firebase/util" "1.10.3" tslib "^2.1.0" "@firebase/webchannel-wrapper@1.0.3": version "1.0.3" - resolved "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz#a73bab8eb491d7b8b7be2f0e6c310647835afe83" + resolved "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz" integrity sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ== "@grpc/grpc-js@~1.9.0": version "1.9.15" - resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz#433d7ac19b1754af690ea650ab72190bd700739b" + resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz" integrity sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ== dependencies: "@grpc/proto-loader" "^0.7.8" @@ -1497,7 +1539,7 @@ "@grpc/proto-loader@^0.7.8": version "0.7.13" - resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz#f6a44b2b7c9f7b609f5748c6eac2d420e37670cf" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz" integrity sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw== dependencies: lodash.camelcase "^4.3.0" @@ -1507,7 +1549,7 @@ "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" - resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: camelcase "^5.3.1" @@ -1518,12 +1560,12 @@ "@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" - resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== "@jest/console@^29.7.0": version "29.7.0" - resolved "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + resolved "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz" integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: "@jest/types" "^29.6.3" @@ -1535,7 +1577,7 @@ "@jest/core@^29.7.0": version "29.7.0" - resolved "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + resolved "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz" integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== dependencies: "@jest/console" "^29.7.0" @@ -1569,7 +1611,7 @@ "@jest/environment@^29.7.0": version "29.7.0" - resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz" integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== dependencies: "@jest/fake-timers" "^29.7.0" @@ -1579,14 +1621,14 @@ "@jest/expect-utils@^29.7.0": version "29.7.0" - resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz" integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== dependencies: jest-get-type "^29.6.3" "@jest/expect@^29.7.0": version "29.7.0" - resolved "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + resolved "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz" integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== dependencies: expect "^29.7.0" @@ -1594,7 +1636,7 @@ "@jest/fake-timers@^29.7.0": version "29.7.0" - resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz" integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== dependencies: "@jest/types" "^29.6.3" @@ -1606,7 +1648,7 @@ "@jest/globals@^29.7.0": version "29.7.0" - resolved "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz" integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== dependencies: "@jest/environment" "^29.7.0" @@ -1616,7 +1658,7 @@ "@jest/reporters@^29.7.0": version "29.7.0" - resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz" integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== dependencies: "@bcoe/v8-coverage" "^0.2.3" @@ -1646,14 +1688,14 @@ "@jest/schemas@^29.6.3": version "29.6.3" - resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz" integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: "@sinclair/typebox" "^0.27.8" "@jest/source-map@^29.6.3": version "29.6.3" - resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz" integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== dependencies: "@jridgewell/trace-mapping" "^0.3.18" @@ -1662,7 +1704,7 @@ "@jest/test-result@^29.7.0": version "29.7.0" - resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz" integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== dependencies: "@jest/console" "^29.7.0" @@ -1672,7 +1714,7 @@ "@jest/test-sequencer@^29.7.0": version "29.7.0" - resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz" integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== dependencies: "@jest/test-result" "^29.7.0" @@ -1682,7 +1724,7 @@ "@jest/transform@^29.7.0": version "29.7.0" - resolved "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz" integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== dependencies: "@babel/core" "^7.11.6" @@ -1703,7 +1745,7 @@ "@jest/types@^29.6.3": version "29.6.3" - resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz" integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: "@jest/schemas" "^29.6.3" @@ -1715,7 +1757,7 @@ "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz" integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== dependencies: "@jridgewell/set-array" "^1.2.1" @@ -1724,17 +1766,17 @@ "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/set-array@^1.2.1": version "1.2.1" - resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== "@jridgewell/source-map@^0.3.3": version "0.3.6" - resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz" integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== dependencies: "@jridgewell/gen-mapping" "^0.3.5" @@ -1742,33 +1784,33 @@ "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.5.0" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jsonjoy.com/base64@^1.1.1": version "1.1.2" - resolved "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz#cf8ea9dcb849b81c95f14fc0aaa151c6b54d2578" + resolved "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz" integrity sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA== "@jsonjoy.com/json-pack@^1.0.3": version "1.1.0" - resolved "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz#33ca57ee29d12feef540f2139225597469dec894" + resolved "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz" integrity sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg== dependencies: "@jsonjoy.com/base64" "^1.1.1" @@ -1778,37 +1820,37 @@ "@jsonjoy.com/util@^1.1.2", "@jsonjoy.com/util@^1.3.0": version "1.5.0" - resolved "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz#6008e35b9d9d8ee27bc4bfaa70c8cbf33a537b4c" + resolved "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz" integrity sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA== "@leichtgewicht/ip-codec@^2.0.1": version "2.0.5" - resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1" + resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz" integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" - resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== "@protobufjs/base64@^1.1.2": version "1.1.2" - resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== "@protobufjs/codegen@^2.0.4": version "2.0.4" - resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz" integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== "@protobufjs/eventemitter@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== "@protobufjs/fetch@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz" integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== dependencies: "@protobufjs/aspromise" "^1.1.1" @@ -1816,76 +1858,76 @@ "@protobufjs/float@^1.0.2": version "1.0.2" - resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== "@protobufjs/inquire@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz" integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== "@protobufjs/path@^1.1.2": version "1.1.2" - resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== "@protobufjs/pool@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== "@protobufjs/utf8@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== "@sinclair/typebox@^0.27.8": version "0.27.8" - resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@sinonjs/commons@^3.0.0": version "3.0.1" - resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz" integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^10.0.2": version "10.3.0" - resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz" integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: "@sinonjs/commons" "^3.0.0" "@tootallnate/once@2": version "2.0.0" - resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== "@tsconfig/node10@^1.0.7": version "1.0.11" - resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz" integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== "@tsconfig/node12@^1.0.7": version "1.0.11" - resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": version "1.0.3" - resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": version "1.0.4" - resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@types/babel__core@^7.1.14": version "7.20.5" - resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== dependencies: "@babel/parser" "^7.20.7" @@ -1896,14 +1938,14 @@ "@types/babel__generator@*": version "7.6.8" - resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz" integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": version "7.4.4" - resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz" integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== dependencies: "@babel/parser" "^7.1.0" @@ -1911,14 +1953,14 @@ "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": version "7.20.6" - resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz" integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== dependencies: "@babel/types" "^7.20.7" "@types/body-parser@*": version "1.19.5" - resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz" integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== dependencies: "@types/connect" "*" @@ -1926,14 +1968,14 @@ "@types/bonjour@^3.5.13": version "3.5.13" - resolved "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + resolved "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz" integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== dependencies: "@types/node" "*" "@types/connect-history-api-fallback@^1.5.4": version "1.5.4" - resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz" integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== dependencies: "@types/express-serve-static-core" "*" @@ -1941,19 +1983,29 @@ "@types/connect@*": version "3.4.38" - resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz" integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== dependencies: "@types/node" "*" -"@types/estree@^1.0.5": - version "1.0.6" - resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" - integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== +"@types/eslint-scope@^3.7.7": + version "3.7.7" + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "9.6.1" + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.6": + version "1.0.7" "@types/express-serve-static-core@*", "@types/express-serve-static-core@^5.0.0": version "5.0.1" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz#3c9997ae9d00bc236e45c6374e84f2596458d9db" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz" integrity sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA== dependencies: "@types/node" "*" @@ -1963,7 +2015,7 @@ "@types/express-serve-static-core@^4.17.33": version "4.19.6" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz#e01324c2a024ff367d92c66f48553ced0ab50267" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz" integrity sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A== dependencies: "@types/node" "*" @@ -1973,7 +2025,7 @@ "@types/express@*": version "5.0.0" - resolved "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz#13a7d1f75295e90d19ed6e74cab3678488eaa96c" + resolved "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz" integrity sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ== dependencies: "@types/body-parser" "*" @@ -1981,9 +2033,9 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/express@^4.17.21": +"@types/express@^4.17.13", "@types/express@^4.17.21": version "4.17.21" - resolved "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz" integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== dependencies: "@types/body-parser" "*" @@ -1993,50 +2045,50 @@ "@types/gensync@^1.0.0": version "1.0.4" - resolved "https://registry.npmjs.org/@types/gensync/-/gensync-1.0.4.tgz#7122d8f0cd3bf437f9725cc95b180197190cf50b" + resolved "https://registry.npmjs.org/@types/gensync/-/gensync-1.0.4.tgz" integrity sha512-C3YYeRQWp2fmq9OryX+FoDy8nXS6scQ7dPptD8LnFDAUNcKWJjXQKDNJD3HVm+kOUsXhTOkpi69vI4EuAr95bA== "@types/graceful-fs@^4.1.3": version "4.1.9" - resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz" integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== dependencies: "@types/node" "*" "@types/http-errors@*": version "2.0.4" - resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz" integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== "@types/http-proxy@^1.17.8": version "1.17.15" - resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz#12118141ce9775a6499ecb4c01d02f90fc839d36" + resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz" integrity sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ== dependencies: "@types/node" "*" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.6" - resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz" integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": version "3.0.3" - resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz" integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": version "3.0.4" - resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz" integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" "@types/jest@29.5.14": version "29.5.14" - resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz" integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== dependencies: expect "^29.0.0" @@ -2044,55 +2096,55 @@ "@types/jsdom@^20.0.0": version "20.0.1" - resolved "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" + resolved "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz" integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ== dependencies: "@types/node" "*" "@types/tough-cookie" "*" parse5 "^7.0.0" -"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.9": version "7.0.15" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/mime@^1": version "1.3.5" - resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz" integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== "@types/node-forge@^1.3.0": version "1.3.11" - resolved "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" + resolved "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz" integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== dependencies: "@types/node" "*" "@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": version "22.8.0" - resolved "https://registry.npmjs.org/@types/node/-/node-22.8.0.tgz#193c6f82f9356ce0e6bba86b59f2ffe06e7e320b" + resolved "https://registry.npmjs.org/@types/node/-/node-22.8.0.tgz" integrity sha512-84rafSBHC/z1i1E3p0cJwKA+CfYDNSXX9WSZBRopjIzLET8oNt6ht2tei4C7izwDeEiLLfdeSVBv1egOH916hg== dependencies: undici-types "~6.19.8" "@types/qs@*": version "6.9.16" - resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz#52bba125a07c0482d26747d5d4947a64daf8f794" + resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz" integrity sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A== "@types/range-parser@*": version "1.2.7" - resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== "@types/retry@0.12.2": version "0.12.2" - resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" + resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz" integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== "@types/send@*": version "0.17.4" - resolved "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + resolved "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz" integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== dependencies: "@types/mime" "^1" @@ -2100,14 +2152,14 @@ "@types/serve-index@^1.9.4": version "1.9.4" - resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz" integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== dependencies: "@types/express" "*" "@types/serve-static@*", "@types/serve-static@^1.15.5": version "1.15.7" - resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz" integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== dependencies: "@types/http-errors" "*" @@ -2116,43 +2168,43 @@ "@types/sockjs@^0.3.36": version "0.3.36" - resolved "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + resolved "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz" integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== dependencies: "@types/node" "*" "@types/stack-utils@^2.0.0": version "2.0.3" - resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/tough-cookie@*": version "4.0.5" - resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== "@types/ws@^8.5.10": version "8.5.12" - resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz" integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ== dependencies: "@types/node" "*" "@types/yargs-parser@*": version "21.0.3" - resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^17.0.8": version "17.0.33" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz" integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== dependencies: "@types/yargs-parser" "*" -"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.12.1": +"@webassemblyjs/ast@^1.14.1", "@webassemblyjs/ast@1.14.1": version "1.14.1" - resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz" integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== dependencies: "@webassemblyjs/helper-numbers" "1.13.2" @@ -2160,22 +2212,22 @@ "@webassemblyjs/floating-point-hex-parser@1.13.2": version "1.13.2" - resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz" integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== "@webassemblyjs/helper-api-error@1.13.2": version "1.13.2" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz" integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== "@webassemblyjs/helper-buffer@1.14.1": version "1.14.1" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz" integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== "@webassemblyjs/helper-numbers@1.13.2": version "1.13.2" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz" integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== dependencies: "@webassemblyjs/floating-point-hex-parser" "1.13.2" @@ -2184,12 +2236,12 @@ "@webassemblyjs/helper-wasm-bytecode@1.13.2": version "1.13.2" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz" integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== "@webassemblyjs/helper-wasm-section@1.14.1": version "1.14.1" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz" integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -2199,27 +2251,25 @@ "@webassemblyjs/ieee754@1.13.2": version "1.13.2" - resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz" integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== dependencies: "@xtuc/ieee754" "^1.2.0" "@webassemblyjs/leb128@1.13.2": version "1.13.2" - resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz" integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== dependencies: "@xtuc/long" "4.2.2" "@webassemblyjs/utf8@1.13.2": version "1.13.2" - resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz" integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== -"@webassemblyjs/wasm-edit@^1.12.1": +"@webassemblyjs/wasm-edit@^1.14.1": version "1.14.1" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" - integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== dependencies: "@webassemblyjs/ast" "1.14.1" "@webassemblyjs/helper-buffer" "1.14.1" @@ -2232,7 +2282,7 @@ "@webassemblyjs/wasm-gen@1.14.1": version "1.14.1" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz" integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -2243,7 +2293,7 @@ "@webassemblyjs/wasm-opt@1.14.1": version "1.14.1" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz" integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -2251,9 +2301,9 @@ "@webassemblyjs/wasm-gen" "1.14.1" "@webassemblyjs/wasm-parser" "1.14.1" -"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.12.1": +"@webassemblyjs/wasm-parser@^1.14.1", "@webassemblyjs/wasm-parser@1.14.1": version "1.14.1" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz" integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -2265,7 +2315,7 @@ "@webassemblyjs/wast-printer@1.14.1": version "1.14.1" - resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz" integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -2273,37 +2323,37 @@ "@webpack-cli/configtest@^2.1.1": version "2.1.1" - resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz" integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== "@webpack-cli/info@^2.0.2": version "2.0.2" - resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz" integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== "@webpack-cli/serve@^2.0.5": version "2.0.5" - resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz" integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== "@xtuc/ieee754@^1.2.0": version "1.2.0" - resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== "@xtuc/long@4.2.2": version "4.2.2" - resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== abab@^2.0.6: version "2.0.6" - resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: mime-types "~2.1.34" @@ -2311,63 +2361,61 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: acorn-globals@^7.0.0: version "7.0.1" - resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" + resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz" integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== dependencies: acorn "^8.1.0" acorn-walk "^8.0.2" -acorn-import-attributes@^1.9.5: - version "1.9.5" - resolved "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" - integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== - acorn-walk@^8.0.2, acorn-walk@^8.1.1: version "8.3.4" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== dependencies: acorn "^8.11.0" acorn@^8.1.0, acorn@^8.11.0, acorn@^8.4.1, acorn@^8.8.1: version "8.14.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== -acorn@^8.7.1, acorn@^8.8.2: +acorn@^8.14.0: + version "8.14.1" + +acorn@^8.8.2: version "8.13.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz#2a30d670818ad16ddd6a35d3842dacec9e5d7ca3" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz" integrity sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w== agent-base@6: version "6.0.2" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" ajv-formats@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== dependencies: ajv "^8.0.0" ajv-keywords@^3.5.2: version "3.5.2" - resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== ajv-keywords@^5.1.0: version "5.1.0" - resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz" integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.4, ajv@^6.9.1: version "6.12.6" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -2375,9 +2423,9 @@ ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.9.0: +ajv@^8.0.0, ajv@^8.8.2, ajv@^8.9.0: version "8.17.1" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== dependencies: fast-deep-equal "^3.1.3" @@ -2387,36 +2435,36 @@ ajv@^8.0.0, ajv@^8.9.0: ansi-escapes@^4.2.1: version "4.3.2" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" ansi-html-community@^0.0.8: version "0.0.8" - resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz" integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^5.0.0: version "5.2.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.3" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" @@ -2424,29 +2472,29 @@ anymatch@^3.0.3, anymatch@~3.1.2: arg@^4.1.0: version "4.1.3" - resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" - resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" array-flatten@1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -babel-jest@29.7.0, babel-jest@^29.7.0: +babel-jest@^29.7.0, babel-jest@29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz" integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== dependencies: "@jest/transform" "^29.7.0" @@ -2459,7 +2507,7 @@ babel-jest@29.7.0, babel-jest@^29.7.0: babel-loader@8.4.1: version "8.4.1" - resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz#6ccb75c66e62c3b144e1c5f2eaec5b8f6c08c675" + resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz" integrity sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA== dependencies: find-cache-dir "^3.3.1" @@ -2469,7 +2517,7 @@ babel-loader@8.4.1: babel-plugin-istanbul@^6.1.1: version "6.1.1" - resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz" integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -2480,7 +2528,7 @@ babel-plugin-istanbul@^6.1.1: babel-plugin-jest-hoist@^29.6.3: version "29.6.3" - resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz" integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: "@babel/template" "^7.3.3" @@ -2490,7 +2538,7 @@ babel-plugin-jest-hoist@^29.6.3: babel-plugin-polyfill-corejs2@^0.4.10: version "0.4.11" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz#30320dfe3ffe1a336c15afdcdafd6fd615b25e33" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz" integrity sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q== dependencies: "@babel/compat-data" "^7.22.6" @@ -2499,7 +2547,7 @@ babel-plugin-polyfill-corejs2@^0.4.10: babel-plugin-polyfill-corejs3@^0.11.0: version "0.11.1" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz#4e4e182f1bb37c7ba62e2af81d8dd09df31344f6" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz" integrity sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ== dependencies: "@babel/helper-define-polyfill-provider" "^0.6.3" @@ -2507,14 +2555,14 @@ babel-plugin-polyfill-corejs3@^0.11.0: babel-plugin-polyfill-regenerator@^0.6.1: version "0.6.2" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz#addc47e240edd1da1058ebda03021f382bba785e" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz" integrity sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg== dependencies: "@babel/helper-define-polyfill-provider" "^0.6.2" babel-preset-current-node-syntax@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz#9a929eafece419612ef4ae4f60b1862ebad8ef30" + resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz" integrity sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -2535,7 +2583,7 @@ babel-preset-current-node-syntax@^1.0.0: babel-preset-jest@^29.6.3: version "29.6.3" - resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz" integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== dependencies: babel-plugin-jest-hoist "^29.6.3" @@ -2543,27 +2591,27 @@ babel-preset-jest@^29.6.3: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== batch@0.6.1: version "0.6.1" - resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== big.js@^5.2.2: version "5.2.2" - resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== binary-extensions@^2.0.0: version "2.3.0" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== body-parser@1.20.3: version "1.20.3" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz" integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== dependencies: bytes "3.1.2" @@ -2581,7 +2629,7 @@ body-parser@1.20.3: bonjour-service@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" + resolved "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz" integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== dependencies: fast-deep-equal "^3.1.3" @@ -2589,7 +2637,7 @@ bonjour-service@^1.2.1: brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -2597,24 +2645,14 @@ brace-expansion@^1.1.7: braces@^3.0.3, braces@~3.0.2: version "3.0.3" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" -browserslist@^4.21.10, browserslist@^4.24.4: - version "4.24.4" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" - integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== - dependencies: - caniuse-lite "^1.0.30001688" - electron-to-chromium "^1.5.73" - node-releases "^2.0.19" - update-browserslist-db "^1.1.1" - -browserslist@^4.24.0: +browserslist@^4.24.0, "browserslist@>= 4.21.0": version "4.24.2" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz" integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== dependencies: caniuse-lite "^1.0.30001669" @@ -2622,38 +2660,48 @@ browserslist@^4.24.0: node-releases "^2.0.18" update-browserslist-db "^1.1.1" +browserslist@^4.24.4: + version "4.24.4" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + node-releases "^2.0.19" + update-browserslist-db "^1.1.1" + bser@2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== bundle-name@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + resolved "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz" integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== dependencies: run-applescript "^7.0.0" bytes@3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== bytes@3.1.2: version "3.1.2" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== call-bind@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz" integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== dependencies: es-define-property "^1.0.0" @@ -2664,32 +2712,32 @@ call-bind@^1.0.7: callsites@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase@^5.3.1: version "5.3.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.2.0: version "6.3.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001669: version "1.0.30001669" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz#fda8f1d29a8bfdc42de0c170d7f34a9cf19ed7a3" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz" integrity sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w== caniuse-lite@^1.0.30001688: version "1.0.30001702" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001702.tgz#cde16fa8adaa066c04aec2967b6cde46354644c4" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001702.tgz" integrity sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA== chalk@^4.0.0: version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -2697,12 +2745,12 @@ chalk@^4.0.0: char-regex@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== chokidar@^3.6.0: version "3.6.0" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== dependencies: anymatch "~3.1.2" @@ -2717,22 +2765,22 @@ chokidar@^3.6.0: chrome-trace-event@^1.0.2: version "1.0.4" - resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz" integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== ci-info@^3.2.0: version "3.9.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cjs-module-lexer@^1.0.0: version "1.4.1" - resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" + resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz" integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== cliui@^8.0.1: version "8.0.1" - resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" @@ -2741,7 +2789,7 @@ cliui@^8.0.1: clone-deep@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== dependencies: is-plain-object "^2.0.4" @@ -2750,63 +2798,63 @@ clone-deep@^4.0.1: co@^4.6.0: version "4.6.0" - resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== collect-v8-coverage@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz" integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@~1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^2.0.10, colorette@^2.0.14: version "2.0.20" - resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" commander@^10.0.1: version "10.0.1" - resolved "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + resolved "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== commander@^2.20.0: version "2.20.3" - resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commondir@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== compressible@~2.0.16: version "2.0.18" - resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" compression@^1.7.4: version "1.7.4" - resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz" integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== dependencies: accepts "~1.3.5" @@ -2819,56 +2867,56 @@ compression@^1.7.4: concat-map@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== connect-history-api-fallback@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz" integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== content-disposition@0.5.4: version "0.5.4" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== convert-source-map@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== cookie-signature@1.0.6: version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== cookie@0.7.1: version "0.7.1" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== core-js-compat@^3.40.0: version "3.41.0" - resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz#4cdfce95f39a8f27759b667cf693d96e5dda3d17" + resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz" integrity sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A== dependencies: browserslist "^4.24.4" core-util-is@~1.0.0: version "1.0.3" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== create-jest@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + resolved "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz" integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== dependencies: "@jest/types" "^29.6.3" @@ -2881,12 +2929,12 @@ create-jest@^29.7.0: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -2895,67 +2943,67 @@ cross-spawn@^7.0.3: cssom@^0.5.0: version "0.5.0" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz" integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== cssom@~0.3.6: version "0.3.8" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== cssstyle@^2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: cssom "~0.3.6" data-urls@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz" integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== dependencies: abab "^2.0.6" whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@4: + version "4.3.7" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + debug@2.6.9: version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: - version "4.3.7" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" - integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== - dependencies: - ms "^2.1.3" - decimal.js@^10.4.2: version "10.4.3" - resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== dedent@^1.0.0: version "1.5.3" - resolved "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" + resolved "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz" integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== deepmerge@^4.2.2: version "4.3.1" - resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== default-browser-id@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + resolved "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz" integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== default-browser@^5.2.1: version "5.2.1" - resolved "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + resolved "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz" integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== dependencies: bundle-name "^4.1.0" @@ -2963,7 +3011,7 @@ default-browser@^5.2.1: define-data-property@^1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: es-define-property "^1.0.0" @@ -2972,106 +3020,106 @@ define-data-property@^1.1.4: define-lazy-prop@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz" integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -depd@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - depd@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + destroy@1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== detect-newline@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== detect-node@^2.0.4: version "2.1.0" - resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== diff-sequences@^29.6.3: version "29.6.3" - resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz" integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== diff@^4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== dns-packet@^5.2.2: version "5.6.1" - resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" + resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz" integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== dependencies: "@leichtgewicht/ip-codec" "^2.0.1" domexception@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + resolved "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz" integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== dependencies: webidl-conversions "^7.0.0" ee-first@1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.5.41: version "1.5.45" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.45.tgz#fa592ce6a88b44d23acbc7453a2feab98996e6c9" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.45.tgz" integrity sha512-vOzZS6uZwhhbkZbcRyiy99Wg+pYFV5hk+5YaECvx0+Z31NR3Tt5zS6dze2OepT6PCTzVzT0dIJItti+uAW5zmw== electron-to-chromium@^1.5.73: version "1.5.112" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.112.tgz#8d3d95d4d5653836327890282c8eda5c6f26626d" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.112.tgz" integrity sha512-oen93kVyqSb3l+ziUgzIOlWt/oOuy4zRmpwestMn4rhFWAoFJeFuCVte9F2fASjeZZo7l/Cif9TiyrdW4CwEMA== emittery@^0.13.1: version "0.13.1" - resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emojis-list@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== encodeurl@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== encodeurl@~2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== enhanced-resolve@^5.17.1: version "5.18.1" - resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz#728ab082f8b7b6836de51f1637aab5d3b9568faf" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz" integrity sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg== dependencies: graceful-fs "^4.2.4" @@ -3079,56 +3127,56 @@ enhanced-resolve@^5.17.1: entities@^4.5.0: version "4.5.0" - resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== envinfo@^7.7.3: version "7.14.0" - resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" + resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz" integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" es-define-property@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz" integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== dependencies: get-intrinsic "^1.2.4" es-errors@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-module-lexer@^1.2.1: version "1.6.0" - resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz#da49f587fd9e68ee2404fe4e256c0c7d3a81be21" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz" integrity sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ== escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-html@~1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escodegen@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz" integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== dependencies: esprima "^4.0.1" @@ -3139,7 +3187,7 @@ escodegen@^2.0.0: eslint-scope@5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" @@ -3147,49 +3195,49 @@ eslint-scope@5.1.1: esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^4.1.1: version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.2.0: version "5.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== etag@~1.8.1: version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== eventemitter3@^4.0.0: version "4.0.7" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== events@^3.2.0: version "3.3.0" - resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== execa@^5.0.0: version "5.1.1" - resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -3204,12 +3252,12 @@ execa@^5.0.0: exit@^0.1.2: version "0.1.2" - resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== expect@^29.0.0, expect@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + resolved "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz" integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== dependencies: "@jest/expect-utils" "^29.7.0" @@ -3220,7 +3268,7 @@ expect@^29.0.0, expect@^29.7.0: express@^4.21.2: version "4.21.2" - resolved "https://registry.npmjs.org/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" + resolved "https://registry.npmjs.org/express/-/express-4.21.2.tgz" integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== dependencies: accepts "~1.3.8" @@ -3257,48 +3305,48 @@ express@^4.21.2: fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-uri@^3.0.1: version "3.0.3" - resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" + resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz" integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw== fastest-levenshtein@^1.0.12: version "1.0.16" - resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== -faye-websocket@0.11.4, faye-websocket@^0.11.3: +faye-websocket@^0.11.3, faye-websocket@0.11.4: version "0.11.4" - resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== dependencies: websocket-driver ">=0.5.1" fb-watchman@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz" integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" fill-range@^7.1.1: version "7.1.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" finalhandler@1.3.1: version "1.3.1" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz" integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== dependencies: debug "2.6.9" @@ -3311,7 +3359,7 @@ finalhandler@1.3.1: find-cache-dir@^3.3.1: version "3.3.2" - resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== dependencies: commondir "^1.0.1" @@ -3320,59 +3368,59 @@ find-cache-dir@^3.3.1: find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" path-exists "^4.0.0" -firebase@11.3.0: - version "11.3.0" - resolved "https://registry.npmjs.org/firebase/-/firebase-11.3.0.tgz#3e6a61411cee00c1c7b307c546b5c7d40b3e2d1a" - integrity sha512-wLuBsWqg/M3pay2qOOLLKjTQxPUO2yrJgZLt4TKUwA3c3lrFNM2zc40uzD9LQdUk6H9HEK6bXjGPFrpwmu7HzA== - dependencies: - "@firebase/analytics" "0.10.11" - "@firebase/analytics-compat" "0.2.17" - "@firebase/app" "0.11.0" - "@firebase/app-check" "0.8.11" - "@firebase/app-check-compat" "0.3.18" - "@firebase/app-compat" "0.2.49" +firebase@^11.8.0-20250512211235: + version "11.8.0-20250512211235" + resolved "https://registry.npmjs.org/firebase/-/firebase-11.8.0-20250512211235.tgz" + integrity sha512-0juBB1XpC52s4BF/6OwJd7ka8imyXIIbsnjp6yVr1n8W0GpYIpvXzKP0oahiV2Wiv9l0DfVRZpsMB0OJvk9nQw== + dependencies: + "@firebase/ai" "1.3.0-20250512211235" + "@firebase/analytics" "0.10.14-20250512211235" + "@firebase/analytics-compat" "0.2.20-20250512211235" + "@firebase/app" "0.13.0-20250512211235" + "@firebase/app-check" "0.10.0-20250512211235" + "@firebase/app-check-compat" "0.3.23-20250512211235" + "@firebase/app-compat" "0.4.0-20250512211235" "@firebase/app-types" "0.9.3" - "@firebase/auth" "1.9.0" - "@firebase/auth-compat" "0.5.18" - "@firebase/data-connect" "0.3.0" - "@firebase/database" "1.0.12" - "@firebase/database-compat" "2.0.3" - "@firebase/firestore" "4.7.7" - "@firebase/firestore-compat" "0.3.42" - "@firebase/functions" "0.12.2" - "@firebase/functions-compat" "0.3.19" - "@firebase/installations" "0.6.12" - "@firebase/installations-compat" "0.2.12" - "@firebase/messaging" "0.12.16" - "@firebase/messaging-compat" "0.2.16" - "@firebase/performance" "0.7.0" - "@firebase/performance-compat" "0.2.13" - "@firebase/remote-config" "0.5.0" - "@firebase/remote-config-compat" "0.2.12" - "@firebase/storage" "0.13.6" - "@firebase/storage-compat" "0.3.16" - "@firebase/util" "1.10.3" - "@firebase/vertexai" "1.0.4" + "@firebase/auth" "1.10.3-20250512211235" + "@firebase/auth-compat" "0.5.23-20250512211235" + "@firebase/data-connect" "0.3.6-20250512211235" + "@firebase/database" "1.0.16-20250512211235" + "@firebase/database-compat" "2.0.7-20250512211235" + "@firebase/firestore" "4.7.13-20250512211235" + "@firebase/firestore-compat" "0.3.48-20250512211235" + "@firebase/functions" "0.12.5-20250512211235" + "@firebase/functions-compat" "0.3.22-20250512211235" + "@firebase/installations" "0.6.15-20250512211235" + "@firebase/installations-compat" "0.2.15-20250512211235" + "@firebase/messaging" "0.12.19-20250512211235" + "@firebase/messaging-compat" "0.2.19-20250512211235" + "@firebase/performance" "0.7.4-20250512211235" + "@firebase/performance-compat" "0.2.17-20250512211235" + "@firebase/remote-config" "0.6.2-20250512211235" + "@firebase/remote-config-compat" "0.2.15-20250512211235" + "@firebase/storage" "0.13.9-20250512211235" + "@firebase/storage-compat" "0.3.19-20250512211235" + "@firebase/util" "1.12.0-20250512211235" flat@^5.0.2: version "5.0.2" - resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== follow-redirects@^1.0.0: version "1.15.9" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== form-data@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz" integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== dependencies: asynckit "^0.4.0" @@ -3381,42 +3429,42 @@ form-data@^4.0.0: forwarded@0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fresh@0.5.2: version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: version "1.2.4" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz" integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== dependencies: es-errors "^1.3.0" @@ -3427,29 +3475,29 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: get-package-type@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-stream@^6.0.0: version "6.0.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== glob-parent@~5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob-to-regexp@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== glob@^7.1.3, glob@^7.1.4: version "7.2.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" @@ -3461,58 +3509,58 @@ glob@^7.1.3, glob@^7.1.4: globals@^11.1.0: version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== gopd@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== dependencies: get-intrinsic "^1.1.3" graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== handle-thing@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: es-define-property "^1.0.0" has-proto@^1.0.1: version "1.0.3" - resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz" integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== has-symbols@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== hasown@^2.0.0, hasown@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" hpack.js@^2.1.6: version "2.1.6" - resolved "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + resolved "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz" integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== dependencies: inherits "^2.0.1" @@ -3522,24 +3570,34 @@ hpack.js@^2.1.6: html-encoding-sniffer@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz" integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== dependencies: whatwg-encoding "^2.0.0" html-escaper@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-deceiver@^1.2.7: version "1.2.7" - resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-errors@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: depd "2.0.0" @@ -3548,24 +3606,14 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - http-parser-js@>=0.5.1: version "0.5.8" - resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== http-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz" integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: "@tootallnate/once" "2" @@ -3574,7 +3622,7 @@ http-proxy-agent@^5.0.0: http-proxy-middleware@^2.0.7: version "2.0.7" - resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz#915f236d92ae98ef48278a95dedf17e991936ec6" + resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz" integrity sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA== dependencies: "@types/http-proxy" "^1.17.8" @@ -3585,7 +3633,7 @@ http-proxy-middleware@^2.0.7: http-proxy@^1.18.1: version "1.18.1" - resolved "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + resolved "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz" integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== dependencies: eventemitter3 "^4.0.0" @@ -3594,7 +3642,7 @@ http-proxy@^1.18.1: https-proxy-agent@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" @@ -3602,36 +3650,36 @@ https-proxy-agent@^5.0.1: human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== hyperdyperid@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" + resolved "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz" integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== iconv-lite@0.4.24: version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" iconv-lite@0.6.3: version "0.6.3" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" idb@7.1.1: version "7.1.1" - resolved "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b" + resolved "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz" integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== import-local@^3.0.2: version "3.2.0" - resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz" integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" @@ -3639,157 +3687,157 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@2, inherits@2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.3: version "2.0.3" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== interpret@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + resolved "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz" integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - ipaddr.js@^2.1.0: version "2.2.0" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-binary-path@~2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" is-core-module@^2.13.0: version "2.15.1" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz" integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== dependencies: hasown "^2.0.2" is-docker@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz" integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-fn@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.3" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-inside-container@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + resolved "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz" integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== dependencies: is-docker "^3.0.0" is-network-error@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz#d26a760e3770226d11c169052f266a4803d9c997" + resolved "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz" integrity sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g== is-number@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-plain-obj@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-potential-custom-element-name@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-wsl@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz" integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== dependencies: is-inside-container "^1.0.0" isarray@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isobject@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" - resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz" integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-instrument@^5.0.4: version "5.2.1" - resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== dependencies: "@babel/core" "^7.12.3" @@ -3800,7 +3848,7 @@ istanbul-lib-instrument@^5.0.4: istanbul-lib-instrument@^6.0.0: version "6.0.3" - resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz" integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== dependencies: "@babel/core" "^7.23.9" @@ -3811,7 +3859,7 @@ istanbul-lib-instrument@^6.0.0: istanbul-lib-report@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz" integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" @@ -3820,7 +3868,7 @@ istanbul-lib-report@^3.0.0: istanbul-lib-source-maps@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz" integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" @@ -3829,7 +3877,7 @@ istanbul-lib-source-maps@^4.0.0: istanbul-reports@^3.1.3: version "3.1.7" - resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz" integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== dependencies: html-escaper "^2.0.0" @@ -3837,7 +3885,7 @@ istanbul-reports@^3.1.3: jest-changed-files@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz" integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== dependencies: execa "^5.0.0" @@ -3846,7 +3894,7 @@ jest-changed-files@^29.7.0: jest-circus@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz" integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== dependencies: "@jest/environment" "^29.7.0" @@ -3872,7 +3920,7 @@ jest-circus@^29.7.0: jest-cli@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz" integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== dependencies: "@jest/core" "^29.7.0" @@ -3889,7 +3937,7 @@ jest-cli@^29.7.0: jest-config@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz" integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== dependencies: "@babel/core" "^7.11.6" @@ -3917,7 +3965,7 @@ jest-config@^29.7.0: jest-diff@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz" integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== dependencies: chalk "^4.0.0" @@ -3927,14 +3975,14 @@ jest-diff@^29.7.0: jest-docblock@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz" integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== dependencies: detect-newline "^3.0.0" jest-each@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz" integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== dependencies: "@jest/types" "^29.6.3" @@ -3945,7 +3993,7 @@ jest-each@^29.7.0: jest-environment-jsdom@29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz" integrity sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA== dependencies: "@jest/environment" "^29.7.0" @@ -3959,7 +4007,7 @@ jest-environment-jsdom@29.7.0: jest-environment-node@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz" integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== dependencies: "@jest/environment" "^29.7.0" @@ -3971,12 +4019,12 @@ jest-environment-node@^29.7.0: jest-get-type@^29.6.3: version "29.6.3" - resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== jest-haste-map@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz" integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== dependencies: "@jest/types" "^29.6.3" @@ -3995,7 +4043,7 @@ jest-haste-map@^29.7.0: jest-leak-detector@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz" integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== dependencies: jest-get-type "^29.6.3" @@ -4003,7 +4051,7 @@ jest-leak-detector@^29.7.0: jest-matcher-utils@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz" integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== dependencies: chalk "^4.0.0" @@ -4013,7 +4061,7 @@ jest-matcher-utils@^29.7.0: jest-message-util@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz" integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: "@babel/code-frame" "^7.12.13" @@ -4028,7 +4076,7 @@ jest-message-util@^29.7.0: jest-mock@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz" integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== dependencies: "@jest/types" "^29.6.3" @@ -4037,25 +4085,25 @@ jest-mock@^29.7.0: jest-pnp-resolver@^1.2.2: version "1.2.3" - resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== jest-regex-util@^29.6.3: version "29.6.3" - resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz" integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== jest-resolve-dependencies@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz" integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== dependencies: jest-regex-util "^29.6.3" jest-snapshot "^29.7.0" -jest-resolve@^29.7.0: +jest-resolve@*, jest-resolve@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz" integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== dependencies: chalk "^4.0.0" @@ -4070,7 +4118,7 @@ jest-resolve@^29.7.0: jest-runner@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz" integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== dependencies: "@jest/console" "^29.7.0" @@ -4097,7 +4145,7 @@ jest-runner@^29.7.0: jest-runtime@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz" integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== dependencies: "@jest/environment" "^29.7.0" @@ -4125,7 +4173,7 @@ jest-runtime@^29.7.0: jest-snapshot@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz" integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== dependencies: "@babel/core" "^7.11.6" @@ -4151,7 +4199,7 @@ jest-snapshot@^29.7.0: jest-util@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz" integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: "@jest/types" "^29.6.3" @@ -4163,7 +4211,7 @@ jest-util@^29.7.0: jest-validate@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz" integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== dependencies: "@jest/types" "^29.6.3" @@ -4175,7 +4223,7 @@ jest-validate@^29.7.0: jest-watcher@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz" integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== dependencies: "@jest/test-result" "^29.7.0" @@ -4189,7 +4237,7 @@ jest-watcher@^29.7.0: jest-worker@^27.4.5: version "27.5.1" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== dependencies: "@types/node" "*" @@ -4198,7 +4246,7 @@ jest-worker@^27.4.5: jest-worker@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz" integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" @@ -4208,7 +4256,7 @@ jest-worker@^29.7.0: jest@29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: "@jest/core" "^29.7.0" @@ -4218,12 +4266,12 @@ jest@29.7.0: js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: version "3.14.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" @@ -4231,7 +4279,7 @@ js-yaml@^3.13.1: jsdom@^20.0.0: version "20.0.3" - resolved "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz" integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== dependencies: abab "^2.0.6" @@ -4263,42 +4311,42 @@ jsdom@^20.0.0: jsesc@^3.0.2, jsesc@~3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz" integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema-traverse@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== json5@^2.1.2, json5@^2.2.3: version "2.2.3" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== kind-of@^6.0.2: version "6.0.3" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== kleur@^3.0.3: version "3.0.3" - resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== launch-editor@^2.6.1: version "2.9.1" - resolved "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz#253f173bd441e342d4344b4dae58291abb425047" + resolved "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz" integrity sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w== dependencies: picocolors "^1.0.0" @@ -4306,22 +4354,22 @@ launch-editor@^2.6.1: leven@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== loader-runner@^4.2.0: version "4.3.0" - resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== loader-utils@^2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz" integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" @@ -4330,67 +4378,67 @@ loader-utils@^2.0.4: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" lodash.camelcase@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== lodash.debounce@^4.0.8: version "4.0.8" - resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== long@^5.0.0: version "5.2.3" - resolved "https://registry.npmjs.org/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" + resolved "https://registry.npmjs.org/long/-/long-5.2.3.tgz" integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" make-dir@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz" integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== dependencies: semver "^7.5.3" make-error@^1.1.1: version "1.3.6" - resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== makeerror@1.0.12: version "1.0.12" - resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz" integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== dependencies: tmpl "1.0.5" media-typer@0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== memfs@^4.6.0: version "4.14.0" - resolved "https://registry.npmjs.org/memfs/-/memfs-4.14.0.tgz#48d5e85a03ea0b428280003212fbca3063531be3" + resolved "https://registry.npmjs.org/memfs/-/memfs-4.14.0.tgz" integrity sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA== dependencies: "@jsonjoy.com/json-pack" "^1.0.3" @@ -4400,79 +4448,79 @@ memfs@^4.6.0: merge-descriptors@1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== methods@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.8" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" picomatch "^2.3.1" -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - "mime-db@>= 1.43.0 < 2": version "1.53.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz" integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" mime@1.6.0: version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== minimalistic-assert@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" +ms@^2.1.3, ms@2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + ms@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.3, ms@^2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - multicast-dns@^7.2.5: version "7.2.5" - resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== dependencies: dns-packet "^5.2.2" @@ -4480,95 +4528,95 @@ multicast-dns@^7.2.5: natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== negotiator@0.6.3: version "0.6.3" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== neo-async@^2.6.2: version "2.6.2" - resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== node-forge@^1: version "1.3.1" - resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-releases@^2.0.18: version "2.0.18" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== node-releases@^2.0.19: version "2.0.19" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" nwsapi@^2.2.2: version "2.2.16" - resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz#177760bba02c351df1d2644e220c31dfec8cdb43" + resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz" integrity sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ== object-inspect@^1.13.1: version "1.13.2" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz" integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@2.4.1, on-finished@^2.4.1: +on-finished@^2.4.1, on-finished@2.4.1: version "2.4.1" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" on-headers@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== once@^1.3.0: version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" onetime@^5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" open@^10.0.3: version "10.1.0" - resolved "https://registry.npmjs.org/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" + resolved "https://registry.npmjs.org/open/-/open-10.1.0.tgz" integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== dependencies: default-browser "^5.2.1" @@ -4578,28 +4626,28 @@ open@^10.0.3: p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-limit@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-retry@^6.2.0: version "6.2.0" - resolved "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz#8d6df01af298750009691ce2f9b3ad2d5968f3bd" + resolved "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz" integrity sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA== dependencies: "@types/retry" "0.12.2" @@ -4608,12 +4656,12 @@ p-retry@^6.2.0: p-try@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== parse-json@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -4623,66 +4671,66 @@ parse-json@^5.2.0: parse5@^7.0.0, parse5@^7.1.1: version "7.2.1" - resolved "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a" + resolved "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz" integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== dependencies: entities "^4.5.0" parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-to-regexp@0.1.12: version "0.1.12" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz" integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== picocolors@^1.0.0, picocolors@^1.1.0: version "1.1.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pirates@^4.0.4: version "4.0.6" - resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz" integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== dependencies: "@jest/schemas" "^29.6.3" @@ -4691,12 +4739,12 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== prompts@^2.0.1: version "2.4.2" - resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== dependencies: kleur "^3.0.3" @@ -4704,7 +4752,7 @@ prompts@^2.0.1: protobufjs@^7.2.5: version "7.4.0" - resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz#7efe324ce9b3b61c82aae5de810d287bc08a248a" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz" integrity sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw== dependencies: "@protobufjs/aspromise" "^1.1.2" @@ -4722,7 +4770,7 @@ protobufjs@^7.2.5: proxy-addr@~2.0.7: version "2.0.7" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: forwarded "0.2.0" @@ -4730,48 +4778,48 @@ proxy-addr@~2.0.7: psl@^1.1.33: version "1.15.0" - resolved "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" + resolved "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz" integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== dependencies: punycode "^2.3.1" punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== pure-rand@^6.0.0: version "6.1.0" - resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== qs@6.13.0: version "6.13.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + resolved "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz" integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: side-channel "^1.0.6" querystringify@^2.1.1: version "2.2.0" - resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== randombytes@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== raw-body@2.5.2: version "2.5.2" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz" integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== dependencies: bytes "3.1.2" @@ -4781,12 +4829,12 @@ raw-body@2.5.2: react-is@^18.0.0: version "18.3.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== readable-stream@^2.0.1: version "2.3.8" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" @@ -4799,7 +4847,7 @@ readable-stream@^2.0.1: readable-stream@^3.0.6: version "3.6.2" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" @@ -4808,45 +4856,45 @@ readable-stream@^3.0.6: readdirp@~3.6.0: version "3.6.0" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" rechoir@^0.8.0: version "0.8.0" - resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz" integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== dependencies: resolve "^1.20.0" regenerate-unicode-properties@^10.2.0: version "10.2.0" - resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" + resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz" integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA== dependencies: regenerate "^1.4.2" regenerate@^1.4.2: version "1.4.2" - resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== regenerator-runtime@^0.14.0: version "0.14.1" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== regenerator-transform@^0.15.2: version "0.15.2" - resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz" integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== dependencies: "@babel/runtime" "^7.8.4" regexpu-core@^6.1.1: version "6.1.1" - resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz#b469b245594cb2d088ceebc6369dceb8c00becac" + resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz" integrity sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw== dependencies: regenerate "^1.4.2" @@ -4858,51 +4906,51 @@ regexpu-core@^6.1.1: regjsgen@^0.8.0: version "0.8.0" - resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" + resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== regjsparser@^0.11.0: version "0.11.1" - resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz#ae55c74f646db0c8fcb922d4da635e33da405149" + resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz" integrity sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ== dependencies: jsesc "~3.0.2" require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== requires-port@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve.exports@^2.0.0: version "2.0.3" - resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" + resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz" integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== resolve@^1.14.2, resolve@^1.20.0: version "1.22.8" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: is-core-module "^2.13.0" @@ -4911,57 +4959,53 @@ resolve@^1.14.2, resolve@^1.20.0: retry@^0.13.1: version "0.13.1" - resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== run-applescript@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + resolved "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz" integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== saxes@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + resolved "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz" integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== dependencies: xmlchars "^2.2.0" schema-utils@^2.6.5: version "2.7.1" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz" integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== dependencies: "@types/json-schema" "^7.0.5" ajv "^6.12.4" ajv-keywords "^3.5.2" -schema-utils@^3.2.0: - version "3.3.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" - integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== - dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - schema-utils@^4.0.0, schema-utils@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz" integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== dependencies: "@types/json-schema" "^7.0.9" @@ -4971,7 +5015,7 @@ schema-utils@^4.0.0, schema-utils@^4.2.0: schema-utils@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz#3b669f04f71ff2dfb5aba7ce2d5a9d79b35622c0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz" integrity sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g== dependencies: "@types/json-schema" "^7.0.9" @@ -4981,12 +5025,12 @@ schema-utils@^4.3.0: select-hose@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== selfsigned@^2.4.1: version "2.4.1" - resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" + resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz" integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== dependencies: "@types/node-forge" "^1.3.0" @@ -4994,17 +5038,22 @@ selfsigned@^2.4.1: semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.3, semver@^7.5.4: +semver@^7.5.3: version "7.6.3" - resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +semver@^7.5.4: + version "7.6.3" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== send@0.19.0: version "0.19.0" - resolved "https://registry.npmjs.org/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + resolved "https://registry.npmjs.org/send/-/send-0.19.0.tgz" integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== dependencies: debug "2.6.9" @@ -5023,14 +5072,14 @@ send@0.19.0: serialize-javascript@^6.0.2: version "6.0.2" - resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz" integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" serve-index@^1.9.1: version "1.9.1" - resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== dependencies: accepts "~1.3.4" @@ -5043,7 +5092,7 @@ serve-index@^1.9.1: serve-static@1.16.2: version "1.16.2" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz" integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== dependencies: encodeurl "~2.0.0" @@ -5053,7 +5102,7 @@ serve-static@1.16.2: set-function-length@^1.2.1: version "1.2.2" - resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== dependencies: define-data-property "^1.1.4" @@ -5065,41 +5114,41 @@ set-function-length@^1.2.1: setprototypeof@1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== setprototypeof@1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shallow-clone@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== dependencies: kind-of "^6.0.2" shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.8.1: version "1.8.1" - resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz" integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== side-channel@^1.0.6: version "1.0.6" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz" integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== dependencies: call-bind "^1.0.7" @@ -5109,52 +5158,52 @@ side-channel@^1.0.6: signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== sisteransi@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== slash@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== sockjs@^0.3.24: version "0.3.24" - resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== dependencies: faye-websocket "^0.11.3" uuid "^8.3.2" websocket-driver "^0.7.4" -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== spdy-transport@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + resolved "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz" integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== dependencies: debug "^4.1.0" @@ -5166,7 +5215,7 @@ spdy-transport@^3.0.0: spdy@^4.0.2: version "4.0.2" - resolved "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + resolved "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz" integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== dependencies: debug "^4.1.0" @@ -5177,29 +5226,43 @@ spdy@^4.0.2: sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== stack-utils@^2.0.3: version "2.0.6" - resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + statuses@2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -"statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" string-length@^4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: char-regex "^1.0.2" @@ -5207,82 +5270,66 @@ string-length@^4.0.1: string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-bom@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-color@^8.0.0: version "8.1.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== symbol-tree@^3.2.4: version "3.2.4" - resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== tapable@^2.1.1, tapable@^2.2.0: version "2.2.1" - resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -terser-webpack-plugin@^5.3.10: +terser-webpack-plugin@^5.3.11: version "5.3.14" - resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz#9031d48e57ab27567f02ace85c7d690db66c3e06" - integrity sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw== dependencies: "@jridgewell/trace-mapping" "^0.3.25" jest-worker "^27.4.5" @@ -5292,7 +5339,7 @@ terser-webpack-plugin@^5.3.10: terser@^5.31.1: version "5.39.0" - resolved "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz#0e82033ed57b3ddf1f96708d123cca717d86ca3a" + resolved "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz" integrity sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw== dependencies: "@jridgewell/source-map" "^0.3.3" @@ -5302,7 +5349,7 @@ terser@^5.31.1: test-exclude@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: "@istanbuljs/schema" "^0.1.2" @@ -5311,34 +5358,34 @@ test-exclude@^6.0.0: thingies@^1.20.0: version "1.21.0" - resolved "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz#e80fbe58fd6fdaaab8fad9b67bd0a5c943c445c1" + resolved "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz" integrity sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g== thunky@^1.0.2: version "1.1.0" - resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== tmpl@1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" toidentifier@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== tough-cookie@^4.1.2: version "4.1.4" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz" integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== dependencies: psl "^1.1.33" @@ -5348,19 +5395,19 @@ tough-cookie@^4.1.2: tr46@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + resolved "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz" integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== dependencies: punycode "^2.1.1" tree-dump@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz#c460d5921caeb197bde71d0e9a7b479848c5b8ac" + resolved "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz" integrity sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ== -ts-node@10.9.2: +ts-node@>=9.0.0, ts-node@10.9.2: version "10.9.2" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: "@cspotcode/source-map-support" "^0.8.0" @@ -5377,47 +5424,47 @@ ts-node@10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tslib@^2.0.0, tslib@^2.1.0: +tslib@^2, tslib@^2.0.0, tslib@^2.1.0, tslib@2: version "2.8.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz" integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA== type-detect@4.0.8: version "4.0.8" - resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-is@~1.6.18: version "1.6.18" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" mime-types "~2.1.24" -typescript@5.5.4: +typescript@>=2.7, typescript@5.5.4: version "5.5.4" - resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz" integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== undici-types@~6.19.8: version "6.19.8" - resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2" + resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz" integrity sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg== unicode-match-property-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz" integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== dependencies: unicode-canonical-property-names-ecmascript "^2.0.0" @@ -5425,27 +5472,27 @@ unicode-match-property-ecmascript@^2.0.0: unicode-match-property-value-ecmascript@^2.1.0: version "2.2.0" - resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz#a0401aee72714598f739b68b104e4fe3a0cb3c71" + resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz" integrity sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg== unicode-property-aliases-ecmascript@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== universalify@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@~1.0.0, unpipe@1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== update-browserslist-db@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz" integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== dependencies: escalade "^3.2.0" @@ -5453,14 +5500,14 @@ update-browserslist-db@^1.1.1: uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" url-parse@^1.5.3: version "1.5.10" - resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz" integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== dependencies: querystringify "^2.1.1" @@ -5468,27 +5515,27 @@ url-parse@^1.5.3: util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== utils-merge@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== uuid@^8.3.2: version "8.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== v8-compile-cache-lib@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-to-istanbul@^9.0.1: version "9.3.0" - resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz" integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== dependencies: "@jridgewell/trace-mapping" "^0.3.12" @@ -5497,26 +5544,26 @@ v8-to-istanbul@^9.0.1: vary@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== w3c-xmlserializer@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz" integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== dependencies: xml-name-validator "^4.0.0" walker@^1.0.8: version "1.0.8" - resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: makeerror "1.0.12" watchpack@^2.4.1: version "2.4.2" - resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz" integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== dependencies: glob-to-regexp "^0.4.1" @@ -5524,24 +5571,24 @@ watchpack@^2.4.1: wbuf@^1.1.0, wbuf@^1.7.3: version "1.7.3" - resolved "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + resolved "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz" integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== dependencies: minimalistic-assert "^1.0.0" web-vitals@^4.2.4: version "4.2.4" - resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz#1d20bc8590a37769bd0902b289550936069184b7" + resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz" integrity sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw== webidl-conversions@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -webpack-cli@5.1.4: +webpack-cli@5.1.4, webpack-cli@5.x.x: version "5.1.4" - resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz" integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== dependencies: "@discoveryjs/json-ext" "^0.5.0" @@ -5560,7 +5607,7 @@ webpack-cli@5.1.4: webpack-dev-middleware@^7.4.2: version "7.4.2" - resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz#40e265a3d3d26795585cff8207630d3a8ff05877" + resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz" integrity sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA== dependencies: colorette "^2.0.10" @@ -5572,7 +5619,7 @@ webpack-dev-middleware@^7.4.2: webpack-dev-server@5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.0.tgz#68043886edaa3fd875ad20e01589990a79612f9c" + resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.0.tgz" integrity sha512-90SqqYXA2SK36KcT6o1bvwvZfJFcmoamqeJY7+boioffX9g9C0wjjJRGUrQIuh43pb0ttX7+ssavmj/WN2RHtA== dependencies: "@types/bonjour" "^3.5.13" @@ -5605,7 +5652,7 @@ webpack-dev-server@5.2.0: webpack-merge@^5.7.3: version "5.10.0" - resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz" integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== dependencies: clone-deep "^4.0.1" @@ -5614,21 +5661,19 @@ webpack-merge@^5.7.3: webpack-sources@^3.2.3: version "3.2.3" - resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@5.94.0: - version "5.94.0" - resolved "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f" - integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== - dependencies: - "@types/estree" "^1.0.5" - "@webassemblyjs/ast" "^1.12.1" - "@webassemblyjs/wasm-edit" "^1.12.1" - "@webassemblyjs/wasm-parser" "^1.12.1" - acorn "^8.7.1" - acorn-import-attributes "^1.9.5" - browserslist "^4.21.10" +webpack@^5.0.0, webpack@^5.1.0, webpack@>=2, webpack@5.98.0, webpack@5.x.x: + version "5.98.0" + dependencies: + "@types/eslint-scope" "^3.7.7" + "@types/estree" "^1.0.6" + "@webassemblyjs/ast" "^1.14.1" + "@webassemblyjs/wasm-edit" "^1.14.1" + "@webassemblyjs/wasm-parser" "^1.14.1" + acorn "^8.14.0" + browserslist "^4.24.0" chrome-trace-event "^1.0.2" enhanced-resolve "^5.17.1" es-module-lexer "^1.2.1" @@ -5640,15 +5685,15 @@ webpack@5.94.0: loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.2.0" + schema-utils "^4.3.0" tapable "^2.1.1" - terser-webpack-plugin "^5.3.10" + terser-webpack-plugin "^5.3.11" watchpack "^2.4.1" webpack-sources "^3.2.3" -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: +websocket-driver@^0.7.4, websocket-driver@>=0.5.1: version "0.7.4" - resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== dependencies: http-parser-js ">=0.5.1" @@ -5657,24 +5702,24 @@ websocket-driver@>=0.5.1, websocket-driver@^0.7.4: websocket-extensions@>=0.1.1: version "0.1.4" - resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== whatwg-encoding@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz" integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== dependencies: iconv-lite "0.6.3" whatwg-mimetype@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz" integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== whatwg-url@^11.0.0: version "11.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz" integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== dependencies: tr46 "^3.0.0" @@ -5682,19 +5727,19 @@ whatwg-url@^11.0.0: which@^2.0.1: version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" wildcard@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz" integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -5703,12 +5748,12 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^4.0.2: version "4.0.2" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" @@ -5716,37 +5761,37 @@ write-file-atomic@^4.0.2: ws@^8.11.0, ws@^8.18.0: version "8.18.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== xml-name-validator@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== xmlchars@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^3.0.2: version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yargs-parser@^21.1.1: version "21.1.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@^17.3.1, yargs@^17.7.2: version "17.7.2" - resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" @@ -5759,10 +5804,10 @@ yargs@^17.3.1, yargs@^17.7.2: yn@3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 2fe754727d29e2fcc2c28a6c59ff5d0b84a12033 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 20 May 2025 08:34:52 -0700 Subject: [PATCH 46/77] Version Packages (#9016) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/eighty-starfishes-listen.md | 6 --- .changeset/fast-mangos-chew.md | 5 -- .changeset/ninety-ways-dress.md | 8 ---- .changeset/perfect-camels-try.md | 6 --- .changeset/tall-zoos-stare.md | 6 --- integration/compat-interop/package.json | 28 +++++------ integration/firestore/package.json | 4 +- integration/messaging/package.json | 2 +- packages/ai/CHANGELOG.md | 16 +++++++ packages/ai/package.json | 8 ++-- packages/analytics-compat/CHANGELOG.md | 9 ++++ packages/analytics-compat/package.json | 10 ++-- packages/analytics/CHANGELOG.md | 9 ++++ packages/analytics/package.json | 10 ++-- packages/app-check-compat/CHANGELOG.md | 9 ++++ packages/app-check-compat/package.json | 10 ++-- packages/app-check/CHANGELOG.md | 12 +++++ packages/app-check/package.json | 8 ++-- packages/app-compat/CHANGELOG.md | 13 +++++ packages/app-compat/package.json | 8 ++-- packages/app/CHANGELOG.md | 12 +++++ packages/app/package.json | 6 +-- packages/auth-compat/CHANGELOG.md | 9 ++++ packages/auth-compat/package.json | 10 ++-- packages/auth/CHANGELOG.md | 8 ++++ packages/auth/package.json | 8 ++-- packages/component/CHANGELOG.md | 7 +++ packages/component/package.json | 4 +- packages/data-connect/CHANGELOG.md | 8 ++++ packages/data-connect/package.json | 8 ++-- packages/database-compat/CHANGELOG.md | 10 ++++ packages/database-compat/package.json | 12 ++--- packages/database-types/CHANGELOG.md | 7 +++ packages/database-types/package.json | 4 +- packages/database/CHANGELOG.md | 8 ++++ packages/database/package.json | 8 ++-- packages/firebase/CHANGELOG.md | 41 ++++++++++++++++ packages/firebase/package.json | 56 +++++++++++----------- packages/firestore-compat/CHANGELOG.md | 9 ++++ packages/firestore-compat/package.json | 10 ++-- packages/firestore/CHANGELOG.md | 10 ++++ packages/firestore/package.json | 12 ++--- packages/functions-compat/CHANGELOG.md | 9 ++++ packages/functions-compat/package.json | 10 ++-- packages/functions/CHANGELOG.md | 8 ++++ packages/functions/package.json | 8 ++-- packages/installations-compat/CHANGELOG.md | 9 ++++ packages/installations-compat/package.json | 10 ++-- packages/installations/CHANGELOG.md | 8 ++++ packages/installations/package.json | 8 ++-- packages/messaging-compat/CHANGELOG.md | 9 ++++ packages/messaging-compat/package.json | 10 ++-- packages/messaging/CHANGELOG.md | 9 ++++ packages/messaging/package.json | 10 ++-- packages/performance-compat/CHANGELOG.md | 9 ++++ packages/performance-compat/package.json | 10 ++-- packages/performance/CHANGELOG.md | 9 ++++ packages/performance/package.json | 10 ++-- packages/remote-config-compat/CHANGELOG.md | 9 ++++ packages/remote-config-compat/package.json | 10 ++-- packages/remote-config/CHANGELOG.md | 9 ++++ packages/remote-config/package.json | 10 ++-- packages/storage-compat/CHANGELOG.md | 9 ++++ packages/storage-compat/package.json | 12 ++--- packages/storage/CHANGELOG.md | 8 ++++ packages/storage/package.json | 10 ++-- packages/template/package.json | 2 +- packages/util/CHANGELOG.md | 6 +++ packages/util/package.json | 2 +- repo-scripts/size-analysis/package.json | 4 +- 70 files changed, 484 insertions(+), 207 deletions(-) delete mode 100644 .changeset/eighty-starfishes-listen.md delete mode 100644 .changeset/fast-mangos-chew.md delete mode 100644 .changeset/ninety-ways-dress.md delete mode 100644 .changeset/perfect-camels-try.md delete mode 100644 .changeset/tall-zoos-stare.md diff --git a/.changeset/eighty-starfishes-listen.md b/.changeset/eighty-starfishes-listen.md deleted file mode 100644 index 4f53b0fb786..00000000000 --- a/.changeset/eighty-starfishes-listen.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@firebase/firestore": patch -"@firebase/util": minor ---- - -Fix Safari/WebKit cache issues when client-side indexing is used. diff --git a/.changeset/fast-mangos-chew.md b/.changeset/fast-mangos-chew.md deleted file mode 100644 index 4e1f7ab3121..00000000000 --- a/.changeset/fast-mangos-chew.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@firebase/ai': patch ---- - -Pass `GenerativeModel`'s `BaseParams` to created chat sessions. This fixes an issue where `GenerationConfig` would not be inherited from `ChatSession`. diff --git a/.changeset/ninety-ways-dress.md b/.changeset/ninety-ways-dress.md deleted file mode 100644 index 82c9e15cf4d..00000000000 --- a/.changeset/ninety-ways-dress.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@firebase/app-compat': minor -'@firebase/app-check': minor -'@firebase/app': minor -'firebase': minor ---- - -Default `automaticDataCollectionEnabled` to true without changing App Check's default behavior. diff --git a/.changeset/perfect-camels-try.md b/.changeset/perfect-camels-try.md deleted file mode 100644 index 52292807f0a..00000000000 --- a/.changeset/perfect-camels-try.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'firebase': minor -'@firebase/ai': minor ---- - -Add support for Gemini multimodal output diff --git a/.changeset/tall-zoos-stare.md b/.changeset/tall-zoos-stare.md deleted file mode 100644 index 96d480762ea..00000000000 --- a/.changeset/tall-zoos-stare.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'firebase': minor -'@firebase/ai': minor ---- - -Add support for the Gemini Developer API, enabling usage in a free tier, and add new `AI` API to accomodate new product naming. diff --git a/integration/compat-interop/package.json b/integration/compat-interop/package.json index 7b97998233e..11360a5b954 100644 --- a/integration/compat-interop/package.json +++ b/integration/compat-interop/package.json @@ -8,20 +8,20 @@ "test:debug": "karma start --browsers Chrome --auto-watch" }, "dependencies": { - "@firebase/app": "0.12.3", - "@firebase/app-compat": "0.3.3", - "@firebase/analytics": "0.10.15", - "@firebase/analytics-compat": "0.2.21", - "@firebase/auth": "1.10.4", - "@firebase/auth-compat": "0.5.24", - "@firebase/functions": "0.12.6", - "@firebase/functions-compat": "0.3.23", - "@firebase/messaging": "0.12.20", - "@firebase/messaging-compat": "0.2.20", - "@firebase/performance": "0.7.5", - "@firebase/performance-compat": "0.2.18", - "@firebase/remote-config": "0.6.3", - "@firebase/remote-config-compat": "0.2.16" + "@firebase/app": "0.13.0", + "@firebase/app-compat": "0.4.0", + "@firebase/analytics": "0.10.16", + "@firebase/analytics-compat": "0.2.22", + "@firebase/auth": "1.10.5", + "@firebase/auth-compat": "0.5.25", + "@firebase/functions": "0.12.7", + "@firebase/functions-compat": "0.3.24", + "@firebase/messaging": "0.12.21", + "@firebase/messaging-compat": "0.2.21", + "@firebase/performance": "0.7.6", + "@firebase/performance-compat": "0.2.19", + "@firebase/remote-config": "0.6.4", + "@firebase/remote-config-compat": "0.2.17" }, "devDependencies": { "typescript": "5.5.4" diff --git a/integration/firestore/package.json b/integration/firestore/package.json index dac850a09b3..c509b23e5b5 100644 --- a/integration/firestore/package.json +++ b/integration/firestore/package.json @@ -14,8 +14,8 @@ "test:memory:debug": "yarn build:memory; karma start --auto-watch --browsers Chrome" }, "dependencies": { - "@firebase/app": "0.12.3", - "@firebase/firestore": "4.7.14" + "@firebase/app": "0.13.0", + "@firebase/firestore": "4.7.15" }, "devDependencies": { "@types/mocha": "9.1.1", diff --git a/integration/messaging/package.json b/integration/messaging/package.json index f883f54f098..b4eaccebdab 100644 --- a/integration/messaging/package.json +++ b/integration/messaging/package.json @@ -9,7 +9,7 @@ "test:manual": "mocha --exit" }, "devDependencies": { - "firebase": "11.7.3", + "firebase": "11.8.0", "chai": "4.5.0", "chromedriver": "119.0.1", "express": "4.21.2", diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 1dab92bcca1..298fb06a9b5 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -1,5 +1,21 @@ # @firebase/ai +## 1.3.0 + +### Minor Changes + +- [`e99683b`](https://github.com/firebase/firebase-js-sdk/commit/e99683b17cf75c581bd362a1d7cb85f0b9c110ba) [#8922](https://github.com/firebase/firebase-js-sdk/pull/8922) - Add support for Gemini multimodal output + +- [`d5082f9`](https://github.com/firebase/firebase-js-sdk/commit/d5082f9f2fc4de98a6bfd1c6a5af4571af4d0bc6) [#8931](https://github.com/firebase/firebase-js-sdk/pull/8931) - Add support for the Gemini Developer API, enabling usage in a free tier, and add new `AI` API to accomodate new product naming. + +### Patch Changes + +- [`050c1b6`](https://github.com/firebase/firebase-js-sdk/commit/050c1b6a099b87be1488b9207e4fad4da9f8f64b) [#8972](https://github.com/firebase/firebase-js-sdk/pull/8972) - Pass `GenerativeModel`'s `BaseParams` to created chat sessions. This fixes an issue where `GenerationConfig` would not be inherited from `ChatSession`. + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + ## 1.2.4 ### Patch Changes diff --git a/packages/ai/package.json b/packages/ai/package.json index 6aeede834ad..98e204320bc 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/ai", - "version": "1.2.4", + "version": "1.3.0", "description": "The Firebase AI SDK", "author": "Firebase (https://firebase.google.com/)", "engines": { @@ -49,14 +49,14 @@ }, "dependencies": { "@firebase/app-check-interop-types": "0.3.3", - "@firebase/component": "0.6.16", + "@firebase/component": "0.6.17", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/packages/analytics-compat/CHANGELOG.md b/packages/analytics-compat/CHANGELOG.md index e950b6ad5eb..ce440211b4a 100644 --- a/packages/analytics-compat/CHANGELOG.md +++ b/packages/analytics-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/analytics-compat +## 0.2.22 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/analytics@0.10.16 + - @firebase/component@0.6.17 + ## 0.2.21 ### Patch Changes diff --git a/packages/analytics-compat/package.json b/packages/analytics-compat/package.json index fc86eff94ff..cb8c4c5a2f7 100644 --- a/packages/analytics-compat/package.json +++ b/packages/analytics-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/analytics-compat", - "version": "0.2.21", + "version": "0.2.22", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -22,7 +22,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.3.3", + "@firebase/app-compat": "0.4.0", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -52,10 +52,10 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.16", - "@firebase/analytics": "0.10.15", + "@firebase/component": "0.6.17", + "@firebase/analytics": "0.10.16", "@firebase/analytics-types": "0.8.3", - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/analytics/CHANGELOG.md b/packages/analytics/CHANGELOG.md index 06548ecd2cd..2507a977522 100644 --- a/packages/analytics/CHANGELOG.md +++ b/packages/analytics/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/analytics +## 0.10.16 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + - @firebase/installations@0.6.17 + ## 0.10.15 ### Patch Changes diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 2ffd405cc81..2f7b6cfbadf 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/analytics", - "version": "0.10.15", + "version": "0.10.16", "description": "A analytics package for new firebase packages", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -39,15 +39,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.16", + "@firebase/installations": "0.6.17", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check-compat/CHANGELOG.md b/packages/app-check-compat/CHANGELOG.md index 825cf013e08..b5456ba7b69 100644 --- a/packages/app-check-compat/CHANGELOG.md +++ b/packages/app-check-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/app-check-compat +## 0.3.25 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27), [`6be75f7`](https://github.com/firebase/firebase-js-sdk/commit/6be75f74dec92d1b84f77f79ccb770a3e23280b7)]: + - @firebase/util@1.12.0 + - @firebase/app-check@0.10.0 + - @firebase/component@0.6.17 + ## 0.3.24 ### Patch Changes diff --git a/packages/app-check-compat/package.json b/packages/app-check-compat/package.json index e2e214c2d24..15cc2a9273f 100644 --- a/packages/app-check-compat/package.json +++ b/packages/app-check-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-check-compat", - "version": "0.3.24", + "version": "0.3.25", "description": "A compat App Check package for new firebase packages", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -34,16 +34,16 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/app-check": "0.9.3", + "@firebase/app-check": "0.10.0", "@firebase/app-check-types": "0.5.3", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.3.3", + "@firebase/app-compat": "0.4.0", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check/CHANGELOG.md b/packages/app-check/CHANGELOG.md index d7e6fdf6446..764c17928cb 100644 --- a/packages/app-check/CHANGELOG.md +++ b/packages/app-check/CHANGELOG.md @@ -1,5 +1,17 @@ # @firebase/app-check +## 0.10.0 + +### Minor Changes + +- [`6be75f7`](https://github.com/firebase/firebase-js-sdk/commit/6be75f74dec92d1b84f77f79ccb770a3e23280b7) [#9010](https://github.com/firebase/firebase-js-sdk/pull/9010) - Default `automaticDataCollectionEnabled` to true without changing App Check's default behavior. + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + ## 0.9.3 ### Patch Changes diff --git a/packages/app-check/package.json b/packages/app-check/package.json index 25b27f90ca4..4187145ed67 100644 --- a/packages/app-check/package.json +++ b/packages/app-check/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-check", - "version": "0.9.3", + "version": "0.10.0", "description": "The App Check component of the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,14 +37,14 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "@firebase/logger": "0.4.4", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-compat/CHANGELOG.md b/packages/app-compat/CHANGELOG.md index 38ae80099ea..6e27613a249 100644 --- a/packages/app-compat/CHANGELOG.md +++ b/packages/app-compat/CHANGELOG.md @@ -1,5 +1,18 @@ # @firebase/app-compat +## 0.4.0 + +### Minor Changes + +- [`6be75f7`](https://github.com/firebase/firebase-js-sdk/commit/6be75f74dec92d1b84f77f79ccb770a3e23280b7) [#9010](https://github.com/firebase/firebase-js-sdk/pull/9010) - Default `automaticDataCollectionEnabled` to true without changing App Check's default behavior. + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27), [`6be75f7`](https://github.com/firebase/firebase-js-sdk/commit/6be75f74dec92d1b84f77f79ccb770a3e23280b7)]: + - @firebase/util@1.12.0 + - @firebase/app@0.13.0 + - @firebase/component@0.6.17 + ## 0.3.3 ### Patch Changes diff --git a/packages/app-compat/package.json b/packages/app-compat/package.json index a0e0b89a9fc..3d70accd107 100644 --- a/packages/app-compat/package.json +++ b/packages/app-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-compat", - "version": "0.3.3", + "version": "0.4.0", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,10 +37,10 @@ }, "license": "Apache-2.0", "dependencies": { - "@firebase/app": "0.12.3", - "@firebase/util": "1.11.3", + "@firebase/app": "0.13.0", + "@firebase/util": "1.12.0", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.16", + "@firebase/component": "0.6.17", "tslib": "^2.1.0" }, "devDependencies": { diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 10a196ca0fa..2b239c453cc 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,17 @@ # @firebase/app +## 0.13.0 + +### Minor Changes + +- [`6be75f7`](https://github.com/firebase/firebase-js-sdk/commit/6be75f74dec92d1b84f77f79ccb770a3e23280b7) [#9010](https://github.com/firebase/firebase-js-sdk/pull/9010) - Default `automaticDataCollectionEnabled` to true without changing App Check's default behavior. + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + ## 0.12.3 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 07e70ba14ec..3d385028412 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app", - "version": "0.12.3", + "version": "0.13.0", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,9 +37,9 @@ "typings:internal": "node ../../scripts/build/use_typings.js ./dist/app.d.ts" }, "dependencies": { - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.16", + "@firebase/component": "0.6.17", "idb": "7.1.1", "tslib": "^2.1.0" }, diff --git a/packages/auth-compat/CHANGELOG.md b/packages/auth-compat/CHANGELOG.md index 97ec10f7a77..b0876dd935c 100644 --- a/packages/auth-compat/CHANGELOG.md +++ b/packages/auth-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/auth-compat +## 0.5.25 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/auth@1.10.5 + - @firebase/component@0.6.17 + ## 0.5.24 ### Patch Changes diff --git a/packages/auth-compat/package.json b/packages/auth-compat/package.json index c244b8e3f7a..f2fcfac236a 100644 --- a/packages/auth-compat/package.json +++ b/packages/auth-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth-compat", - "version": "0.5.24", + "version": "0.5.25", "description": "FirebaseAuth compatibility package that uses API style compatible with Firebase@8 and prior versions", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,15 +49,15 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/auth": "1.10.4", + "@firebase/auth": "1.10.5", "@firebase/auth-types": "0.13.0", - "@firebase/component": "0.6.16", - "@firebase/util": "1.11.3", + "@firebase/component": "0.6.17", + "@firebase/util": "1.12.0", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.3.3", + "@firebase/app-compat": "0.4.0", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md index 9999db32429..cb1ecc4fda7 100644 --- a/packages/auth/CHANGELOG.md +++ b/packages/auth/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/auth +## 1.10.5 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + ## 1.10.4 ### Patch Changes diff --git a/packages/auth/package.json b/packages/auth/package.json index deb2006f9b9..5777c7500d1 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth", - "version": "1.10.4", + "version": "1.10.5", "description": "The Firebase Authenticaton component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/node/index.js", @@ -124,14 +124,14 @@ } }, "dependencies": { - "@firebase/component": "0.6.16", + "@firebase/component": "0.6.17", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-strip": "2.1.0", "@types/express": "4.17.21", diff --git a/packages/component/CHANGELOG.md b/packages/component/CHANGELOG.md index 5dca079098c..f4e008fc2b1 100644 --- a/packages/component/CHANGELOG.md +++ b/packages/component/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/component +## 0.6.17 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + ## 0.6.16 ### Patch Changes diff --git a/packages/component/package.json b/packages/component/package.json index cb0e6a336d2..b95204050f3 100644 --- a/packages/component/package.json +++ b/packages/component/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/component", - "version": "0.6.16", + "version": "0.6.17", "description": "Firebase Component Platform", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -31,7 +31,7 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "tslib": "^2.1.0" }, "license": "Apache-2.0", diff --git a/packages/data-connect/CHANGELOG.md b/packages/data-connect/CHANGELOG.md index 5e9eb3532bb..dfec86ea4d0 100644 --- a/packages/data-connect/CHANGELOG.md +++ b/packages/data-connect/CHANGELOG.md @@ -1,5 +1,13 @@ ## Unreleased +## 0.3.8 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + ## 0.3.7 ### Patch Changes diff --git a/packages/data-connect/package.json b/packages/data-connect/package.json index f4dfa7e0863..aeb5aaee50f 100644 --- a/packages/data-connect/package.json +++ b/packages/data-connect/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/data-connect", - "version": "0.3.7", + "version": "0.3.8", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,13 +49,13 @@ }, "dependencies": { "@firebase/auth-interop-types": "0.2.4", - "@firebase/component": "0.6.16", + "@firebase/component": "0.6.17", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/database-compat/CHANGELOG.md b/packages/database-compat/CHANGELOG.md index 1dab50a408f..3376ad0c953 100644 --- a/packages/database-compat/CHANGELOG.md +++ b/packages/database-compat/CHANGELOG.md @@ -1,5 +1,15 @@ # @firebase/database-compat +## 2.0.9 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + - @firebase/database@1.0.18 + - @firebase/database-types@1.0.14 + ## 2.0.8 ### Patch Changes diff --git a/packages/database-compat/package.json b/packages/database-compat/package.json index d24167a9965..6a3f4d83335 100644 --- a/packages/database-compat/package.json +++ b/packages/database-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database-compat", - "version": "2.0.8", + "version": "2.0.9", "description": "The Realtime Database component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.js", @@ -49,15 +49,15 @@ "add-compat-overloads": "ts-node-script ../../scripts/build/create-overloads.ts -i ../database/dist/public.d.ts -o dist/database-compat/src/index.d.ts -a -r Database:types.FirebaseDatabase -r Query:types.Query -r DatabaseReference:types.Reference -r FirebaseApp:FirebaseAppCompat --moduleToEnhance @firebase/database" }, "dependencies": { - "@firebase/database": "1.0.17", - "@firebase/database-types": "1.0.13", + "@firebase/database": "1.0.18", + "@firebase/database-types": "1.0.14", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.3", + "@firebase/app-compat": "0.4.0", "typescript": "5.5.4" }, "repository": { diff --git a/packages/database-types/CHANGELOG.md b/packages/database-types/CHANGELOG.md index b2be70c2efc..316d77556a7 100644 --- a/packages/database-types/CHANGELOG.md +++ b/packages/database-types/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/database-types +## 1.0.14 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + ## 1.0.13 ### Patch Changes diff --git a/packages/database-types/package.json b/packages/database-types/package.json index f6cbb176699..95866297115 100644 --- a/packages/database-types/package.json +++ b/packages/database-types/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database-types", - "version": "1.0.13", + "version": "1.0.14", "description": "@firebase/database Types", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -13,7 +13,7 @@ ], "dependencies": { "@firebase/app-types": "0.9.3", - "@firebase/util": "1.11.3" + "@firebase/util": "1.12.0" }, "repository": { "directory": "packages/database-types", diff --git a/packages/database/CHANGELOG.md b/packages/database/CHANGELOG.md index ea27ec478d8..c7fcbd37dd0 100644 --- a/packages/database/CHANGELOG.md +++ b/packages/database/CHANGELOG.md @@ -1,5 +1,13 @@ # Unreleased +## 1.0.18 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + ## 1.0.17 ### Patch Changes diff --git a/packages/database/package.json b/packages/database/package.json index 719469d5cca..72050a5bd2e 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database", - "version": "1.0.17", + "version": "1.0.18", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,15 +49,15 @@ "peerDependencies": {}, "dependencies": { "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "@firebase/app-check-interop-types": "0.3.3", "@firebase/auth-interop-types": "0.2.4", "faye-websocket": "0.11.4", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/firebase/CHANGELOG.md b/packages/firebase/CHANGELOG.md index ed42e9ee853..436053673b2 100644 --- a/packages/firebase/CHANGELOG.md +++ b/packages/firebase/CHANGELOG.md @@ -1,5 +1,46 @@ # firebase +## 11.8.0 + +### Minor Changes + +- [`6be75f7`](https://github.com/firebase/firebase-js-sdk/commit/6be75f74dec92d1b84f77f79ccb770a3e23280b7) [#9010](https://github.com/firebase/firebase-js-sdk/pull/9010) - Default `automaticDataCollectionEnabled` to true without changing App Check's default behavior. + +- [`e99683b`](https://github.com/firebase/firebase-js-sdk/commit/e99683b17cf75c581bd362a1d7cb85f0b9c110ba) [#8922](https://github.com/firebase/firebase-js-sdk/pull/8922) - Add support for Gemini multimodal output + +- [`d5082f9`](https://github.com/firebase/firebase-js-sdk/commit/d5082f9f2fc4de98a6bfd1c6a5af4571af4d0bc6) [#8931](https://github.com/firebase/firebase-js-sdk/pull/8931) - Add support for the Gemini Developer API, enabling usage in a free tier, and add new `AI` API to accomodate new product naming. + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27), [`050c1b6`](https://github.com/firebase/firebase-js-sdk/commit/050c1b6a099b87be1488b9207e4fad4da9f8f64b), [`6be75f7`](https://github.com/firebase/firebase-js-sdk/commit/6be75f74dec92d1b84f77f79ccb770a3e23280b7), [`e99683b`](https://github.com/firebase/firebase-js-sdk/commit/e99683b17cf75c581bd362a1d7cb85f0b9c110ba), [`d5082f9`](https://github.com/firebase/firebase-js-sdk/commit/d5082f9f2fc4de98a6bfd1c6a5af4571af4d0bc6)]: + - @firebase/firestore@4.7.15 + - @firebase/util@1.12.0 + - @firebase/ai@1.3.0 + - @firebase/app-compat@0.4.0 + - @firebase/app-check@0.10.0 + - @firebase/app@0.13.0 + - @firebase/firestore-compat@0.3.50 + - @firebase/analytics@0.10.16 + - @firebase/analytics-compat@0.2.22 + - @firebase/app-check-compat@0.3.25 + - @firebase/auth@1.10.5 + - @firebase/auth-compat@0.5.25 + - @firebase/data-connect@0.3.8 + - @firebase/database@1.0.18 + - @firebase/database-compat@2.0.9 + - @firebase/functions@0.12.7 + - @firebase/functions-compat@0.3.24 + - @firebase/installations@0.6.17 + - @firebase/installations-compat@0.2.17 + - @firebase/messaging@0.12.21 + - @firebase/messaging-compat@0.2.21 + - @firebase/performance@0.7.6 + - @firebase/performance-compat@0.2.19 + - @firebase/remote-config@0.6.4 + - @firebase/remote-config-compat@0.2.17 + - @firebase/storage@0.13.11 + - @firebase/storage-compat@0.3.21 + ## 11.7.3 ### Patch Changes diff --git a/packages/firebase/package.json b/packages/firebase/package.json index 0ed42235a3e..9ce4835a2f4 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -1,6 +1,6 @@ { "name": "firebase", - "version": "11.7.3", + "version": "11.8.0", "description": "Firebase JavaScript library for web and Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -411,34 +411,34 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { - "@firebase/ai": "1.2.4", - "@firebase/app": "0.12.3", - "@firebase/app-compat": "0.3.3", + "@firebase/ai": "1.3.0", + "@firebase/app": "0.13.0", + "@firebase/app-compat": "0.4.0", "@firebase/app-types": "0.9.3", - "@firebase/auth": "1.10.4", - "@firebase/auth-compat": "0.5.24", - "@firebase/data-connect": "0.3.7", - "@firebase/database": "1.0.17", - "@firebase/database-compat": "2.0.8", - "@firebase/firestore": "4.7.14", - "@firebase/firestore-compat": "0.3.49", - "@firebase/functions": "0.12.6", - "@firebase/functions-compat": "0.3.23", - "@firebase/installations": "0.6.16", - "@firebase/installations-compat": "0.2.16", - "@firebase/messaging": "0.12.20", - "@firebase/messaging-compat": "0.2.20", - "@firebase/storage": "0.13.10", - "@firebase/storage-compat": "0.3.20", - "@firebase/performance": "0.7.5", - "@firebase/performance-compat": "0.2.18", - "@firebase/remote-config": "0.6.3", - "@firebase/remote-config-compat": "0.2.16", - "@firebase/analytics": "0.10.15", - "@firebase/analytics-compat": "0.2.21", - "@firebase/app-check": "0.9.3", - "@firebase/app-check-compat": "0.3.24", - "@firebase/util": "1.11.3" + "@firebase/auth": "1.10.5", + "@firebase/auth-compat": "0.5.25", + "@firebase/data-connect": "0.3.8", + "@firebase/database": "1.0.18", + "@firebase/database-compat": "2.0.9", + "@firebase/firestore": "4.7.15", + "@firebase/firestore-compat": "0.3.50", + "@firebase/functions": "0.12.7", + "@firebase/functions-compat": "0.3.24", + "@firebase/installations": "0.6.17", + "@firebase/installations-compat": "0.2.17", + "@firebase/messaging": "0.12.21", + "@firebase/messaging-compat": "0.2.21", + "@firebase/storage": "0.13.11", + "@firebase/storage-compat": "0.3.21", + "@firebase/performance": "0.7.6", + "@firebase/performance-compat": "0.2.19", + "@firebase/remote-config": "0.6.4", + "@firebase/remote-config-compat": "0.2.17", + "@firebase/analytics": "0.10.16", + "@firebase/analytics-compat": "0.2.22", + "@firebase/app-check": "0.10.0", + "@firebase/app-check-compat": "0.3.25", + "@firebase/util": "1.12.0" }, "devDependencies": { "rollup": "2.79.2", diff --git a/packages/firestore-compat/CHANGELOG.md b/packages/firestore-compat/CHANGELOG.md index f22abcaf049..2ab0cc98150 100644 --- a/packages/firestore-compat/CHANGELOG.md +++ b/packages/firestore-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/firestore-compat +## 0.3.50 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/firestore@4.7.15 + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + ## 0.3.49 ### Patch Changes diff --git a/packages/firestore-compat/package.json b/packages/firestore-compat/package.json index e2243ccba9f..68c135058e6 100644 --- a/packages/firestore-compat/package.json +++ b/packages/firestore-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore-compat", - "version": "0.3.49", + "version": "0.3.50", "description": "The Cloud Firestore component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -46,14 +46,14 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/component": "0.6.16", - "@firebase/firestore": "4.7.14", - "@firebase/util": "1.11.3", + "@firebase/component": "0.6.17", + "@firebase/firestore": "4.7.15", + "@firebase/util": "1.12.0", "@firebase/firestore-types": "3.0.3", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.3", + "@firebase/app-compat": "0.4.0", "@types/eslint": "7.29.0", "rollup": "2.79.2", "rollup-plugin-sourcemaps": "0.6.3", diff --git a/packages/firestore/CHANGELOG.md b/packages/firestore/CHANGELOG.md index 19144f16afd..7f9e6fe4a91 100644 --- a/packages/firestore/CHANGELOG.md +++ b/packages/firestore/CHANGELOG.md @@ -1,5 +1,15 @@ # @firebase/firestore +## 4.7.15 + +### Patch Changes + +- [`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27) [#8993](https://github.com/firebase/firebase-js-sdk/pull/8993) - Fix Safari/WebKit cache issues when client-side indexing is used. + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + ## 4.7.14 ### Patch Changes diff --git a/packages/firestore/package.json b/packages/firestore/package.json index 24daaf37b42..b7d76fb4fca 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore", - "version": "4.7.14", + "version": "4.7.15", "engines": { "node": ">=18.0.0" }, @@ -100,9 +100,9 @@ "lite/package.json" ], "dependencies": { - "@firebase/component": "0.6.16", + "@firebase/component": "0.6.17", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "@firebase/webchannel-wrapper": "1.0.3", "@grpc/grpc-js": "~1.9.0", "@grpc/proto-loader": "^0.7.8", @@ -112,9 +112,9 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.3", - "@firebase/app-compat": "0.3.3", - "@firebase/auth": "1.10.4", + "@firebase/app": "0.13.0", + "@firebase/app-compat": "0.4.0", + "@firebase/auth": "1.10.5", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", "@types/eslint": "7.29.0", diff --git a/packages/functions-compat/CHANGELOG.md b/packages/functions-compat/CHANGELOG.md index 991f41c0868..ddff93a8f81 100644 --- a/packages/functions-compat/CHANGELOG.md +++ b/packages/functions-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/functions-compat +## 0.3.24 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + - @firebase/functions@0.12.7 + ## 0.3.23 ### Patch Changes diff --git a/packages/functions-compat/package.json b/packages/functions-compat/package.json index 60d1ffacfbc..56c5bcbb89d 100644 --- a/packages/functions-compat/package.json +++ b/packages/functions-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/functions-compat", - "version": "0.3.23", + "version": "0.3.24", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -29,7 +29,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.3.3", + "@firebase/app-compat": "0.4.0", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -62,10 +62,10 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.16", - "@firebase/functions": "0.12.6", + "@firebase/component": "0.6.17", + "@firebase/functions": "0.12.7", "@firebase/functions-types": "0.6.3", - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/functions/CHANGELOG.md b/packages/functions/CHANGELOG.md index a63cd968b70..68afc468a1c 100644 --- a/packages/functions/CHANGELOG.md +++ b/packages/functions/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/functions +## 0.12.7 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + ## 0.12.6 ### Patch Changes diff --git a/packages/functions/package.json b/packages/functions/package.json index 6fde3987e75..80ca8986c56 100644 --- a/packages/functions/package.json +++ b/packages/functions/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/functions", - "version": "0.12.6", + "version": "0.12.7", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -49,7 +49,7 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", @@ -65,11 +65,11 @@ }, "typings": "dist/src/index.d.ts", "dependencies": { - "@firebase/component": "0.6.16", + "@firebase/component": "0.6.17", "@firebase/messaging-interop-types": "0.2.3", "@firebase/auth-interop-types": "0.2.4", "@firebase/app-check-interop-types": "0.3.3", - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "tslib": "^2.1.0" }, "nyc": { diff --git a/packages/installations-compat/CHANGELOG.md b/packages/installations-compat/CHANGELOG.md index c46d91d70ab..213a1ba641e 100644 --- a/packages/installations-compat/CHANGELOG.md +++ b/packages/installations-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/installations-compat +## 0.2.17 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + - @firebase/installations@0.6.17 + ## 0.2.16 ### Patch Changes diff --git a/packages/installations-compat/package.json b/packages/installations-compat/package.json index 73b318577ce..11f1838f66c 100644 --- a/packages/installations-compat/package.json +++ b/packages/installations-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/installations-compat", - "version": "0.2.16", + "version": "0.2.17", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", "module": "dist/esm/index.esm2017.js", @@ -44,7 +44,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app-compat": "0.3.3", + "@firebase/app-compat": "0.4.0", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", @@ -57,10 +57,10 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.16", + "@firebase/installations": "0.6.17", "@firebase/installations-types": "0.5.3", - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "tslib": "^2.1.0" } } diff --git a/packages/installations/CHANGELOG.md b/packages/installations/CHANGELOG.md index 035655cf96f..bb0e09f277f 100644 --- a/packages/installations/CHANGELOG.md +++ b/packages/installations/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/installations +## 0.6.17 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + ## 0.6.16 ### Patch Changes diff --git a/packages/installations/package.json b/packages/installations/package.json index bd35c91a7ee..be4d1b4a79f 100644 --- a/packages/installations/package.json +++ b/packages/installations/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/installations", - "version": "0.6.16", + "version": "0.6.17", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", "module": "dist/esm/index.esm2017.js", @@ -49,7 +49,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", @@ -62,8 +62,8 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "idb": "7.1.1", "tslib": "^2.1.0" } diff --git a/packages/messaging-compat/CHANGELOG.md b/packages/messaging-compat/CHANGELOG.md index cafca2b6ec5..b150f2f2939 100644 --- a/packages/messaging-compat/CHANGELOG.md +++ b/packages/messaging-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/messaging-compat +## 0.2.21 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + - @firebase/messaging@0.12.21 + ## 0.2.20 ### Patch Changes diff --git a/packages/messaging-compat/package.json b/packages/messaging-compat/package.json index 4f36674d8ef..fd145f06bf7 100644 --- a/packages/messaging-compat/package.json +++ b/packages/messaging-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/messaging-compat", - "version": "0.2.20", + "version": "0.2.21", "license": "Apache-2.0", "description": "", "author": "Firebase (https://firebase.google.com/)", @@ -38,13 +38,13 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/messaging": "0.12.20", - "@firebase/component": "0.6.16", - "@firebase/util": "1.11.3", + "@firebase/messaging": "0.12.21", + "@firebase/component": "0.6.17", + "@firebase/util": "1.12.0", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.3", + "@firebase/app-compat": "0.4.0", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", "ts-essentials": "9.4.2", diff --git a/packages/messaging/CHANGELOG.md b/packages/messaging/CHANGELOG.md index c8b10a8e5d1..8df2d983835 100644 --- a/packages/messaging/CHANGELOG.md +++ b/packages/messaging/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/messaging +## 0.12.21 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + - @firebase/installations@0.6.17 + ## 0.12.20 ### Patch Changes diff --git a/packages/messaging/package.json b/packages/messaging/package.json index c412ba5eecc..9d8cfc71926 100644 --- a/packages/messaging/package.json +++ b/packages/messaging/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/messaging", - "version": "0.12.20", + "version": "0.12.21", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -52,15 +52,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.16", + "@firebase/installations": "0.6.17", "@firebase/messaging-interop-types": "0.2.3", - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "idb": "7.1.1", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/performance-compat/CHANGELOG.md b/packages/performance-compat/CHANGELOG.md index ee1ed073fdb..11b9eed3d2f 100644 --- a/packages/performance-compat/CHANGELOG.md +++ b/packages/performance-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/performance-compat +## 0.2.19 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + - @firebase/performance@0.7.6 + ## 0.2.18 ### Patch Changes diff --git a/packages/performance-compat/package.json b/packages/performance-compat/package.json index 71286577acc..0ec3cc56e66 100644 --- a/packages/performance-compat/package.json +++ b/packages/performance-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/performance-compat", - "version": "0.2.18", + "version": "0.2.19", "description": "The compatibility package of Firebase Performance", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -38,11 +38,11 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/performance": "0.7.5", + "@firebase/performance": "0.7.6", "@firebase/performance-types": "0.2.3", - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.16", + "@firebase/component": "0.6.17", "tslib": "^2.1.0" }, "devDependencies": { @@ -51,7 +51,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.3.3" + "@firebase/app-compat": "0.4.0" }, "repository": { "directory": "packages/performance-compat", diff --git a/packages/performance/CHANGELOG.md b/packages/performance/CHANGELOG.md index fe169a3ad39..ce6d7e391e5 100644 --- a/packages/performance/CHANGELOG.md +++ b/packages/performance/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/performance +## 0.7.6 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + - @firebase/installations@0.6.17 + ## 0.7.5 ### Patch Changes diff --git a/packages/performance/package.json b/packages/performance/package.json index e7a9fed6020..b3495c8a566 100644 --- a/packages/performance/package.json +++ b/packages/performance/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/performance", - "version": "0.7.5", + "version": "0.7.6", "description": "Firebase performance for web", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -39,15 +39,15 @@ }, "dependencies": { "@firebase/logger": "0.4.4", - "@firebase/installations": "0.6.16", - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/installations": "0.6.17", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "tslib": "^2.1.0", "web-vitals": "^4.2.4" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/remote-config-compat/CHANGELOG.md b/packages/remote-config-compat/CHANGELOG.md index d2bdfb1fac2..052bc765c9c 100644 --- a/packages/remote-config-compat/CHANGELOG.md +++ b/packages/remote-config-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/remote-config-compat +## 0.2.17 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + - @firebase/remote-config@0.6.4 + ## 0.2.16 ### Patch Changes diff --git a/packages/remote-config-compat/package.json b/packages/remote-config-compat/package.json index 7d2e5483efe..84c66d579bc 100644 --- a/packages/remote-config-compat/package.json +++ b/packages/remote-config-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/remote-config-compat", - "version": "0.2.16", + "version": "0.2.17", "description": "The compatibility package of Remote Config", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,11 +37,11 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/remote-config": "0.6.3", + "@firebase/remote-config": "0.6.4", "@firebase/remote-config-types": "0.4.0", - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "@firebase/logger": "0.4.4", - "@firebase/component": "0.6.16", + "@firebase/component": "0.6.17", "tslib": "^2.1.0" }, "devDependencies": { @@ -50,7 +50,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.3.3" + "@firebase/app-compat": "0.4.0" }, "repository": { "directory": "packages/remote-config-compat", diff --git a/packages/remote-config/CHANGELOG.md b/packages/remote-config/CHANGELOG.md index 07fe5cc9605..529e0417e75 100644 --- a/packages/remote-config/CHANGELOG.md +++ b/packages/remote-config/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/remote-config +## 0.6.4 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + - @firebase/installations@0.6.17 + ## 0.6.3 ### Patch Changes diff --git a/packages/remote-config/package.json b/packages/remote-config/package.json index ffa93164d3a..09dd8b6531e 100644 --- a/packages/remote-config/package.json +++ b/packages/remote-config/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/remote-config", - "version": "0.6.3", + "version": "0.6.4", "description": "The Remote Config package of the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -40,15 +40,15 @@ "@firebase/app": "0.x" }, "dependencies": { - "@firebase/installations": "0.6.16", + "@firebase/installations": "0.6.17", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "tslib": "^2.1.0" }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/storage-compat/CHANGELOG.md b/packages/storage-compat/CHANGELOG.md index a9a3f9e1621..2bb419259cf 100644 --- a/packages/storage-compat/CHANGELOG.md +++ b/packages/storage-compat/CHANGELOG.md @@ -1,5 +1,14 @@ # @firebase/storage-compat +## 0.3.21 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + - @firebase/storage@0.13.11 + ## 0.3.20 ### Patch Changes diff --git a/packages/storage-compat/package.json b/packages/storage-compat/package.json index 046b0a79563..26633a8218c 100644 --- a/packages/storage-compat/package.json +++ b/packages/storage-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage-compat", - "version": "0.3.20", + "version": "0.3.21", "description": "The Firebase Firestore compatibility package", "author": "Firebase (https://firebase.google.com/)", "main": "./dist/index.cjs.js", @@ -37,15 +37,15 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/storage": "0.13.10", + "@firebase/storage": "0.13.11", "@firebase/storage-types": "0.8.3", - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.3.3", - "@firebase/auth-compat": "0.5.24", + "@firebase/app-compat": "0.4.0", + "@firebase/auth-compat": "0.5.25", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/storage/CHANGELOG.md b/packages/storage/CHANGELOG.md index 2872e5a792b..e123d70cdce 100644 --- a/packages/storage/CHANGELOG.md +++ b/packages/storage/CHANGELOG.md @@ -1,5 +1,13 @@ #Unreleased +## 0.13.11 + +### Patch Changes + +- Updated dependencies [[`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27)]: + - @firebase/util@1.12.0 + - @firebase/component@0.6.17 + ## 0.13.10 ### Patch Changes diff --git a/packages/storage/package.json b/packages/storage/package.json index 9c5a738bee1..67c205ac245 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage", - "version": "0.13.10", + "version": "0.13.11", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -46,16 +46,16 @@ }, "license": "Apache-2.0", "dependencies": { - "@firebase/util": "1.11.3", - "@firebase/component": "0.6.16", + "@firebase/util": "1.12.0", + "@firebase/component": "0.6.17", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.12.3", - "@firebase/auth": "1.10.4", + "@firebase/app": "0.13.0", + "@firebase/auth": "1.10.5", "rollup": "2.79.2", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", diff --git a/packages/template/package.json b/packages/template/package.json index 5d8c84d3566..6d178ded575 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -48,7 +48,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/util/CHANGELOG.md b/packages/util/CHANGELOG.md index 9eaaa3415e4..c208cce58db 100644 --- a/packages/util/CHANGELOG.md +++ b/packages/util/CHANGELOG.md @@ -1,5 +1,11 @@ # @firebase/util +## 1.12.0 + +### Minor Changes + +- [`8a03143`](https://github.com/firebase/firebase-js-sdk/commit/8a03143b9217effdd86d68bdf195493c0979aa27) [#8993](https://github.com/firebase/firebase-js-sdk/pull/8993) - Fix Safari/WebKit cache issues when client-side indexing is used. + ## 1.11.3 ### Patch Changes diff --git a/packages/util/package.json b/packages/util/package.json index 4e50552bb2f..8a1ebb49dd5 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/util", - "version": "1.11.3", + "version": "1.12.0", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", diff --git a/repo-scripts/size-analysis/package.json b/repo-scripts/size-analysis/package.json index 09b6cc14ca6..184020a8acc 100644 --- a/repo-scripts/size-analysis/package.json +++ b/repo-scripts/size-analysis/package.json @@ -20,9 +20,9 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.12.3", + "@firebase/app": "0.13.0", "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.3", + "@firebase/util": "1.12.0", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-node-resolve": "16.0.0", From 35ad5266304e14425988fcf5ad06d028b37588ac Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Tue, 20 May 2025 11:32:24 -0700 Subject: [PATCH 47/77] Revert "Fixed scroll behavior (#8977)" (#9053) * Revert "Fixed scroll behavior (#9043)" This reverts commit 9bcd1ea9b8cc5b55692765d40df000da8ddef02b. * Revert "Add Emulator Overlay (#8977) (#9031)" This reverts commit 8593fa05bd884c2f1f6f3b4ae062efa48af93d24. * Undid revert of util * Create dirty-crews-cross.md * Removed unnecessary markdown file --- .changeset/dirty-crews-cross.md | 9 +++++++++ packages/auth/src/core/auth/emulator.ts | 16 ++++++---------- packages/auth/src/platform_browser/index.ts | 8 +------- packages/database/src/api/Database.ts | 8 +------- packages/firestore/src/lite-api/database.ts | 11 +++-------- packages/functions/src/api.ts | 4 +--- packages/functions/src/service.ts | 7 +------ packages/storage/src/api.ts | 4 +--- packages/storage/src/service.ts | 4 +--- 9 files changed, 24 insertions(+), 47 deletions(-) create mode 100644 .changeset/dirty-crews-cross.md diff --git a/.changeset/dirty-crews-cross.md b/.changeset/dirty-crews-cross.md new file mode 100644 index 00000000000..08a962670f0 --- /dev/null +++ b/.changeset/dirty-crews-cross.md @@ -0,0 +1,9 @@ +--- +"@firebase/auth": patch +"@firebase/database": patch +"@firebase/firestore": patch +"@firebase/functions": patch +"@firebase/storage": patch +--- + +Revert "Fixed scroll behavior (#9043)" diff --git a/packages/auth/src/core/auth/emulator.ts b/packages/auth/src/core/auth/emulator.ts index 42fbda3f095..8547f7bad6c 100644 --- a/packages/auth/src/core/auth/emulator.ts +++ b/packages/auth/src/core/auth/emulator.ts @@ -18,12 +18,7 @@ import { Auth } from '../../model/public_types'; import { AuthErrorCode } from '../errors'; import { _assert } from '../util/assert'; import { _castAuth } from './auth_impl'; -import { - deepEqual, - isCloudWorkstation, - pingServer, - updateEmulatorBanner -} from '@firebase/util'; +import { deepEqual, isCloudWorkstation, pingServer } from '@firebase/util'; /** * Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production @@ -102,12 +97,13 @@ export function connectAuthEmulator( authInternal.emulatorConfig = emulatorConfig; authInternal.settings.appVerificationDisabledForTesting = true; + if (!disableWarnings) { + emitEmulatorWarning(); + } + + // Workaround to get cookies in Firebase Studio if (isCloudWorkstation(host)) { - updateEmulatorBanner('Auth', true); - // Workaround to get cookies in Firebase Studio void pingServer(`${protocol}//${host}${portStr}`); - } else if (!disableWarnings) { - emitEmulatorWarning(); } } diff --git a/packages/auth/src/platform_browser/index.ts b/packages/auth/src/platform_browser/index.ts index 99ab834cbdb..f94525bfeb7 100644 --- a/packages/auth/src/platform_browser/index.ts +++ b/packages/auth/src/platform_browser/index.ts @@ -30,11 +30,7 @@ import { browserSessionPersistence } from './persistence/session_storage'; import { indexedDBLocalPersistence } from './persistence/indexed_db'; import { browserPopupRedirectResolver } from './popup_redirect'; import { Auth, User } from '../model/public_types'; -import { - getDefaultEmulatorHost, - getExperimentalSetting, - updateEmulatorBanner -} from '@firebase/util'; +import { getDefaultEmulatorHost, getExperimentalSetting } from '@firebase/util'; import { _setExternalJSProvider } from './load_js'; import { _createError } from '../core/util/assert'; import { AuthErrorCode } from '../core/errors'; @@ -114,8 +110,6 @@ export function getAuth(app: FirebaseApp = getApp()): Auth { const authEmulatorHost = getDefaultEmulatorHost('auth'); if (authEmulatorHost) { connectAuthEmulator(auth, `http://${authEmulatorHost}`); - } else { - updateEmulatorBanner('Auth', false); } return auth; diff --git a/packages/database/src/api/Database.ts b/packages/database/src/api/Database.ts index a94b04518d7..515e278b5c5 100644 --- a/packages/database/src/api/Database.ts +++ b/packages/database/src/api/Database.ts @@ -31,8 +31,7 @@ import { EmulatorMockTokenOptions, getDefaultEmulatorHostnameAndPort, isCloudWorkstation, - pingServer, - updateEmulatorBanner + pingServer } from '@firebase/util'; import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider'; @@ -258,10 +257,6 @@ export class Database implements _FirebaseService { this.app.options['databaseAuthVariableOverride'] ); this._instanceStarted = true; - updateEmulatorBanner( - 'Database', - this._repo.repoInfo_.emulatorOptions !== null - ); } return this._repoInternal; } @@ -398,7 +393,6 @@ export function connectDatabaseEmulator( // Workaround to get cookies in Firebase Studio if (isCloudWorkstation(host)) { void pingServer(host); - updateEmulatorBanner('Database', true); } // Modify the repo to apply emulator settings diff --git a/packages/firestore/src/lite-api/database.ts b/packages/firestore/src/lite-api/database.ts index fce6d5843b7..8e7fdb27e90 100644 --- a/packages/firestore/src/lite-api/database.ts +++ b/packages/firestore/src/lite-api/database.ts @@ -28,7 +28,6 @@ import { EmulatorMockTokenOptions, getDefaultEmulatorHostnameAndPort, isCloudWorkstation, - updateEmulatorBanner, pingServer } from '@firebase/util'; @@ -143,7 +142,6 @@ export class Firestore implements FirestoreService { _freezeSettings(): FirestoreSettingsImpl { this._settingsFrozen = true; - updateEmulatorBanner('Firestore', this._settings.isUsingEmulator); return this._settings; } @@ -336,7 +334,9 @@ export function connectFirestoreEmulator( emulatorOptions: firestore._getEmulatorOptions() }; const newHostSetting = `${host}:${port}`; - + if (useSsl) { + void pingServer(`https://${newHostSetting}`); + } if (settings.host !== DEFAULT_HOST && settings.host !== newHostSetting) { logWarn( 'Host has been set in both settings() and connectFirestoreEmulator(), emulator host ' + @@ -357,11 +357,6 @@ export function connectFirestoreEmulator( firestore._setSettings(newConfig); - if (useSsl) { - void pingServer(`https://${newHostSetting}`); - updateEmulatorBanner('Firestore', true); - } - if (options.mockUserToken) { let token: string; let user: User; diff --git a/packages/functions/src/api.ts b/packages/functions/src/api.ts index cb987035145..7f92cba8343 100644 --- a/packages/functions/src/api.ts +++ b/packages/functions/src/api.ts @@ -29,8 +29,7 @@ import { } from './service'; import { getModularInstance, - getDefaultEmulatorHostnameAndPort, - updateEmulatorBanner + getDefaultEmulatorHostnameAndPort } from '@firebase/util'; export { FunctionsError } from './error'; @@ -48,7 +47,6 @@ export function getFunctions( app: FirebaseApp = getApp(), regionOrCustomDomain: string = DEFAULT_REGION ): Functions { - updateEmulatorBanner('Functions', false); // Dependencies const functionsProvider: Provider<'functions'> = _getProvider( getModularInstance(app), diff --git a/packages/functions/src/service.ts b/packages/functions/src/service.ts index 57504a4c7a4..af9d8898d2e 100644 --- a/packages/functions/src/service.ts +++ b/packages/functions/src/service.ts @@ -30,11 +30,7 @@ import { Provider } from '@firebase/component'; import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; import { MessagingInternalComponentName } from '@firebase/messaging-interop-types'; import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types'; -import { - isCloudWorkstation, - pingServer, - updateEmulatorBanner -} from '@firebase/util'; +import { isCloudWorkstation, pingServer } from '@firebase/util'; export const DEFAULT_REGION = 'us-central1'; @@ -186,7 +182,6 @@ export function connectFunctionsEmulator( // Workaround to get cookies in Firebase Studio if (useSsl) { void pingServer(functionsInstance.emulatorOrigin); - updateEmulatorBanner('Functions', true); } } diff --git a/packages/storage/src/api.ts b/packages/storage/src/api.ts index b164a1324c3..84c77ea0c8c 100644 --- a/packages/storage/src/api.ts +++ b/packages/storage/src/api.ts @@ -53,8 +53,7 @@ import { STORAGE_TYPE } from './constants'; import { EmulatorMockTokenOptions, getModularInstance, - getDefaultEmulatorHostnameAndPort, - updateEmulatorBanner + getDefaultEmulatorHostnameAndPort } from '@firebase/util'; import { StringFormat } from './implementation/string'; @@ -333,7 +332,6 @@ export function getStorage( bucketUrl?: string ): FirebaseStorage { app = getModularInstance(app); - updateEmulatorBanner('Storage', false); const storageProvider: Provider<'storage'> = _getProvider(app, STORAGE_TYPE); const storageInstance = storageProvider.getImmediate({ identifier: bucketUrl diff --git a/packages/storage/src/service.ts b/packages/storage/src/service.ts index 97d1407bb52..741dd6eaa1a 100644 --- a/packages/storage/src/service.ts +++ b/packages/storage/src/service.ts @@ -46,8 +46,7 @@ import { createMockUserToken, EmulatorMockTokenOptions, isCloudWorkstation, - pingServer, - updateEmulatorBanner + pingServer } from '@firebase/util'; import { Connection, ConnectionType } from './implementation/connection'; @@ -151,7 +150,6 @@ export function connectStorageEmulator( // Workaround to get cookies in Firebase Studio if (useSsl) { void pingServer(`https://${storage.host}`); - updateEmulatorBanner('Storage', true); } storage._isUsingEmulator = true; storage._protocol = useSsl ? 'https' : 'http'; From b5df4ae71c1b5b54d9237e7929d0f793189b82c9 Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Wed, 21 May 2025 16:20:48 -0700 Subject: [PATCH 48/77] Updated to only show banner when calling connect*Emulator (#9055) --- .changeset/large-windows-mate.md | 10 ++++++++++ packages/auth/src/core/auth/emulator.ts | 14 +++++++++----- packages/data-connect/src/api/DataConnect.ts | 7 ++++++- packages/database/src/api/Database.ts | 4 +++- packages/firestore/src/lite-api/database.ts | 4 +++- packages/functions/src/service.ts | 7 ++++++- packages/storage/src/service.ts | 4 +++- 7 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 .changeset/large-windows-mate.md diff --git a/.changeset/large-windows-mate.md b/.changeset/large-windows-mate.md new file mode 100644 index 00000000000..004f3284936 --- /dev/null +++ b/.changeset/large-windows-mate.md @@ -0,0 +1,10 @@ +--- +"@firebase/auth": patch +"@firebase/data-connect": patch +"@firebase/database": patch +"@firebase/firestore": patch +"@firebase/functions": patch +"@firebase/storage": patch +--- + +Updated to only show banner when calling connect*Emulator diff --git a/packages/auth/src/core/auth/emulator.ts b/packages/auth/src/core/auth/emulator.ts index 8547f7bad6c..15da907286f 100644 --- a/packages/auth/src/core/auth/emulator.ts +++ b/packages/auth/src/core/auth/emulator.ts @@ -18,7 +18,12 @@ import { Auth } from '../../model/public_types'; import { AuthErrorCode } from '../errors'; import { _assert } from '../util/assert'; import { _castAuth } from './auth_impl'; -import { deepEqual, isCloudWorkstation, pingServer } from '@firebase/util'; +import { + deepEqual, + isCloudWorkstation, + pingServer, + updateEmulatorBanner +} from '@firebase/util'; /** * Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production @@ -97,13 +102,12 @@ export function connectAuthEmulator( authInternal.emulatorConfig = emulatorConfig; authInternal.settings.appVerificationDisabledForTesting = true; - if (!disableWarnings) { - emitEmulatorWarning(); - } - // Workaround to get cookies in Firebase Studio if (isCloudWorkstation(host)) { void pingServer(`${protocol}//${host}${portStr}`); + updateEmulatorBanner('Auth', true); + } else if (!disableWarnings) { + emitEmulatorWarning(); } } diff --git a/packages/data-connect/src/api/DataConnect.ts b/packages/data-connect/src/api/DataConnect.ts index c25a09039ac..b7311363784 100644 --- a/packages/data-connect/src/api/DataConnect.ts +++ b/packages/data-connect/src/api/DataConnect.ts @@ -24,7 +24,11 @@ import { import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types'; import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; import { Provider } from '@firebase/component'; -import { isCloudWorkstation, pingServer } from '@firebase/util'; +import { + isCloudWorkstation, + pingServer, + updateEmulatorBanner +} from '@firebase/util'; import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider'; import { Code, DataConnectError } from '../core/error'; @@ -241,6 +245,7 @@ export function connectDataConnectEmulator( // Workaround to get cookies in Firebase Studio if (isCloudWorkstation(host)) { void pingServer(`https://${host}${port ? `:${port}` : ''}`); + updateEmulatorBanner('Data Connect', true); } dc.enableEmulator({ host, port, sslEnabled }); } diff --git a/packages/database/src/api/Database.ts b/packages/database/src/api/Database.ts index 515e278b5c5..338255be46f 100644 --- a/packages/database/src/api/Database.ts +++ b/packages/database/src/api/Database.ts @@ -31,7 +31,8 @@ import { EmulatorMockTokenOptions, getDefaultEmulatorHostnameAndPort, isCloudWorkstation, - pingServer + pingServer, + updateEmulatorBanner } from '@firebase/util'; import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider'; @@ -393,6 +394,7 @@ export function connectDatabaseEmulator( // Workaround to get cookies in Firebase Studio if (isCloudWorkstation(host)) { void pingServer(host); + updateEmulatorBanner('Database', true); } // Modify the repo to apply emulator settings diff --git a/packages/firestore/src/lite-api/database.ts b/packages/firestore/src/lite-api/database.ts index 8e7fdb27e90..6af324e4ba4 100644 --- a/packages/firestore/src/lite-api/database.ts +++ b/packages/firestore/src/lite-api/database.ts @@ -28,7 +28,8 @@ import { EmulatorMockTokenOptions, getDefaultEmulatorHostnameAndPort, isCloudWorkstation, - pingServer + pingServer, + updateEmulatorBanner } from '@firebase/util'; import { @@ -336,6 +337,7 @@ export function connectFirestoreEmulator( const newHostSetting = `${host}:${port}`; if (useSsl) { void pingServer(`https://${newHostSetting}`); + updateEmulatorBanner('Firestore', true); } if (settings.host !== DEFAULT_HOST && settings.host !== newHostSetting) { logWarn( diff --git a/packages/functions/src/service.ts b/packages/functions/src/service.ts index af9d8898d2e..57504a4c7a4 100644 --- a/packages/functions/src/service.ts +++ b/packages/functions/src/service.ts @@ -30,7 +30,11 @@ import { Provider } from '@firebase/component'; import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; import { MessagingInternalComponentName } from '@firebase/messaging-interop-types'; import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types'; -import { isCloudWorkstation, pingServer } from '@firebase/util'; +import { + isCloudWorkstation, + pingServer, + updateEmulatorBanner +} from '@firebase/util'; export const DEFAULT_REGION = 'us-central1'; @@ -182,6 +186,7 @@ export function connectFunctionsEmulator( // Workaround to get cookies in Firebase Studio if (useSsl) { void pingServer(functionsInstance.emulatorOrigin); + updateEmulatorBanner('Functions', true); } } diff --git a/packages/storage/src/service.ts b/packages/storage/src/service.ts index 741dd6eaa1a..97d1407bb52 100644 --- a/packages/storage/src/service.ts +++ b/packages/storage/src/service.ts @@ -46,7 +46,8 @@ import { createMockUserToken, EmulatorMockTokenOptions, isCloudWorkstation, - pingServer + pingServer, + updateEmulatorBanner } from '@firebase/util'; import { Connection, ConnectionType } from './implementation/connection'; @@ -150,6 +151,7 @@ export function connectStorageEmulator( // Workaround to get cookies in Firebase Studio if (useSsl) { void pingServer(`https://${storage.host}`); + updateEmulatorBanner('Storage', true); } storage._isUsingEmulator = true; storage._protocol = useSsl ? 'https' : 'http'; From 770e455a63c32d4340c3204ca013ed08440e73ec Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 21 May 2025 17:04:09 -0700 Subject: [PATCH 49/77] Version Packages (#9054) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/dirty-crews-cross.md | 9 --------- .changeset/large-windows-mate.md | 10 ---------- integration/compat-interop/package.json | 8 ++++---- integration/firestore/package.json | 2 +- integration/messaging/package.json | 2 +- packages/auth-compat/CHANGELOG.md | 7 +++++++ packages/auth-compat/package.json | 4 ++-- packages/auth/CHANGELOG.md | 8 ++++++++ packages/auth/package.json | 2 +- packages/data-connect/CHANGELOG.md | 6 ++++++ packages/data-connect/package.json | 2 +- packages/database-compat/CHANGELOG.md | 7 +++++++ packages/database-compat/package.json | 4 ++-- packages/database/CHANGELOG.md | 8 ++++++++ packages/database/package.json | 2 +- packages/firebase/CHANGELOG.md | 17 +++++++++++++++++ packages/firebase/package.json | 24 ++++++++++++------------ packages/firestore-compat/CHANGELOG.md | 7 +++++++ packages/firestore-compat/package.json | 4 ++-- packages/firestore/CHANGELOG.md | 8 ++++++++ packages/firestore/package.json | 4 ++-- packages/functions-compat/CHANGELOG.md | 7 +++++++ packages/functions-compat/package.json | 4 ++-- packages/functions/CHANGELOG.md | 8 ++++++++ packages/functions/package.json | 2 +- packages/storage-compat/CHANGELOG.md | 7 +++++++ packages/storage-compat/package.json | 6 +++--- packages/storage/CHANGELOG.md | 8 ++++++++ packages/storage/package.json | 4 ++-- 29 files changed, 135 insertions(+), 56 deletions(-) delete mode 100644 .changeset/dirty-crews-cross.md delete mode 100644 .changeset/large-windows-mate.md diff --git a/.changeset/dirty-crews-cross.md b/.changeset/dirty-crews-cross.md deleted file mode 100644 index 08a962670f0..00000000000 --- a/.changeset/dirty-crews-cross.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@firebase/auth": patch -"@firebase/database": patch -"@firebase/firestore": patch -"@firebase/functions": patch -"@firebase/storage": patch ---- - -Revert "Fixed scroll behavior (#9043)" diff --git a/.changeset/large-windows-mate.md b/.changeset/large-windows-mate.md deleted file mode 100644 index 004f3284936..00000000000 --- a/.changeset/large-windows-mate.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@firebase/auth": patch -"@firebase/data-connect": patch -"@firebase/database": patch -"@firebase/firestore": patch -"@firebase/functions": patch -"@firebase/storage": patch ---- - -Updated to only show banner when calling connect*Emulator diff --git a/integration/compat-interop/package.json b/integration/compat-interop/package.json index 11360a5b954..117a8220fc0 100644 --- a/integration/compat-interop/package.json +++ b/integration/compat-interop/package.json @@ -12,10 +12,10 @@ "@firebase/app-compat": "0.4.0", "@firebase/analytics": "0.10.16", "@firebase/analytics-compat": "0.2.22", - "@firebase/auth": "1.10.5", - "@firebase/auth-compat": "0.5.25", - "@firebase/functions": "0.12.7", - "@firebase/functions-compat": "0.3.24", + "@firebase/auth": "1.10.6", + "@firebase/auth-compat": "0.5.26", + "@firebase/functions": "0.12.8", + "@firebase/functions-compat": "0.3.25", "@firebase/messaging": "0.12.21", "@firebase/messaging-compat": "0.2.21", "@firebase/performance": "0.7.6", diff --git a/integration/firestore/package.json b/integration/firestore/package.json index c509b23e5b5..0c722a5352b 100644 --- a/integration/firestore/package.json +++ b/integration/firestore/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@firebase/app": "0.13.0", - "@firebase/firestore": "4.7.15" + "@firebase/firestore": "4.7.16" }, "devDependencies": { "@types/mocha": "9.1.1", diff --git a/integration/messaging/package.json b/integration/messaging/package.json index b4eaccebdab..9cb2bf233fc 100644 --- a/integration/messaging/package.json +++ b/integration/messaging/package.json @@ -9,7 +9,7 @@ "test:manual": "mocha --exit" }, "devDependencies": { - "firebase": "11.8.0", + "firebase": "11.8.1", "chai": "4.5.0", "chromedriver": "119.0.1", "express": "4.21.2", diff --git a/packages/auth-compat/CHANGELOG.md b/packages/auth-compat/CHANGELOG.md index b0876dd935c..aed6222d935 100644 --- a/packages/auth-compat/CHANGELOG.md +++ b/packages/auth-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/auth-compat +## 0.5.26 + +### Patch Changes + +- Updated dependencies [[`35ad526`](https://github.com/firebase/firebase-js-sdk/commit/35ad5266304e14425988fcf5ad06d028b37588ac), [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9)]: + - @firebase/auth@1.10.6 + ## 0.5.25 ### Patch Changes diff --git a/packages/auth-compat/package.json b/packages/auth-compat/package.json index f2fcfac236a..c8a6f40b357 100644 --- a/packages/auth-compat/package.json +++ b/packages/auth-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth-compat", - "version": "0.5.25", + "version": "0.5.26", "description": "FirebaseAuth compatibility package that uses API style compatible with Firebase@8 and prior versions", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,7 +49,7 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/auth": "1.10.5", + "@firebase/auth": "1.10.6", "@firebase/auth-types": "0.13.0", "@firebase/component": "0.6.17", "@firebase/util": "1.12.0", diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md index cb1ecc4fda7..fe546adc28a 100644 --- a/packages/auth/CHANGELOG.md +++ b/packages/auth/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/auth +## 1.10.6 + +### Patch Changes + +- [`35ad526`](https://github.com/firebase/firebase-js-sdk/commit/35ad5266304e14425988fcf5ad06d028b37588ac) [#9053](https://github.com/firebase/firebase-js-sdk/pull/9053) - Revert "Fixed scroll behavior (#9043)" + +- [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9) [#9055](https://github.com/firebase/firebase-js-sdk/pull/9055) - Updated to only show banner when calling connect\*Emulator + ## 1.10.5 ### Patch Changes diff --git a/packages/auth/package.json b/packages/auth/package.json index 5777c7500d1..ea47732d398 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth", - "version": "1.10.5", + "version": "1.10.6", "description": "The Firebase Authenticaton component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/node/index.js", diff --git a/packages/data-connect/CHANGELOG.md b/packages/data-connect/CHANGELOG.md index dfec86ea4d0..cb5e53a5c54 100644 --- a/packages/data-connect/CHANGELOG.md +++ b/packages/data-connect/CHANGELOG.md @@ -1,5 +1,11 @@ ## Unreleased +## 0.3.9 + +### Patch Changes + +- [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9) [#9055](https://github.com/firebase/firebase-js-sdk/pull/9055) - Updated to only show banner when calling connect\*Emulator + ## 0.3.8 ### Patch Changes diff --git a/packages/data-connect/package.json b/packages/data-connect/package.json index aeb5aaee50f..a342c7d4f14 100644 --- a/packages/data-connect/package.json +++ b/packages/data-connect/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/data-connect", - "version": "0.3.8", + "version": "0.3.9", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", diff --git a/packages/database-compat/CHANGELOG.md b/packages/database-compat/CHANGELOG.md index 3376ad0c953..b15d780739a 100644 --- a/packages/database-compat/CHANGELOG.md +++ b/packages/database-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/database-compat +## 2.0.10 + +### Patch Changes + +- Updated dependencies [[`35ad526`](https://github.com/firebase/firebase-js-sdk/commit/35ad5266304e14425988fcf5ad06d028b37588ac), [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9)]: + - @firebase/database@1.0.19 + ## 2.0.9 ### Patch Changes diff --git a/packages/database-compat/package.json b/packages/database-compat/package.json index 6a3f4d83335..104f57d56dc 100644 --- a/packages/database-compat/package.json +++ b/packages/database-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database-compat", - "version": "2.0.9", + "version": "2.0.10", "description": "The Realtime Database component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.js", @@ -49,7 +49,7 @@ "add-compat-overloads": "ts-node-script ../../scripts/build/create-overloads.ts -i ../database/dist/public.d.ts -o dist/database-compat/src/index.d.ts -a -r Database:types.FirebaseDatabase -r Query:types.Query -r DatabaseReference:types.Reference -r FirebaseApp:FirebaseAppCompat --moduleToEnhance @firebase/database" }, "dependencies": { - "@firebase/database": "1.0.18", + "@firebase/database": "1.0.19", "@firebase/database-types": "1.0.14", "@firebase/logger": "0.4.4", "@firebase/util": "1.12.0", diff --git a/packages/database/CHANGELOG.md b/packages/database/CHANGELOG.md index c7fcbd37dd0..2c8060f7860 100644 --- a/packages/database/CHANGELOG.md +++ b/packages/database/CHANGELOG.md @@ -1,5 +1,13 @@ # Unreleased +## 1.0.19 + +### Patch Changes + +- [`35ad526`](https://github.com/firebase/firebase-js-sdk/commit/35ad5266304e14425988fcf5ad06d028b37588ac) [#9053](https://github.com/firebase/firebase-js-sdk/pull/9053) - Revert "Fixed scroll behavior (#9043)" + +- [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9) [#9055](https://github.com/firebase/firebase-js-sdk/pull/9055) - Updated to only show banner when calling connect\*Emulator + ## 1.0.18 ### Patch Changes diff --git a/packages/database/package.json b/packages/database/package.json index 72050a5bd2e..0fe42bddeed 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/database", - "version": "1.0.18", + "version": "1.0.19", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", diff --git a/packages/firebase/CHANGELOG.md b/packages/firebase/CHANGELOG.md index 436053673b2..2a17f24d933 100644 --- a/packages/firebase/CHANGELOG.md +++ b/packages/firebase/CHANGELOG.md @@ -1,5 +1,22 @@ # firebase +## 11.8.1 + +### Patch Changes + +- Updated dependencies [[`35ad526`](https://github.com/firebase/firebase-js-sdk/commit/35ad5266304e14425988fcf5ad06d028b37588ac), [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9)]: + - @firebase/auth@1.10.6 + - @firebase/database@1.0.19 + - @firebase/firestore@4.7.16 + - @firebase/functions@0.12.8 + - @firebase/storage@0.13.12 + - @firebase/data-connect@0.3.9 + - @firebase/auth-compat@0.5.26 + - @firebase/database-compat@2.0.10 + - @firebase/firestore-compat@0.3.51 + - @firebase/functions-compat@0.3.25 + - @firebase/storage-compat@0.3.22 + ## 11.8.0 ### Minor Changes diff --git a/packages/firebase/package.json b/packages/firebase/package.json index 9ce4835a2f4..aec5e6e8fec 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -1,6 +1,6 @@ { "name": "firebase", - "version": "11.8.0", + "version": "11.8.1", "description": "Firebase JavaScript library for web and Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -415,21 +415,21 @@ "@firebase/app": "0.13.0", "@firebase/app-compat": "0.4.0", "@firebase/app-types": "0.9.3", - "@firebase/auth": "1.10.5", - "@firebase/auth-compat": "0.5.25", - "@firebase/data-connect": "0.3.8", - "@firebase/database": "1.0.18", - "@firebase/database-compat": "2.0.9", - "@firebase/firestore": "4.7.15", - "@firebase/firestore-compat": "0.3.50", - "@firebase/functions": "0.12.7", - "@firebase/functions-compat": "0.3.24", + "@firebase/auth": "1.10.6", + "@firebase/auth-compat": "0.5.26", + "@firebase/data-connect": "0.3.9", + "@firebase/database": "1.0.19", + "@firebase/database-compat": "2.0.10", + "@firebase/firestore": "4.7.16", + "@firebase/firestore-compat": "0.3.51", + "@firebase/functions": "0.12.8", + "@firebase/functions-compat": "0.3.25", "@firebase/installations": "0.6.17", "@firebase/installations-compat": "0.2.17", "@firebase/messaging": "0.12.21", "@firebase/messaging-compat": "0.2.21", - "@firebase/storage": "0.13.11", - "@firebase/storage-compat": "0.3.21", + "@firebase/storage": "0.13.12", + "@firebase/storage-compat": "0.3.22", "@firebase/performance": "0.7.6", "@firebase/performance-compat": "0.2.19", "@firebase/remote-config": "0.6.4", diff --git a/packages/firestore-compat/CHANGELOG.md b/packages/firestore-compat/CHANGELOG.md index 2ab0cc98150..861e35792c5 100644 --- a/packages/firestore-compat/CHANGELOG.md +++ b/packages/firestore-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/firestore-compat +## 0.3.51 + +### Patch Changes + +- Updated dependencies [[`35ad526`](https://github.com/firebase/firebase-js-sdk/commit/35ad5266304e14425988fcf5ad06d028b37588ac), [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9)]: + - @firebase/firestore@4.7.16 + ## 0.3.50 ### Patch Changes diff --git a/packages/firestore-compat/package.json b/packages/firestore-compat/package.json index 68c135058e6..6e3db51e597 100644 --- a/packages/firestore-compat/package.json +++ b/packages/firestore-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore-compat", - "version": "0.3.50", + "version": "0.3.51", "description": "The Cloud Firestore component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -47,7 +47,7 @@ }, "dependencies": { "@firebase/component": "0.6.17", - "@firebase/firestore": "4.7.15", + "@firebase/firestore": "4.7.16", "@firebase/util": "1.12.0", "@firebase/firestore-types": "3.0.3", "tslib": "^2.1.0" diff --git a/packages/firestore/CHANGELOG.md b/packages/firestore/CHANGELOG.md index 7f9e6fe4a91..0671f0e8c63 100644 --- a/packages/firestore/CHANGELOG.md +++ b/packages/firestore/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/firestore +## 4.7.16 + +### Patch Changes + +- [`35ad526`](https://github.com/firebase/firebase-js-sdk/commit/35ad5266304e14425988fcf5ad06d028b37588ac) [#9053](https://github.com/firebase/firebase-js-sdk/pull/9053) - Revert "Fixed scroll behavior (#9043)" + +- [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9) [#9055](https://github.com/firebase/firebase-js-sdk/pull/9055) - Updated to only show banner when calling connect\*Emulator + ## 4.7.15 ### Patch Changes diff --git a/packages/firestore/package.json b/packages/firestore/package.json index b7d76fb4fca..6fc04a3579d 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore", - "version": "4.7.15", + "version": "4.7.16", "engines": { "node": ">=18.0.0" }, @@ -114,7 +114,7 @@ "devDependencies": { "@firebase/app": "0.13.0", "@firebase/app-compat": "0.4.0", - "@firebase/auth": "1.10.5", + "@firebase/auth": "1.10.6", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", "@types/eslint": "7.29.0", diff --git a/packages/functions-compat/CHANGELOG.md b/packages/functions-compat/CHANGELOG.md index ddff93a8f81..e2317dd8152 100644 --- a/packages/functions-compat/CHANGELOG.md +++ b/packages/functions-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/functions-compat +## 0.3.25 + +### Patch Changes + +- Updated dependencies [[`35ad526`](https://github.com/firebase/firebase-js-sdk/commit/35ad5266304e14425988fcf5ad06d028b37588ac), [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9)]: + - @firebase/functions@0.12.8 + ## 0.3.24 ### Patch Changes diff --git a/packages/functions-compat/package.json b/packages/functions-compat/package.json index 56c5bcbb89d..09a4514114c 100644 --- a/packages/functions-compat/package.json +++ b/packages/functions-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/functions-compat", - "version": "0.3.24", + "version": "0.3.25", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -63,7 +63,7 @@ "typings": "dist/src/index.d.ts", "dependencies": { "@firebase/component": "0.6.17", - "@firebase/functions": "0.12.7", + "@firebase/functions": "0.12.8", "@firebase/functions-types": "0.6.3", "@firebase/util": "1.12.0", "tslib": "^2.1.0" diff --git a/packages/functions/CHANGELOG.md b/packages/functions/CHANGELOG.md index 68afc468a1c..dcad8633b23 100644 --- a/packages/functions/CHANGELOG.md +++ b/packages/functions/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/functions +## 0.12.8 + +### Patch Changes + +- [`35ad526`](https://github.com/firebase/firebase-js-sdk/commit/35ad5266304e14425988fcf5ad06d028b37588ac) [#9053](https://github.com/firebase/firebase-js-sdk/pull/9053) - Revert "Fixed scroll behavior (#9043)" + +- [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9) [#9055](https://github.com/firebase/firebase-js-sdk/pull/9055) - Updated to only show banner when calling connect\*Emulator + ## 0.12.7 ### Patch Changes diff --git a/packages/functions/package.json b/packages/functions/package.json index 80ca8986c56..004181eeb39 100644 --- a/packages/functions/package.json +++ b/packages/functions/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/functions", - "version": "0.12.7", + "version": "0.12.8", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", diff --git a/packages/storage-compat/CHANGELOG.md b/packages/storage-compat/CHANGELOG.md index 2bb419259cf..48117be7e87 100644 --- a/packages/storage-compat/CHANGELOG.md +++ b/packages/storage-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/storage-compat +## 0.3.22 + +### Patch Changes + +- Updated dependencies [[`35ad526`](https://github.com/firebase/firebase-js-sdk/commit/35ad5266304e14425988fcf5ad06d028b37588ac), [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9)]: + - @firebase/storage@0.13.12 + ## 0.3.21 ### Patch Changes diff --git a/packages/storage-compat/package.json b/packages/storage-compat/package.json index 26633a8218c..724685153ce 100644 --- a/packages/storage-compat/package.json +++ b/packages/storage-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage-compat", - "version": "0.3.21", + "version": "0.3.22", "description": "The Firebase Firestore compatibility package", "author": "Firebase (https://firebase.google.com/)", "main": "./dist/index.cjs.js", @@ -37,7 +37,7 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/storage": "0.13.11", + "@firebase/storage": "0.13.12", "@firebase/storage-types": "0.8.3", "@firebase/util": "1.12.0", "@firebase/component": "0.6.17", @@ -45,7 +45,7 @@ }, "devDependencies": { "@firebase/app-compat": "0.4.0", - "@firebase/auth-compat": "0.5.25", + "@firebase/auth-compat": "0.5.26", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/storage/CHANGELOG.md b/packages/storage/CHANGELOG.md index e123d70cdce..cabce495546 100644 --- a/packages/storage/CHANGELOG.md +++ b/packages/storage/CHANGELOG.md @@ -1,5 +1,13 @@ #Unreleased +## 0.13.12 + +### Patch Changes + +- [`35ad526`](https://github.com/firebase/firebase-js-sdk/commit/35ad5266304e14425988fcf5ad06d028b37588ac) [#9053](https://github.com/firebase/firebase-js-sdk/pull/9053) - Revert "Fixed scroll behavior (#9043)" + +- [`b5df4ae`](https://github.com/firebase/firebase-js-sdk/commit/b5df4ae71c1b5b54d9237e7929d0f793189b82c9) [#9055](https://github.com/firebase/firebase-js-sdk/pull/9055) - Updated to only show banner when calling connect\*Emulator + ## 0.13.11 ### Patch Changes diff --git a/packages/storage/package.json b/packages/storage/package.json index 67c205ac245..f7d52a2e8e2 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage", - "version": "0.13.11", + "version": "0.13.12", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -55,7 +55,7 @@ }, "devDependencies": { "@firebase/app": "0.13.0", - "@firebase/auth": "1.10.5", + "@firebase/auth": "1.10.6", "rollup": "2.79.2", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", From 9964849e9540f08d02fa3825ecec32c1bfedc62d Mon Sep 17 00:00:00 2001 From: Mark Duckworth <1124037+MarkDuckworth@users.noreply.github.com> Date: Thu, 22 May 2025 10:40:43 -0600 Subject: [PATCH 50/77] Close webchannel instances on terminate (#9041) Explicitly close any unclosed WebChannel instances on Firestore#terminate() --- .changeset/spotty-ghosts-kneel.md | 5 +++ .../platform/browser/webchannel_connection.ts | 33 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 .changeset/spotty-ghosts-kneel.md diff --git a/.changeset/spotty-ghosts-kneel.md b/.changeset/spotty-ghosts-kneel.md new file mode 100644 index 00000000000..0db91b7bf19 --- /dev/null +++ b/.changeset/spotty-ghosts-kneel.md @@ -0,0 +1,5 @@ +--- +"@firebase/firestore": patch +--- + +Clean up leaked WebChannel instances when the Firestore instance is terminated. diff --git a/packages/firestore/src/platform/browser/webchannel_connection.ts b/packages/firestore/src/platform/browser/webchannel_connection.ts index 9a69164457e..56f57aa9595 100644 --- a/packages/firestore/src/platform/browser/webchannel_connection.ts +++ b/packages/firestore/src/platform/browser/webchannel_connection.ts @@ -59,6 +59,9 @@ export class WebChannelConnection extends RestConnection { private readonly useFetchStreams: boolean; private readonly longPollingOptions: ExperimentalLongPollingOptions; + /** A collection of open WebChannel instances */ + private openWebChannels: WebChannel[] = []; + constructor(info: DatabaseInfo) { super(info); this.forceLongPolling = info.forceLongPolling; @@ -239,6 +242,7 @@ export class WebChannelConnection extends RestConnection { request ); const channel = webchannelTransport.createWebChannel(url, request); + this.addOpenWebChannel(channel); // WebChannel supports sending the first message with the handshake - saving // a network round trip. However, it will have to call send in the same @@ -321,6 +325,7 @@ export class WebChannelConnection extends RestConnection { `RPC '${rpcName}' stream ${streamId} transport closed` ); streamBridge.callOnClose(); + this.removeOpenWebChannel(channel); } }); @@ -427,4 +432,32 @@ export class WebChannelConnection extends RestConnection { }, 0); return streamBridge; } + + /** + * Closes and cleans up any resources associated with the connection. + */ + terminate(): void { + // If the Firestore instance is terminated, we will explicitly + // close any remaining open WebChannel instances. + this.openWebChannels.forEach(webChannel => webChannel.close()); + this.openWebChannels = []; + } + + /** + * Add a WebChannel instance to the collection of open instances. + * @param webChannel + */ + addOpenWebChannel(webChannel: WebChannel): void { + this.openWebChannels.push(webChannel); + } + + /** + * Remove a WebChannel instance from the collection of open instances. + * @param webChannel + */ + removeOpenWebChannel(webChannel: WebChannel): void { + this.openWebChannels = this.openWebChannels.filter( + instance => instance === webChannel + ); + } } From d5e5795c944930eb8df424c3e9bc33e17ab00b84 Mon Sep 17 00:00:00 2001 From: Mark Duckworth <1124037+MarkDuckworth@users.noreply.github.com> Date: Thu, 22 May 2025 12:17:01 -0600 Subject: [PATCH 51/77] Reorder reporters to prevent log message duplication in CI (#9040) --- config/karma.base.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/karma.base.js b/config/karma.base.js index 9d92053115f..fdfe0d35907 100644 --- a/config/karma.base.js +++ b/config/karma.base.js @@ -68,7 +68,7 @@ const config = { // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['mocha', 'coverage-istanbul'], + reporters: ['coverage-istanbul', 'mocha'], // web server port port: 8089, From ec91a8611b069204a9043d55b66e71c7798862ce Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Tue, 27 May 2025 09:34:08 -0500 Subject: [PATCH 52/77] docs(ai): Fix 'occured' typo in error codes (#9021) --- docs-devsite/ai.md | 4 ++-- packages/ai/src/types/error.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs-devsite/ai.md b/docs-devsite/ai.md index c43c0391ba4..9c279febad3 100644 --- a/docs-devsite/ai.md +++ b/docs-devsite/ai.md @@ -448,13 +448,13 @@ export declare const enum AIErrorCode | INVALID\_CONTENT | "invalid-content" | An error associated with a Content object. | | INVALID\_SCHEMA | "invalid-schema" | An error due to invalid Schema input. | | NO\_API\_KEY | "no-api-key" | An error occurred due to a missing Firebase API key. | -| NO\_APP\_ID | "no-app-id" | An error occured due to a missing Firebase app ID. | +| NO\_APP\_ID | "no-app-id" | An error occurred due to a missing Firebase app ID. | | NO\_MODEL | "no-model" | An error occurred due to a model name not being specified during initialization. | | NO\_PROJECT\_ID | "no-project-id" | An error occurred due to a missing project ID. | | PARSE\_FAILED | "parse-failed" | An error occurred while parsing. | | REQUEST\_ERROR | "request-error" | An error occurred in a request. | | RESPONSE\_ERROR | "response-error" | An error occurred in a response. | -| UNSUPPORTED | "unsupported" | An error occured due an attempt to use an unsupported feature. | +| UNSUPPORTED | "unsupported" | An error occurred due an attempt to use an unsupported feature. | ## BlockReason diff --git a/packages/ai/src/types/error.ts b/packages/ai/src/types/error.ts index ef3ad7fc30c..84a30f4e872 100644 --- a/packages/ai/src/types/error.ts +++ b/packages/ai/src/types/error.ts @@ -87,7 +87,7 @@ export const enum AIErrorCode { /** An error occurred due to a missing Firebase API key. */ NO_API_KEY = 'no-api-key', - /** An error occured due to a missing Firebase app ID. */ + /** An error occurred due to a missing Firebase app ID. */ NO_APP_ID = 'no-app-id', /** An error occurred due to a model name not being specified during initialization. */ @@ -99,6 +99,6 @@ export const enum AIErrorCode { /** An error occurred while parsing. */ PARSE_FAILED = 'parse-failed', - /** An error occured due an attempt to use an unsupported feature. */ + /** An error occurred due an attempt to use an unsupported feature. */ UNSUPPORTED = 'unsupported' } From 8cb21ffc588d8dd963ad8c78a957eb6929593a3e Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Tue, 27 May 2025 09:34:30 -0500 Subject: [PATCH 53/77] feat(ai): Add `HarmBlockThreshold.OFF` (#9050) --- common/api-review/ai.api.md | 3 ++- docs-devsite/ai.md | 1 + packages/ai/src/types/enums.ts | 7 ++++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/common/api-review/ai.api.md b/common/api-review/ai.api.md index d096d4c27f6..d24e24e636e 100644 --- a/common/api-review/ai.api.md +++ b/common/api-review/ai.api.md @@ -518,7 +518,8 @@ export enum HarmBlockThreshold { BLOCK_LOW_AND_ABOVE = "BLOCK_LOW_AND_ABOVE", BLOCK_MEDIUM_AND_ABOVE = "BLOCK_MEDIUM_AND_ABOVE", BLOCK_NONE = "BLOCK_NONE", - BLOCK_ONLY_HIGH = "BLOCK_ONLY_HIGH" + BLOCK_ONLY_HIGH = "BLOCK_ONLY_HIGH", + OFF = "OFF" } // @public diff --git a/docs-devsite/ai.md b/docs-devsite/ai.md index 9c279febad3..286c8351fd7 100644 --- a/docs-devsite/ai.md +++ b/docs-devsite/ai.md @@ -551,6 +551,7 @@ export declare enum HarmBlockThreshold | BLOCK\_MEDIUM\_AND\_ABOVE | "BLOCK_MEDIUM_AND_ABOVE" | Content with NEGLIGIBLE and LOW will be allowed. | | BLOCK\_NONE | "BLOCK_NONE" | All content will be allowed. | | BLOCK\_ONLY\_HIGH | "BLOCK_ONLY_HIGH" | Content with NEGLIGIBLE, LOW, and MEDIUM will be allowed. | +| OFF | "OFF" | All content will be allowed. This is the same as BLOCK_NONE, but the metadata corresponding to the [HarmCategory](./ai.md#harmcategory) will not be present in the response. | ## HarmCategory diff --git a/packages/ai/src/types/enums.ts b/packages/ai/src/types/enums.ts index 5c6612ce26a..47d654bbcd1 100644 --- a/packages/ai/src/types/enums.ts +++ b/packages/ai/src/types/enums.ts @@ -58,7 +58,12 @@ export enum HarmBlockThreshold { /** * All content will be allowed. */ - BLOCK_NONE = 'BLOCK_NONE' + BLOCK_NONE = 'BLOCK_NONE', + /** + * All content will be allowed. This is the same as `BLOCK_NONE`, but the metadata corresponding + * to the {@link HarmCategory} will not be present in the response. + */ + OFF = 'OFF' } /** From 40be2dbb884b8e1485862af8bb015e23db69ccbf Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Tue, 27 May 2025 09:35:06 -0500 Subject: [PATCH 54/77] feat(ai): Schema `title`, `maximum`, `minimum`, and `propertyOrdering` (#9047) --- .changeset/tricky-years-pump.md | 6 + common/api-review/ai.api.md | 4 + docs-devsite/ai.schemashared.md | 44 +++ .../ai/src/requests/schema-builder.test.ts | 373 +++++++++--------- packages/ai/src/types/schema.ts | 12 + 5 files changed, 261 insertions(+), 178 deletions(-) create mode 100644 .changeset/tricky-years-pump.md diff --git a/.changeset/tricky-years-pump.md b/.changeset/tricky-years-pump.md new file mode 100644 index 00000000000..94bf68604cc --- /dev/null +++ b/.changeset/tricky-years-pump.md @@ -0,0 +1,6 @@ +--- +'firebase': minor +'@firebase/ai': minor +--- + +Add `title`, `maximum`, `minimum`, `propertyOrdering` to Schema builder diff --git a/common/api-review/ai.api.md b/common/api-review/ai.api.md index d24e24e636e..523b51d7533 100644 --- a/common/api-review/ai.api.md +++ b/common/api-review/ai.api.md @@ -832,10 +832,14 @@ export interface SchemaShared { example?: unknown; format?: string; items?: T; + maximum?: number; + minimum?: number; nullable?: boolean; properties?: { [k: string]: T; }; + propertyOrdering?: string[]; + title?: string; } // @public diff --git a/docs-devsite/ai.schemashared.md b/docs-devsite/ai.schemashared.md index eba57f82935..7f0ed27026c 100644 --- a/docs-devsite/ai.schemashared.md +++ b/docs-devsite/ai.schemashared.md @@ -27,8 +27,12 @@ export interface SchemaShared | [example](./ai.schemashared.md#schemasharedexample) | unknown | Optional. The example of the property. | | [format](./ai.schemashared.md#schemasharedformat) | string | Optional. The format of the property. When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this must be either 'enum' or 'date-time', otherwise requests will fail. | | [items](./ai.schemashared.md#schemashareditems) | T | Optional. The items of the property. | +| [maximum](./ai.schemashared.md#schemasharedmaximum) | number | The maximum value of a numeric type. | +| [minimum](./ai.schemashared.md#schemasharedminimum) | number | The minimum value of a numeric type. | | [nullable](./ai.schemashared.md#schemasharednullable) | boolean | Optional. Whether the property is nullable. | | [properties](./ai.schemashared.md#schemasharedproperties) | { \[k: string\]: T; } | Optional. Map of Schema objects. | +| [propertyOrdering](./ai.schemashared.md#schemasharedpropertyordering) | string\[\] | A hint suggesting the order in which the keys should appear in the generated JSON string. | +| [title](./ai.schemashared.md#schemasharedtitle) | string | The title of the property. This helps document the schema's purpose but does not typically constrain the generated value. It can subtly guide the model by clarifying the intent of a field. | ## SchemaShared.description @@ -80,6 +84,26 @@ Optional. The items of the property. items?: T; ``` +## SchemaShared.maximum + +The maximum value of a numeric type. + +Signature: + +```typescript +maximum?: number; +``` + +## SchemaShared.minimum + +The minimum value of a numeric type. + +Signature: + +```typescript +minimum?: number; +``` + ## SchemaShared.nullable Optional. Whether the property is nullable. @@ -101,3 +125,23 @@ properties?: { [k: string]: T; }; ``` + +## SchemaShared.propertyOrdering + +A hint suggesting the order in which the keys should appear in the generated JSON string. + +Signature: + +```typescript +propertyOrdering?: string[]; +``` + +## SchemaShared.title + +The title of the property. This helps document the schema's purpose but does not typically constrain the generated value. It can subtly guide the model by clarifying the intent of a field. + +Signature: + +```typescript +title?: string; +``` diff --git a/packages/ai/src/requests/schema-builder.test.ts b/packages/ai/src/requests/schema-builder.test.ts index d05b81381ea..ddf655b738d 100644 --- a/packages/ai/src/requests/schema-builder.test.ts +++ b/packages/ai/src/requests/schema-builder.test.ts @@ -31,11 +31,20 @@ describe('Schema builder', () => { }); }); it('builds integer schema with options and overrides', () => { - const schema = Schema.integer({ nullable: true, format: 'int32' }); + const schema = Schema.integer({ + nullable: true, + format: 'int32', + title: 'Age', + minimum: 0, + maximum: 120 + }); expect(schema.toJSON()).to.eql({ type: 'integer', format: 'int32', - nullable: true + nullable: true, + title: 'Age', + minimum: 0, + maximum: 120 }); }); it('builds number schema', () => { @@ -46,46 +55,60 @@ describe('Schema builder', () => { }); }); it('builds number schema with options and unknown options', () => { - const schema = Schema.number({ format: 'float', futureOption: 'test' }); + const schema = Schema.number({ + format: 'float', + futureOption: 'test', + title: 'Price', + minimum: 0.01, + maximum: 1000.99 + }); expect(schema.toJSON()).to.eql({ type: 'number', format: 'float', futureOption: 'test', - nullable: false + nullable: false, + title: 'Price', + minimum: 0.01, + maximum: 1000.99 }); }); it('builds boolean schema', () => { - const schema = Schema.boolean(); + const schema = Schema.boolean({ title: 'Is Active' }); expect(schema.toJSON()).to.eql({ type: 'boolean', - nullable: false + nullable: false, + title: 'Is Active' }); }); it('builds string schema', () => { - const schema = Schema.string({ description: 'hey' }); + const schema = Schema.string({ description: 'hey', title: 'Greeting' }); expect(schema.toJSON()).to.eql({ type: 'string', description: 'hey', - nullable: false + nullable: false, + title: 'Greeting' }); }); it('builds enumString schema', () => { const schema = Schema.enumString({ example: 'east', - enum: ['east', 'west'] + enum: ['east', 'west'], + title: 'Direction' }); expect(schema.toJSON()).to.eql({ type: 'string', example: 'east', enum: ['east', 'west'], - nullable: false + nullable: false, + title: 'Direction' }); }); it('builds an object schema', () => { const schema = Schema.object({ properties: { 'someInput': Schema.string() - } + }, + title: 'Input Object' }); expect(schema.toJSON()).to.eql({ type: 'object', @@ -96,16 +119,20 @@ describe('Schema builder', () => { nullable: false } }, - required: ['someInput'] + required: ['someInput'], + title: 'Input Object' }); }); - it('builds an object schema with optional properties', () => { + it('builds an object schema with optional properties and propertyOrdering', () => { const schema = Schema.object({ properties: { 'someInput': Schema.string(), - 'someBool': Schema.boolean() + 'someBool': Schema.boolean(), + 'anotherInput': Schema.integer() }, - optionalProperties: ['someBool'] + optionalProperties: ['someBool'], + propertyOrdering: ['someInput', 'anotherInput', 'someBool'], + title: 'Ordered Object' }); expect(schema.toJSON()).to.eql({ type: 'object', @@ -118,9 +145,15 @@ describe('Schema builder', () => { 'someBool': { type: 'boolean', nullable: false + }, + 'anotherInput': { + type: 'integer', + nullable: false } }, - required: ['someInput'] + required: ['someInput', 'anotherInput'], + propertyOrdering: ['someInput', 'anotherInput', 'someBool'], + title: 'Ordered Object' }); }); it('builds layered schema - partially filled out', () => { @@ -128,82 +161,162 @@ describe('Schema builder', () => { items: Schema.object({ properties: { country: Schema.string({ - description: 'A country name' + description: 'A country name', + title: 'Country Name' }), - population: Schema.integer(), + population: Schema.integer({ title: 'Population Count', minimum: 0 }), coordinates: Schema.object({ + title: 'Geographical Coordinates', properties: { - latitude: Schema.number({ format: 'float' }), - longitude: Schema.number({ format: 'double' }) + latitude: Schema.number({ format: 'float', title: 'Latitude' }), + longitude: Schema.number({ format: 'double', title: 'Longitude' }) } }), hemisphere: Schema.object({ + title: 'Hemisphere Information', properties: { - latitudinal: Schema.enumString({ enum: ['N', 'S'] }), - longitudinal: Schema.enumString({ enum: ['E', 'W'] }) + latitudinal: Schema.enumString({ + enum: ['N', 'S'], + title: 'Latitudinal Hemisphere' + }), + longitudinal: Schema.enumString({ + enum: ['E', 'W'], + title: 'Longitudinal Hemisphere' + }) } }), - isCapital: Schema.boolean() + isCapital: Schema.boolean({ title: 'Is Capital City' }) } - }) + }), + title: 'List of Countries' }); - expect(schema.toJSON()).to.eql(layeredSchemaOutputPartial); + const jsonSchema = schema.toJSON(); + expect(jsonSchema.title).to.equal('List of Countries'); + expect(jsonSchema.items?.properties?.country.title).to.equal( + 'Country Name' + ); + expect(jsonSchema.items?.properties?.population.title).to.equal( + 'Population Count' + ); + expect(jsonSchema.items?.properties?.population.minimum).to.equal(0); + expect(jsonSchema.items?.properties?.coordinates.title).to.equal( + 'Geographical Coordinates' + ); + expect(jsonSchema.items?.properties?.hemisphere.title).to.equal( + 'Hemisphere Information' + ); + expect(jsonSchema.items?.properties?.isCapital.title).to.equal( + 'Is Capital City' + ); }); - it('builds layered schema - fully filled out', () => { + it('builds layered schema - fully filled out with new properties', () => { const schema = Schema.array({ + title: 'Detailed Country Profiles', items: Schema.object({ description: 'A country profile', nullable: false, + title: 'Country Profile', + propertyOrdering: [ + 'country', + 'population', + 'isCapital', + 'elevation', + 'coordinates', + 'hemisphere' + ], properties: { country: Schema.string({ nullable: false, description: 'Country name', - format: undefined + format: undefined, + title: 'Official Country Name' }), population: Schema.integer({ nullable: false, description: 'Number of people in country', - format: 'int64' + format: 'int64', + title: 'Total Population', + minimum: 1 }), coordinates: Schema.object({ nullable: false, description: 'Latitude and longitude', + title: 'Capital Coordinates', properties: { latitude: Schema.number({ nullable: false, description: 'Latitude of capital', - format: 'float' + format: 'float', + title: 'Latitude Value', + minimum: -90, + maximum: 90 }), longitude: Schema.number({ nullable: false, description: 'Longitude of capital', - format: 'double' + format: 'double', + title: 'Longitude Value', + minimum: -180, + maximum: 180 }) } }), hemisphere: Schema.object({ nullable: false, description: 'Hemisphere(s) country is in', + title: 'Geographical Hemispheres', properties: { - latitudinal: Schema.enumString({ enum: ['N', 'S'] }), - longitudinal: Schema.enumString({ enum: ['E', 'W'] }) + latitudinal: Schema.enumString({ + enum: ['N', 'S'], + title: 'Latitudinal' + }), + longitudinal: Schema.enumString({ + enum: ['E', 'W'], + title: 'Longitudinal' + }) } }), isCapital: Schema.boolean({ nullable: false, - description: "This doesn't make a lot of sense but it's a demo" + description: "This doesn't make a lot of sense but it's a demo", + title: 'Is it a capital?' }), elevation: Schema.integer({ nullable: false, - description: 'Average elevation', - format: 'float' + description: 'Average elevation in meters', + format: 'int32', + title: 'Average Elevation (m)', + minimum: -500, + maximum: 9000 }) }, optionalProperties: [] }) }); - expect(schema.toJSON()).to.eql(layeredSchemaOutput); + const jsonResult = schema.toJSON(); + expect(jsonResult.title).to.equal('Detailed Country Profiles'); + expect(jsonResult.items?.title).to.equal('Country Profile'); + expect(jsonResult.items?.propertyOrdering).to.deep.equal([ + 'country', + 'population', + 'isCapital', + 'elevation', + 'coordinates', + 'hemisphere' + ]); + expect(jsonResult.items?.properties?.population.title).to.equal( + 'Total Population' + ); + expect(jsonResult.items?.properties?.population.minimum).to.equal(1); + expect( + jsonResult.items?.properties?.coordinates.properties?.latitude.maximum + ).to.equal(90); + expect(jsonResult.items?.properties?.elevation.title).to.equal( + 'Average Elevation (m)' + ); + expect(jsonResult.items?.properties?.elevation.minimum).to.equal(-500); + expect(jsonResult.items?.properties?.elevation.maximum).to.equal(9000); }); it('can override "nullable" and set optional properties', () => { const schema = Schema.object({ @@ -245,149 +358,53 @@ describe('Schema builder', () => { }); expect(() => schema.toJSON()).to.throw(AIErrorCode.INVALID_SCHEMA); }); -}); + it('builds schema with minimum and maximum for integer', () => { + const schema = Schema.integer({ minimum: 5, maximum: 10, title: 'Rating' }); + expect(schema.toJSON()).to.eql({ + type: 'integer', + nullable: false, + minimum: 5, + maximum: 10, + title: 'Rating' + }); + }); -const layeredSchemaOutputPartial = { - 'type': 'array', - 'nullable': false, - 'items': { - 'type': 'object', - 'nullable': false, - 'properties': { - 'country': { - 'type': 'string', - 'description': 'A country name', - 'nullable': false - }, - 'population': { - 'type': 'integer', - 'nullable': false - }, - 'coordinates': { - 'type': 'object', - 'nullable': false, - 'properties': { - 'latitude': { - 'type': 'number', - 'format': 'float', - 'nullable': false - }, - 'longitude': { - 'type': 'number', - 'format': 'double', - 'nullable': false - } - }, - 'required': ['latitude', 'longitude'] - }, - 'hemisphere': { - 'type': 'object', - 'nullable': false, - 'properties': { - 'latitudinal': { - 'type': 'string', - 'nullable': false, - 'enum': ['N', 'S'] - }, - 'longitudinal': { - 'type': 'string', - 'nullable': false, - 'enum': ['E', 'W'] - } - }, - 'required': ['latitudinal', 'longitudinal'] - }, - 'isCapital': { - 'type': 'boolean', - 'nullable': false - } - }, - 'required': [ - 'country', - 'population', - 'coordinates', - 'hemisphere', - 'isCapital' - ] - } -}; + it('builds schema with minimum and maximum for number', () => { + const schema = Schema.number({ + minimum: 1.5, + maximum: 9.9, + title: 'Measurement' + }); + expect(schema.toJSON()).to.eql({ + type: 'number', + nullable: false, + minimum: 1.5, + maximum: 9.9, + title: 'Measurement' + }); + }); -const layeredSchemaOutput = { - 'type': 'array', - 'nullable': false, - 'items': { - 'type': 'object', - 'description': 'A country profile', - 'nullable': false, - 'required': [ - 'country', - 'population', - 'coordinates', - 'hemisphere', - 'isCapital', - 'elevation' - ], - 'properties': { - 'country': { - 'type': 'string', - 'description': 'Country name', - 'nullable': false - }, - 'population': { - 'type': 'integer', - 'format': 'int64', - 'description': 'Number of people in country', - 'nullable': false - }, - 'coordinates': { - 'type': 'object', - 'description': 'Latitude and longitude', - 'nullable': false, - 'required': ['latitude', 'longitude'], - 'properties': { - 'latitude': { - 'type': 'number', - 'format': 'float', - 'description': 'Latitude of capital', - 'nullable': false - }, - 'longitude': { - 'type': 'number', - 'format': 'double', - 'description': 'Longitude of capital', - 'nullable': false - } - } - }, - 'hemisphere': { - 'type': 'object', - 'description': 'Hemisphere(s) country is in', - 'nullable': false, - 'required': ['latitudinal', 'longitudinal'], - 'properties': { - 'latitudinal': { - 'type': 'string', - 'nullable': false, - 'enum': ['N', 'S'] - }, - 'longitudinal': { - 'type': 'string', - 'nullable': false, - 'enum': ['E', 'W'] - } - } + it('builds object schema with propertyOrdering', () => { + const schema = Schema.object({ + title: 'User Data', + properties: { + name: Schema.string(), + age: Schema.integer(), + email: Schema.string() }, - 'isCapital': { - 'type': 'boolean', - 'description': "This doesn't make a lot of sense but it's a demo", - 'nullable': false + propertyOrdering: ['name', 'email', 'age'] + }); + expect(schema.toJSON()).to.eql({ + type: 'object', + nullable: false, + title: 'User Data', + properties: { + name: { type: 'string', nullable: false }, + age: { type: 'integer', nullable: false }, + email: { type: 'string', nullable: false } }, - 'elevation': { - 'type': 'integer', - 'format': 'float', - 'description': 'Average elevation', - 'nullable': false - } - } - } -}; + required: ['name', 'age', 'email'], + propertyOrdering: ['name', 'email', 'age'] + }); + }); +}); diff --git a/packages/ai/src/types/schema.ts b/packages/ai/src/types/schema.ts index e9fe9286b61..9cfdfad654b 100644 --- a/packages/ai/src/types/schema.ts +++ b/packages/ai/src/types/schema.ts @@ -49,18 +49,30 @@ export interface SchemaShared { format?: string; /** Optional. The description of the property. */ description?: string; + /** + * The title of the property. This helps document the schema's purpose but does not typically + * constrain the generated value. It can subtly guide the model by clarifying the intent of a + * field. + */ + title?: string; /** Optional. The items of the property. */ items?: T; /** Optional. Map of `Schema` objects. */ properties?: { [k: string]: T; }; + /** A hint suggesting the order in which the keys should appear in the generated JSON string. */ + propertyOrdering?: string[]; /** Optional. The enum of the property. */ enum?: string[]; /** Optional. The example of the property. */ example?: unknown; /** Optional. Whether the property is nullable. */ nullable?: boolean; + /** The minimum value of a numeric type. */ + minimum?: number; + /** The maximum value of a numeric type. */ + maximum?: number; [key: string]: unknown; } From 1933324e0f3e4c8ed4d4d784f0c701fd0ec6ebc3 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Wed, 28 May 2025 08:29:22 -0500 Subject: [PATCH 55/77] feat(ai): Add support for `minItems` and `maxItems` to `Schema` (#9026) --- .changeset/angry-scissors-sit.md | 6 ++ common/api-review/ai.api.md | 5 ++ docs-devsite/ai.schema.md | 33 +++++++ docs-devsite/ai.schemashared.md | 22 +++++ .../ai/src/requests/schema-builder.test.ts | 87 +++++++++++++++++++ packages/ai/src/requests/schema-builder.ts | 6 ++ packages/ai/src/types/schema.ts | 4 + 7 files changed, 163 insertions(+) create mode 100644 .changeset/angry-scissors-sit.md diff --git a/.changeset/angry-scissors-sit.md b/.changeset/angry-scissors-sit.md new file mode 100644 index 00000000000..8aeb7dcaf52 --- /dev/null +++ b/.changeset/angry-scissors-sit.md @@ -0,0 +1,6 @@ +--- +'firebase': minor +'@firebase/ai': minor +--- + +Add support for `minItems` and `maxItems` to `Schema`. diff --git a/common/api-review/ai.api.md b/common/api-review/ai.api.md index 523b51d7533..23c13688fb1 100644 --- a/common/api-review/ai.api.md +++ b/common/api-review/ai.api.md @@ -791,6 +791,9 @@ export abstract class Schema implements SchemaInterface { format?: string; // (undocumented) static integer(integerParams?: SchemaParams): IntegerSchema; + items?: SchemaInterface; + maxItems?: number; + minItems?: number; nullable: boolean; // (undocumented) static number(numberParams?: SchemaParams): NumberSchema; @@ -833,7 +836,9 @@ export interface SchemaShared { format?: string; items?: T; maximum?: number; + maxItems?: number; minimum?: number; + minItems?: number; nullable?: boolean; properties?: { [k: string]: T; diff --git a/docs-devsite/ai.schema.md b/docs-devsite/ai.schema.md index b0681b0cdf3..fa1225c91e5 100644 --- a/docs-devsite/ai.schema.md +++ b/docs-devsite/ai.schema.md @@ -32,6 +32,9 @@ export declare abstract class Schema implements SchemaInterface | [description](./ai.schema.md#schemadescription) | | string | Optional. The description of the property. | | [example](./ai.schema.md#schemaexample) | | unknown | Optional. The example of the property. | | [format](./ai.schema.md#schemaformat) | | string | Optional. The format of the property. Supported formats:
  • for NUMBER type: "float", "double"
  • for INTEGER type: "int32", "int64"
  • for STRING type: "email", "byte", etc
| +| [items](./ai.schema.md#schemaitems) | | [SchemaInterface](./ai.schemainterface.md#schemainterface_interface) | Optional. The items of the property. | +| [maxItems](./ai.schema.md#schemamaxitems) | | number | The maximum number of items (elements) in a schema of type [SchemaType.ARRAY](./ai.md#schematypearray_enummember). | +| [minItems](./ai.schema.md#schemaminitems) | | number | The minimum number of items (elements) in a schema of type [SchemaType.ARRAY](./ai.md#schematypearray_enummember). | | [nullable](./ai.schema.md#schemanullable) | | boolean | Optional. Whether the property is nullable. Defaults to false. | | [type](./ai.schema.md#schematype) | | [SchemaType](./ai.md#schematype) | Optional. The type of the property. [SchemaType](./ai.md#schematype). | @@ -93,6 +96,36 @@ Optional. The format of the property. Supported formats:
  • for NUMBE format?: string; ``` +## Schema.items + +Optional. The items of the property. + +Signature: + +```typescript +items?: SchemaInterface; +``` + +## Schema.maxItems + +The maximum number of items (elements) in a schema of type [SchemaType.ARRAY](./ai.md#schematypearray_enummember). + +Signature: + +```typescript +maxItems?: number; +``` + +## Schema.minItems + +The minimum number of items (elements) in a schema of type [SchemaType.ARRAY](./ai.md#schematypearray_enummember). + +Signature: + +```typescript +minItems?: number; +``` + ## Schema.nullable Optional. Whether the property is nullable. Defaults to false. diff --git a/docs-devsite/ai.schemashared.md b/docs-devsite/ai.schemashared.md index 7f0ed27026c..fb75fc50841 100644 --- a/docs-devsite/ai.schemashared.md +++ b/docs-devsite/ai.schemashared.md @@ -28,7 +28,9 @@ export interface SchemaShared | [format](./ai.schemashared.md#schemasharedformat) | string | Optional. The format of the property. When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this must be either 'enum' or 'date-time', otherwise requests will fail. | | [items](./ai.schemashared.md#schemashareditems) | T | Optional. The items of the property. | | [maximum](./ai.schemashared.md#schemasharedmaximum) | number | The maximum value of a numeric type. | +| [maxItems](./ai.schemashared.md#schemasharedmaxitems) | number | The maximum number of items (elements) in a schema of type [SchemaType.ARRAY](./ai.md#schematypearray_enummember). | | [minimum](./ai.schemashared.md#schemasharedminimum) | number | The minimum value of a numeric type. | +| [minItems](./ai.schemashared.md#schemasharedminitems) | number | The minimum number of items (elements) in a schema of type [SchemaType.ARRAY](./ai.md#schematypearray_enummember). | | [nullable](./ai.schemashared.md#schemasharednullable) | boolean | Optional. Whether the property is nullable. | | [properties](./ai.schemashared.md#schemasharedproperties) | { \[k: string\]: T; } | Optional. Map of Schema objects. | | [propertyOrdering](./ai.schemashared.md#schemasharedpropertyordering) | string\[\] | A hint suggesting the order in which the keys should appear in the generated JSON string. | @@ -94,6 +96,16 @@ The maximum value of a numeric type. maximum?: number; ``` +## SchemaShared.maxItems + +The maximum number of items (elements) in a schema of type [SchemaType.ARRAY](./ai.md#schematypearray_enummember). + +Signature: + +```typescript +maxItems?: number; +``` + ## SchemaShared.minimum The minimum value of a numeric type. @@ -104,6 +116,16 @@ The minimum value of a numeric type. minimum?: number; ``` +## SchemaShared.minItems + +The minimum number of items (elements) in a schema of type [SchemaType.ARRAY](./ai.md#schematypearray_enummember). + +Signature: + +```typescript +minItems?: number; +``` + ## SchemaShared.nullable Optional. Whether the property is nullable. diff --git a/packages/ai/src/requests/schema-builder.test.ts b/packages/ai/src/requests/schema-builder.test.ts index ddf655b738d..27de1076c5f 100644 --- a/packages/ai/src/requests/schema-builder.test.ts +++ b/packages/ai/src/requests/schema-builder.test.ts @@ -103,6 +103,93 @@ describe('Schema builder', () => { title: 'Direction' }); }); + describe('Schema.array', () => { + it('builds an array schema with basic items', () => { + const schema = Schema.array({ + items: Schema.string() + }); + expect(schema.toJSON()).to.eql({ + type: 'array', + nullable: false, + items: { + type: 'string', + nullable: false + } + }); + }); + + it('builds an array schema with items and minItems', () => { + const schema = Schema.array({ + items: Schema.number(), + minItems: 1 + }); + expect(schema.toJSON()).to.eql({ + type: 'array', + nullable: false, + items: { + type: 'number', + nullable: false + }, + minItems: 1 + }); + }); + + it('builds an array schema with items and maxItems', () => { + const schema = Schema.array({ + items: Schema.boolean(), + maxItems: 10 + }); + expect(schema.toJSON()).to.eql({ + type: 'array', + nullable: false, + items: { + type: 'boolean', + nullable: false + }, + maxItems: 10 + }); + }); + + it('builds an array schema with items, minItems, and maxItems', () => { + const schema = Schema.array({ + items: Schema.integer(), + minItems: 0, + maxItems: 5 + }); + expect(schema.toJSON()).to.eql({ + type: 'array', + nullable: false, + items: { + type: 'integer', + nullable: false + }, + minItems: 0, + maxItems: 5 + }); + }); + + it('builds an array schema with items, minItems, maxItems, and other options', () => { + const schema = Schema.array({ + items: Schema.string({ description: 'A list of names' }), + minItems: 1, + maxItems: 3, + nullable: true, + description: 'An array of strings' + }); + expect(schema.toJSON()).to.eql({ + type: 'array', + nullable: true, + description: 'An array of strings', + items: { + type: 'string', + description: 'A list of names', + nullable: false + }, + minItems: 1, + maxItems: 3 + }); + }); + }); it('builds an object schema', () => { const schema = Schema.object({ properties: { diff --git a/packages/ai/src/requests/schema-builder.ts b/packages/ai/src/requests/schema-builder.ts index 524cfdb1c20..7d9ece462b3 100644 --- a/packages/ai/src/requests/schema-builder.ts +++ b/packages/ai/src/requests/schema-builder.ts @@ -49,6 +49,12 @@ export abstract class Schema implements SchemaInterface { format?: string; /** Optional. The description of the property. */ description?: string; + /** Optional. The items of the property. */ + items?: SchemaInterface; + /** The minimum number of items (elements) in a schema of type {@link SchemaType.ARRAY}. */ + minItems?: number; + /** The maximum number of items (elements) in a schema of type {@link SchemaType.ARRAY}. */ + maxItems?: number; /** Optional. Whether the property is nullable. Defaults to false. */ nullable: boolean; /** Optional. The example of the property. */ diff --git a/packages/ai/src/types/schema.ts b/packages/ai/src/types/schema.ts index 9cfdfad654b..3a6c0c7301b 100644 --- a/packages/ai/src/types/schema.ts +++ b/packages/ai/src/types/schema.ts @@ -57,6 +57,10 @@ export interface SchemaShared { title?: string; /** Optional. The items of the property. */ items?: T; + /** The minimum number of items (elements) in a schema of type {@link SchemaType.ARRAY}. */ + minItems?: number; + /** The maximum number of items (elements) in a schema of type {@link SchemaType.ARRAY}. */ + maxItems?: number; /** Optional. Map of `Schema` objects. */ properties?: { [k: string]: T; From af9f8b552d313cff3537dcfbbc3e33bbdca78311 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Fri, 30 May 2025 13:00:02 -0700 Subject: [PATCH 56/77] Build out e2e subdirectories (#9065) --- .github/workflows/e2e-test.yml | 8 +- e2e/README.md | 95 +---- e2e/{ => smoke-tests}/.gitignore | 0 e2e/smoke-tests/README.md | 92 +++++ e2e/{ => smoke-tests}/babel.config.js | 0 e2e/{ => smoke-tests}/build/index.html | 0 .../fix-jsdom-environment.ts | 0 e2e/{ => smoke-tests}/jest.config.ts | 0 e2e/{ => smoke-tests}/package.json | 0 e2e/{ => smoke-tests}/sample-apps/compat.js | 2 +- e2e/{ => smoke-tests}/sample-apps/modular.js | 2 +- e2e/{ => smoke-tests}/tests/compat.test.ts | 0 e2e/{ => smoke-tests}/tests/modular.test.ts | 0 e2e/{ => smoke-tests}/webpack.config.js | 0 e2e/{ => smoke-tests}/yarn.lock | 367 ++++++++---------- e2e/template/README.md | 3 + e2e/template/index.js | 18 + e2e/template/package.json | 15 + e2e/template/yarn.lock | 8 + 19 files changed, 322 insertions(+), 288 deletions(-) rename e2e/{ => smoke-tests}/.gitignore (100%) create mode 100644 e2e/smoke-tests/README.md rename e2e/{ => smoke-tests}/babel.config.js (100%) rename e2e/{ => smoke-tests}/build/index.html (100%) rename e2e/{ => smoke-tests}/fix-jsdom-environment.ts (100%) rename e2e/{ => smoke-tests}/jest.config.ts (100%) rename e2e/{ => smoke-tests}/package.json (100%) rename e2e/{ => smoke-tests}/sample-apps/compat.js (99%) rename e2e/{ => smoke-tests}/sample-apps/modular.js (99%) rename e2e/{ => smoke-tests}/tests/compat.test.ts (100%) rename e2e/{ => smoke-tests}/tests/modular.test.ts (100%) rename e2e/{ => smoke-tests}/webpack.config.js (100%) rename e2e/{ => smoke-tests}/yarn.lock (96%) create mode 100644 e2e/template/README.md create mode 100644 e2e/template/index.js create mode 100644 e2e/template/package.json create mode 100644 e2e/template/yarn.lock diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 0857860571f..fbf43beada1 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -30,8 +30,8 @@ jobs: defaults: run: - # Run any command steps in the /e2e subdir - working-directory: './e2e' + # Run any command steps in the /e2e/smoke-tests subdir + working-directory: './e2e/smoke-tests' steps: - name: Checkout Repo @@ -53,7 +53,9 @@ jobs: - name: Poll npm until version to test is available for install run: | echo "Polling npm for firebase@${{ github.event.client_payload.versionOrTag }}" - node ../scripts/release/poll-npm-publish.js + node ./scripts/release/poll-npm-publish.js + # run in root + working-directory: '.' env: VERSION: ${{ github.event.client_payload.versionOrTag }} - name: Yarn install diff --git a/e2e/README.md b/e2e/README.md index b2674506e8b..25223edb371 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -1,92 +1,11 @@ -# Firebase JS SDK E2E Tests +# E2E Tests -This directory contains end-to-end tests for the Firebase JS SDK package as well as minimal quick start sample apps for debugging and development. +This directory is for Firebase E2E tests that are completely independent of the main SDK workspaces. Packages in here should: -## E2E Tests +* Have a start trigger independent of the main CI PR/push triggers (e.g., manual trigger, repository_dispatch, from an external runner like Kokoro, etc.) + + A common use case might be to clone this repo, cd into the chosen directory under e2e, npm install, and run npm scripts. -### Setup +* Have a self-contained set of NPM dependencies. They should not depend on the local version of Firebase in the SDK nor assume inherited availability of any packages in the top level package.json of this repo (typescript, firebase, karma, etc.). -Before running the tests, you will need: - -- a project config -- a test user with an email/password login which has read/write privileges for Storage, Realtime Database, and Firestore -- an App Check debug token -- to deploy the `callTest` Cloud Function - -#### Project Config and Test User - -Create a file named `firebase-config.js` in the top level of this `e2e/` directory. The contents of the file should be: - -```javascript -// A config for a project -export const config = { - apiKey: ************, - authDomain: ************, - databaseURL: ************, - projectId: ************, - storageBucket: ************, - messagingSenderId: ************, - appId: ************, - measurementId: ************ -}; - * -// A user account with read/write privileges in that project -// for storage, database, firestore -export const testAccount = { - email: ************, - password: ************ -} -``` - -#### App Check Debug Token - -Create an App Check debug token in the Firebase Console. Assign it to an environment variable in your shell named `APP_CHECK_DEBUG_TOKEN`. - -#### Deploy `callTest` Cloud Function - -From the top level of the firebase repo, ensure you have the Firebase CLI (`firebase-tools`) installed (if not, `npm install -g firebase-tools`). - -Ensure you are logged in using the CLI (`firebase login`); - -Then deploy the function with: -`firebase deploy --only functions:callTest --project YOUR_PROJECT_ID` - -### Running the Tests - -To run the tests on the default modular API: - -``` -yarn test:modular -``` - -To run the tests on the compat API: - -``` -yarn test:compat -``` - -## Sample Apps - -Two minimal sample apps are provided for quick debugging and development. These apps import and initialize every product in the SDK and call some basic methods. Products can easily be commented out to focus on one or more products you are interested in looking at. - -### Setup - -The setup is the same as for the E2E tests above. Certain tests can be skipped if you are commenting out that product (e.g, no need to deploy the Cloud Function if you are commenting out the `callFunctions()` line in the sample app, and no need to set the App Check debug token env variable if not using App Check). - -### Running Sample Apps - -To run the modular sample app (uses current default version of the API): - -``` -yarn start:modular -``` - -Then open `localhost:8080` in a browser. - -To run the compat sample app (uses current compat version of the API): - -``` -yarn start:compat -``` - -Then open `localhost:8080` in a browser. \ No newline at end of file +See the `template/` directory for an example. \ No newline at end of file diff --git a/e2e/.gitignore b/e2e/smoke-tests/.gitignore similarity index 100% rename from e2e/.gitignore rename to e2e/smoke-tests/.gitignore diff --git a/e2e/smoke-tests/README.md b/e2e/smoke-tests/README.md new file mode 100644 index 00000000000..eb72486e95e --- /dev/null +++ b/e2e/smoke-tests/README.md @@ -0,0 +1,92 @@ +# Firebase JS SDK E2E Tests + +This directory contains end-to-end tests for the Firebase JS SDK package as well as minimal quick start sample apps for debugging and development. + +## E2E Tests + +### Setup + +Before running the tests, you will need: + +- a project config +- a test user with an email/password login which has read/write privileges for Storage, Realtime Database, and Firestore +- an App Check debug token +- to deploy the `callTest` Cloud Function + +#### Project Config and Test User + +Create a file named `firebase-config.js` in the top level of this `e2e/smoke-tests` directory. The contents of the file should be: + +```javascript +// A config for a project +export const config = { + apiKey: ************, + authDomain: ************, + databaseURL: ************, + projectId: ************, + storageBucket: ************, + messagingSenderId: ************, + appId: ************, + measurementId: ************ +}; + * +// A user account with read/write privileges in that project +// for storage, database, firestore +export const testAccount = { + email: ************, + password: ************ +} +``` + +#### App Check Debug Token + +Create an App Check debug token in the Firebase Console. Assign it to an environment variable in your shell named `APP_CHECK_DEBUG_TOKEN`. + +#### Deploy `callTest` Cloud Function + +From the top level of the firebase repo, ensure you have the Firebase CLI (`firebase-tools`) installed (if not, `npm install -g firebase-tools`). + +Ensure you are logged in using the CLI (`firebase login`); + +Then deploy the function with: +`firebase deploy --only functions:callTest --project YOUR_PROJECT_ID` + +### Running the Tests + +To run the tests on the default modular API: + +``` +yarn test:modular +``` + +To run the tests on the compat API: + +``` +yarn test:compat +``` + +## Sample Apps + +Two minimal sample apps are provided for quick debugging and development. These apps import and initialize every product in the SDK and call some basic methods. Products can easily be commented out to focus on one or more products you are interested in looking at. + +### Setup + +The setup is the same as for the E2E tests above. Certain tests can be skipped if you are commenting out that product (e.g, no need to deploy the Cloud Function if you are commenting out the `callFunctions()` line in the sample app, and no need to set the App Check debug token env variable if not using App Check). + +### Running Sample Apps + +To run the modular sample app (uses current default version of the API): + +``` +yarn start:modular +``` + +Then open `localhost:8080` in a browser. + +To run the compat sample app (uses current compat version of the API): + +``` +yarn start:compat +``` + +Then open `localhost:8080` in a browser. \ No newline at end of file diff --git a/e2e/babel.config.js b/e2e/smoke-tests/babel.config.js similarity index 100% rename from e2e/babel.config.js rename to e2e/smoke-tests/babel.config.js diff --git a/e2e/build/index.html b/e2e/smoke-tests/build/index.html similarity index 100% rename from e2e/build/index.html rename to e2e/smoke-tests/build/index.html diff --git a/e2e/fix-jsdom-environment.ts b/e2e/smoke-tests/fix-jsdom-environment.ts similarity index 100% rename from e2e/fix-jsdom-environment.ts rename to e2e/smoke-tests/fix-jsdom-environment.ts diff --git a/e2e/jest.config.ts b/e2e/smoke-tests/jest.config.ts similarity index 100% rename from e2e/jest.config.ts rename to e2e/smoke-tests/jest.config.ts diff --git a/e2e/package.json b/e2e/smoke-tests/package.json similarity index 100% rename from e2e/package.json rename to e2e/smoke-tests/package.json diff --git a/e2e/sample-apps/compat.js b/e2e/smoke-tests/sample-apps/compat.js similarity index 99% rename from e2e/sample-apps/compat.js rename to e2e/smoke-tests/sample-apps/compat.js index 478b960d8e0..e77b7d087d0 100644 --- a/e2e/sample-apps/compat.js +++ b/e2e/smoke-tests/sample-apps/compat.js @@ -82,7 +82,7 @@ async function authLogout() { * * Call a deployed function. * This cloud function must be deployed in this project first. See - * e2e/README.md for more info. + * e2e/smoke-test/README.md for more info. */ async function callFunctions() { console.log('[FUNCTIONS] start'); diff --git a/e2e/sample-apps/modular.js b/e2e/smoke-tests/sample-apps/modular.js similarity index 99% rename from e2e/sample-apps/modular.js rename to e2e/smoke-tests/sample-apps/modular.js index d01b5b0139d..2d66b752081 100644 --- a/e2e/sample-apps/modular.js +++ b/e2e/smoke-tests/sample-apps/modular.js @@ -121,7 +121,7 @@ async function authLogout(app) { * * Call a deployed function. * This cloud function must be deployed in this project first. See - * e2e/README.md for more info. + * e2e/smoke-tests/README.md for more info. */ async function callFunctions(app) { console.log('[FUNCTIONS] start'); diff --git a/e2e/tests/compat.test.ts b/e2e/smoke-tests/tests/compat.test.ts similarity index 100% rename from e2e/tests/compat.test.ts rename to e2e/smoke-tests/tests/compat.test.ts diff --git a/e2e/tests/modular.test.ts b/e2e/smoke-tests/tests/modular.test.ts similarity index 100% rename from e2e/tests/modular.test.ts rename to e2e/smoke-tests/tests/modular.test.ts diff --git a/e2e/webpack.config.js b/e2e/smoke-tests/webpack.config.js similarity index 100% rename from e2e/webpack.config.js rename to e2e/smoke-tests/webpack.config.js diff --git a/e2e/yarn.lock b/e2e/smoke-tests/yarn.lock similarity index 96% rename from e2e/yarn.lock rename to e2e/smoke-tests/yarn.lock index c20459aecdd..b9a6d8e4451 100644 --- a/e2e/yarn.lock +++ b/e2e/smoke-tests/yarn.lock @@ -19,16 +19,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/code-frame@^7.25.9": - version "7.26.0" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz" - integrity sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g== - dependencies: - "@babel/helper-validator-identifier" "^7.25.9" - js-tokens "^4.0.0" - picocolors "^1.0.0" - -"@babel/code-frame@^7.26.0": +"@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0": version "7.26.0" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz" integrity sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g== @@ -47,7 +38,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz" integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== -"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.8.0", "@babel/core@7.26.8": +"@babel/core@7.26.8": version "7.26.8" resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.8.tgz" integrity sha512-l+lkXCHS6tQEc5oUpK28xBOZ6+HwaH7YwoYQbLFiYb4nS2/l1tKnZEtEWkD0GuiYdvArf9qBS0XlQGXzPMsNqQ== @@ -69,49 +60,7 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/core@^7.11.6": - version "7.26.0" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz" - integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.26.0" - "@babel/generator" "^7.26.0" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-module-transforms" "^7.26.0" - "@babel/helpers" "^7.26.0" - "@babel/parser" "^7.26.0" - "@babel/template" "^7.25.9" - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.26.0" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/core@^7.12.3": - version "7.26.0" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz" - integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.26.0" - "@babel/generator" "^7.26.0" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-module-transforms" "^7.26.0" - "@babel/helpers" "^7.26.0" - "@babel/parser" "^7.26.0" - "@babel/template" "^7.25.9" - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.26.0" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/core@^7.23.9": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": version "7.26.0" resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz" integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== @@ -354,34 +303,20 @@ "@babel/template" "^7.26.9" "@babel/types" "^7.26.9" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.26.3": version "7.26.3" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz" integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== dependencies: "@babel/types" "^7.26.3" -"@babel/parser@^7.25.9": - version "7.26.0" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.0.tgz" - integrity sha512-aP8x5pIw3xvYr/sXT+SEUwyhrXT8rUJRZltK/qN3Db80dcKpTett8cJxHyjk+xYSVXvNnl2SfcJVjbwxpOSscA== - dependencies: - "@babel/types" "^7.26.0" - -"@babel/parser@^7.26.0": +"@babel/parser@^7.25.9", "@babel/parser@^7.26.0": version "7.26.0" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.0.tgz" integrity sha512-aP8x5pIw3xvYr/sXT+SEUwyhrXT8rUJRZltK/qN3Db80dcKpTett8cJxHyjk+xYSVXvNnl2SfcJVjbwxpOSscA== dependencies: "@babel/types" "^7.26.0" -"@babel/parser@^7.26.3": - version "7.26.3" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz" - integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== - dependencies: - "@babel/types" "^7.26.3" - "@babel/parser@^7.26.8", "@babel/parser@^7.26.9": version "7.26.9" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz" @@ -1120,15 +1055,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0": - version "7.26.3" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz" - integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== - dependencies: - "@babel/helper-string-parser" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - -"@babel/types@^7.20.7": +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.26.3", "@babel/types@^7.3.3": version "7.26.3" resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz" integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== @@ -1144,14 +1071,6 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" -"@babel/types@^7.26.3", "@babel/types@^7.3.3": - version "7.26.3" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz" - integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== - dependencies: - "@babel/helper-string-parser" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - "@babel/types@^7.26.8", "@babel/types@^7.26.9": version "7.26.9" resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz" @@ -1190,6 +1109,8 @@ "@firebase/analytics-compat@0.2.20-20250512211235": version "0.2.20-20250512211235" + resolved "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.20-20250512211235.tgz#725ebf8e94ad9aba4ed22f79206c1a2420190a82" + integrity sha512-2MmqqNlHlbs6454pOnKnNbtz8cF54UYgCiFWc0tXV6VNrSAGAyWgY3Ujve5+ayuSmzMuTaLNKk8kOPC90nNmCA== dependencies: "@firebase/analytics" "0.10.14-20250512211235" "@firebase/analytics-types" "0.8.3" @@ -1204,6 +1125,8 @@ "@firebase/analytics@0.10.14-20250512211235": version "0.10.14-20250512211235" + resolved "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.14-20250512211235.tgz#0c9fb8e352e0e641168d1968e616a0c775a4392f" + integrity sha512-yPo0D9Ec5jSWRmI6JYt8PCzTVUHtTvnmhN7s09Wh2ckrbv7kgejIAYOjKOK30Umro+teA6wTsY07Q27C1dOM4A== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/installations" "0.6.15-20250512211235" @@ -1213,6 +1136,8 @@ "@firebase/app-check-compat@0.3.23-20250512211235": version "0.3.23-20250512211235" + resolved "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.23-20250512211235.tgz#f060c925f2bcd936299504a5f34bc81ea872388f" + integrity sha512-iI2mGM/f8z4aQhoB57HnErh8vCqyQJFVQS7USLy6dUyL15FcIe5HQ88N4oD9glagu4IDRlZ+Rohlrw9ObbyNxA== dependencies: "@firebase/app-check" "0.10.0-20250512211235" "@firebase/app-check-types" "0.5.3" @@ -1233,6 +1158,8 @@ "@firebase/app-check@0.10.0-20250512211235": version "0.10.0-20250512211235" + resolved "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.0-20250512211235.tgz#304ae95ed148d0837f64b94527d6b2b68966b117" + integrity sha512-zFbNyt3j6ixg6/Gh3WdA+FvMhUxIRASEq/NRC+Jtofm5la5ZJ88wdLDJ/kSHtdMINQfKuneHbJJ832u5BTqpYQ== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" @@ -1241,6 +1168,8 @@ "@firebase/app-compat@0.4.0-20250512211235": version "0.4.0-20250512211235" + resolved "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.0-20250512211235.tgz#4ae94412aa00afcf4663919af564e321ea17b335" + integrity sha512-epnyKsA97L0FMigDHWOcgJ2TeMnlpuyiGIVZdnQiElKfRs3UFgDvBb6AK5dHgT4REe7ZbXqGa/8TSUUGFhEcmw== dependencies: "@firebase/app" "0.13.0-20250512211235" "@firebase/component" "0.6.15-20250512211235" @@ -1248,13 +1177,15 @@ "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" -"@firebase/app-types@0.9.3", "@firebase/app-types@0.x": +"@firebase/app-types@0.9.3": version "0.9.3" resolved "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz" integrity sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw== -"@firebase/app@0.13.0-20250512211235", "@firebase/app@0.x": +"@firebase/app@0.13.0-20250512211235": version "0.13.0-20250512211235" + resolved "https://registry.npmjs.org/@firebase/app/-/app-0.13.0-20250512211235.tgz#b8028efcd45ac07b3304ac361008f08b0cfa445f" + integrity sha512-9qcJX9fKMFuZf5+8BxK0Dg1gJU+x4shoH+VFiKnZ7ri6sJ0OP67Y4PnHDYdzKmx7/HJt0KSyZMDUX/hT2Wbl/g== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" @@ -1264,6 +1195,8 @@ "@firebase/auth-compat@0.5.23-20250512211235": version "0.5.23-20250512211235" + resolved "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.23-20250512211235.tgz#130b8583e137bae9b7e288e8991583ff04b1ab25" + integrity sha512-dWBbbYAiNmrD6+P5m2TgQ9GWPIffjedQ9HRXnPyqKytTiEQzxvYBd91NS3y3YI6bBg1RWAWccMnwtQVjl1eK6A== dependencies: "@firebase/auth" "1.10.3-20250512211235" "@firebase/auth-types" "0.13.0" @@ -1283,6 +1216,8 @@ "@firebase/auth@1.10.3-20250512211235": version "1.10.3-20250512211235" + resolved "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.3-20250512211235.tgz#47f55c228e4eb441050914c6e545ccb1f021a712" + integrity sha512-f7Lov3vogDMyroncY7OipDmNL5bqZcDxdIVe1qN/LEaaKygG2wPxKMa+LjSFwxwo/oGp/i6R4ixT8otMkOL2MQ== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" @@ -1299,12 +1234,16 @@ "@firebase/component@0.6.15-20250512211235": version "0.6.15-20250512211235" + resolved "https://registry.npmjs.org/@firebase/component/-/component-0.6.15-20250512211235.tgz#91f4f234904480f34d26aa41922b8cf4603944c2" + integrity sha512-8U//EzcIE6frUcWUMcmrVzWBAXLBZt2eGWwt8Of1y+yU6t3Zwwu+1JpCz0hbiHpzXnkuTFjPwWA6fKgZQTk0MQ== dependencies: "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" "@firebase/data-connect@0.3.6-20250512211235": version "0.3.6-20250512211235" + resolved "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.6-20250512211235.tgz#f20ee3c0cc576f000244ad769e1813151e2aa3cd" + integrity sha512-ZMs7lX9/Sd7XNRaylht535sY1mFUasLCY0toKQWF4KIAdUeij0TNzqWpeULNqNJXtczu+eRrG3Nct1rY4JtiiQ== dependencies: "@firebase/auth-interop-types" "0.2.4" "@firebase/component" "0.6.15-20250512211235" @@ -1314,6 +1253,8 @@ "@firebase/database-compat@2.0.7-20250512211235": version "2.0.7-20250512211235" + resolved "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.7-20250512211235.tgz#0d78c287cab62583d46525c690469576787aca22" + integrity sha512-8bt/9/EXqxce32RDg254yfkEUsG9x9h3K6+IFdsCeLhPH9mQLosrcRBSHusHGX65oNbY1ATgxkt/+v+uhoj+nw== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/database" "1.0.16-20250512211235" @@ -1324,12 +1265,16 @@ "@firebase/database-types@1.0.12-20250512211235": version "1.0.12-20250512211235" + resolved "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.12-20250512211235.tgz#4b89e43e99e0fb621fc4abcec792d027c0669f44" + integrity sha512-FsZ/VeSyNLaJWQGLP0Yy955Zonlm/WSPhpdOpof84ToKAekUQobxlI08skPe+Gk2dAUnKrdqfGJPO9pagsByYA== dependencies: "@firebase/app-types" "0.9.3" "@firebase/util" "1.12.0-20250512211235" "@firebase/database@1.0.16-20250512211235": version "1.0.16-20250512211235" + resolved "https://registry.npmjs.org/@firebase/database/-/database-1.0.16-20250512211235.tgz#223f45e9010574d361cbaa53e56b51dab17a804d" + integrity sha512-l2jVH/uCzbuAhCYk37+YWFHE2nIkGyTiFL3RthkDvoT+KHDzvaIxnWmjnbAgrQEYSHaimziiw0DmpQM+Safb0g== dependencies: "@firebase/app-check-interop-types" "0.3.3" "@firebase/auth-interop-types" "0.2.4" @@ -1341,6 +1286,8 @@ "@firebase/firestore-compat@0.3.48-20250512211235": version "0.3.48-20250512211235" + resolved "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.48-20250512211235.tgz#547d62fd41bc15495c26f9675d9ed24ff3c3f50c" + integrity sha512-8nWD3QRkN9rUoApULVfBMg112QNYstUWRm58cMwcdp0Wkx6+RV+9YraDYzg7GmsEklL4CnGF36HGNrtzraxOTg== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/firestore" "4.7.13-20250512211235" @@ -1355,6 +1302,8 @@ "@firebase/firestore@4.7.13-20250512211235": version "4.7.13-20250512211235" + resolved "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.13-20250512211235.tgz#cdebb6b7ddba2b7738896458327ae064bb6b540e" + integrity sha512-m3xEnbYrsgp58a5if00Vqrrglm/22yU7amZ3CjwqUD+/M6TFSf1a9lvMqF9S33AuPbwHDxUb3hJ4ogpTQ+uSjQ== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" @@ -1366,6 +1315,8 @@ "@firebase/functions-compat@0.3.22-20250512211235": version "0.3.22-20250512211235" + resolved "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.22-20250512211235.tgz#2ce718de863e7a26a9ec2f7e19ab6a15cb375306" + integrity sha512-/CrVsUlcVKoCtuzpYzIfBYjdiCBtpMkDIY3M2/MIKv5gwB90ydX4E2OP/E57Ne85eiYfezYoGurt27OXSno/Fg== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/functions" "0.12.5-20250512211235" @@ -1380,6 +1331,8 @@ "@firebase/functions@0.12.5-20250512211235": version "0.12.5-20250512211235" + resolved "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.5-20250512211235.tgz#17696d8918136e319459628a43ea45e08e9bfa39" + integrity sha512-3BTjMHjx4ulr22+G1/ut8ZQW6OTGmpS5LC+2MhTl/W8OgSYP+3Hc844OhhlDq4kBjcWO752PG2+I1JwJNK6rPA== dependencies: "@firebase/app-check-interop-types" "0.3.3" "@firebase/auth-interop-types" "0.2.4" @@ -1390,6 +1343,8 @@ "@firebase/installations-compat@0.2.15-20250512211235": version "0.2.15-20250512211235" + resolved "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.15-20250512211235.tgz#5c91a48c29c358164fe34f254f1509b7404373ca" + integrity sha512-i7sV28ZLE8p65lHyy/vni93aiIKIyo7PuOUNyaMnfdam++Sv2mBkNj0koo9B6LidDaWxZ3gZJ2bTxSyPOOukYg== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/installations" "0.6.15-20250512211235" @@ -1404,6 +1359,8 @@ "@firebase/installations@0.6.15-20250512211235": version "0.6.15-20250512211235" + resolved "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.15-20250512211235.tgz#c0b5b07feda48a92b72e7d919a2a2f524a632b35" + integrity sha512-RinZjbNpImsz0JGi6u6B6Yb7ezrE6ej2pdBT7K3ju1cScpBmKRBeWSl7AevD5PEH+W9E5i6U2VX+q2gycI3ZRA== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/util" "1.12.0-20250512211235" @@ -1419,6 +1376,8 @@ "@firebase/messaging-compat@0.2.19-20250512211235": version "0.2.19-20250512211235" + resolved "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.19-20250512211235.tgz#1980cc4c931085742e568596d7b28af0853aa020" + integrity sha512-vTL0hMyPosU/WRvZ1xmi01aURJyjUI3zeBByYnnwSUJN01eF0YX7eM1RId/HSFcHoT/HgfsHFvINUFjy58TVUQ== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/messaging" "0.12.19-20250512211235" @@ -1432,6 +1391,8 @@ "@firebase/messaging@0.12.19-20250512211235": version "0.12.19-20250512211235" + resolved "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.19-20250512211235.tgz#f225f480879c8a550d2927d314ae62e17f30355f" + integrity sha512-h4fraiJuJrLSOSI6WIFlbV1u6L6/8zPfTzcNxqyjCIVj5eFVpgoLIW5lQ2O+G6h6RQiWG29AJkuWGnp26BOJ6Q== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/installations" "0.6.15-20250512211235" @@ -1442,6 +1403,8 @@ "@firebase/performance-compat@0.2.17-20250512211235": version "0.2.17-20250512211235" + resolved "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.17-20250512211235.tgz#ead4a1201effd57aef5a8e1a4353196d6bde9b41" + integrity sha512-5uUlGWbEyoTzGa0JaS6OIUl5T69VwK4gYRAjcET79kuVSZU85l4M1gsDbXqhl/YH1o/gpQEPmm1T1UrK9bRQbw== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" @@ -1457,6 +1420,8 @@ "@firebase/performance@0.7.4-20250512211235": version "0.7.4-20250512211235" + resolved "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.4-20250512211235.tgz#fadf55c6e8072320e8660e1b8dd97e3d3561650f" + integrity sha512-defCXX20kxX05/3b4qI+V2PreeH/qwn1Egvp82geS+hGyK6cDRFGgV7q94tIcc6idQJoRhsSW32ABKdI99XpWg== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/installations" "0.6.15-20250512211235" @@ -1467,6 +1432,8 @@ "@firebase/remote-config-compat@0.2.15-20250512211235": version "0.2.15-20250512211235" + resolved "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.15-20250512211235.tgz#cb391f23f46aea88e3df0a7f1edb15d24000e965" + integrity sha512-DiHaFVywKBG1GGziN/NA5k8qkvJ68gX+AFe63yQOmzAfvH59W+dbxAgSG116pGrfF3xDH/e/lho4Cs3M6tbL8g== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/logger" "0.4.4" @@ -1482,6 +1449,8 @@ "@firebase/remote-config@0.6.2-20250512211235": version "0.6.2-20250512211235" + resolved "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.2-20250512211235.tgz#c19a7057fdb4493efc70c065cebd85b0841744ee" + integrity sha512-FR/IDz/AkQiSXXZw80OJEl/MT++URfbrrUDev3p1Dgc5yuz2hGCReuiq/YYVTA3n/yppwY+BnRdVidV9dRaHSQ== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/installations" "0.6.15-20250512211235" @@ -1491,6 +1460,8 @@ "@firebase/storage-compat@0.3.19-20250512211235": version "0.3.19-20250512211235" + resolved "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.19-20250512211235.tgz#d81a8cd4fb7c5222d11e8c4f67f83f48c8d064f6" + integrity sha512-Qz3kRo4qc6wRSDq/sCRT9ITqT/Of4puErdhideN+GBi40m42bLWOqBkxpkzPmHphOmFNwKUBA8/9Cksr/fj6IA== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/storage" "0.13.9-20250512211235" @@ -1505,12 +1476,14 @@ "@firebase/storage@0.13.9-20250512211235": version "0.13.9-20250512211235" + resolved "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.9-20250512211235.tgz#1d809b25797bd118ee9b83ac220d8a6ae0eafed8" + integrity sha512-68vuBOmYVUu93Tbri++ubk8ECASRQWYnB1cvPpfBT0WoLSgdgxj+2F/+buXqzFZT/3ae8FBcEdgK6cD0gcT7tA== dependencies: "@firebase/component" "0.6.15-20250512211235" "@firebase/util" "1.12.0-20250512211235" tslib "^2.1.0" -"@firebase/util@1.11.1", "@firebase/util@1.x": +"@firebase/util@1.11.1": version "1.11.1" resolved "https://registry.npmjs.org/@firebase/util/-/util-1.11.1.tgz" integrity sha512-RXg4WE8C2LUrvoV/TMGRTu223zZf9Dq9MR8yHZio9nF9TpLnpCPURw9VWWB2WATDl6HfIdWfl2x2SJYtHkN4hw== @@ -1787,14 +1760,6 @@ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": - version "0.3.25" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" - integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" @@ -1803,6 +1768,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@jsonjoy.com/base64@^1.1.1": version "1.1.2" resolved "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz" @@ -1990,18 +1963,24 @@ "@types/eslint-scope@^3.7.7": version "3.7.7" + resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== dependencies: "@types/eslint" "*" "@types/estree" "*" "@types/eslint@*": version "9.6.1" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" + integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== dependencies: "@types/estree" "*" "@types/json-schema" "*" "@types/estree@*", "@types/estree@^1.0.6": version "1.0.7" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8" + integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== "@types/express-serve-static-core@*", "@types/express-serve-static-core@^5.0.0": version "5.0.1" @@ -2033,7 +2012,7 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/express@^4.17.13", "@types/express@^4.17.21": +"@types/express@^4.17.21": version "4.17.21" resolved "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz" integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== @@ -2202,7 +2181,7 @@ dependencies: "@types/yargs-parser" "*" -"@webassemblyjs/ast@^1.14.1", "@webassemblyjs/ast@1.14.1": +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": version "1.14.1" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz" integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== @@ -2270,6 +2249,8 @@ "@webassemblyjs/wasm-edit@^1.14.1": version "1.14.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== dependencies: "@webassemblyjs/ast" "1.14.1" "@webassemblyjs/helper-buffer" "1.14.1" @@ -2301,7 +2282,7 @@ "@webassemblyjs/wasm-gen" "1.14.1" "@webassemblyjs/wasm-parser" "1.14.1" -"@webassemblyjs/wasm-parser@^1.14.1", "@webassemblyjs/wasm-parser@1.14.1": +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": version "1.14.1" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz" integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== @@ -2381,6 +2362,8 @@ acorn@^8.1.0, acorn@^8.11.0, acorn@^8.4.1, acorn@^8.8.1: acorn@^8.14.0: version "8.14.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" + integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== acorn@^8.8.2: version "8.13.0" @@ -2413,7 +2396,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.4, ajv@^6.9.1: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2423,7 +2406,7 @@ ajv@^6.12.4, ajv@^6.9.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.8.2, ajv@^8.9.0: +ajv@^8.0.0, ajv@^8.9.0: version "8.17.1" resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== @@ -2492,7 +2475,7 @@ asynckit@^0.4.0: resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -babel-jest@^29.7.0, babel-jest@29.7.0: +babel-jest@29.7.0, babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz" integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== @@ -2650,7 +2633,7 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.24.0, "browserslist@>= 4.21.0": +browserslist@^4.24.0: version "4.24.2" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz" integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== @@ -2967,13 +2950,6 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@4: - version "4.3.7" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" - integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== - dependencies: - ms "^2.1.3" - debug@2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" @@ -2981,6 +2957,13 @@ debug@2.6.9: dependencies: ms "2.0.0" +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.3.7" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + decimal.js@^10.4.2: version "10.4.3" resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz" @@ -3028,16 +3011,16 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - depd@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + destroy@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" @@ -3323,7 +3306,7 @@ fastest-levenshtein@^1.0.12: resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== -faye-websocket@^0.11.3, faye-websocket@0.11.4: +faye-websocket@0.11.4, faye-websocket@^0.11.3: version "0.11.4" resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== @@ -3585,16 +3568,6 @@ http-deceiver@^1.2.7: resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - http-errors@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" @@ -3606,6 +3579,16 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-parser-js@>=0.5.1: version "0.5.8" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" @@ -3698,7 +3681,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@2, inherits@2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3713,16 +3696,16 @@ interpret@^3.1.1: resolved "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz" integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== -ipaddr.js@^2.1.0: - version "2.2.0" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== - ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +ipaddr.js@^2.1.0: + version "2.2.0" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" @@ -4101,7 +4084,7 @@ jest-resolve-dependencies@^29.7.0: jest-regex-util "^29.6.3" jest-snapshot "^29.7.0" -jest-resolve@*, jest-resolve@^29.7.0: +jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz" integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== @@ -4469,16 +4452,16 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.3" picomatch "^2.3.1" -"mime-db@>= 1.43.0 < 2": - version "1.53.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz" - integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== - mime-db@1.52.0: version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +"mime-db@>= 1.43.0 < 2": + version "1.53.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz" + integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== + mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" @@ -4508,16 +4491,16 @@ minimatch@^3.0.4, minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" -ms@^2.1.3, ms@2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== +ms@2.1.3, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + multicast-dns@^7.2.5: version "7.2.5" resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" @@ -4588,7 +4571,7 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@^2.4.1, on-finished@2.4.1: +on-finished@2.4.1, on-finished@^2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -4967,20 +4950,15 @@ run-applescript@^7.0.0: resolved "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz" integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== -safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" @@ -5041,12 +5019,7 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.3: - version "7.6.3" - resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== - -semver@^7.5.4: +semver@^7.5.3, semver@^7.5.4: version "7.6.3" resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -5180,14 +5153,6 @@ sockjs@^0.3.24: uuid "^8.3.2" websocket-driver "^0.7.4" -source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@0.5.13: version "0.5.13" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" @@ -5196,6 +5161,14 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" @@ -5236,29 +5209,15 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -"statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - statuses@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== string-length@^4.0.1: version "4.0.2" @@ -5277,6 +5236,20 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -5330,6 +5303,8 @@ tapable@^2.1.1, tapable@^2.2.0: terser-webpack-plugin@^5.3.11: version "5.3.14" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz#9031d48e57ab27567f02ace85c7d690db66c3e06" + integrity sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw== dependencies: "@jridgewell/trace-mapping" "^0.3.25" jest-worker "^27.4.5" @@ -5405,7 +5380,7 @@ tree-dump@^1.0.1: resolved "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz" integrity sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ== -ts-node@>=9.0.0, ts-node@10.9.2: +ts-node@10.9.2: version "10.9.2" resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== @@ -5424,7 +5399,7 @@ ts-node@>=9.0.0, ts-node@10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tslib@^2, tslib@^2.0.0, tslib@^2.1.0, tslib@2: +tslib@^2.0.0, tslib@^2.1.0: version "2.8.0" resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz" integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA== @@ -5447,7 +5422,7 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typescript@>=2.7, typescript@5.5.4: +typescript@5.5.4: version "5.5.4" resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz" integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== @@ -5485,7 +5460,7 @@ universalify@^0.2.0: resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== -unpipe@~1.0.0, unpipe@1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -5586,7 +5561,7 @@ webidl-conversions@^7.0.0: resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -webpack-cli@5.1.4, webpack-cli@5.x.x: +webpack-cli@5.1.4: version "5.1.4" resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz" integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== @@ -5664,8 +5639,10 @@ webpack-sources@^3.2.3: resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.0.0, webpack@^5.1.0, webpack@>=2, webpack@5.98.0, webpack@5.x.x: +webpack@5.98.0: version "5.98.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz#44ae19a8f2ba97537978246072fb89d10d1fbd17" + integrity sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA== dependencies: "@types/eslint-scope" "^3.7.7" "@types/estree" "^1.0.6" @@ -5691,7 +5668,7 @@ webpack@^5.0.0, webpack@^5.1.0, webpack@>=2, webpack@5.98.0, webpack@5.x.x: watchpack "^2.4.1" webpack-sources "^3.2.3" -websocket-driver@^0.7.4, websocket-driver@>=0.5.1: +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== diff --git a/e2e/template/README.md b/e2e/template/README.md new file mode 100644 index 00000000000..ef2b2deb997 --- /dev/null +++ b/e2e/template/README.md @@ -0,0 +1,3 @@ +# E2E Test Package Template + +This is a template for an E2E test package. It contains one small dependency (date-fns) not used in the global monorepo workspace to confirm the dependencies in this directory do not affect the global workspace. \ No newline at end of file diff --git a/e2e/template/index.js b/e2e/template/index.js new file mode 100644 index 00000000000..1e3a1a5d9af --- /dev/null +++ b/e2e/template/index.js @@ -0,0 +1,18 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +console.log('hello'); diff --git a/e2e/template/package.json b/e2e/template/package.json new file mode 100644 index 00000000000..c07c4c5c080 --- /dev/null +++ b/e2e/template/package.json @@ -0,0 +1,15 @@ +{ + "name": "test-project", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "start": "node index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "date-fns": "4.1.0" + } +} diff --git a/e2e/template/yarn.lock b/e2e/template/yarn.lock new file mode 100644 index 00000000000..4d7e448a31b --- /dev/null +++ b/e2e/template/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +date-fns@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14" + integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== From 30de503ec90f1ccbece0485b42d689b8e41cfe73 Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Fri, 30 May 2025 13:42:29 -0700 Subject: [PATCH 57/77] Added test project to fdc (#8924) --- .../js/default-connector/README.md | 257 ++++++ .../js/default-connector/esm/index.esm.js | 55 ++ .../js/default-connector/esm/package.json | 1 + .../js/default-connector/index.cjs.js | 56 ++ .../js/default-connector/index.d.ts | 86 ++ .../js/default-connector/package.json | 25 + .../dataconnect/connector/connector.yaml | 6 + .../dataconnect/connector/mutations.gql | 33 + .../dataconnect/connector/queries.gql | 78 ++ e2e/data-connect/dataconnect/dataconnect.yaml | 12 + .../dataconnect/schema/schema.gql | 52 ++ e2e/data-connect/dataconnect/test | 0 e2e/data-connect/firebase-js-config.json | 3 + e2e/data-connect/firebase.json | 10 + e2e/data-connect/package.json | 21 + e2e/data-connect/test.ts | 23 + e2e/data-connect/tsconfig.json | 10 + e2e/data-connect/yarn.lock | 814 ++++++++++++++++++ 18 files changed, 1542 insertions(+) create mode 100644 e2e/data-connect/dataconnect-generated/js/default-connector/README.md create mode 100644 e2e/data-connect/dataconnect-generated/js/default-connector/esm/index.esm.js create mode 100644 e2e/data-connect/dataconnect-generated/js/default-connector/esm/package.json create mode 100644 e2e/data-connect/dataconnect-generated/js/default-connector/index.cjs.js create mode 100644 e2e/data-connect/dataconnect-generated/js/default-connector/index.d.ts create mode 100644 e2e/data-connect/dataconnect-generated/js/default-connector/package.json create mode 100644 e2e/data-connect/dataconnect/connector/connector.yaml create mode 100644 e2e/data-connect/dataconnect/connector/mutations.gql create mode 100644 e2e/data-connect/dataconnect/connector/queries.gql create mode 100644 e2e/data-connect/dataconnect/dataconnect.yaml create mode 100644 e2e/data-connect/dataconnect/schema/schema.gql create mode 100644 e2e/data-connect/dataconnect/test create mode 100644 e2e/data-connect/firebase-js-config.json create mode 100644 e2e/data-connect/firebase.json create mode 100644 e2e/data-connect/package.json create mode 100644 e2e/data-connect/test.ts create mode 100644 e2e/data-connect/tsconfig.json create mode 100644 e2e/data-connect/yarn.lock diff --git a/e2e/data-connect/dataconnect-generated/js/default-connector/README.md b/e2e/data-connect/dataconnect-generated/js/default-connector/README.md new file mode 100644 index 00000000000..b3f5971d704 --- /dev/null +++ b/e2e/data-connect/dataconnect-generated/js/default-connector/README.md @@ -0,0 +1,257 @@ +# Table of Contents +- [**Overview**](#generated-typescript-readme) +- [**Accessing the connector**](#accessing-the-connector) + - [*Connecting to the local Emulator*](#connecting-to-the-local-emulator) +- [**Queries**](#queries) + - [*ListMovies*](#listmovies) +- [**Mutations**](#mutations) + - [*CreateMovie*](#createmovie) + +# Generated TypeScript README +This README will guide you through the process of using the generated TypeScript SDK package for the connector `default`. It will also provide examples on how to use your generated SDK to call your Data Connect queries and mutations. + +***NOTE:** This README is generated alongside the generated SDK. If you make changes to this file, they will be overwritten when the SDK is regenerated.* + +You can use this generated SDK by importing from the package `@firebasegen/default-connector` as shown below. Both CommonJS and ESM imports are supported. + +You can also follow the instructions from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#set-client). + +# Accessing the connector +A connector is a collection of Queries and Mutations. One SDK is generated for each connector - this SDK is generated for the connector `default`. + +You can find more information about connectors in the [Data Connect documentation](https://firebase.google.com/docs/data-connect#how-does). + +```javascript +import { getDataConnect } from 'firebase/data-connect'; +import { connectorConfig } from '@firebasegen/default-connector'; + +const dataConnect = getDataConnect(connectorConfig); +``` + +## Connecting to the local Emulator +By default, the connector will connect to the production service. + +To connect to the emulator, you can use the following code. +You can also follow the emulator instructions from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#instrument-clients). + +```javascript +import { connectDataConnectEmulator, getDataConnect } from 'firebase/data-connect'; +import { connectorConfig } from '@firebasegen/default-connector'; + +const dataConnect = getDataConnect(connectorConfig); +connectDataConnectEmulator(dataConnect, 'localhost', 9399); +``` + +After it's initialized, you can call your Data Connect [queries](#queries) and [mutations](#mutations) from your generated SDK. + +# Queries + +There are two ways to execute a Data Connect Query using the generated Web SDK: +- Using a Query Reference function, which returns a `QueryRef` + - The `QueryRef` can be used as an argument to `executeQuery()`, which will execute the Query and return a `QueryPromise` +- Using an action shortcut function, which returns a `QueryPromise` + - Calling the action shortcut function will execute the Query and return a `QueryPromise` + +The following is true for both the action shortcut function and the `QueryRef` function: +- The `QueryPromise` returned will resolve to the result of the Query once it has finished executing +- If the Query accepts arguments, both the action shortcut function and the `QueryRef` function accept a single argument: an object that contains all the required variables (and the optional variables) for the Query +- Both functions can be called with or without passing in a `DataConnect` instance as an argument. If no `DataConnect` argument is passed in, then the generated SDK will call `getDataConnect(connectorConfig)` behind the scenes for you. + +Below are examples of how to use the `default` connector's generated functions to execute each query. You can also follow the examples from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#using-queries). + +## ListMovies +You can execute the `ListMovies` query using the following action shortcut function, or by calling `executeQuery()` after calling the following `QueryRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +listMovies(): QueryPromise; + +listMoviesRef(): QueryRef; +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `QueryRef` function. +```javascript +listMovies(dc: DataConnect): QueryPromise; + +listMoviesRef(dc: DataConnect): QueryRef; +``` + +### Variables +The `ListMovies` query has no variables. +### Return Type +Recall that executing the `ListMovies` query returns a `QueryPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `ListMoviesData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface ListMoviesData { + movies: ({ + id: UUIDString; + title: string; + imageUrl: string; + genre?: string | null; + } & Movie_Key)[]; +} +``` +### Using `ListMovies`'s action shortcut function + +```javascript +import { getDataConnect } from 'firebase/data-connect'; +import { connectorConfig, listMovies } from '@firebasegen/default-connector'; + + +// Call the `listMovies()` function to execute the query. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await listMovies(); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const dataConnect = getDataConnect(connectorConfig); +const { data } = await listMovies(dataConnect); + +console.log(data.movies); + +// Or, you can use the `Promise` API. +listMovies().then((response) => { + const data = response.data; + console.log(data.movies); +}); +``` + +### Using `ListMovies`'s `QueryRef` function + +```javascript +import { getDataConnect, executeQuery } from 'firebase/data-connect'; +import { connectorConfig, listMoviesRef } from '@firebasegen/default-connector'; + + +// Call the `listMoviesRef()` function to get a reference to the query. +const ref = listMoviesRef(); + +// You can also pass in a `DataConnect` instance to the `QueryRef` function. +const dataConnect = getDataConnect(connectorConfig); +const ref = listMoviesRef(dataConnect); + +// Call `executeQuery()` on the reference to execute the query. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeQuery(ref); + +console.log(data.movies); + +// Or, you can use the `Promise` API. +executeQuery(ref).then((response) => { + const data = response.data; + console.log(data.movies); +}); +``` + +# Mutations + +There are two ways to execute a Data Connect Mutation using the generated Web SDK: +- Using a Mutation Reference function, which returns a `MutationRef` + - The `MutationRef` can be used as an argument to `executeMutation()`, which will execute the Mutation and return a `MutationPromise` +- Using an action shortcut function, which returns a `MutationPromise` + - Calling the action shortcut function will execute the Mutation and return a `MutationPromise` + +The following is true for both the action shortcut function and the `MutationRef` function: +- The `MutationPromise` returned will resolve to the result of the Mutation once it has finished executing +- If the Mutation accepts arguments, both the action shortcut function and the `MutationRef` function accept a single argument: an object that contains all the required variables (and the optional variables) for the Mutation +- Both functions can be called with or without passing in a `DataConnect` instance as an argument. If no `DataConnect` argument is passed in, then the generated SDK will call `getDataConnect(connectorConfig)` behind the scenes for you. + +Below are examples of how to use the `default` connector's generated functions to execute each mutation. You can also follow the examples from the [Data Connect documentation](https://firebase.google.com/docs/data-connect/web-sdk#using-mutations). + +## CreateMovie +You can execute the `CreateMovie` mutation using the following action shortcut function, or by calling `executeMutation()` after calling the following `MutationRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): +```javascript +createMovie(vars: CreateMovieVariables): MutationPromise; + +createMovieRef(vars: CreateMovieVariables): MutationRef; +``` +You can also pass in a `DataConnect` instance to the action shortcut function or `MutationRef` function. +```javascript +createMovie(dc: DataConnect, vars: CreateMovieVariables): MutationPromise; + +createMovieRef(dc: DataConnect, vars: CreateMovieVariables): MutationRef; +``` + +### Variables +The `CreateMovie` mutation requires an argument of type `CreateMovieVariables`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: + +```javascript +export interface CreateMovieVariables { + title: string; + genre: string; + imageUrl: string; +} +``` +### Return Type +Recall that executing the `CreateMovie` mutation returns a `MutationPromise` that resolves to an object with a `data` property. + +The `data` property is an object of type `CreateMovieData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: +```javascript +export interface CreateMovieData { + movie_insert: Movie_Key; +} +``` +### Using `CreateMovie`'s action shortcut function + +```javascript +import { getDataConnect } from 'firebase/data-connect'; +import { connectorConfig, createMovie, CreateMovieVariables } from '@firebasegen/default-connector'; + +// The `CreateMovie` mutation requires an argument of type `CreateMovieVariables`: +const createMovieVars: CreateMovieVariables = { + title: ..., + genre: ..., + imageUrl: ..., +}; + +// Call the `createMovie()` function to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await createMovie(createMovieVars); +// Variables can be defined inline as well. +const { data } = await createMovie({ title: ..., genre: ..., imageUrl: ..., }); + +// You can also pass in a `DataConnect` instance to the action shortcut function. +const dataConnect = getDataConnect(connectorConfig); +const { data } = await createMovie(dataConnect, createMovieVars); + +console.log(data.movie_insert); + +// Or, you can use the `Promise` API. +createMovie(createMovieVars).then((response) => { + const data = response.data; + console.log(data.movie_insert); +}); +``` + +### Using `CreateMovie`'s `MutationRef` function + +```javascript +import { getDataConnect, executeMutation } from 'firebase/data-connect'; +import { connectorConfig, createMovieRef, CreateMovieVariables } from '@firebasegen/default-connector'; + +// The `CreateMovie` mutation requires an argument of type `CreateMovieVariables`: +const createMovieVars: CreateMovieVariables = { + title: ..., + genre: ..., + imageUrl: ..., +}; + +// Call the `createMovieRef()` function to get a reference to the mutation. +const ref = createMovieRef(createMovieVars); +// Variables can be defined inline as well. +const ref = createMovieRef({ title: ..., genre: ..., imageUrl: ..., }); + +// You can also pass in a `DataConnect` instance to the `MutationRef` function. +const dataConnect = getDataConnect(connectorConfig); +const ref = createMovieRef(dataConnect, createMovieVars); + +// Call `executeMutation()` on the reference to execute the mutation. +// You can use the `await` keyword to wait for the promise to resolve. +const { data } = await executeMutation(ref); + +console.log(data.movie_insert); + +// Or, you can use the `Promise` API. +executeMutation(ref).then((response) => { + const data = response.data; + console.log(data.movie_insert); +}); +``` + diff --git a/e2e/data-connect/dataconnect-generated/js/default-connector/esm/index.esm.js b/e2e/data-connect/dataconnect-generated/js/default-connector/esm/index.esm.js new file mode 100644 index 00000000000..e88d2d56d35 --- /dev/null +++ b/e2e/data-connect/dataconnect-generated/js/default-connector/esm/index.esm.js @@ -0,0 +1,55 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + queryRef, + executeQuery, + mutationRef, + executeMutation, + validateArgs +} from 'firebase/data-connect'; + +export const connectorConfig = { + connector: 'default', + service: 'fdc-test', + location: 'us-central1' +}; + +export function createMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars } = validateArgs( + connectorConfig, + dcOrVars, + vars, + true + ); + dcInstance._useGeneratedSdk(); + return mutationRef(dcInstance, 'CreateMovie', inputVars); +} + +export function createMovie(dcOrVars, vars) { + return executeMutation(createMovieRef(dcOrVars, vars)); +} + +export function listMoviesRef(dc) { + const { dc: dcInstance } = validateArgs(connectorConfig, dc, undefined); + dcInstance._useGeneratedSdk(); + return queryRef(dcInstance, 'ListMovies'); +} + +export function listMovies(dc) { + return executeQuery(listMoviesRef(dc)); +} diff --git a/e2e/data-connect/dataconnect-generated/js/default-connector/esm/package.json b/e2e/data-connect/dataconnect-generated/js/default-connector/esm/package.json new file mode 100644 index 00000000000..7c34deb5837 --- /dev/null +++ b/e2e/data-connect/dataconnect-generated/js/default-connector/esm/package.json @@ -0,0 +1 @@ +{"type":"module"} \ No newline at end of file diff --git a/e2e/data-connect/dataconnect-generated/js/default-connector/index.cjs.js b/e2e/data-connect/dataconnect-generated/js/default-connector/index.cjs.js new file mode 100644 index 00000000000..814406ff090 --- /dev/null +++ b/e2e/data-connect/dataconnect-generated/js/default-connector/index.cjs.js @@ -0,0 +1,56 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { + queryRef, + executeQuery, + mutationRef, + executeMutation, + validateArgs +} = require('firebase/data-connect'); + +const connectorConfig = { + connector: 'default', + service: 'fdc-test', + location: 'us-central1' +}; +exports.connectorConfig = connectorConfig; + +exports.createMovieRef = function createMovieRef(dcOrVars, vars) { + const { dc: dcInstance, vars: inputVars } = validateArgs( + connectorConfig, + dcOrVars, + vars, + true + ); + dcInstance._useGeneratedSdk(); + return mutationRef(dcInstance, 'CreateMovie', inputVars); +}; + +exports.createMovie = function createMovie(dcOrVars, vars) { + return executeMutation(createMovieRef(dcOrVars, vars)); +}; + +exports.listMoviesRef = function listMoviesRef(dc) { + const { dc: dcInstance } = validateArgs(connectorConfig, dc, undefined); + dcInstance._useGeneratedSdk(); + return queryRef(dcInstance, 'ListMovies'); +}; + +exports.listMovies = function listMovies(dc) { + return executeQuery(listMoviesRef(dc)); +}; diff --git a/e2e/data-connect/dataconnect-generated/js/default-connector/index.d.ts b/e2e/data-connect/dataconnect-generated/js/default-connector/index.d.ts new file mode 100644 index 00000000000..474fefd0f4b --- /dev/null +++ b/e2e/data-connect/dataconnect-generated/js/default-connector/index.d.ts @@ -0,0 +1,86 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ConnectorConfig, + DataConnect, + QueryRef, + QueryPromise, + MutationRef, + MutationPromise +} from 'firebase/data-connect'; + +export const connectorConfig: ConnectorConfig; + +export type TimestampString = string; +export type UUIDString = string; +export type Int64String = string; +export type DateString = string; + +export interface CreateMovieData { + movie_insert: Movie_Key; +} + +export interface CreateMovieVariables { + title: string; + genre: string; + imageUrl: string; +} + +export interface ListMoviesData { + movies: ({ + id: UUIDString; + title: string; + imageUrl: string; + genre?: string | null; + } & Movie_Key)[]; +} + +export interface Movie_Key { + id: UUIDString; + __typename?: 'Movie_Key'; +} + +/* Allow users to create refs without passing in DataConnect */ +export function createMovieRef( + vars: CreateMovieVariables +): MutationRef; +/* Allow users to pass in custom DataConnect instances */ +export function createMovieRef( + dc: DataConnect, + vars: CreateMovieVariables +): MutationRef; + +export function createMovie( + vars: CreateMovieVariables +): MutationPromise; +export function createMovie( + dc: DataConnect, + vars: CreateMovieVariables +): MutationPromise; + +/* Allow users to create refs without passing in DataConnect */ +export function listMoviesRef(): QueryRef; +/* Allow users to pass in custom DataConnect instances */ +export function listMoviesRef( + dc: DataConnect +): QueryRef; + +export function listMovies(): QueryPromise; +export function listMovies( + dc: DataConnect +): QueryPromise; diff --git a/e2e/data-connect/dataconnect-generated/js/default-connector/package.json b/e2e/data-connect/dataconnect-generated/js/default-connector/package.json new file mode 100644 index 00000000000..d0c9852ce3e --- /dev/null +++ b/e2e/data-connect/dataconnect-generated/js/default-connector/package.json @@ -0,0 +1,25 @@ +{ + "name": "@firebasegen/default-connector", + "version": "1.0.0", + "author": "Firebase (https://firebase.google.com/)", + "description": "Generated SDK For default", + "license": "Apache-2.0", + "engines": { + "node": " >=18.0" + }, + "typings": "index.d.ts", + "module": "esm/index.esm.js", + "main": "index.cjs.js", + "browser": "esm/index.esm.js", + "exports": { + ".": { + "types": "./index.d.ts", + "require": "./index.cjs.js", + "default": "./esm/index.esm.js" + }, + "./package.json": "./package.json" + }, + "peerDependencies": { + "firebase": "^10.14.0 || ^11.3.0" + } +} \ No newline at end of file diff --git a/e2e/data-connect/dataconnect/connector/connector.yaml b/e2e/data-connect/dataconnect/connector/connector.yaml new file mode 100644 index 00000000000..b698515fe9f --- /dev/null +++ b/e2e/data-connect/dataconnect/connector/connector.yaml @@ -0,0 +1,6 @@ +connectorId: default +generate: + javascriptSdk: + outputDir: ../../dataconnect-generated/js/default-connector + package: "@firebasegen/default-connector" +# packageJsonDir: ../.. diff --git a/e2e/data-connect/dataconnect/connector/mutations.gql b/e2e/data-connect/dataconnect/connector/mutations.gql new file mode 100644 index 00000000000..2379afeb89c --- /dev/null +++ b/e2e/data-connect/dataconnect/connector/mutations.gql @@ -0,0 +1,33 @@ +# # Example mutations for a simple movie app + +# # Create a movie based on user input +mutation CreateMovie($title: String!, $genre: String!, $imageUrl: String!) +@auth(level: USER_EMAIL_VERIFIED) { + movie_insert(data: { title: $title, genre: $genre, imageUrl: $imageUrl }) +} + +# # Upsert (update or insert) a user's username based on their auth.uid +# mutation UpsertUser($username: String!) @auth(level: USER) { +# # The "auth.uid" server value ensures that users can only register their own user. +# user_upsert(data: { id_expr: "auth.uid", username: $username }) +# } + +# # Add a review for a movie +# mutation AddReview($movieId: UUID!, $rating: Int!, $reviewText: String!) +# @auth(level: USER) { +# review_upsert( +# data: { +# userId_expr: "auth.uid" +# movieId: $movieId +# rating: $rating +# reviewText: $reviewText +# # reviewDate defaults to today in the schema. No need to set it manually. +# } +# ) +# } + +# # Logged in user can delete their review for a movie +# mutation DeleteReview($movieId: UUID!) @auth(level: USER) { +# # The "auth.uid" server value ensures that users can only delete their own reviews. +# review_delete(key: { userId_expr: "auth.uid", movieId: $movieId }) +# } diff --git a/e2e/data-connect/dataconnect/connector/queries.gql b/e2e/data-connect/dataconnect/connector/queries.gql new file mode 100644 index 00000000000..cb1a6f630bc --- /dev/null +++ b/e2e/data-connect/dataconnect/connector/queries.gql @@ -0,0 +1,78 @@ +# # Example queries for a simple movie app. + +# # @auth() directives control who can call each operation. +# # Anyone should be able to list all movies, so the auth level is set to PUBLIC +query ListMovies @auth(level: PUBLIC) { + movies { + id + title + imageUrl + genre + } +} + +# # List all users, only admins should be able to list all users, so we use NO_ACCESS +# query ListUsers @auth(level: NO_ACCESS) { +# users { +# id +# username +# } +# } + +# # Logged in users can list all their reviews and movie titles associated with the review +# # Since the query uses the uid of the current authenticated user, we set auth level to USER +# query ListUserReviews @auth(level: USER) { +# user(key: { id_expr: "auth.uid" }) { +# id +# username +# # _on_ makes it easy to grab info from another table +# # Here, we use it to grab all the reviews written by the user. +# reviews: reviews_on_user { +# rating +# reviewDate +# reviewText +# movie { +# id +# title +# } +# } +# } +# } + +# # Get movie by id +# query GetMovieById($id: UUID!) @auth(level: PUBLIC) { +# movie(id: $id) { +# id +# title +# imageUrl +# genre +# metadata: movieMetadata_on_movie { +# rating +# releaseYear +# description +# } +# reviews: reviews_on_movie { +# reviewText +# reviewDate +# rating +# user { +# id +# username +# } +# } +# } +# } + +# # Search for movies, actors, and reviews +# query SearchMovie($titleInput: String, $genre: String) @auth(level: PUBLIC) { +# movies( +# where: { +# _and: [{ genre: { eq: $genre } }, { title: { contains: $titleInput } }] +# } +# ) { +# id +# title +# genre +# imageUrl +# } +# } diff --git a/e2e/data-connect/dataconnect/dataconnect.yaml b/e2e/data-connect/dataconnect/dataconnect.yaml new file mode 100644 index 00000000000..623fad3e209 --- /dev/null +++ b/e2e/data-connect/dataconnect/dataconnect.yaml @@ -0,0 +1,12 @@ +specVersion: "v1beta" +serviceId: "fdc-test" +location: "us-central1" +schema: + source: "./schema" + datasource: + postgresql: + database: "fdcdb" + cloudSql: + instanceId: "fdc-test-fdc" + # schemaValidation: "COMPATIBLE" +connectorDirs: ["./connector"] diff --git a/e2e/data-connect/dataconnect/schema/schema.gql b/e2e/data-connect/dataconnect/schema/schema.gql new file mode 100644 index 00000000000..f114d9a678a --- /dev/null +++ b/e2e/data-connect/dataconnect/schema/schema.gql @@ -0,0 +1,52 @@ +# # Example schema for simple movie review app + +# # User table is keyed by Firebase Auth UID. +# type User @table { +# # `@default(expr: "auth.uid")` sets it to Firebase Auth UID during insert and upsert. +# id: String! @default(expr: "auth.uid") +# username: String! @col(dataType: "varchar(50)") +# # The `user: User!` field in the Review table generates the following one-to-many query field. +# # reviews_on_user: [Review!]! +# # The `Review` join table the following many-to-many query field. +# # movies_via_Review: [Movie!]! +# } + +# # Movie is keyed by a randomly generated UUID. +type Movie @table { + # If you do not pass a 'key' to `@table`, Data Connect automatically adds the following 'id' column. + # Feel free to uncomment and customize it. + # id: UUID! @default(expr: "uuidV4()") + title: String! + imageUrl: String! + genre: String +} + +# # MovieMetadata is a metadata attached to a Movie. +# # Movie <-> MovieMetadata is a one-to-one relationship +# type MovieMetadata @table { +# # @unique ensures each Movie can only one MovieMetadata. +# movie: Movie! @unique +# # The movie field adds the following foreign key field. Feel free to uncomment and customize it. +# # movieId: UUID! +# rating: Float +# releaseYear: Int +# description: String +# } + +# # Reviews is a join table between User and Movie. +# # It has a composite primary keys `userUid` and `movieId`. +# # A user can leave reviews for many movies. A movie can have reviews from many users. +# # User <-> Review is a one-to-many relationship +# # Movie <-> Review is a one-to-many relationship +# # Movie <-> User is a many-to-many relationship +# type Review @table(name: "Reviews", key: ["movie", "user"]) { +# user: User! +# # The user field adds the following foreign key field. Feel free to uncomment and customize it. +# # userUid: String! +# movie: Movie! +# # The movie field adds the following foreign key field. Feel free to uncomment and customize it. +# # movieId: UUID! +# rating: Int +# reviewText: String +# reviewDate: Date! @default(expr: "request.time") +# } diff --git a/e2e/data-connect/dataconnect/test b/e2e/data-connect/dataconnect/test new file mode 100644 index 00000000000..e69de29bb2d diff --git a/e2e/data-connect/firebase-js-config.json b/e2e/data-connect/firebase-js-config.json new file mode 100644 index 00000000000..0e0dcd235c4 --- /dev/null +++ b/e2e/data-connect/firebase-js-config.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/e2e/data-connect/firebase.json b/e2e/data-connect/firebase.json new file mode 100644 index 00000000000..6c9ad90b440 --- /dev/null +++ b/e2e/data-connect/firebase.json @@ -0,0 +1,10 @@ +{ + "emulators": { + "dataconnect": { + "dataDir": "dataconnect/.dataconnect/pgliteData" + } + }, + "dataconnect": { + "source": "dataconnect" + } +} diff --git a/e2e/data-connect/package.json b/e2e/data-connect/package.json new file mode 100644 index 00000000000..d9d1f234ab1 --- /dev/null +++ b/e2e/data-connect/package.json @@ -0,0 +1,21 @@ +{ + "name": "firebase-dataconnect-kokoro-test", + "version": "1.0.0", + "description": "", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "tsc" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@types/node": "18.19.83", + "ts-node": "^10.9.2", + "tslib": "^2.1.0", + "typescript": "5.5.4" + }, + "dependencies": { + "firebase": "11.8.1" + } +} diff --git a/e2e/data-connect/test.ts b/e2e/data-connect/test.ts new file mode 100644 index 00000000000..cc177b8e47c --- /dev/null +++ b/e2e/data-connect/test.ts @@ -0,0 +1,23 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { listMovies } from './dataconnect-generated/js/default-connector'; +import * as json from './firebase-js-config.json'; +import { initializeApp } from 'firebase/app'; + +initializeApp(json); +listMovies(); diff --git a/e2e/data-connect/tsconfig.json b/e2e/data-connect/tsconfig.json new file mode 100644 index 00000000000..e3c427a3163 --- /dev/null +++ b/e2e/data-connect/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "ES5", + "module": "CommonJS", + "resolveJsonModule": true, + "sourceMap": true, + "noEmit": true, + "skipLibCheck": false + } + } diff --git a/e2e/data-connect/yarn.lock b/e2e/data-connect/yarn.lock new file mode 100644 index 00000000000..d8d5a981fef --- /dev/null +++ b/e2e/data-connect/yarn.lock @@ -0,0 +1,814 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@firebase/ai@1.3.0": + version "1.3.0" + resolved "https://registry.npmjs.org/@firebase/ai/-/ai-1.3.0.tgz#66b8edaa32d8e5d46c99be0efc337cbf034dffb7" + integrity sha512-qBxJTtl9hpgZr050kVFTRADX6I0Ss6mEQyp/JEkBgKwwxixKnaRNqEDGFba4OKNL7K8E4Y7LlA/ZW6L8aCKH4A== + dependencies: + "@firebase/app-check-interop-types" "0.3.3" + "@firebase/component" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/analytics-compat@0.2.22": + version "0.2.22" + resolved "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.22.tgz#5ec880cf719642c233742ad805ba95e5b4dad999" + integrity sha512-VogWHgwkdYhjWKh8O1XU04uPrRaiDihkWvE/EMMmtWtaUtVALnpLnUurc3QtSKdPnvTz5uaIGKlW84DGtSPFbw== + dependencies: + "@firebase/analytics" "0.10.16" + "@firebase/analytics-types" "0.8.3" + "@firebase/component" "0.6.17" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/analytics-types@0.8.3": + version "0.8.3" + resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.8.3.tgz#d08cd39a6209693ca2039ba7a81570dfa6c1518f" + integrity sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg== + +"@firebase/analytics@0.10.16": + version "0.10.16" + resolved "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.16.tgz#516f211465060008538fed46c7f78f0ea14dd549" + integrity sha512-cMtp19He7Fd6uaj/nDEul+8JwvJsN8aRSJyuA1QN3QrKvfDDp+efjVurJO61sJpkVftw9O9nNMdhFbRcTmTfRQ== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/installations" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/app-check-compat@0.3.25": + version "0.3.25" + resolved "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.25.tgz#03d1941dba78626bdacf544f9d1d48b451dbb49c" + integrity sha512-3zrsPZWAKfV7DVC20T2dgfjzjtQnSJS65OfMOiddMUtJL1S5i0nAZKsdX0bOEvvrd0SBIL8jYnfpfDeQRnhV3w== + dependencies: + "@firebase/app-check" "0.10.0" + "@firebase/app-check-types" "0.5.3" + "@firebase/component" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/app-check-interop-types@0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz#ed9c4a4f48d1395ef378f007476db3940aa5351a" + integrity sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A== + +"@firebase/app-check-types@0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@firebase/app-check-types/-/app-check-types-0.5.3.tgz#38ba954acf4bffe451581a32fffa20337f11d8e5" + integrity sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng== + +"@firebase/app-check@0.10.0": + version "0.10.0" + resolved "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.0.tgz#dc833ead2b31930eee13b5c9eb028091c630d21b" + integrity sha512-AZlRlVWKcu8BH4Yf8B5EI8sOi2UNGTS8oMuthV45tbt6OVUTSQwFPIEboZzhNJNKY+fPsg7hH8vixUWFZ3lrhw== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/app-compat@0.4.0": + version "0.4.0" + resolved "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.0.tgz#4c908115b2a68756e4944ee8f9bc7042d8715f92" + integrity sha512-LjLUrzbUgTa/sCtPoLKT2C7KShvLVHS3crnU1Du02YxnGVLE0CUBGY/NxgfR/Zg84mEbj1q08/dgesojxjn0dA== + dependencies: + "@firebase/app" "0.13.0" + "@firebase/component" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/app-types@0.9.3": + version "0.9.3" + resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.9.3.tgz#8408219eae9b1fb74f86c24e7150a148460414ad" + integrity sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw== + +"@firebase/app@0.13.0": + version "0.13.0" + resolved "https://registry.npmjs.org/@firebase/app/-/app-0.13.0.tgz#ef67c7e5dc3f47efb430157f9de969a67abd53a7" + integrity sha512-Vj3MST245nq+V5UmmfEkB3isIgPouyUr8yGJlFeL9Trg/umG5ogAvrjAYvQ8gV7daKDoQSRnJKWI2JFpQqRsuQ== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/auth-compat@0.5.26": + version "0.5.26" + resolved "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.26.tgz#ff795ad7dd43077dc603a8576bf92e756c6a2064" + integrity sha512-4baB7tR0KukyGzrlD25aeO4t0ChLifwvDQXTBiVJE9WWwJEOjkZpHmoU9Iww0+Vdalsq4sZ3abp6YTNjHyB1dA== + dependencies: + "@firebase/auth" "1.10.6" + "@firebase/auth-types" "0.13.0" + "@firebase/component" "0.6.17" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/auth-interop-types@0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz#176a08686b0685596ff03d7879b7e4115af53de0" + integrity sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA== + +"@firebase/auth-types@0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@firebase/auth-types/-/auth-types-0.13.0.tgz#ae6e0015e3bd4bfe18edd0942b48a0a118a098d9" + integrity sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg== + +"@firebase/auth@1.10.6": + version "1.10.6" + resolved "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.6.tgz#2403e92e382034367f7fefd6715f992e061c3e59" + integrity sha512-cFbo2FymQltog4atI9cKTO6CxKxS0dOMXslTQrlNZRH7qhDG44/d7QeI6GXLweFZtrnlecf52ESnNz1DU6ek8w== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/component@0.6.17": + version "0.6.17" + resolved "https://registry.npmjs.org/@firebase/component/-/component-0.6.17.tgz#6cef28dbe6de80ce393f97147bf3f1ea0a5453ec" + integrity sha512-M6DOg7OySrKEFS8kxA3MU5/xc37fiOpKPMz6cTsMUcsuKB6CiZxxNAvgFta8HGRgEpZbi8WjGIj6Uf+TpOhyzg== + dependencies: + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/data-connect@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.9.tgz#9fe2f30d73c4013367b8ddd7ad57387d0438dce1" + integrity sha512-B5tGEh5uQrQeH0i7RvlU8kbZrKOJUmoyxVIX4zLA8qQJIN6A7D+kfBlGXtSwbPdrvyaejcRPcbOtqsDQ9HPJKw== + dependencies: + "@firebase/auth-interop-types" "0.2.4" + "@firebase/component" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/database-compat@2.0.10": + version "2.0.10" + resolved "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.10.tgz#5cfe487b5fdb68752ded5ad17dabd99e763ba152" + integrity sha512-3sjl6oGaDDYJw/Ny0E5bO6v+KM3KoD4Qo/sAfHGdRFmcJ4QnfxOX9RbG9+ce/evI3m64mkPr24LlmTDduqMpog== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/database" "1.0.19" + "@firebase/database-types" "1.0.14" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/database-types@1.0.14": + version "1.0.14" + resolved "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.14.tgz#454a33f55da46c243302d6b420b8b319ab80c6ce" + integrity sha512-8a0Q1GrxM0akgF0RiQHliinhmZd+UQPrxEmUv7MnQBYfVFiLtKOgs3g6ghRt/WEGJHyQNslZ+0PocIwNfoDwKw== + dependencies: + "@firebase/app-types" "0.9.3" + "@firebase/util" "1.12.0" + +"@firebase/database@1.0.19": + version "1.0.19" + resolved "https://registry.npmjs.org/@firebase/database/-/database-1.0.19.tgz#2d52e731407431bdc1581990d99b533784700fc6" + integrity sha512-khE+MIYK+XlIndVn/7mAQ9F1fwG5JHrGKaG72hblCC6JAlUBDd3SirICH6SMCf2PQ0iYkruTECth+cRhauacyQ== + dependencies: + "@firebase/app-check-interop-types" "0.3.3" + "@firebase/auth-interop-types" "0.2.4" + "@firebase/component" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + faye-websocket "0.11.4" + tslib "^2.1.0" + +"@firebase/firestore-compat@0.3.51": + version "0.3.51" + resolved "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.51.tgz#46ecebe9de16b60febf27d5014c9f21298064703" + integrity sha512-E5iubPhS6aAM7oSsHMx/FGBwfA2nbEHaK/hCs+MD3l3N7rHKnq4SYCGmVu/AraSJaMndZR1I37N9A/BH7aCq5A== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/firestore" "4.7.16" + "@firebase/firestore-types" "3.0.3" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/firestore-types@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@firebase/firestore-types/-/firestore-types-3.0.3.tgz#7d0c3dd8850c0193d8f5ee0cc8f11961407742c1" + integrity sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q== + +"@firebase/firestore@4.7.16": + version "4.7.16" + resolved "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.16.tgz#9807d3355965406da106a098ce15d9950585ac51" + integrity sha512-5OpvlwYVUTLEnqewOlXmtIpH8t2ISlZHDW0NDbKROM2D0ATMqFkMHdvl+/wz9zOAcb8GMQYlhCihOnVAliUbpQ== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + "@firebase/webchannel-wrapper" "1.0.3" + "@grpc/grpc-js" "~1.9.0" + "@grpc/proto-loader" "^0.7.8" + tslib "^2.1.0" + +"@firebase/functions-compat@0.3.25": + version "0.3.25" + resolved "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.25.tgz#9022df60d7471c7111766789f05991966ab692f0" + integrity sha512-V0JKUw5W/7aznXf9BQ8LIYHCX6zVCM8Hdw7XUQ/LU1Y9TVP8WKRCnPB/qdPJ0xGjWWn7fhtwIYbgEw/syH4yTQ== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/functions" "0.12.8" + "@firebase/functions-types" "0.6.3" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/functions-types@0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@firebase/functions-types/-/functions-types-0.6.3.tgz#f5faf770248b13f45d256f614230da6a11bfb654" + integrity sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg== + +"@firebase/functions@0.12.8": + version "0.12.8" + resolved "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.8.tgz#c2e42b4ba82746142c9fe1e120c3f71f0464aa3d" + integrity sha512-p+ft6dQW0CJ3BLLxeDb5Hwk9ARw01kHTZjLqiUdPRzycR6w7Z75ThkegNmL6gCss3S0JEpldgvehgZ3kHybVhA== + dependencies: + "@firebase/app-check-interop-types" "0.3.3" + "@firebase/auth-interop-types" "0.2.4" + "@firebase/component" "0.6.17" + "@firebase/messaging-interop-types" "0.2.3" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/installations-compat@0.2.17": + version "0.2.17" + resolved "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.17.tgz#5d61fb652658432e43ca82b8f91a17d5d21a459b" + integrity sha512-J7afeCXB7yq25FrrJAgbx8mn1nG1lZEubOLvYgG7ZHvyoOCK00sis5rj7TgDrLYJgdj/SJiGaO1BD3BAp55TeA== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/installations" "0.6.17" + "@firebase/installations-types" "0.5.3" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/installations-types@0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@firebase/installations-types/-/installations-types-0.5.3.tgz#cac8a14dd49f09174da9df8ae453f9b359c3ef2f" + integrity sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA== + +"@firebase/installations@0.6.17": + version "0.6.17" + resolved "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.17.tgz#f184e49fcf3053ba25015b0509d9de28db99db77" + integrity sha512-zfhqCNJZRe12KyADtRrtOj+SeSbD1H/K8J24oQAJVv/u02eQajEGlhZtcx9Qk7vhGWF5z9dvIygVDYqLL4o1XQ== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/util" "1.12.0" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/logger@0.4.4": + version "0.4.4" + resolved "https://registry.yarnpkg.com/@firebase/logger/-/logger-0.4.4.tgz#29e8379d20fd1149349a195ee6deee4573a86f48" + integrity sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g== + dependencies: + tslib "^2.1.0" + +"@firebase/messaging-compat@0.2.21": + version "0.2.21" + resolved "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.21.tgz#8d4c5b9557ed9890a774fba2ea7e5459860a901b" + integrity sha512-1yMne+4BGLbHbtyu/VyXWcLiefUE1+K3ZGfVTyKM4BH4ZwDFRGoWUGhhx+tKRX4Tu9z7+8JN67SjnwacyNWK5g== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/messaging" "0.12.21" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/messaging-interop-types@0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz#e647c9cd1beecfe6a6e82018a6eec37555e4da3e" + integrity sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q== + +"@firebase/messaging@0.12.21": + version "0.12.21" + resolved "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.21.tgz#05d76039133c3d2a33208416699a3d3ebd2c0012" + integrity sha512-bYJ2Evj167Z+lJ1ach6UglXz5dUKY1zrJZd15GagBUJSR7d9KfiM1W8dsyL0lDxcmhmA/sLaBYAAhF1uilwN0g== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/installations" "0.6.17" + "@firebase/messaging-interop-types" "0.2.3" + "@firebase/util" "1.12.0" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/performance-compat@0.2.19": + version "0.2.19" + resolved "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.19.tgz#f817a82037285b2240c4c55cc03eb1943ae932d6" + integrity sha512-4cU0T0BJ+LZK/E/UwFcvpBCVdkStgBMQwBztM9fJPT6udrEUk3ugF5/HT+E2Z22FCXtIaXDukJbYkE/c3c6IHw== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/performance" "0.7.6" + "@firebase/performance-types" "0.2.3" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/performance-types@0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@firebase/performance-types/-/performance-types-0.2.3.tgz#5ce64e90fa20ab5561f8b62a305010cf9fab86fb" + integrity sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ== + +"@firebase/performance@0.7.6": + version "0.7.6" + resolved "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.6.tgz#36376130c725d8f6df492be4da32c1d1f9a605f8" + integrity sha512-AsOz74dSTlyQGlnnbLWXiHFAsrxhpssPOsFFi4HgOJ5DjzkK7ZdZ/E9uMPrwFoXJyMVoybGRuqsL/wkIbFITsA== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/installations" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + web-vitals "^4.2.4" + +"@firebase/remote-config-compat@0.2.17": + version "0.2.17" + resolved "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.17.tgz#d91583dfdd85ddb3e8cdd9c0e433d38f02095f1e" + integrity sha512-KelsBD0sXSC0u3esr/r6sJYGRN6pzn3bYuI/6pTvvmZbjBlxQkRabHAVH6d+YhLcjUXKIAYIjZszczd1QJtOyA== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/remote-config" "0.6.4" + "@firebase/remote-config-types" "0.4.0" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/remote-config-types@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz#91b9a836d5ca30ced68c1516163b281fbb544537" + integrity sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg== + +"@firebase/remote-config@0.6.4": + version "0.6.4" + resolved "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.4.tgz#30610bf52452639a127f1b40a94de096ac6cf43a" + integrity sha512-ZyLJRT46wtycyz2+opEkGaoFUOqRQjt/0NX1WfUISOMCI/PuVoyDjqGpq24uK+e8D5NknyTpiXCVq5dowhScmg== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/installations" "0.6.17" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/storage-compat@0.3.22": + version "0.3.22" + resolved "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.22.tgz#8d5b95b5583d2acac2183e8e3cc302c66c2c3902" + integrity sha512-29j6JgXTjQ76sOIkxmTNHQfYA/hDTeV9qGbn0jolynPXSg/AmzCB0CpCoCYrS0ja0Flgmy1hkA3XYDZ/eiV1Cg== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/storage" "0.13.12" + "@firebase/storage-types" "0.8.3" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/storage-types@0.8.3": + version "0.8.3" + resolved "https://registry.yarnpkg.com/@firebase/storage-types/-/storage-types-0.8.3.tgz#2531ef593a3452fc12c59117195d6485c6632d3d" + integrity sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg== + +"@firebase/storage@0.13.12": + version "0.13.12" + resolved "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.12.tgz#213c5e7832b77b361e0c454ff8b787437686c45c" + integrity sha512-5JmoFS01MYjW1XMQa5F5rD/kvMwBN10QF03bmcuJWq4lg+BJ3nRgL3sscWnyJPhwM/ZCyv2eRwcfzESVmsYkdQ== + dependencies: + "@firebase/component" "0.6.17" + "@firebase/util" "1.12.0" + tslib "^2.1.0" + +"@firebase/util@1.12.0": + version "1.12.0" + resolved "https://registry.npmjs.org/@firebase/util/-/util-1.12.0.tgz#202e96cfd832f8dde551232e4868861681b8b89a" + integrity sha512-Z4rK23xBCwgKDqmzGVMef+Vb4xso2j5Q8OG0vVL4m4fA5ZjPMYQazu8OJJC3vtQRC3SQ/Pgx/6TPNVsCd70QRw== + dependencies: + tslib "^2.1.0" + +"@firebase/webchannel-wrapper@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz#a73bab8eb491d7b8b7be2f0e6c310647835afe83" + integrity sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ== + +"@grpc/grpc-js@~1.9.0": + version "1.9.15" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.15.tgz#433d7ac19b1754af690ea650ab72190bd700739b" + integrity sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ== + dependencies: + "@grpc/proto-loader" "^0.7.8" + "@types/node" ">=12.12.47" + +"@grpc/proto-loader@^0.7.8": + version "0.7.13" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.13.tgz#f6a44b2b7c9f7b609f5748c6eac2d420e37670cf" + integrity sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.5" + yargs "^17.7.2" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/node@18.19.83": + version "18.19.83" + resolved "https://registry.npmjs.org/@types/node/-/node-18.19.83.tgz#44d302cd09364640bdd45d001bc75e596f7da920" + integrity sha512-D69JeR5SfFS5H6FLbUaS0vE4r1dGhmMBbG4Ed6BNS4wkDK8GZjsdCShT5LCN59vOHEUHnFCY9J4aclXlIphMkA== + dependencies: + undici-types "~5.26.4" + +"@types/node@>=12.12.47", "@types/node@>=13.7.0": + version "22.14.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.14.0.tgz#d3bfa3936fef0dbacd79ea3eb17d521c628bb47e" + integrity sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA== + dependencies: + undici-types "~6.21.0" + +acorn-walk@^8.1.1: + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.4.1: + version "8.14.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" + integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escalade@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +faye-websocket@0.11.4: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +firebase@11.8.1: + version "11.8.1" + resolved "https://registry.npmjs.org/firebase/-/firebase-11.8.1.tgz#7658c4fed6c10102d8a2b00de576b903af78d767" + integrity sha512-oetXhPCvJZM4DVL/n/06442emMU+KzM0JLZjszpwlU6mqdFZqBwumBxn6hQkLukJyU5wsjihZHUY8HEAE2micg== + dependencies: + "@firebase/ai" "1.3.0" + "@firebase/analytics" "0.10.16" + "@firebase/analytics-compat" "0.2.22" + "@firebase/app" "0.13.0" + "@firebase/app-check" "0.10.0" + "@firebase/app-check-compat" "0.3.25" + "@firebase/app-compat" "0.4.0" + "@firebase/app-types" "0.9.3" + "@firebase/auth" "1.10.6" + "@firebase/auth-compat" "0.5.26" + "@firebase/data-connect" "0.3.9" + "@firebase/database" "1.0.19" + "@firebase/database-compat" "2.0.10" + "@firebase/firestore" "4.7.16" + "@firebase/firestore-compat" "0.3.51" + "@firebase/functions" "0.12.8" + "@firebase/functions-compat" "0.3.25" + "@firebase/installations" "0.6.17" + "@firebase/installations-compat" "0.2.17" + "@firebase/messaging" "0.12.21" + "@firebase/messaging-compat" "0.2.21" + "@firebase/performance" "0.7.6" + "@firebase/performance-compat" "0.2.19" + "@firebase/remote-config" "0.6.4" + "@firebase/remote-config-compat" "0.2.17" + "@firebase/storage" "0.13.12" + "@firebase/storage-compat" "0.3.22" + "@firebase/util" "1.12.0" + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +http-parser-js@>=0.5.1: + version "0.5.9" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.9.tgz#b817b3ca0edea6236225000d795378707c169cec" + integrity sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw== + +idb@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b" + integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +long@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/long/-/long-5.3.1.tgz#9d4222d3213f38a5ec809674834e0f0ab21abe96" + integrity sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng== + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +protobufjs@^7.2.5: + version "7.4.0" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.4.0.tgz#7efe324ce9b3b61c82aae5de810d287bc08a248a" + integrity sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw== + dependencies: + "@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/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/node" ">=13.7.0" + long "^5.0.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +safe-buffer@>=5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tslib@^2.1.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +typescript@5.5.4: + version "5.5.4" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" + integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +web-vitals@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.4.tgz#1d20bc8590a37769bd0902b289550936069184b7" + integrity sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw== + +websocket-driver@>=0.5.1: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== From 89051ca4df350f1881bce8081c5118c8aa31f2a8 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 5 Jun 2025 07:30:17 -0700 Subject: [PATCH 58/77] Version Packages (#9073) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Release version 11.9.0. --- .changeset/angry-scissors-sit.md | 6 ------ .changeset/spotty-ghosts-kneel.md | 5 ----- .changeset/tricky-years-pump.md | 6 ------ integration/compat-interop/package.json | 4 ++-- integration/firestore/package.json | 4 ++-- integration/messaging/package.json | 2 +- packages/ai/CHANGELOG.md | 8 ++++++++ packages/ai/package.json | 4 ++-- packages/analytics-compat/package.json | 2 +- packages/analytics/package.json | 2 +- packages/app-check-compat/package.json | 2 +- packages/app-check/package.json | 2 +- packages/app-compat/CHANGELOG.md | 7 +++++++ packages/app-compat/package.json | 4 ++-- packages/app/CHANGELOG.md | 6 ++++++ packages/app/package.json | 2 +- packages/auth-compat/package.json | 2 +- packages/auth/package.json | 2 +- packages/data-connect/package.json | 2 +- packages/database-compat/package.json | 2 +- packages/database/package.json | 2 +- packages/firebase/CHANGELOG.md | 17 +++++++++++++++++ packages/firebase/package.json | 12 ++++++------ packages/firestore-compat/CHANGELOG.md | 7 +++++++ packages/firestore-compat/package.json | 6 +++--- packages/firestore/CHANGELOG.md | 6 ++++++ packages/firestore/package.json | 6 +++--- packages/functions-compat/package.json | 2 +- packages/functions/package.json | 2 +- packages/installations-compat/package.json | 2 +- packages/installations/package.json | 2 +- packages/messaging-compat/package.json | 2 +- packages/messaging/package.json | 2 +- packages/performance-compat/package.json | 2 +- packages/performance/package.json | 2 +- packages/remote-config-compat/package.json | 2 +- packages/remote-config/package.json | 2 +- packages/storage-compat/package.json | 2 +- packages/storage/package.json | 2 +- packages/template/package.json | 2 +- repo-scripts/size-analysis/package.json | 2 +- 41 files changed, 96 insertions(+), 62 deletions(-) delete mode 100644 .changeset/angry-scissors-sit.md delete mode 100644 .changeset/spotty-ghosts-kneel.md delete mode 100644 .changeset/tricky-years-pump.md diff --git a/.changeset/angry-scissors-sit.md b/.changeset/angry-scissors-sit.md deleted file mode 100644 index 8aeb7dcaf52..00000000000 --- a/.changeset/angry-scissors-sit.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'firebase': minor -'@firebase/ai': minor ---- - -Add support for `minItems` and `maxItems` to `Schema`. diff --git a/.changeset/spotty-ghosts-kneel.md b/.changeset/spotty-ghosts-kneel.md deleted file mode 100644 index 0db91b7bf19..00000000000 --- a/.changeset/spotty-ghosts-kneel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@firebase/firestore": patch ---- - -Clean up leaked WebChannel instances when the Firestore instance is terminated. diff --git a/.changeset/tricky-years-pump.md b/.changeset/tricky-years-pump.md deleted file mode 100644 index 94bf68604cc..00000000000 --- a/.changeset/tricky-years-pump.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'firebase': minor -'@firebase/ai': minor ---- - -Add `title`, `maximum`, `minimum`, `propertyOrdering` to Schema builder diff --git a/integration/compat-interop/package.json b/integration/compat-interop/package.json index 117a8220fc0..34a1336c733 100644 --- a/integration/compat-interop/package.json +++ b/integration/compat-interop/package.json @@ -8,8 +8,8 @@ "test:debug": "karma start --browsers Chrome --auto-watch" }, "dependencies": { - "@firebase/app": "0.13.0", - "@firebase/app-compat": "0.4.0", + "@firebase/app": "0.13.1", + "@firebase/app-compat": "0.4.1", "@firebase/analytics": "0.10.16", "@firebase/analytics-compat": "0.2.22", "@firebase/auth": "1.10.6", diff --git a/integration/firestore/package.json b/integration/firestore/package.json index 0c722a5352b..903c182a5c0 100644 --- a/integration/firestore/package.json +++ b/integration/firestore/package.json @@ -14,8 +14,8 @@ "test:memory:debug": "yarn build:memory; karma start --auto-watch --browsers Chrome" }, "dependencies": { - "@firebase/app": "0.13.0", - "@firebase/firestore": "4.7.16" + "@firebase/app": "0.13.1", + "@firebase/firestore": "4.7.17" }, "devDependencies": { "@types/mocha": "9.1.1", diff --git a/integration/messaging/package.json b/integration/messaging/package.json index 9cb2bf233fc..e3682df4657 100644 --- a/integration/messaging/package.json +++ b/integration/messaging/package.json @@ -9,7 +9,7 @@ "test:manual": "mocha --exit" }, "devDependencies": { - "firebase": "11.8.1", + "firebase": "11.9.0", "chai": "4.5.0", "chromedriver": "119.0.1", "express": "4.21.2", diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 298fb06a9b5..874cdb40e69 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -1,5 +1,13 @@ # @firebase/ai +## 1.4.0 + +### Minor Changes + +- [`1933324`](https://github.com/firebase/firebase-js-sdk/commit/1933324e0f3e4c8ed4d4d784f0c701fd0ec6ebc3) [#9026](https://github.com/firebase/firebase-js-sdk/pull/9026) - Add support for `minItems` and `maxItems` to `Schema`. + +- [`40be2db`](https://github.com/firebase/firebase-js-sdk/commit/40be2dbb884b8e1485862af8bb015e23db69ccbf) [#9047](https://github.com/firebase/firebase-js-sdk/pull/9047) - Add `title`, `maximum`, `minimum`, `propertyOrdering` to Schema builder + ## 1.3.0 ### Minor Changes diff --git a/packages/ai/package.json b/packages/ai/package.json index 98e204320bc..7be0163d9bc 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/ai", - "version": "1.3.0", + "version": "1.4.0", "description": "The Firebase AI SDK", "author": "Firebase (https://firebase.google.com/)", "engines": { @@ -56,7 +56,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/packages/analytics-compat/package.json b/packages/analytics-compat/package.json index cb8c4c5a2f7..d126569486a 100644 --- a/packages/analytics-compat/package.json +++ b/packages/analytics-compat/package.json @@ -22,7 +22,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.4.0", + "@firebase/app-compat": "0.4.1", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 2f7b6cfbadf..3f8decfc35f 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -47,7 +47,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check-compat/package.json b/packages/app-check-compat/package.json index 15cc2a9273f..29ed9977205 100644 --- a/packages/app-check-compat/package.json +++ b/packages/app-check-compat/package.json @@ -43,7 +43,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.4.0", + "@firebase/app-compat": "0.4.1", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-check/package.json b/packages/app-check/package.json index 4187145ed67..e52ff90dbc7 100644 --- a/packages/app-check/package.json +++ b/packages/app-check/package.json @@ -44,7 +44,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/app-compat/CHANGELOG.md b/packages/app-compat/CHANGELOG.md index 6e27613a249..bc8b1b97982 100644 --- a/packages/app-compat/CHANGELOG.md +++ b/packages/app-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/app-compat +## 0.4.1 + +### Patch Changes + +- Updated dependencies []: + - @firebase/app@0.13.1 + ## 0.4.0 ### Minor Changes diff --git a/packages/app-compat/package.json b/packages/app-compat/package.json index 3d70accd107..8dba6ff2ff6 100644 --- a/packages/app-compat/package.json +++ b/packages/app-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app-compat", - "version": "0.4.0", + "version": "0.4.1", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", @@ -37,7 +37,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "@firebase/util": "1.12.0", "@firebase/logger": "0.4.4", "@firebase/component": "0.6.17", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 2b239c453cc..aa13db67c63 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,11 @@ # @firebase/app +## 0.13.1 + +### Patch Changes + +- Update SDK_VERSION. + ## 0.13.0 ### Minor Changes diff --git a/packages/app/package.json b/packages/app/package.json index 3d385028412..5f892ecc525 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/app", - "version": "0.13.0", + "version": "0.13.1", "description": "The primary entrypoint to the Firebase JS SDK", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.cjs.js", diff --git a/packages/auth-compat/package.json b/packages/auth-compat/package.json index c8a6f40b357..cfca5a5bee6 100644 --- a/packages/auth-compat/package.json +++ b/packages/auth-compat/package.json @@ -57,7 +57,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app-compat": "0.4.0", + "@firebase/app-compat": "0.4.1", "@rollup/plugin-json": "6.1.0", "rollup": "2.79.2", "rollup-plugin-replace": "2.2.0", diff --git a/packages/auth/package.json b/packages/auth/package.json index ea47732d398..2da4cb10b30 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -131,7 +131,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-strip": "2.1.0", "@types/express": "4.17.21", diff --git a/packages/data-connect/package.json b/packages/data-connect/package.json index a342c7d4f14..99b97e39f3a 100644 --- a/packages/data-connect/package.json +++ b/packages/data-connect/package.json @@ -55,7 +55,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/database-compat/package.json b/packages/database-compat/package.json index 104f57d56dc..c746b8dde07 100644 --- a/packages/database-compat/package.json +++ b/packages/database-compat/package.json @@ -57,7 +57,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.4.0", + "@firebase/app-compat": "0.4.1", "typescript": "5.5.4" }, "repository": { diff --git a/packages/database/package.json b/packages/database/package.json index 0fe42bddeed..54a549b9f68 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -57,7 +57,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/firebase/CHANGELOG.md b/packages/firebase/CHANGELOG.md index 2a17f24d933..84eb512291d 100644 --- a/packages/firebase/CHANGELOG.md +++ b/packages/firebase/CHANGELOG.md @@ -1,5 +1,22 @@ # firebase +## 11.9.0 + +### Minor Changes + +- [`1933324`](https://github.com/firebase/firebase-js-sdk/commit/1933324e0f3e4c8ed4d4d784f0c701fd0ec6ebc3) [#9026](https://github.com/firebase/firebase-js-sdk/pull/9026) - Add support for `minItems` and `maxItems` to `Schema`. + +- [`40be2db`](https://github.com/firebase/firebase-js-sdk/commit/40be2dbb884b8e1485862af8bb015e23db69ccbf) [#9047](https://github.com/firebase/firebase-js-sdk/pull/9047) - Add `title`, `maximum`, `minimum`, `propertyOrdering` to Schema builder + +### Patch Changes + +- Updated dependencies [[`1933324`](https://github.com/firebase/firebase-js-sdk/commit/1933324e0f3e4c8ed4d4d784f0c701fd0ec6ebc3), [`9964849`](https://github.com/firebase/firebase-js-sdk/commit/9964849e9540f08d02fa3825ecec32c1bfedc62d), [`40be2db`](https://github.com/firebase/firebase-js-sdk/commit/40be2dbb884b8e1485862af8bb015e23db69ccbf)]: + - @firebase/ai@1.4.0 + - @firebase/app@0.13.1 + - @firebase/firestore@4.7.17 + - @firebase/app-compat@0.4.1 + - @firebase/firestore-compat@0.3.52 + ## 11.8.1 ### Patch Changes diff --git a/packages/firebase/package.json b/packages/firebase/package.json index aec5e6e8fec..85bcab03aa6 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -1,6 +1,6 @@ { "name": "firebase", - "version": "11.8.1", + "version": "11.9.0", "description": "Firebase JavaScript library for web and Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -411,17 +411,17 @@ "trusted-type-check": "tsec -p tsconfig.json --noEmit" }, "dependencies": { - "@firebase/ai": "1.3.0", - "@firebase/app": "0.13.0", - "@firebase/app-compat": "0.4.0", + "@firebase/ai": "1.4.0", + "@firebase/app": "0.13.1", + "@firebase/app-compat": "0.4.1", "@firebase/app-types": "0.9.3", "@firebase/auth": "1.10.6", "@firebase/auth-compat": "0.5.26", "@firebase/data-connect": "0.3.9", "@firebase/database": "1.0.19", "@firebase/database-compat": "2.0.10", - "@firebase/firestore": "4.7.16", - "@firebase/firestore-compat": "0.3.51", + "@firebase/firestore": "4.7.17", + "@firebase/firestore-compat": "0.3.52", "@firebase/functions": "0.12.8", "@firebase/functions-compat": "0.3.25", "@firebase/installations": "0.6.17", diff --git a/packages/firestore-compat/CHANGELOG.md b/packages/firestore-compat/CHANGELOG.md index 861e35792c5..264da74339b 100644 --- a/packages/firestore-compat/CHANGELOG.md +++ b/packages/firestore-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/firestore-compat +## 0.3.52 + +### Patch Changes + +- Updated dependencies [[`9964849`](https://github.com/firebase/firebase-js-sdk/commit/9964849e9540f08d02fa3825ecec32c1bfedc62d)]: + - @firebase/firestore@4.7.17 + ## 0.3.51 ### Patch Changes diff --git a/packages/firestore-compat/package.json b/packages/firestore-compat/package.json index 6e3db51e597..4071cdb236f 100644 --- a/packages/firestore-compat/package.json +++ b/packages/firestore-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore-compat", - "version": "0.3.51", + "version": "0.3.52", "description": "The Cloud Firestore component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -47,13 +47,13 @@ }, "dependencies": { "@firebase/component": "0.6.17", - "@firebase/firestore": "4.7.16", + "@firebase/firestore": "4.7.17", "@firebase/util": "1.12.0", "@firebase/firestore-types": "3.0.3", "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.4.0", + "@firebase/app-compat": "0.4.1", "@types/eslint": "7.29.0", "rollup": "2.79.2", "rollup-plugin-sourcemaps": "0.6.3", diff --git a/packages/firestore/CHANGELOG.md b/packages/firestore/CHANGELOG.md index 0671f0e8c63..3c73ea511d9 100644 --- a/packages/firestore/CHANGELOG.md +++ b/packages/firestore/CHANGELOG.md @@ -1,5 +1,11 @@ # @firebase/firestore +## 4.7.17 + +### Patch Changes + +- [`9964849`](https://github.com/firebase/firebase-js-sdk/commit/9964849e9540f08d02fa3825ecec32c1bfedc62d) [#9041](https://github.com/firebase/firebase-js-sdk/pull/9041) - Clean up leaked WebChannel instances when the Firestore instance is terminated. + ## 4.7.16 ### Patch Changes diff --git a/packages/firestore/package.json b/packages/firestore/package.json index 6fc04a3579d..d53f8d36bb0 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/firestore", - "version": "4.7.16", + "version": "4.7.17", "engines": { "node": ">=18.0.0" }, @@ -112,8 +112,8 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.13.0", - "@firebase/app-compat": "0.4.0", + "@firebase/app": "0.13.1", + "@firebase/app-compat": "0.4.1", "@firebase/auth": "1.10.6", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", diff --git a/packages/functions-compat/package.json b/packages/functions-compat/package.json index 09a4514114c..24a8efb46d8 100644 --- a/packages/functions-compat/package.json +++ b/packages/functions-compat/package.json @@ -29,7 +29,7 @@ "@firebase/app-compat": "0.x" }, "devDependencies": { - "@firebase/app-compat": "0.4.0", + "@firebase/app-compat": "0.4.1", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/functions/package.json b/packages/functions/package.json index 004181eeb39..6354ed4dadb 100644 --- a/packages/functions/package.json +++ b/packages/functions/package.json @@ -49,7 +49,7 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/installations-compat/package.json b/packages/installations-compat/package.json index 11f1838f66c..cb5f6f730a6 100644 --- a/packages/installations-compat/package.json +++ b/packages/installations-compat/package.json @@ -44,7 +44,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app-compat": "0.4.0", + "@firebase/app-compat": "0.4.1", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/installations/package.json b/packages/installations/package.json index be4d1b4a79f..a1d89e48321 100644 --- a/packages/installations/package.json +++ b/packages/installations/package.json @@ -49,7 +49,7 @@ "url": "https://github.com/firebase/firebase-js-sdk/issues" }, "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "rollup": "2.79.2", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/messaging-compat/package.json b/packages/messaging-compat/package.json index fd145f06bf7..c536cbef067 100644 --- a/packages/messaging-compat/package.json +++ b/packages/messaging-compat/package.json @@ -44,7 +44,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.4.0", + "@firebase/app-compat": "0.4.1", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", "ts-essentials": "9.4.2", diff --git a/packages/messaging/package.json b/packages/messaging/package.json index 9d8cfc71926..4419ad3acf4 100644 --- a/packages/messaging/package.json +++ b/packages/messaging/package.json @@ -60,7 +60,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "@rollup/plugin-json": "6.1.0", diff --git a/packages/performance-compat/package.json b/packages/performance-compat/package.json index 0ec3cc56e66..0e84088a37d 100644 --- a/packages/performance-compat/package.json +++ b/packages/performance-compat/package.json @@ -51,7 +51,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.4.0" + "@firebase/app-compat": "0.4.1" }, "repository": { "directory": "packages/performance-compat", diff --git a/packages/performance/package.json b/packages/performance/package.json index b3495c8a566..27ff0073509 100644 --- a/packages/performance/package.json +++ b/packages/performance/package.json @@ -47,7 +47,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/remote-config-compat/package.json b/packages/remote-config-compat/package.json index 84c66d579bc..507bce652f3 100644 --- a/packages/remote-config-compat/package.json +++ b/packages/remote-config-compat/package.json @@ -50,7 +50,7 @@ "rollup-plugin-replace": "2.2.0", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4", - "@firebase/app-compat": "0.4.0" + "@firebase/app-compat": "0.4.1" }, "repository": { "directory": "packages/remote-config-compat", diff --git a/packages/remote-config/package.json b/packages/remote-config/package.json index 09dd8b6531e..194f9806cb7 100644 --- a/packages/remote-config/package.json +++ b/packages/remote-config/package.json @@ -48,7 +48,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/packages/storage-compat/package.json b/packages/storage-compat/package.json index 724685153ce..f631350bce8 100644 --- a/packages/storage-compat/package.json +++ b/packages/storage-compat/package.json @@ -44,7 +44,7 @@ "tslib": "^2.1.0" }, "devDependencies": { - "@firebase/app-compat": "0.4.0", + "@firebase/app-compat": "0.4.1", "@firebase/auth-compat": "0.5.26", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", diff --git a/packages/storage/package.json b/packages/storage/package.json index f7d52a2e8e2..2884f28dea2 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -54,7 +54,7 @@ "@firebase/app": "0.x" }, "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "@firebase/auth": "1.10.6", "rollup": "2.79.2", "@rollup/plugin-alias": "5.1.1", diff --git a/packages/template/package.json b/packages/template/package.json index 6d178ded575..9274862e9d1 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -48,7 +48,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "rollup": "2.79.2", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" diff --git a/repo-scripts/size-analysis/package.json b/repo-scripts/size-analysis/package.json index 184020a8acc..5084ee78d04 100644 --- a/repo-scripts/size-analysis/package.json +++ b/repo-scripts/size-analysis/package.json @@ -20,7 +20,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@firebase/app": "0.13.0", + "@firebase/app": "0.13.1", "@firebase/logger": "0.4.4", "@firebase/util": "1.12.0", "@rollup/plugin-commonjs": "21.1.0", From d590889d6d9eecfa643d98beaa8c89e170f2e016 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Thu, 5 Jun 2025 12:50:13 -0400 Subject: [PATCH 59/77] test(ai): add integration tests (#8853) --- .gitignore | 2 +- .vscode/launch.json | 22 +- packages/ai/integration/chat.test.ts | 132 ++++++++ packages/ai/integration/constants.ts | 81 +++++ packages/ai/integration/count-tokens.test.ts | 286 ++++++++++++++++++ packages/ai/integration/firebase-config.ts | 20 ++ .../ai/integration/generate-content.test.ts | 141 +++++++++ packages/ai/karma.conf.js | 16 +- packages/ai/package.json | 1 + packages/ai/rollup.config.js | 7 +- 10 files changed, 704 insertions(+), 4 deletions(-) create mode 100644 packages/ai/integration/chat.test.ts create mode 100644 packages/ai/integration/constants.ts create mode 100644 packages/ai/integration/count-tokens.test.ts create mode 100644 packages/ai/integration/firebase-config.ts create mode 100644 packages/ai/integration/generate-content.test.ts diff --git a/.gitignore b/.gitignore index 5aaf5c0b5be..a989576ccf3 100644 --- a/.gitignore +++ b/.gitignore @@ -103,4 +103,4 @@ vertexai-sdk-test-data mocks-lookup.ts # temp changeset output -changeset-temp.json \ No newline at end of file +changeset-temp.json diff --git a/.vscode/launch.json b/.vscode/launch.json index df14c96daa6..1f627304b61 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "node", "request": "launch", "program": "${workspaceFolder}/node_modules/.bin/_mocha", - "cwd": "${workspaceRoot}/packages/vertexai", + "cwd": "${workspaceRoot}/packages/ai", "args": [ "--require", "ts-node/register", @@ -24,6 +24,26 @@ }, "sourceMaps": true }, + { + "name": "AI Integration Tests (node)", + "type": "node", + "request": "launch", + "program": "${workspaceFolder}/node_modules/.bin/_mocha", + "cwd": "${workspaceRoot}/packages/ai", + "args": [ + "--require", + "ts-node/register", + "--require", + "src/index.node.ts", + "--timeout", + "5000", + "integration/**/*.test.ts" + ], + "env": { + "TS_NODE_COMPILER_OPTIONS": "{\"module\":\"commonjs\"}" + }, + "sourceMaps": true + }, { "type": "node", "request": "launch", diff --git a/packages/ai/integration/chat.test.ts b/packages/ai/integration/chat.test.ts new file mode 100644 index 00000000000..6af0e7a9af9 --- /dev/null +++ b/packages/ai/integration/chat.test.ts @@ -0,0 +1,132 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { + Content, + GenerationConfig, + HarmBlockThreshold, + HarmCategory, + SafetySetting, + getGenerativeModel +} from '../src'; +import { testConfigs, TOKEN_COUNT_DELTA } from './constants'; + +describe('Chat Session', () => { + testConfigs.forEach(testConfig => { + describe(`${testConfig.toString()}`, () => { + const commonGenerationConfig: GenerationConfig = { + temperature: 0, + topP: 0, + responseMimeType: 'text/plain' + }; + + const commonSafetySettings: SafetySetting[] = [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE + }, + { + category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE + }, + { + category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE + }, + { + category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE + } + ]; + + const commonSystemInstruction: Content = { + role: 'system', + parts: [ + { + text: 'You are a friendly and helpful assistant.' + } + ] + }; + + it('startChat and sendMessage: text input, text output', async () => { + const model = getGenerativeModel(testConfig.ai, { + model: testConfig.model, + generationConfig: commonGenerationConfig, + safetySettings: commonSafetySettings, + systemInstruction: commonSystemInstruction + }); + + const chat = model.startChat(); + const result1 = await chat.sendMessage( + 'What is the capital of France?' + ); + const response1 = result1.response; + expect(response1.text().trim().toLowerCase()).to.include('paris'); + + let history = await chat.getHistory(); + expect(history.length).to.equal(2); + expect(history[0].role).to.equal('user'); + expect(history[0].parts[0].text).to.equal( + 'What is the capital of France?' + ); + expect(history[1].role).to.equal('model'); + expect(history[1].parts[0].text?.toLowerCase()).to.include('paris'); + + expect(response1.usageMetadata).to.not.be.null; + // Token counts can vary slightly in chat context + expect(response1.usageMetadata!.promptTokenCount).to.be.closeTo( + 15, // "What is the capital of France?" + system instruction + TOKEN_COUNT_DELTA + 2 // More variance for chat context + ); + expect(response1.usageMetadata!.candidatesTokenCount).to.be.closeTo( + 8, // "Paris" + TOKEN_COUNT_DELTA + ); + expect(response1.usageMetadata!.totalTokenCount).to.be.closeTo( + 23, // "What is the capital of France?" + system instruction + "Paris" + TOKEN_COUNT_DELTA + 3 // More variance for chat context + ); + + const result2 = await chat.sendMessage('And what about Italy?'); + const response2 = result2.response; + expect(response2.text().trim().toLowerCase()).to.include('rome'); + + history = await chat.getHistory(); + expect(history.length).to.equal(4); + expect(history[2].role).to.equal('user'); + expect(history[2].parts[0].text).to.equal('And what about Italy?'); + expect(history[3].role).to.equal('model'); + expect(history[3].parts[0].text?.toLowerCase()).to.include('rome'); + + expect(response2.usageMetadata).to.not.be.null; + expect(response2.usageMetadata!.promptTokenCount).to.be.closeTo( + 28, // History + "And what about Italy?" + system instruction + TOKEN_COUNT_DELTA + 5 // More variance for chat context with history + ); + expect(response2.usageMetadata!.candidatesTokenCount).to.be.closeTo( + 8, + TOKEN_COUNT_DELTA + ); + expect(response2.usageMetadata!.totalTokenCount).to.be.closeTo( + 36, + TOKEN_COUNT_DELTA + ); + }); + }); + }); +}); diff --git a/packages/ai/integration/constants.ts b/packages/ai/integration/constants.ts new file mode 100644 index 00000000000..68aebf9eddc --- /dev/null +++ b/packages/ai/integration/constants.ts @@ -0,0 +1,81 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { initializeApp } from '@firebase/app'; +import { + AI, + Backend, + BackendType, + GoogleAIBackend, + VertexAIBackend, + getAI +} from '../src'; +import { FIREBASE_CONFIG } from './firebase-config'; + +const app = initializeApp(FIREBASE_CONFIG); + +/** + * Test config that all tests will be ran against. + */ +export type TestConfig = Readonly<{ + ai: AI; + model: string; + /** This will be used to output the test config at runtime */ + toString: () => string; +}>; + +function formatConfigAsString(config: { ai: AI; model: string }): string { + return `${backendNames.get(config.ai.backend.backendType)} ${config.model}`; +} + +const backends: readonly Backend[] = [ + new GoogleAIBackend(), + new VertexAIBackend() +]; + +const backendNames: Map = new Map([ + [BackendType.GOOGLE_AI, 'Google AI'], + [BackendType.VERTEX_AI, 'Vertex AI'] +]); + +const modelNames: readonly string[] = ['gemini-2.0-flash']; + +/** + * Array of test configurations that is iterated over to get full coverage + * of backends and models. Contains all combinations of backends and models. + */ +export const testConfigs: readonly TestConfig[] = backends.flatMap(backend => { + return modelNames.map(modelName => { + const ai = getAI(app, { backend }); + return { + ai: getAI(app, { backend }), + model: modelName, + toString: () => formatConfigAsString({ ai, model: modelName }) + }; + }); +}); + +export const TINY_IMG_BASE64 = + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII='; +export const IMAGE_MIME_TYPE = 'image/png'; +export const TINY_MP3_BASE64 = + 'SUQzBAAAAAAAIlRTU0UAAAAOAAADTGF2ZjYxLjcuMTAwAAAAAAAAAAAAAAD/+0DAAAAAAAAAAAAAAAAAAAAAAABJbmZvAAAADwAAAAUAAAK+AGhoaGhoaGhoaGhoaGhoaGhoaGiOjo6Ojo6Ojo6Ojo6Ojo6Ojo6OjrS0tLS0tLS0tLS0tLS0tLS0tLS02tra2tra2tra2tra2tra2tra2tr//////////////////////////wAAAABMYXZjNjEuMTkAAAAAAAAAAAAAAAAkAwYAAAAAAAACvhC6DYoAAAAAAP/7EMQAA8AAAaQAAAAgAAA0gAAABExBTUUzLjEwMFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sQxCmDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX/+xDEUwPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/7EMR8g8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sQxKYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU='; +export const AUDIO_MIME_TYPE = 'audio/mpeg'; + +// Token counts are only expected to differ by at most this number of tokens. +// Set to 1 for whitespace that is not always present. +export const TOKEN_COUNT_DELTA = 1; diff --git a/packages/ai/integration/count-tokens.test.ts b/packages/ai/integration/count-tokens.test.ts new file mode 100644 index 00000000000..3256a9ed9d7 --- /dev/null +++ b/packages/ai/integration/count-tokens.test.ts @@ -0,0 +1,286 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { + Content, + GenerationConfig, + HarmBlockMethod, + HarmBlockThreshold, + HarmCategory, + Modality, + SafetySetting, + getGenerativeModel, + Part, + CountTokensRequest, + InlineDataPart, + FileDataPart, + BackendType +} from '../src'; +import { + AUDIO_MIME_TYPE, + IMAGE_MIME_TYPE, + TINY_IMG_BASE64, + TINY_MP3_BASE64, + testConfigs +} from './constants'; +import { FIREBASE_CONFIG } from './firebase-config'; + +describe('Count Tokens', () => { + testConfigs.forEach(testConfig => { + describe(`${testConfig.toString()}`, () => { + it('text input', async () => { + const generationConfig: GenerationConfig = { + temperature: 0, + topP: 0, + responseMimeType: 'text/plain' + }; + + const safetySettings: SafetySetting[] = [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, + method: HarmBlockMethod.PROBABILITY + }, + { + category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, + method: HarmBlockMethod.SEVERITY + }, + { + category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE + }, + { + category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE + } + ]; + + const systemInstruction: Content = { + role: 'system', + parts: [ + { + text: 'You are a friendly and helpful assistant.' + } + ] + }; + const model = getGenerativeModel(testConfig.ai, { + model: testConfig.model, + generationConfig, + systemInstruction, + safetySettings + }); + + const response = await model.countTokens('Why is the sky blue?'); + + expect(response.promptTokensDetails).to.exist; + expect(response.promptTokensDetails!.length).to.equal(1); + expect(response.promptTokensDetails![0].modality).to.equal( + Modality.TEXT + ); + if (testConfig.ai.backend.backendType === BackendType.GOOGLE_AI) { + expect(response.totalTokens).to.equal(7); + expect(response.totalBillableCharacters).to.be.undefined; + expect(response.promptTokensDetails![0].tokenCount).to.equal(7); + } else if ( + testConfig.ai.backend.backendType === BackendType.VERTEX_AI + ) { + expect(response.totalTokens).to.equal(6); + expect(response.totalBillableCharacters).to.equal(16); + expect(response.promptTokensDetails![0].tokenCount).to.equal(6); + } + }); + + it('image input', async () => { + const model = getGenerativeModel(testConfig.ai, { + model: testConfig.model + }); + const imagePart: Part = { + inlineData: { + mimeType: IMAGE_MIME_TYPE, + data: TINY_IMG_BASE64 + } + }; + const response = await model.countTokens([imagePart]); + + if (testConfig.ai.backend.backendType === BackendType.GOOGLE_AI) { + const expectedImageTokens = 259; + expect(response.totalTokens).to.equal(expectedImageTokens); + expect(response.totalBillableCharacters).to.be.undefined; // Incorrect behavior + expect(response.promptTokensDetails!.length).to.equal(2); + expect(response.promptTokensDetails![0]).to.deep.equal({ + modality: Modality.TEXT, // Note: 1 unexpected text token observed for Google AI with image-only input. + tokenCount: 1 + }); + expect(response.promptTokensDetails![1]).to.deep.equal({ + modality: Modality.IMAGE, + tokenCount: 258 + }); + } else if ( + testConfig.ai.backend.backendType === BackendType.VERTEX_AI + ) { + const expectedImageTokens = 258; + expect(response.totalTokens).to.equal(expectedImageTokens); + expect(response.totalBillableCharacters).to.be.undefined; // Incorrect behavior + expect(response.promptTokensDetails!.length).to.equal(1); + // Note: No text tokens are present for Vertex AI with image-only input. + expect(response.promptTokensDetails![0]).to.deep.equal({ + modality: Modality.IMAGE, + tokenCount: 258 + }); + expect(response.promptTokensDetails![0].tokenCount).to.equal( + expectedImageTokens + ); + } + }); + + it('audio input', async () => { + const model = getGenerativeModel(testConfig.ai, { + model: testConfig.model + }); + const audioPart: InlineDataPart = { + inlineData: { + mimeType: AUDIO_MIME_TYPE, + data: TINY_MP3_BASE64 + } + }; + + const response = await model.countTokens([audioPart]); + + expect(response.promptTokensDetails).to.exist; + const textDetails = response.promptTokensDetails!.find( + d => d.modality === Modality.TEXT + ); + const audioDetails = response.promptTokensDetails!.find( + d => d.modality === Modality.AUDIO + ); + + if (testConfig.ai.backend.backendType === BackendType.GOOGLE_AI) { + expect(response.totalTokens).to.equal(6); + expect(response.promptTokensDetails!.length).to.equal(2); + expect(textDetails).to.deep.equal({ + modality: Modality.TEXT, + tokenCount: 1 + }); + expect(audioDetails).to.deep.equal({ + modality: Modality.AUDIO, + tokenCount: 5 + }); + } else if ( + testConfig.ai.backend.backendType === BackendType.VERTEX_AI + ) { + expect(response.totalTokens).to.be.undefined; + expect(response.promptTokensDetails!.length).to.equal(1); // Note: Text modality details absent for Vertex AI with audio-only input. + expect(audioDetails).to.deep.equal({ modality: Modality.AUDIO }); // Note: Audio tokenCount is undefined for Vertex AI with audio-only input. + } + + expect(response.totalBillableCharacters).to.be.undefined; // Incorrect behavior + }); + + it('text, image, and audio input', async () => { + const model = getGenerativeModel(testConfig.ai, { + model: testConfig.model + }); + const textPart: Part = { text: 'Describe these:' }; + const imagePart: Part = { + inlineData: { mimeType: IMAGE_MIME_TYPE, data: TINY_IMG_BASE64 } + }; + const audioPart: Part = { + inlineData: { mimeType: AUDIO_MIME_TYPE, data: TINY_MP3_BASE64 } + }; + + const request: CountTokensRequest = { + contents: [{ role: 'user', parts: [textPart, imagePart, audioPart] }] + }; + const response = await model.countTokens(request); + const textDetails = response.promptTokensDetails!.find( + d => d.modality === Modality.TEXT + ); + const imageDetails = response.promptTokensDetails!.find( + d => d.modality === Modality.IMAGE + ); + const audioDetails = response.promptTokensDetails!.find( + d => d.modality === Modality.AUDIO + ); + expect(response.promptTokensDetails).to.exist; + expect(response.promptTokensDetails!.length).to.equal(3); + + expect(imageDetails).to.deep.equal({ + modality: Modality.IMAGE, + tokenCount: 258 + }); + + if (testConfig.ai.backend.backendType === BackendType.GOOGLE_AI) { + expect(response.totalTokens).to.equal(267); + expect(response.totalBillableCharacters).to.be.undefined; + expect(textDetails).to.deep.equal({ + modality: Modality.TEXT, + tokenCount: 4 + }); + expect(audioDetails).to.deep.equal({ + modality: Modality.AUDIO, + tokenCount: 5 + }); + } else if ( + testConfig.ai.backend.backendType === BackendType.VERTEX_AI + ) { + expect(response.totalTokens).to.equal(261); + expect(textDetails).to.deep.equal({ + modality: Modality.TEXT, + tokenCount: 3 + }); + const expectedText = 'Describe these:'; + expect(response.totalBillableCharacters).to.equal( + expectedText.length - 1 + ); // Note: BillableCharacters observed as (text length - 1) for Vertex AI. + expect(audioDetails).to.deep.equal({ modality: Modality.AUDIO }); // Incorrect behavior because there's no tokenCount + } + }); + + it('public storage reference', async () => { + // This test is not expected to pass when using Google AI. + if (testConfig.ai.backend.backendType === BackendType.GOOGLE_AI) { + return; + } + const model = getGenerativeModel(testConfig.ai, { + model: testConfig.model + }); + const filePart: FileDataPart = { + fileData: { + mimeType: IMAGE_MIME_TYPE, + fileUri: `gs://${FIREBASE_CONFIG.storageBucket}/images/tree.png` + } + }; + + const response = await model.countTokens([filePart]); + + const expectedFileTokens = 258; + expect(response.totalTokens).to.equal(expectedFileTokens); + expect(response.totalBillableCharacters).to.be.undefined; + expect(response.promptTokensDetails).to.exist; + expect(response.promptTokensDetails!.length).to.equal(1); + expect(response.promptTokensDetails![0].modality).to.equal( + Modality.IMAGE + ); + expect(response.promptTokensDetails![0].tokenCount).to.equal( + expectedFileTokens + ); + }); + }); + }); +}); diff --git a/packages/ai/integration/firebase-config.ts b/packages/ai/integration/firebase-config.ts new file mode 100644 index 00000000000..2527f020530 --- /dev/null +++ b/packages/ai/integration/firebase-config.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as config from '../../../config/ci.config.json'; + +export const FIREBASE_CONFIG = config; diff --git a/packages/ai/integration/generate-content.test.ts b/packages/ai/integration/generate-content.test.ts new file mode 100644 index 00000000000..af877396cc8 --- /dev/null +++ b/packages/ai/integration/generate-content.test.ts @@ -0,0 +1,141 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { + Content, + GenerationConfig, + HarmBlockThreshold, + HarmCategory, + Modality, + SafetySetting, + getGenerativeModel +} from '../src'; +import { testConfigs, TOKEN_COUNT_DELTA } from './constants'; + +describe('Generate Content', () => { + testConfigs.forEach(testConfig => { + describe(`${testConfig.toString()}`, () => { + const commonGenerationConfig: GenerationConfig = { + temperature: 0, + topP: 0, + responseMimeType: 'text/plain' + }; + + const commonSafetySettings: SafetySetting[] = [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE + }, + { + category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE + }, + { + category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE + }, + { + category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, + threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE + } + ]; + + const commonSystemInstruction: Content = { + role: 'system', + parts: [ + { + text: 'You are a friendly and helpful assistant.' + } + ] + }; + + it('generateContent: text input, text output', async () => { + const model = getGenerativeModel(testConfig.ai, { + model: testConfig.model, + generationConfig: commonGenerationConfig, + safetySettings: commonSafetySettings, + systemInstruction: commonSystemInstruction + }); + + const result = await model.generateContent( + 'Where is Google headquarters located? Answer with the city name only.' + ); + const response = result.response; + + const trimmedText = response.text().trim(); + expect(trimmedText).to.equal('Mountain View'); + + expect(response.usageMetadata).to.not.be.null; + expect(response.usageMetadata!.promptTokenCount).to.be.closeTo( + 21, + TOKEN_COUNT_DELTA + ); + expect(response.usageMetadata!.candidatesTokenCount).to.be.closeTo( + 4, + TOKEN_COUNT_DELTA + ); + expect(response.usageMetadata!.totalTokenCount).to.be.closeTo( + 25, + TOKEN_COUNT_DELTA * 2 + ); + expect(response.usageMetadata!.promptTokensDetails).to.not.be.null; + expect(response.usageMetadata!.promptTokensDetails!.length).to.equal(1); + expect( + response.usageMetadata!.promptTokensDetails![0].modality + ).to.equal(Modality.TEXT); + expect( + response.usageMetadata!.promptTokensDetails![0].tokenCount + ).to.equal(21); + expect(response.usageMetadata!.candidatesTokensDetails).to.not.be.null; + expect( + response.usageMetadata!.candidatesTokensDetails!.length + ).to.equal(1); + expect( + response.usageMetadata!.candidatesTokensDetails![0].modality + ).to.equal(Modality.TEXT); + expect( + response.usageMetadata!.candidatesTokensDetails![0].tokenCount + ).to.be.closeTo(4, TOKEN_COUNT_DELTA); + }); + + it('generateContentStream: text input, text output', async () => { + const model = getGenerativeModel(testConfig.ai, { + model: testConfig.model, + generationConfig: commonGenerationConfig, + safetySettings: commonSafetySettings, + systemInstruction: commonSystemInstruction + }); + + const result = await model.generateContentStream( + 'Where is Google headquarters located? Answer with the city name only.' + ); + + let streamText = ''; + for await (const chunk of result.stream) { + streamText += chunk.text(); + } + expect(streamText.trim()).to.equal('Mountain View'); + + const response = await result.response; + const trimmedText = response.text().trim(); + expect(trimmedText).to.equal('Mountain View'); + expect(response.usageMetadata).to.be.undefined; // Note: This is incorrect behavior. + }); + }); + }); +}); diff --git a/packages/ai/karma.conf.js b/packages/ai/karma.conf.js index 3fe2a2f9633..e64048d5f6d 100644 --- a/packages/ai/karma.conf.js +++ b/packages/ai/karma.conf.js @@ -16,14 +16,28 @@ */ const karmaBase = require('../../config/karma.base'); +const { argv } = require('yargs'); const files = [`src/**/*.test.ts`]; module.exports = function (config) { const karmaConfig = { ...karmaBase, + + preprocessors: { + ...karmaBase.preprocessors, + 'integration/**/*.ts': ['webpack', 'sourcemap'] + }, + // files to load into karma - files, + files: (() => { + if (argv.integration) { + return ['integration/**']; + } else { + return ['src/**/*.test.ts']; + } + })(), + // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['mocha'] diff --git a/packages/ai/package.json b/packages/ai/package.json index 7be0163d9bc..468f166ad7e 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -39,6 +39,7 @@ "test:ci": "yarn testsetup && node ../../scripts/run_tests_in_ci.js -s test", "test:skip-clone": "karma start", "test:browser": "yarn testsetup && karma start", + "test:integration": "karma start --integration", "api-report": "api-extractor run --local --verbose", "typings:public": "node ../../scripts/build/use_typings.js ./dist/ai-public.d.ts", "trusted-type-check": "tsec -p tsconfig.json --noEmit" diff --git a/packages/ai/rollup.config.js b/packages/ai/rollup.config.js index b3a1e5ebcf8..3b155335898 100644 --- a/packages/ai/rollup.config.js +++ b/packages/ai/rollup.config.js @@ -32,7 +32,12 @@ const buildPlugins = [ typescriptPlugin({ typescript, tsconfigOverride: { - exclude: [...tsconfig.exclude, '**/*.test.ts', 'test-utils'], + exclude: [ + ...tsconfig.exclude, + '**/*.test.ts', + 'test-utils', + 'integration' + ], compilerOptions: { target: 'es2017' } From 5871fd656355b1fd4cb248bfe9f7dd3a7cb1c2e8 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Thu, 5 Jun 2025 11:29:29 -0700 Subject: [PATCH 60/77] Update issue template to rename VertexAI > AI (#9074) --- .github/ISSUE_TEMPLATE/bug_report_v2.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report_v2.yaml b/.github/ISSUE_TEMPLATE/bug_report_v2.yaml index 24cc0efb53e..b466840862d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_v2.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report_v2.yaml @@ -58,6 +58,7 @@ body: description: Select the Firebase product(s) relevant to your issue. You can select multiple options in the dropdown. multiple: true options: + - AI - Analytics - AppCheck - Auth @@ -72,7 +73,6 @@ body: - Performance - Remote-Config - Storage - - VertexAI validations: required: true - type: textarea From 6cc9a0732a4139fc82e6d07c341d087f65ce9f02 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Thu, 5 Jun 2025 14:37:37 -0700 Subject: [PATCH 61/77] ci: Fix Firefox error and streamline cross-browser tests (#9015) --- packages/util/test/errors.test.ts | 4 ++-- scripts/run_tests_in_ci.js | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/util/test/errors.test.ts b/packages/util/test/errors.test.ts index 4980b101ee1..6cb6273d326 100644 --- a/packages/util/test/errors.test.ts +++ b/packages/util/test/errors.test.ts @@ -89,8 +89,8 @@ describe('FirebaseError', () => { throw e; } catch (error) { assert.isDefined((error as Error).stack); - // Multi-line match trick - .* does not match \n - assert.match((error as Error).stack!, /FirebaseError[\s\S]/); + // Firefox no longer puts the error class name in the stack + // as of 139.0 so we don't have a string to match on. } }); diff --git a/scripts/run_tests_in_ci.js b/scripts/run_tests_in_ci.js index 0cf6a7f5d3c..7252b6acc3c 100644 --- a/scripts/run_tests_in_ci.js +++ b/scripts/run_tests_in_ci.js @@ -27,7 +27,9 @@ const crossBrowserPackages = { 'packages/auth': 'test:browser:unit', 'packages/auth-compat': 'test:browser:unit', 'packages/firestore': 'test:browser:unit', - 'packages/firestore-compat': 'test:browser' + 'packages/firestore-compat': 'test:browser', + 'packages/storage': 'test:browser:unit', + 'packages/storage-compat': 'test:browser:unit' }; function writeLogs(status, name, logText) { @@ -59,17 +61,27 @@ const argv = yargs.options({ const myPath = argv.d; let scriptName = argv.s; const dir = path.resolve(myPath); - const { name } = require(`${dir}/package.json`); + const { name, scripts } = require(`${dir}/package.json`); let testProcessOutput = ''; try { if (process.env?.BROWSERS) { + if (scripts['test:browser']) { + scriptName = 'test:browser'; + } for (const package in crossBrowserPackages) { if (dir.endsWith(package)) { scriptName = crossBrowserPackages[package]; } } } + + console.log( + `[${name}][${ + process.env.BROWSERS ?? 'chrome/node' + }]: Running script ${scriptName}` + ); + const testProcess = spawn('yarn', ['--cwd', dir, scriptName]); testProcess.childProcess.stdout.on('data', data => { From c0617a341a693c2578a21b35a4f7b27b726defef Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Thu, 5 Jun 2025 14:47:19 -0700 Subject: [PATCH 62/77] Fixed issue where requestSts wasn't including the Studio cookie in it (#9075) --- .changeset/wicked-scissors-yell.md | 5 +++++ .../auth/src/api/authentication/token.test.ts | 20 +++++++++++++++++++ packages/auth/src/api/authentication/token.ts | 13 +++++++++--- 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 .changeset/wicked-scissors-yell.md diff --git a/.changeset/wicked-scissors-yell.md b/.changeset/wicked-scissors-yell.md new file mode 100644 index 00000000000..7005f64f8f6 --- /dev/null +++ b/.changeset/wicked-scissors-yell.md @@ -0,0 +1,5 @@ +--- +"@firebase/auth": patch +--- + +Fixed issue where requestSts wasn't including the Firebase Studio cookie in it diff --git a/packages/auth/src/api/authentication/token.test.ts b/packages/auth/src/api/authentication/token.test.ts index c30bf552720..9177b4bd4ad 100644 --- a/packages/auth/src/api/authentication/token.test.ts +++ b/packages/auth/src/api/authentication/token.test.ts @@ -95,6 +95,26 @@ describe('requestStsToken', () => { ); }); + it('should use credentials: include when using Firebase Studio', async () => { + const mock = fetch.mock(endpoint, { + 'access_token': 'new-access-token', + 'expires_in': '3600', + 'refresh_token': 'new-refresh-token' + }); + + auth._logFramework('Mythical'); + auth.emulatorConfig = { + host: 'something.cloudworkstations.dev', + port: 443, + options: { disableWarnings: false }, + protocol: 'https' + }; + await requestStsToken(auth, 'some-refresh-token'); + expect(mock.calls[0].fullRequest?.credentials).to.eq('include'); + + auth.emulatorConfig = null; + }); + it('should include whatever headers come from auth impl', async () => { sinon.stub(auth, '_getAdditionalHeaders').returns( Promise.resolve({ diff --git a/packages/auth/src/api/authentication/token.ts b/packages/auth/src/api/authentication/token.ts index 6646321fbe0..478e9451e8b 100644 --- a/packages/auth/src/api/authentication/token.ts +++ b/packages/auth/src/api/authentication/token.ts @@ -17,7 +17,7 @@ /* eslint-disable camelcase */ -import { querystring } from '@firebase/util'; +import { isCloudWorkstation, querystring } from '@firebase/util'; import { _getFinalTarget, @@ -84,11 +84,18 @@ export async function requestStsToken( const headers = await (auth as AuthInternal)._getAdditionalHeaders(); headers[HttpHeader.CONTENT_TYPE] = 'application/x-www-form-urlencoded'; - return FetchProvider.fetch()(url, { + const options: RequestInit = { method: HttpMethod.POST, headers, body - }); + }; + if ( + auth.emulatorConfig && + isCloudWorkstation(auth.emulatorConfig.host) + ) { + options.credentials = 'include'; + } + return FetchProvider.fetch()(url, options); } ); From 0f891d861bdf4e7bac8cd777f5fb32d0b7b9bf8e Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Fri, 6 Jun 2025 10:17:38 -0700 Subject: [PATCH 63/77] Fix issue where we ping an endpoint that hasn't been implemented (#9059) --- .changeset/many-mails-marry.md | 5 +++++ README.md | 2 ++ packages/storage/src/service.ts | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .changeset/many-mails-marry.md diff --git a/.changeset/many-mails-marry.md b/.changeset/many-mails-marry.md new file mode 100644 index 00000000000..409001defc6 --- /dev/null +++ b/.changeset/many-mails-marry.md @@ -0,0 +1,5 @@ +--- +'@firebase/storage': patch +--- + +Fixed issue where Firebase Studio wasn't populating cookies for Storage users diff --git a/README.md b/README.md index d1947bd7fe6..1db82c79608 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,8 @@ and follow the instructions to login. For more information, visit https://firebase.google.com/docs/storage/web/download-files#cors_configuration +Then, make sure you have anonymous sign-in provider enabled: + #### Authentication Support Visit the authentication config in your project and enable the `Anonymous` diff --git a/packages/storage/src/service.ts b/packages/storage/src/service.ts index 97d1407bb52..a4252c77870 100644 --- a/packages/storage/src/service.ts +++ b/packages/storage/src/service.ts @@ -150,7 +150,7 @@ export function connectStorageEmulator( const useSsl = isCloudWorkstation(host); // Workaround to get cookies in Firebase Studio if (useSsl) { - void pingServer(`https://${storage.host}`); + void pingServer(`https://${storage.host}/b`); updateEmulatorBanner('Storage', true); } storage._isUsingEmulator = true; From 23069208726dc1924011eb84c8bf34d6f914a3a9 Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Mon, 9 Jun 2025 16:10:06 -0700 Subject: [PATCH 64/77] Updated firebase auth changeset (#9088) --- .changeset/wicked-scissors-yell.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/wicked-scissors-yell.md b/.changeset/wicked-scissors-yell.md index 7005f64f8f6..a013da3001b 100644 --- a/.changeset/wicked-scissors-yell.md +++ b/.changeset/wicked-scissors-yell.md @@ -2,4 +2,4 @@ "@firebase/auth": patch --- -Fixed issue where requestSts wasn't including the Firebase Studio cookie in it +Fixed issue where Firebase Auth cookie refresh attempts issues in Firebase Studio resulted in CORS errors. From a0583094aac1bfd20ce695692623770d02068543 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 10 Jun 2025 11:28:14 -0700 Subject: [PATCH 65/77] build(all): Version Packages v11.9.1 (#9089) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> v11.9.1 release. --- .changeset/many-mails-marry.md | 5 ----- .changeset/wicked-scissors-yell.md | 5 ----- integration/compat-interop/package.json | 4 ++-- integration/messaging/package.json | 2 +- packages/auth-compat/CHANGELOG.md | 7 +++++++ packages/auth-compat/package.json | 4 ++-- packages/auth/CHANGELOG.md | 6 ++++++ packages/auth/package.json | 2 +- packages/firebase/CHANGELOG.md | 10 ++++++++++ packages/firebase/package.json | 10 +++++----- packages/firestore/package.json | 2 +- packages/storage-compat/CHANGELOG.md | 7 +++++++ packages/storage-compat/package.json | 6 +++--- packages/storage/CHANGELOG.md | 6 ++++++ packages/storage/package.json | 4 ++-- 15 files changed, 53 insertions(+), 27 deletions(-) delete mode 100644 .changeset/many-mails-marry.md delete mode 100644 .changeset/wicked-scissors-yell.md diff --git a/.changeset/many-mails-marry.md b/.changeset/many-mails-marry.md deleted file mode 100644 index 409001defc6..00000000000 --- a/.changeset/many-mails-marry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@firebase/storage': patch ---- - -Fixed issue where Firebase Studio wasn't populating cookies for Storage users diff --git a/.changeset/wicked-scissors-yell.md b/.changeset/wicked-scissors-yell.md deleted file mode 100644 index a013da3001b..00000000000 --- a/.changeset/wicked-scissors-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@firebase/auth": patch ---- - -Fixed issue where Firebase Auth cookie refresh attempts issues in Firebase Studio resulted in CORS errors. diff --git a/integration/compat-interop/package.json b/integration/compat-interop/package.json index 34a1336c733..f7da859c705 100644 --- a/integration/compat-interop/package.json +++ b/integration/compat-interop/package.json @@ -12,8 +12,8 @@ "@firebase/app-compat": "0.4.1", "@firebase/analytics": "0.10.16", "@firebase/analytics-compat": "0.2.22", - "@firebase/auth": "1.10.6", - "@firebase/auth-compat": "0.5.26", + "@firebase/auth": "1.10.7", + "@firebase/auth-compat": "0.5.27", "@firebase/functions": "0.12.8", "@firebase/functions-compat": "0.3.25", "@firebase/messaging": "0.12.21", diff --git a/integration/messaging/package.json b/integration/messaging/package.json index e3682df4657..9c62c70ca5b 100644 --- a/integration/messaging/package.json +++ b/integration/messaging/package.json @@ -9,7 +9,7 @@ "test:manual": "mocha --exit" }, "devDependencies": { - "firebase": "11.9.0", + "firebase": "11.9.1", "chai": "4.5.0", "chromedriver": "119.0.1", "express": "4.21.2", diff --git a/packages/auth-compat/CHANGELOG.md b/packages/auth-compat/CHANGELOG.md index aed6222d935..4dfdf4ce661 100644 --- a/packages/auth-compat/CHANGELOG.md +++ b/packages/auth-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/auth-compat +## 0.5.27 + +### Patch Changes + +- Updated dependencies [[`c0617a3`](https://github.com/firebase/firebase-js-sdk/commit/c0617a341a693c2578a21b35a4f7b27b726defef)]: + - @firebase/auth@1.10.7 + ## 0.5.26 ### Patch Changes diff --git a/packages/auth-compat/package.json b/packages/auth-compat/package.json index cfca5a5bee6..c27a8e1f31a 100644 --- a/packages/auth-compat/package.json +++ b/packages/auth-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth-compat", - "version": "0.5.26", + "version": "0.5.27", "description": "FirebaseAuth compatibility package that uses API style compatible with Firebase@8 and prior versions", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -49,7 +49,7 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/auth": "1.10.6", + "@firebase/auth": "1.10.7", "@firebase/auth-types": "0.13.0", "@firebase/component": "0.6.17", "@firebase/util": "1.12.0", diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md index fe546adc28a..5277c61cfa8 100644 --- a/packages/auth/CHANGELOG.md +++ b/packages/auth/CHANGELOG.md @@ -1,5 +1,11 @@ # @firebase/auth +## 1.10.7 + +### Patch Changes + +- [`c0617a3`](https://github.com/firebase/firebase-js-sdk/commit/c0617a341a693c2578a21b35a4f7b27b726defef) [#9075](https://github.com/firebase/firebase-js-sdk/pull/9075) - Fixed issue where Firebase Auth cookie refresh attempts issues in Firebase Studio resulted in CORS errors. + ## 1.10.6 ### Patch Changes diff --git a/packages/auth/package.json b/packages/auth/package.json index 2da4cb10b30..9ec35cfaec2 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/auth", - "version": "1.10.6", + "version": "1.10.7", "description": "The Firebase Authenticaton component of the Firebase JS SDK.", "author": "Firebase (https://firebase.google.com/)", "main": "dist/node/index.js", diff --git a/packages/firebase/CHANGELOG.md b/packages/firebase/CHANGELOG.md index 84eb512291d..7885817632d 100644 --- a/packages/firebase/CHANGELOG.md +++ b/packages/firebase/CHANGELOG.md @@ -1,5 +1,15 @@ # firebase +## 11.9.1 + +### Patch Changes + +- Updated dependencies [[`0f891d8`](https://github.com/firebase/firebase-js-sdk/commit/0f891d861bdf4e7bac8cd777f5fb32d0b7b9bf8e), [`c0617a3`](https://github.com/firebase/firebase-js-sdk/commit/c0617a341a693c2578a21b35a4f7b27b726defef)]: + - @firebase/storage@0.13.13 + - @firebase/auth@1.10.7 + - @firebase/storage-compat@0.3.23 + - @firebase/auth-compat@0.5.27 + ## 11.9.0 ### Minor Changes diff --git a/packages/firebase/package.json b/packages/firebase/package.json index 85bcab03aa6..4f785d15c8c 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -1,6 +1,6 @@ { "name": "firebase", - "version": "11.9.0", + "version": "11.9.1", "description": "Firebase JavaScript library for web and Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -415,8 +415,8 @@ "@firebase/app": "0.13.1", "@firebase/app-compat": "0.4.1", "@firebase/app-types": "0.9.3", - "@firebase/auth": "1.10.6", - "@firebase/auth-compat": "0.5.26", + "@firebase/auth": "1.10.7", + "@firebase/auth-compat": "0.5.27", "@firebase/data-connect": "0.3.9", "@firebase/database": "1.0.19", "@firebase/database-compat": "2.0.10", @@ -428,8 +428,8 @@ "@firebase/installations-compat": "0.2.17", "@firebase/messaging": "0.12.21", "@firebase/messaging-compat": "0.2.21", - "@firebase/storage": "0.13.12", - "@firebase/storage-compat": "0.3.22", + "@firebase/storage": "0.13.13", + "@firebase/storage-compat": "0.3.23", "@firebase/performance": "0.7.6", "@firebase/performance-compat": "0.2.19", "@firebase/remote-config": "0.6.4", diff --git a/packages/firestore/package.json b/packages/firestore/package.json index d53f8d36bb0..638b8914483 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -114,7 +114,7 @@ "devDependencies": { "@firebase/app": "0.13.1", "@firebase/app-compat": "0.4.1", - "@firebase/auth": "1.10.6", + "@firebase/auth": "1.10.7", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", "@types/eslint": "7.29.0", diff --git a/packages/storage-compat/CHANGELOG.md b/packages/storage-compat/CHANGELOG.md index 48117be7e87..7988847b87b 100644 --- a/packages/storage-compat/CHANGELOG.md +++ b/packages/storage-compat/CHANGELOG.md @@ -1,5 +1,12 @@ # @firebase/storage-compat +## 0.3.23 + +### Patch Changes + +- Updated dependencies [[`0f891d8`](https://github.com/firebase/firebase-js-sdk/commit/0f891d861bdf4e7bac8cd777f5fb32d0b7b9bf8e)]: + - @firebase/storage@0.13.13 + ## 0.3.22 ### Patch Changes diff --git a/packages/storage-compat/package.json b/packages/storage-compat/package.json index f631350bce8..c5a31d6c6f0 100644 --- a/packages/storage-compat/package.json +++ b/packages/storage-compat/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage-compat", - "version": "0.3.22", + "version": "0.3.23", "description": "The Firebase Firestore compatibility package", "author": "Firebase (https://firebase.google.com/)", "main": "./dist/index.cjs.js", @@ -37,7 +37,7 @@ "@firebase/app-compat": "0.x" }, "dependencies": { - "@firebase/storage": "0.13.12", + "@firebase/storage": "0.13.13", "@firebase/storage-types": "0.8.3", "@firebase/util": "1.12.0", "@firebase/component": "0.6.17", @@ -45,7 +45,7 @@ }, "devDependencies": { "@firebase/app-compat": "0.4.1", - "@firebase/auth-compat": "0.5.26", + "@firebase/auth-compat": "0.5.27", "rollup": "2.79.2", "@rollup/plugin-json": "6.1.0", "rollup-plugin-typescript2": "0.36.0", diff --git a/packages/storage/CHANGELOG.md b/packages/storage/CHANGELOG.md index cabce495546..af1ec2bd2f0 100644 --- a/packages/storage/CHANGELOG.md +++ b/packages/storage/CHANGELOG.md @@ -1,5 +1,11 @@ #Unreleased +## 0.13.13 + +### Patch Changes + +- [`0f891d8`](https://github.com/firebase/firebase-js-sdk/commit/0f891d861bdf4e7bac8cd777f5fb32d0b7b9bf8e) [#9059](https://github.com/firebase/firebase-js-sdk/pull/9059) - Fixed issue where Firebase Studio wasn't populating cookies for Storage users + ## 0.13.12 ### Patch Changes diff --git a/packages/storage/package.json b/packages/storage/package.json index 2884f28dea2..bb4a5004840 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@firebase/storage", - "version": "0.13.12", + "version": "0.13.13", "description": "", "author": "Firebase (https://firebase.google.com/)", "main": "dist/index.node.cjs.js", @@ -55,7 +55,7 @@ }, "devDependencies": { "@firebase/app": "0.13.1", - "@firebase/auth": "1.10.6", + "@firebase/auth": "1.10.7", "rollup": "2.79.2", "@rollup/plugin-alias": "5.1.1", "@rollup/plugin-json": "6.1.0", From 7fb64dd82118289950d62a64b2c4a77f00e15c4b Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Fri, 13 Jun 2025 10:07:17 -0400 Subject: [PATCH 66/77] test(ai): Upgrade mock responses to v14 (#9091) --- packages/ai/src/requests/stream-reader.test.ts | 8 ++++---- scripts/update_vertexai_responses.sh | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ai/src/requests/stream-reader.test.ts b/packages/ai/src/requests/stream-reader.test.ts index ea832c7816f..f0298082f68 100644 --- a/packages/ai/src/requests/stream-reader.test.ts +++ b/packages/ai/src/requests/stream-reader.test.ts @@ -102,8 +102,8 @@ describe('processStream', () => { expect(response.text()).to.not.be.empty; } const aggregatedResponse = await result.response; - expect(aggregatedResponse.text()).to.include('**Cats:**'); - expect(aggregatedResponse.text()).to.include('to their owners.'); + expect(aggregatedResponse.text()).to.include('Okay'); + expect(aggregatedResponse.text()).to.include('brewing delicious coffee'); }); it('streaming response - long - big chunk', async () => { const fakeResponse = getMockResponseStreaming( @@ -116,8 +116,8 @@ describe('processStream', () => { expect(response.text()).to.not.be.empty; } const aggregatedResponse = await result.response; - expect(aggregatedResponse.text()).to.include('**Cats:**'); - expect(aggregatedResponse.text()).to.include('to their owners.'); + expect(aggregatedResponse.text()).to.include('Okay'); + expect(aggregatedResponse.text()).to.include('brewing delicious coffee'); }); it('streaming response - utf8', async () => { const fakeResponse = getMockResponseStreaming( diff --git a/scripts/update_vertexai_responses.sh b/scripts/update_vertexai_responses.sh index 680a71ff776..f6557ab0c0a 100755 --- a/scripts/update_vertexai_responses.sh +++ b/scripts/update_vertexai_responses.sh @@ -17,7 +17,7 @@ # This script replaces mock response files for Vertex AI unit tests with a fresh # clone of the shared repository of Vertex AI test data. -RESPONSES_VERSION='v11.*' # The major version of mock responses to use +RESPONSES_VERSION='v14.*' # The major version of mock responses to use REPO_NAME="vertexai-sdk-test-data" REPO_LINK="https://github.com/FirebaseExtended/$REPO_NAME.git" From 13e6cce882d687e06c8d9bfb56895f8a77fc57b5 Mon Sep 17 00:00:00 2001 From: eranc-google Date: Wed, 18 Jun 2025 00:40:56 +0300 Subject: [PATCH 67/77] Feat/google3 typings support (#9085) --- .changeset/brave-boats-arrive.md | 7 +++++++ packages/analytics/package.json | 1 + packages/analytics/rollup.config.js | 16 +++++++++++++++- packages/remote-config/package.json | 1 + packages/remote-config/rollup.config.js | 16 +++++++++++++++- 5 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 .changeset/brave-boats-arrive.md diff --git a/.changeset/brave-boats-arrive.md b/.changeset/brave-boats-arrive.md new file mode 100644 index 00000000000..1e2f703ddb3 --- /dev/null +++ b/.changeset/brave-boats-arrive.md @@ -0,0 +1,7 @@ +--- +'@firebase/remote-config': patch +'@firebase/analytics': patch +'firebase': patch +--- + +Add rollup config to generate modular typings for google3 diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 3f8decfc35f..5d4c4d06735 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -49,6 +49,7 @@ "devDependencies": { "@firebase/app": "0.13.1", "rollup": "2.79.2", + "rollup-plugin-dts": "5.3.1", "@rollup/plugin-commonjs": "21.1.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-node-resolve": "16.0.0", diff --git a/packages/analytics/rollup.config.js b/packages/analytics/rollup.config.js index a22194d1d4a..529858f147f 100644 --- a/packages/analytics/rollup.config.js +++ b/packages/analytics/rollup.config.js @@ -19,6 +19,7 @@ import json from '@rollup/plugin-json'; import typescriptPlugin from 'rollup-plugin-typescript2'; import replace from 'rollup-plugin-replace'; import typescript from 'typescript'; +import dts from 'rollup-plugin-dts'; import { generateBuildTargetReplaceConfig } from '../../scripts/build/rollup_replace_build_target'; import { emitModulePackageFile } from '../../scripts/build/rollup_emit_module_package_file'; import pkg from './package.json'; @@ -77,4 +78,17 @@ const cjsBuilds = [ } ]; -export default [...esmBuilds, ...cjsBuilds]; +const google3TypingsBuild = { + input: 'dist/src/index.d.ts', + output: { + file: 'dist/src/global_index.d.ts', + format: 'es' + }, + plugins: [ + dts({ + respectExternal: true + }) + ] +}; + +export default [...esmBuilds, ...cjsBuilds, google3TypingsBuild]; diff --git a/packages/remote-config/package.json b/packages/remote-config/package.json index 194f9806cb7..43648d267d4 100644 --- a/packages/remote-config/package.json +++ b/packages/remote-config/package.json @@ -50,6 +50,7 @@ "devDependencies": { "@firebase/app": "0.13.1", "rollup": "2.79.2", + "rollup-plugin-dts": "5.3.1", "rollup-plugin-typescript2": "0.36.0", "typescript": "5.5.4" }, diff --git a/packages/remote-config/rollup.config.js b/packages/remote-config/rollup.config.js index 36622f4dae2..d8eb3abd315 100644 --- a/packages/remote-config/rollup.config.js +++ b/packages/remote-config/rollup.config.js @@ -19,6 +19,7 @@ import json from '@rollup/plugin-json'; // Enables package.json import in TypeSc import typescriptPlugin from 'rollup-plugin-typescript2'; import replace from 'rollup-plugin-replace'; import typescript from 'typescript'; +import dts from 'rollup-plugin-dts'; import { generateBuildTargetReplaceConfig } from '../../scripts/build/rollup_replace_build_target'; import { emitModulePackageFile } from '../../scripts/build/rollup_emit_module_package_file'; import pkg from './package.json'; @@ -70,4 +71,17 @@ const cjsBuild = { ] }; -export default [esmBuild, cjsBuild]; +const google3TypingsBuild = { + input: 'dist/src/index.d.ts', + output: { + file: 'dist/src/global_index.d.ts', + format: 'es' + }, + plugins: [ + dts({ + respectExternal: true + }) + ] +}; + +export default [esmBuild, cjsBuild, google3TypingsBuild]; From b97eab36a3553c906c35f4751a0b17c717178b13 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Wed, 18 Jun 2025 15:54:01 -0400 Subject: [PATCH 68/77] fix(ai): Add deprecation tag to `totalBillableCharacters` (#9090) --- .changeset/old-candles-confess.md | 5 +++++ common/api-review/ai.api.md | 1 + docs-devsite/ai.counttokensresponse.md | 11 +++++++---- packages/ai/src/types/responses.ts | 5 ++--- 4 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 .changeset/old-candles-confess.md diff --git a/.changeset/old-candles-confess.md b/.changeset/old-candles-confess.md new file mode 100644 index 00000000000..6fbe742818f --- /dev/null +++ b/.changeset/old-candles-confess.md @@ -0,0 +1,5 @@ +--- +'@firebase/ai': patch +--- + +Add deprecation label to `totalBillableCharacters`. `totalTokens` should be used instead. diff --git a/common/api-review/ai.api.md b/common/api-review/ai.api.md index 23c13688fb1..ab79447798f 100644 --- a/common/api-review/ai.api.md +++ b/common/api-review/ai.api.md @@ -164,6 +164,7 @@ export interface CountTokensRequest { // @public export interface CountTokensResponse { promptTokensDetails?: ModalityTokenCount[]; + // @deprecated (undocumented) totalBillableCharacters?: number; totalTokens: number; } diff --git a/docs-devsite/ai.counttokensresponse.md b/docs-devsite/ai.counttokensresponse.md index 71e64d885d8..8a7181a2e03 100644 --- a/docs-devsite/ai.counttokensresponse.md +++ b/docs-devsite/ai.counttokensresponse.md @@ -23,7 +23,7 @@ export interface CountTokensResponse | Property | Type | Description | | --- | --- | --- | | [promptTokensDetails](./ai.counttokensresponse.md#counttokensresponseprompttokensdetails) | [ModalityTokenCount](./ai.modalitytokencount.md#modalitytokencount_interface)\[\] | The breakdown, by modality, of how many tokens are consumed by the prompt. | -| [totalBillableCharacters](./ai.counttokensresponse.md#counttokensresponsetotalbillablecharacters) | number | The total number of billable characters counted across all instances from the request.This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. | +| [totalBillableCharacters](./ai.counttokensresponse.md#counttokensresponsetotalbillablecharacters) | number | | | [totalTokens](./ai.counttokensresponse.md#counttokensresponsetotaltokens) | number | The total number of tokens counted across all instances from the request. | ## CountTokensResponse.promptTokensDetails @@ -38,9 +38,12 @@ promptTokensDetails?: ModalityTokenCount[]; ## CountTokensResponse.totalBillableCharacters -The total number of billable characters counted across all instances from the request. - -This property is only supported when using the Vertex AI Gemini API ([VertexAIBackend](./ai.vertexaibackend.md#vertexaibackend_class)). When using the Gemini Developer API ([GoogleAIBackend](./ai.googleaibackend.md#googleaibackend_class)), this property is not supported and will default to 0. +> Warning: This API is now obsolete. +> +> Use `totalTokens` instead. This property is undefined when using models greater than `gemini-1.5-*`. +> +> The total number of billable characters counted across all instances from the request. +> Signature: diff --git a/packages/ai/src/types/responses.ts b/packages/ai/src/types/responses.ts index e33b8a86bd3..8072e06fd02 100644 --- a/packages/ai/src/types/responses.ts +++ b/packages/ai/src/types/responses.ts @@ -270,11 +270,10 @@ export interface CountTokensResponse { */ totalTokens: number; /** + * @deprecated Use `totalTokens` instead. This property is undefined when using models greater than `gemini-1.5-*`. + * * The total number of billable characters counted across all instances * from the request. - * - * This property is only supported when using the Vertex AI Gemini API ({@link VertexAIBackend}). - * When using the Gemini Developer API ({@link GoogleAIBackend}), this property is not supported and will default to 0. */ totalBillableCharacters?: number; /** From 41e3c4cdfc459dcbb9c0e1239684aa12c7d1f779 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Fri, 20 Jun 2025 15:45:18 -0400 Subject: [PATCH 69/77] test(ai): Add `gemini-2.5-flash` to integration tests (#9110) Added gemini-2.5-flash to our integration tests now that it's publicly available. The token counts differ slightly between 2.0-flash and 2.5-flash so I introduced conditionals when checking token counts. --- packages/ai/integration/chat.test.ts | 105 +++++++++++------- packages/ai/integration/constants.ts | 2 +- .../ai/integration/generate-content.test.ts | 91 ++++++++++----- 3 files changed, 129 insertions(+), 69 deletions(-) diff --git a/packages/ai/integration/chat.test.ts b/packages/ai/integration/chat.test.ts index 6af0e7a9af9..b6772a38fb1 100644 --- a/packages/ai/integration/chat.test.ts +++ b/packages/ai/integration/chat.test.ts @@ -76,56 +76,85 @@ describe('Chat Session', () => { 'What is the capital of France?' ); const response1 = result1.response; - expect(response1.text().trim().toLowerCase()).to.include('paris'); + const result2 = await chat.sendMessage('And what about Italy?'); + const response2 = result2.response; + const history = await chat.getHistory(); - let history = await chat.getHistory(); - expect(history.length).to.equal(2); + expect(response1.text().trim().toLowerCase()).to.include('paris'); + expect(response1.usageMetadata).to.not.be.null; + expect(response2.text().trim().toLowerCase()).to.include('rome'); + expect(response2.usageMetadata).to.not.be.null; + expect(history.length).to.equal(4); expect(history[0].role).to.equal('user'); expect(history[0].parts[0].text).to.equal( 'What is the capital of France?' ); expect(history[1].role).to.equal('model'); expect(history[1].parts[0].text?.toLowerCase()).to.include('paris'); - - expect(response1.usageMetadata).to.not.be.null; - // Token counts can vary slightly in chat context - expect(response1.usageMetadata!.promptTokenCount).to.be.closeTo( - 15, // "What is the capital of France?" + system instruction - TOKEN_COUNT_DELTA + 2 // More variance for chat context - ); - expect(response1.usageMetadata!.candidatesTokenCount).to.be.closeTo( - 8, // "Paris" - TOKEN_COUNT_DELTA - ); - expect(response1.usageMetadata!.totalTokenCount).to.be.closeTo( - 23, // "What is the capital of France?" + system instruction + "Paris" - TOKEN_COUNT_DELTA + 3 // More variance for chat context - ); - - const result2 = await chat.sendMessage('And what about Italy?'); - const response2 = result2.response; - expect(response2.text().trim().toLowerCase()).to.include('rome'); - - history = await chat.getHistory(); - expect(history.length).to.equal(4); expect(history[2].role).to.equal('user'); expect(history[2].parts[0].text).to.equal('And what about Italy?'); expect(history[3].role).to.equal('model'); expect(history[3].parts[0].text?.toLowerCase()).to.include('rome'); - expect(response2.usageMetadata).to.not.be.null; - expect(response2.usageMetadata!.promptTokenCount).to.be.closeTo( - 28, // History + "And what about Italy?" + system instruction - TOKEN_COUNT_DELTA + 5 // More variance for chat context with history - ); - expect(response2.usageMetadata!.candidatesTokenCount).to.be.closeTo( - 8, - TOKEN_COUNT_DELTA - ); - expect(response2.usageMetadata!.totalTokenCount).to.be.closeTo( - 36, - TOKEN_COUNT_DELTA - ); + if (model.model.includes('gemini-2.5-flash')) { + // Token counts can vary slightly in chat context + expect(response1.usageMetadata!.promptTokenCount).to.be.closeTo( + 17, // "What is the capital of France?" + system instruction + TOKEN_COUNT_DELTA + 2 // More variance for chat context + ); + expect(response1.usageMetadata!.candidatesTokenCount).to.be.closeTo( + 8, // "Paris" + TOKEN_COUNT_DELTA + ); + expect(response1.usageMetadata!.totalTokenCount).to.be.closeTo( + 49, // "What is the capital of France?" + system instruction + "Paris" + TOKEN_COUNT_DELTA + 3 // More variance for chat context + ); + expect(response1.usageMetadata!.totalTokenCount).to.be.closeTo( + 49, // "What is the capital of France?" + system instruction + "Paris" + TOKEN_COUNT_DELTA + 3 // More variance for chat context + ); + + expect(response2.usageMetadata!.promptTokenCount).to.be.closeTo( + 32, // History + "And what about Italy?" + system instruction + TOKEN_COUNT_DELTA + 5 // More variance for chat context with history + ); + expect(response2.usageMetadata!.candidatesTokenCount).to.be.closeTo( + 8, + TOKEN_COUNT_DELTA + ); + expect(response2.usageMetadata!.totalTokenCount).to.be.closeTo( + 68, + TOKEN_COUNT_DELTA + 2 + ); + } else if (model.model.includes('gemini-2.0-flash')) { + expect(response1.usageMetadata).to.not.be.null; + // Token counts can vary slightly in chat context + expect(response1.usageMetadata!.promptTokenCount).to.be.closeTo( + 15, // "What is the capital of France?" + system instruction + TOKEN_COUNT_DELTA + 2 // More variance for chat context + ); + expect(response1.usageMetadata!.candidatesTokenCount).to.be.closeTo( + 8, // "Paris" + TOKEN_COUNT_DELTA + ); + expect(response1.usageMetadata!.totalTokenCount).to.be.closeTo( + 23, // "What is the capital of France?" + system instruction + "Paris" + TOKEN_COUNT_DELTA + 3 // More variance for chat context + ); + expect(response2.usageMetadata!.promptTokenCount).to.be.closeTo( + 28, // History + "And what about Italy?" + system instruction + TOKEN_COUNT_DELTA + 5 // More variance for chat context with history + ); + expect(response2.usageMetadata!.candidatesTokenCount).to.be.closeTo( + 8, + TOKEN_COUNT_DELTA + ); + expect(response2.usageMetadata!.totalTokenCount).to.be.closeTo( + 36, + TOKEN_COUNT_DELTA + ); + } }); }); }); diff --git a/packages/ai/integration/constants.ts b/packages/ai/integration/constants.ts index 68aebf9eddc..1adfa4f47a0 100644 --- a/packages/ai/integration/constants.ts +++ b/packages/ai/integration/constants.ts @@ -52,7 +52,7 @@ const backendNames: Map = new Map([ [BackendType.VERTEX_AI, 'Vertex AI'] ]); -const modelNames: readonly string[] = ['gemini-2.0-flash']; +const modelNames: readonly string[] = ['gemini-2.0-flash', 'gemini-2.5-flash']; /** * Array of test configurations that is iterated over to get full coverage diff --git a/packages/ai/integration/generate-content.test.ts b/packages/ai/integration/generate-content.test.ts index af877396cc8..22e4b0a30ac 100644 --- a/packages/ai/integration/generate-content.test.ts +++ b/packages/ai/integration/generate-content.test.ts @@ -81,36 +81,67 @@ describe('Generate Content', () => { expect(trimmedText).to.equal('Mountain View'); expect(response.usageMetadata).to.not.be.null; - expect(response.usageMetadata!.promptTokenCount).to.be.closeTo( - 21, - TOKEN_COUNT_DELTA - ); - expect(response.usageMetadata!.candidatesTokenCount).to.be.closeTo( - 4, - TOKEN_COUNT_DELTA - ); - expect(response.usageMetadata!.totalTokenCount).to.be.closeTo( - 25, - TOKEN_COUNT_DELTA * 2 - ); - expect(response.usageMetadata!.promptTokensDetails).to.not.be.null; - expect(response.usageMetadata!.promptTokensDetails!.length).to.equal(1); - expect( - response.usageMetadata!.promptTokensDetails![0].modality - ).to.equal(Modality.TEXT); - expect( - response.usageMetadata!.promptTokensDetails![0].tokenCount - ).to.equal(21); - expect(response.usageMetadata!.candidatesTokensDetails).to.not.be.null; - expect( - response.usageMetadata!.candidatesTokensDetails!.length - ).to.equal(1); - expect( - response.usageMetadata!.candidatesTokensDetails![0].modality - ).to.equal(Modality.TEXT); - expect( - response.usageMetadata!.candidatesTokensDetails![0].tokenCount - ).to.be.closeTo(4, TOKEN_COUNT_DELTA); + + if (model.model.includes('gemini-2.5-flash')) { + expect(response.usageMetadata!.promptTokenCount).to.be.closeTo( + 22, + TOKEN_COUNT_DELTA + ); + expect(response.usageMetadata!.candidatesTokenCount).to.be.closeTo( + 2, + TOKEN_COUNT_DELTA + ); + expect(response.usageMetadata!.totalTokenCount).to.be.closeTo( + 55, + TOKEN_COUNT_DELTA * 2 + ); + expect(response.usageMetadata!.promptTokensDetails).to.not.be.null; + expect(response.usageMetadata!.promptTokensDetails!.length).to.equal( + 1 + ); + expect( + response.usageMetadata!.promptTokensDetails![0].modality + ).to.equal(Modality.TEXT); + expect( + response.usageMetadata!.promptTokensDetails![0].tokenCount + ).to.closeTo(22, TOKEN_COUNT_DELTA); + + // candidatesTokenDetails comes back about half the time, so let's just not test it. + } else if (model.model.includes('gemini-2.0-flash')) { + expect(response.usageMetadata!.promptTokenCount).to.be.closeTo( + 21, + TOKEN_COUNT_DELTA + ); + expect(response.usageMetadata!.candidatesTokenCount).to.be.closeTo( + 4, + TOKEN_COUNT_DELTA + ); + expect(response.usageMetadata!.totalTokenCount).to.be.closeTo( + 25, + TOKEN_COUNT_DELTA * 2 + ); + expect(response.usageMetadata!.promptTokensDetails).to.not.be.null; + expect(response.usageMetadata!.promptTokensDetails!.length).to.equal( + 1 + ); + expect( + response.usageMetadata!.promptTokensDetails![0].modality + ).to.equal(Modality.TEXT); + expect( + response.usageMetadata!.promptTokensDetails![0].tokenCount + ).to.equal(21); + expect(response.usageMetadata!.candidatesTokensDetails).to.not.be + .null; + expect( + response.usageMetadata!.candidatesTokensDetails!.length + ).to.equal(1); + expect( + response.usageMetadata!.candidatesTokensDetails![0].modality + ).to.equal(Modality.TEXT); + expect( + response.usageMetadata!.candidatesTokensDetails![0].tokenCount + ).to.be.closeTo(4, TOKEN_COUNT_DELTA); + } }); it('generateContentStream: text input, text output', async () => { From f73e08b212314547b39a10cd3e393f9e94776f21 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Fri, 20 Jun 2025 21:23:24 +0000 Subject: [PATCH 70/77] firestore: minor refactor of listener registration of "versionchange" indexedb events (#9087) --- .changeset/long-pets-sell.md | 5 +++ .../firestore/src/core/firestore_client.ts | 22 ++++++++--- .../src/local/indexeddb_persistence.ts | 29 +++++++++----- packages/firestore/src/local/persistence.ts | 18 +++++++-- packages/firestore/src/local/simple_db.ts | 38 +++++++++++++------ .../test/unit/specs/spec_test_runner.ts | 10 ++++- 6 files changed, 90 insertions(+), 32 deletions(-) create mode 100644 .changeset/long-pets-sell.md diff --git a/.changeset/long-pets-sell.md b/.changeset/long-pets-sell.md new file mode 100644 index 00000000000..d340f7da82c --- /dev/null +++ b/.changeset/long-pets-sell.md @@ -0,0 +1,5 @@ +--- +'@firebase/firestore': patch +--- + +Internal listener registration change for IndexedDB "versionchange" events. diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index e2aa19aaba8..e43060d229d 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -230,11 +230,23 @@ export async function setOfflineComponentProvider( } }); - // When a user calls clearPersistence() in one client, all other clients - // need to be terminated to allow the delete to succeed. - offlineComponentProvider.persistence.setDatabaseDeletedListener(() => - client.terminate() - ); + offlineComponentProvider.persistence.setDatabaseDeletedListener(() => { + logWarn('Terminating Firestore due to IndexedDb database deletion'); + client + .terminate() + .then(() => { + logDebug( + 'Terminating Firestore due to IndexedDb database deletion ' + + 'completed successfully' + ); + }) + .catch(error => { + logWarn( + 'Terminating Firestore due to IndexedDb database deletion failed', + error + ); + }); + }); client._offlineComponents = offlineComponentProvider; } diff --git a/packages/firestore/src/local/indexeddb_persistence.ts b/packages/firestore/src/local/indexeddb_persistence.ts index 57c26ea5baa..0ec2baabfe4 100644 --- a/packages/firestore/src/local/indexeddb_persistence.ts +++ b/packages/firestore/src/local/indexeddb_persistence.ts @@ -58,7 +58,11 @@ import { IndexedDbTargetCache } from './indexeddb_target_cache'; import { getStore, IndexedDbTransaction } from './indexeddb_transaction'; import { LocalSerializer } from './local_serializer'; import { LruParams } from './lru_garbage_collector'; -import { Persistence, PrimaryStateListener } from './persistence'; +import { + DatabaseDeletedListener, + Persistence, + PrimaryStateListener +} from './persistence'; import { PersistencePromise } from './persistence_promise'; import { PersistenceTransaction, @@ -324,20 +328,25 @@ export class IndexedDbPersistence implements Persistence { } /** - * Registers a listener that gets called when the database receives a - * version change event indicating that it has deleted. + * Registers a listener that gets called when the underlying database receives + * an event indicating that it either has been deleted or is pending deletion + * and must be closed. + * + * For example, this callback will be called in the case that multi-tab + * IndexedDB persistence is in use and another tab calls + * clearIndexedDbPersistence(). In that case, this Firestore instance must + * close its IndexedDB connection in order to allow the deletion initiated by + * the other tab to proceed. + * + * This method may only be called once; subsequent invocations will result in + * an exception, refusing to supersede the previously-registered listener. * * PORTING NOTE: This is only used for Web multi-tab. */ setDatabaseDeletedListener( - databaseDeletedListener: () => Promise + databaseDeletedListener: DatabaseDeletedListener ): void { - this.simpleDb.setVersionChangeListener(async event => { - // Check if an attempt is made to delete IndexedDB. - if (event.newVersion === null) { - await databaseDeletedListener(); - } - }); + this.simpleDb.setDatabaseDeletedListener(databaseDeletedListener); } /** diff --git a/packages/firestore/src/local/persistence.ts b/packages/firestore/src/local/persistence.ts index b014a6479ac..113efe7b7d3 100644 --- a/packages/firestore/src/local/persistence.ts +++ b/packages/firestore/src/local/persistence.ts @@ -98,6 +98,8 @@ export interface ReferenceDelegate { ): PersistencePromise; } +export type DatabaseDeletedListener = () => void; + /** * Persistence is the lowest-level shared interface to persistent storage in * Firestore. @@ -151,13 +153,23 @@ export interface Persistence { shutdown(): Promise; /** - * Registers a listener that gets called when the database receives a - * version change event indicating that it has deleted. + * Registers a listener that gets called when the underlying database receives + * an event indicating that it either has been deleted or is pending deletion + * and must be closed. + * + * For example, this callback will be called in the case that multi-tab + * IndexedDB persistence is in use and another tab calls + * clearIndexedDbPersistence(). In that case, this Firestore instance must + * close its IndexedDB connection in order to allow the deletion initiated by + * the other tab to proceed. + * + * This method may only be called once; subsequent invocations will result in + * an exception, refusing to supersede the previously-registered listener. * * PORTING NOTE: This is only used for Web multi-tab. */ setDatabaseDeletedListener( - databaseDeletedListener: () => Promise + databaseDeletedListener: DatabaseDeletedListener ): void; /** diff --git a/packages/firestore/src/local/simple_db.ts b/packages/firestore/src/local/simple_db.ts index 6d27702e725..1e315c5dae6 100644 --- a/packages/firestore/src/local/simple_db.ts +++ b/packages/firestore/src/local/simple_db.ts @@ -19,9 +19,10 @@ import { getGlobal, getUA, isIndexedDBAvailable } from '@firebase/util'; import { debugAssert } from '../util/assert'; import { Code, FirestoreError } from '../util/error'; -import { logDebug, logError } from '../util/log'; +import { logDebug, logError, logWarn } from '../util/log'; import { Deferred } from '../util/promise'; +import { DatabaseDeletedListener } from './persistence'; import { PersistencePromise } from './persistence_promise'; // References to `indexedDB` are guarded by SimpleDb.isAvailable() and getGlobal() @@ -158,8 +159,8 @@ export class SimpleDbTransaction { */ export class SimpleDb { private db?: IDBDatabase; + private databaseDeletedListener?: DatabaseDeletedListener; private lastClosedDbVersion: number | null = null; - private versionchangelistener?: (event: IDBVersionChangeEvent) => void; /** Deletes the specified database. */ static delete(name: string): Promise { @@ -392,22 +393,35 @@ export class SimpleDb { ); } - if (this.versionchangelistener) { - this.db.onversionchange = event => this.versionchangelistener!(event); - } + this.db.addEventListener( + 'versionchange', + event => { + // Notify the listener if another tab attempted to delete the IndexedDb + // database, such as by calling clearIndexedDbPersistence(). + if (event.newVersion === null) { + logWarn( + `Received "versionchange" event with newVersion===null; ` + + 'notifying the registered DatabaseDeletedListener, if any' + ); + this.databaseDeletedListener?.(); + } + }, + { passive: true } + ); return this.db; } - setVersionChangeListener( - versionChangeListener: (event: IDBVersionChangeEvent) => void + setDatabaseDeletedListener( + databaseDeletedListener: DatabaseDeletedListener ): void { - this.versionchangelistener = versionChangeListener; - if (this.db) { - this.db.onversionchange = (event: IDBVersionChangeEvent) => { - return versionChangeListener(event); - }; + if (this.databaseDeletedListener) { + throw new Error( + 'setDatabaseDeletedListener() may only be called once, ' + + 'and it has already been called' + ); } + this.databaseDeletedListener = databaseDeletedListener; } async runTransaction( diff --git a/packages/firestore/test/unit/specs/spec_test_runner.ts b/packages/firestore/test/unit/specs/spec_test_runner.ts index 51d2229b8a1..daa513edb68 100644 --- a/packages/firestore/test/unit/specs/spec_test_runner.ts +++ b/packages/firestore/test/unit/specs/spec_test_runner.ts @@ -365,8 +365,14 @@ abstract class TestRunner { this.eventManager.onLastRemoteStoreUnlisten = triggerRemoteStoreUnlisten.bind(null, this.syncEngine); - await this.persistence.setDatabaseDeletedListener(async () => { - await this.shutdown(); + this.persistence.setDatabaseDeletedListener(() => { + this.shutdown().catch(error => { + console.warn( + 'WARNING: this.shutdown() failed in callback ' + + 'specified to persistence.setDatabaseDeletedListener', + error + ); + }); }); this.started = true; From 42ac4011787db6bb7a08f8c84f364ea86ea51e83 Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Mon, 23 Jun 2025 10:07:19 -0700 Subject: [PATCH 71/77] Fixed issue where storage wasn't passing cookies when using firebase Studio (#9111) --- .changeset/brown-pens-confess.md | 6 ++++++ common/api-review/util.api.md | 2 +- packages/storage/test/browser/connection.test.ts | 2 +- packages/util/src/url.ts | 16 ++++++++++++++-- 4 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 .changeset/brown-pens-confess.md diff --git a/.changeset/brown-pens-confess.md b/.changeset/brown-pens-confess.md new file mode 100644 index 00000000000..038b177796e --- /dev/null +++ b/.changeset/brown-pens-confess.md @@ -0,0 +1,6 @@ +--- +"@firebase/storage": patch +"@firebase/util": patch +--- + +Fixed issue where Storage on Firebase Studio throws CORS errors. diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index f263f450da3..4ac51fda550 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -270,7 +270,7 @@ export function isBrowserExtension(): boolean; export function isCloudflareWorker(): boolean; // @public -export function isCloudWorkstation(host: string): boolean; +export function isCloudWorkstation(url: string): boolean; // Warning: (ae-missing-release-tag) "isElectron" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/packages/storage/test/browser/connection.test.ts b/packages/storage/test/browser/connection.test.ts index 2a0320d0c02..4cb4d4cb919 100644 --- a/packages/storage/test/browser/connection.test.ts +++ b/packages/storage/test/browser/connection.test.ts @@ -35,7 +35,7 @@ describe('Connections', () => { const fakeXHR = useFakeXMLHttpRequest(); const connection = new XhrBytesConnection(); const sendPromise = connection.send( - 'https://abc.cloudworkstations.dev', + 'https://abc.cloudworkstations.dev/test', 'GET', true ); diff --git a/packages/util/src/url.ts b/packages/util/src/url.ts index e41d26594c2..6de92584979 100644 --- a/packages/util/src/url.ts +++ b/packages/util/src/url.ts @@ -19,8 +19,20 @@ * Checks whether host is a cloud workstation or not. * @public */ -export function isCloudWorkstation(host: string): boolean { - return host.endsWith('.cloudworkstations.dev'); +export function isCloudWorkstation(url: string): boolean { + // `isCloudWorkstation` is called without protocol in certain connect*Emulator functions + // In HTTP request builders, it's called with the protocol. + // If called with protocol prefix, it's a valid URL, so we extract the hostname + // If called without, we assume the string is the hostname. + try { + const host = + url.startsWith('http://') || url.startsWith('https://') + ? new URL(url).hostname + : url; + return host.endsWith('.cloudworkstations.dev'); + } catch { + return false; + } } /** From bb57947c942e44b39e5b0254324bee6bf665fd4e Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Mon, 23 Jun 2025 10:56:53 -0700 Subject: [PATCH 72/77] fix: Add "react-native" entry point to @firebase/app (#9112) --- .changeset/giant-lamps-live.md | 5 +++++ packages/app/package.json | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 .changeset/giant-lamps-live.md diff --git a/.changeset/giant-lamps-live.md b/.changeset/giant-lamps-live.md new file mode 100644 index 00000000000..f66c22deb86 --- /dev/null +++ b/.changeset/giant-lamps-live.md @@ -0,0 +1,5 @@ +--- +'@firebase/app': patch +--- + +Add "react-native" entry point to @firebase/app diff --git a/packages/app/package.json b/packages/app/package.json index 5f892ecc525..3d6a0ca5dca 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -6,10 +6,12 @@ "main": "dist/index.cjs.js", "browser": "dist/esm/index.esm2017.js", "module": "dist/esm/index.esm2017.js", + "react-native": "dist/index.cjs.js", "exports": { ".": { "types": "./dist/app-public.d.ts", "require": "./dist/index.cjs.js", + "react-native": "./dist/index.cjs.js", "default": "./dist/esm/index.esm2017.js" }, "./package.json": "./package.json" From 7ae5b12b901620aa101b5caed94064271fb76fb6 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Mon, 23 Jun 2025 14:57:19 -0400 Subject: [PATCH 73/77] Feat(Firestore) JSON serialization of types to improve SSR support. (#8926) Support the ability to resume `onSnapshot` listeners in the CSR phase based on serialized `DataSnapshot`s and `QuerySnapshot`s built in the SSR phase. Allow Firestore result types to be serialized with `toJSON` and then deserialized with `fromJSON` methods on the objects. `DocumentSnapshot` and `QuerySnapshot` deserialization methods will be standalone, tree-shakable functions `dataSnapshotFromJSON` and `querySnapshotFromJSON`. --- common/api-review/firestore-lite.api.md | 11 + common/api-review/firestore.api.md | 65 ++ docs-devsite/firestore_.bytes.md | 39 + docs-devsite/firestore_.documentreference.md | 65 ++ docs-devsite/firestore_.documentsnapshot.md | 16 + docs-devsite/firestore_.geopoint.md | 32 +- docs-devsite/firestore_.md | 353 +++++++++ docs-devsite/firestore_.querysnapshot.md | 16 + docs-devsite/firestore_.timestamp.md | 24 +- docs-devsite/firestore_.vectorvalue.md | 39 + docs-devsite/firestore_lite.bytes.md | 39 + .../firestore_lite.documentreference.md | 65 ++ docs-devsite/firestore_lite.geopoint.md | 32 +- docs-devsite/firestore_lite.timestamp.md | 24 +- docs-devsite/firestore_lite.vectorvalue.md | 39 + packages/firestore/src/api.ts | 3 + packages/firestore/src/api/reference_impl.ts | 672 +++++++++++++++--- packages/firestore/src/api/snapshot.ts | 326 +++++++++ packages/firestore/src/core/bundle_impl.ts | 49 +- .../firestore/src/core/firestore_client.ts | 12 +- .../firestore/src/core/sync_engine_impl.ts | 8 +- packages/firestore/src/lite-api/bytes.ts | 38 + packages/firestore/src/lite-api/geo_point.ts | 45 +- packages/firestore/src/lite-api/reference.ts | 70 ++ packages/firestore/src/lite-api/timestamp.ts | 35 +- .../src/lite-api/user_data_reader.ts | 2 +- .../firestore/src/lite-api/vector_value.ts | 48 ++ .../src/platform/browser/snapshot_to_json.ts | 43 ++ .../platform/browser_lite/snapshot_to_json.ts | 18 + .../src/platform/node/snapshot_to_json.ts | 84 +++ .../platform/node_lite/snapshot_to_json.ts | 18 + .../src/platform/rn/snapshot_to_json.ts | 21 + .../src/platform/rn_lite/snapshot_to_json.ts | 18 + .../src/platform/snapshot_to_json.ts | 62 ++ packages/firestore/src/remote/serializer.ts | 5 +- .../firestore/src/util/bundle_builder_impl.ts | 284 ++++++++ packages/firestore/src/util/bundle_reader.ts | 21 + .../src/util/bundle_reader_sync_impl.ts | 129 ++++ .../firestore/src/util/json_validation.ts | 142 ++++ .../test/integration/api/database.test.ts | 428 ++++++++++- .../test/integration/api/query.test.ts | 44 ++ .../firestore/test/lite/integration.test.ts | 14 + .../firestore/test/unit/api/bytes.test.ts | 48 ++ .../firestore/test/unit/api/database.test.ts | 517 ++++++++++++++ .../firestore/test/unit/api/geo_point.test.ts | 87 ++- .../firestore/test/unit/api/timestamp.test.ts | 92 ++- .../test/unit/api/vector_value.test.ts | 73 ++ .../unit/local/indexeddb_persistence.test.ts | 6 +- packages/firestore/test/util/api_helpers.ts | 8 +- 49 files changed, 4190 insertions(+), 139 deletions(-) create mode 100644 packages/firestore/src/platform/browser/snapshot_to_json.ts create mode 100644 packages/firestore/src/platform/browser_lite/snapshot_to_json.ts create mode 100644 packages/firestore/src/platform/node/snapshot_to_json.ts create mode 100644 packages/firestore/src/platform/node_lite/snapshot_to_json.ts create mode 100644 packages/firestore/src/platform/rn/snapshot_to_json.ts create mode 100644 packages/firestore/src/platform/rn_lite/snapshot_to_json.ts create mode 100644 packages/firestore/src/platform/snapshot_to_json.ts create mode 100644 packages/firestore/src/util/bundle_builder_impl.ts create mode 100644 packages/firestore/src/util/bundle_reader_sync_impl.ts create mode 100644 packages/firestore/src/util/json_validation.ts create mode 100644 packages/firestore/test/unit/api/vector_value.test.ts diff --git a/common/api-review/firestore-lite.api.md b/common/api-review/firestore-lite.api.md index 4a9ef4c0171..46b85a0efc5 100644 --- a/common/api-review/firestore-lite.api.md +++ b/common/api-review/firestore-lite.api.md @@ -68,9 +68,11 @@ export function average(field: string | FieldPath): AggregateField { readonly converter: FirestoreDataConverter | null; readonly firestore: Firestore; + static fromJSON(firestore: Firestore, json: object): DocumentReference; + static fromJSON(firestore: Firestore, json: object, converter: FirestoreDataConverter): DocumentReference; get id(): string; get parent(): CollectionReference; get path(): string; + toJSON(): object; readonly type = "document"; withConverter(converter: FirestoreDataConverter): DocumentReference; withConverter(converter: null): DocumentReference; @@ -205,12 +210,14 @@ export type FirestoreErrorCode = 'cancelled' | 'unknown' | 'invalid-argument' | // @public export class GeoPoint { constructor(latitude: number, longitude: number); + static fromJSON(json: object): GeoPoint; isEqual(other: GeoPoint): boolean; get latitude(): number; get longitude(): number; toJSON(): { latitude: number; longitude: number; + type: string; }; } @@ -416,6 +423,7 @@ export class Timestamp { seconds: number, nanoseconds: number); static fromDate(date: Date): Timestamp; + static fromJSON(json: object): Timestamp; static fromMillis(milliseconds: number): Timestamp; isEqual(other: Timestamp): boolean; readonly nanoseconds: number; @@ -425,6 +433,7 @@ export class Timestamp { toJSON(): { seconds: number; nanoseconds: number; + type: string; }; toMillis(): number; toString(): string; @@ -466,8 +475,10 @@ export function vector(values?: number[]): VectorValue; // @public export class VectorValue { /* Excluded from this release type: __constructor */ + static fromJSON(json: object): VectorValue; isEqual(other: VectorValue): boolean; toArray(): number[]; + toJSON(): object; } // @public diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 34b56b97f21..292d81d7a75 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -68,9 +68,11 @@ export function average(field: string | FieldPath): AggregateField { readonly converter: FirestoreDataConverter | null; readonly firestore: Firestore; + static fromJSON(firestore: Firestore, json: object): DocumentReference; + static fromJSON(firestore: Firestore, json: object, converter: FirestoreDataConverter): DocumentReference; get id(): string; get parent(): CollectionReference; get path(): string; + toJSON(): object; readonly type = "document"; withConverter(converter: FirestoreDataConverter): DocumentReference; withConverter(converter: null): DocumentReference; @@ -178,8 +183,15 @@ export class DocumentSnapshot; + toJSON(): object; } +// @public +export function documentSnapshotFromJSON(db: Firestore, json: object): DocumentSnapshot; + +// @public +export function documentSnapshotFromJSON(db: Firestore, json: object, converter: FirestoreDataConverter): DocumentSnapshot; + export { EmulatorMockTokenOptions } // @public @deprecated @@ -264,12 +276,14 @@ export interface FirestoreSettings { // @public export class GeoPoint { constructor(latitude: number, longitude: number); + static fromJSON(json: object): GeoPoint; isEqual(other: GeoPoint): boolean; get latitude(): number; get longitude(): number; toJSON(): { latitude: number; longitude: number; + type: string; }; } @@ -459,6 +473,46 @@ export function onSnapshot(query // @public export function onSnapshot(query: Query, options: SnapshotListenOptions, onNext: (snapshot: QuerySnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void): Unsubscribe; +// @public +export function onSnapshotResume(firestore: Firestore, snapshotJson: object, onNext: (snapshot: QuerySnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter): Unsubscribe; + +// @public +export function onSnapshotResume(firestore: Firestore, snapshotJson: object, onNext: (snapshot: DocumentSnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter): Unsubscribe; + +// @public +export function onSnapshotResume(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, onNext: (snapshot: QuerySnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter): Unsubscribe; + +// @public +export function onSnapshotResume(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, onNext: (snapshot: DocumentSnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter): Unsubscribe; + +// @public +export function onSnapshotResume(firestore: Firestore, snapshotJson: object, observer: { + next: (snapshot: QuerySnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; +}, converter?: FirestoreDataConverter): Unsubscribe; + +// @public +export function onSnapshotResume(firestore: Firestore, snapshotJson: object, observer: { + next: (snapshot: DocumentSnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; +}, converter?: FirestoreDataConverter): Unsubscribe; + +// @public +export function onSnapshotResume(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, observer: { + next: (snapshot: QuerySnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; +}, converter?: FirestoreDataConverter): Unsubscribe; + +// @public +export function onSnapshotResume(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, observer: { + next: (snapshot: DocumentSnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; +}, converter?: FirestoreDataConverter): Unsubscribe; + // @public export function onSnapshotsInSync(firestore: Firestore, observer: { next?: (value: void) => void; @@ -610,8 +664,15 @@ export class QuerySnapshot; get size(): number; + toJSON(): object; } +// @public +export function querySnapshotFromJSON(db: Firestore, json: object): QuerySnapshot; + +// @public +export function querySnapshotFromJSON(db: Firestore, json: object, converter: FirestoreDataConverter): QuerySnapshot; + // @public export class QueryStartAtConstraint extends QueryConstraint { readonly type: 'startAt' | 'startAfter'; @@ -696,6 +757,7 @@ export class Timestamp { seconds: number, nanoseconds: number); static fromDate(date: Date): Timestamp; + static fromJSON(json: object): Timestamp; static fromMillis(milliseconds: number): Timestamp; isEqual(other: Timestamp): boolean; readonly nanoseconds: number; @@ -705,6 +767,7 @@ export class Timestamp { toJSON(): { seconds: number; nanoseconds: number; + type: string; }; toMillis(): number; toString(): string; @@ -751,8 +814,10 @@ export function vector(values?: number[]): VectorValue; // @public export class VectorValue { /* Excluded from this release type: __constructor */ + static fromJSON(json: object): VectorValue; isEqual(other: VectorValue): boolean; toArray(): number[]; + toJSON(): object; } // @public diff --git a/docs-devsite/firestore_.bytes.md b/docs-devsite/firestore_.bytes.md index 8060d394a45..164ddc1acd9 100644 --- a/docs-devsite/firestore_.bytes.md +++ b/docs-devsite/firestore_.bytes.md @@ -23,9 +23,11 @@ export declare class Bytes | Method | Modifiers | Description | | --- | --- | --- | | [fromBase64String(base64)](./firestore_.bytes.md#bytesfrombase64string) | static | Creates a new Bytes object from the given Base64 string, converting it to bytes. | +| [fromJSON(json)](./firestore_.bytes.md#bytesfromjson) | static | Builds a Bytes instance from a JSON object created by [Bytes.toJSON()](./firestore_.bytes.md#bytestojson). | | [fromUint8Array(array)](./firestore_.bytes.md#bytesfromuint8array) | static | Creates a new Bytes object from the given Uint8Array. | | [isEqual(other)](./firestore_.bytes.md#bytesisequal) | | Returns true if this Bytes object is equal to the provided one. | | [toBase64()](./firestore_.bytes.md#bytestobase64) | | Returns the underlying bytes as a Base64-encoded string. | +| [toJSON()](./firestore_.bytes.md#bytestojson) | | Returns a JSON-serializable representation of this Bytes instance. | | [toString()](./firestore_.bytes.md#bytestostring) | | Returns a string representation of the Bytes object. | | [toUint8Array()](./firestore_.bytes.md#bytestouint8array) | | Returns the underlying bytes in a new Uint8Array. | @@ -49,6 +51,28 @@ static fromBase64String(base64: string): Bytes; [Bytes](./firestore_.bytes.md#bytes_class) +## Bytes.fromJSON() + +Builds a `Bytes` instance from a JSON object created by [Bytes.toJSON()](./firestore_.bytes.md#bytestojson). + +Signature: + +```typescript +static fromJSON(json: object): Bytes; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| json | object | a JSON object represention of a Bytes instance | + +Returns: + +[Bytes](./firestore_.bytes.md#bytes_class) + +an instance of [Bytes](./firestore_.bytes.md#bytes_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + ## Bytes.fromUint8Array() Creates a new `Bytes` object from the given Uint8Array. @@ -106,6 +130,21 @@ string The Base64-encoded string created from the `Bytes` object. +## Bytes.toJSON() + +Returns a JSON-serializable representation of this `Bytes` instance. + +Signature: + +```typescript +toJSON(): object; +``` +Returns: + +object + +a JSON representation of this object. + ## Bytes.toString() Returns a string representation of the `Bytes` object. diff --git a/docs-devsite/firestore_.documentreference.md b/docs-devsite/firestore_.documentreference.md index c63ba6eab0a..ee4be972b0c 100644 --- a/docs-devsite/firestore_.documentreference.md +++ b/docs-devsite/firestore_.documentreference.md @@ -33,6 +33,9 @@ export declare class DocumentReferencestatic | Builds a DocumentReference instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson). | +| [fromJSON(firestore, json, converter)](./firestore_.documentreference.md#documentreferencefromjson) | static | Builds a DocumentReference instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson). | +| [toJSON()](./firestore_.documentreference.md#documentreferencetojson) | | Returns a JSON-serializable representation of this DocumentReference instance. | | [withConverter(converter)](./firestore_.documentreference.md#documentreferencewithconverter) | | Applies a custom data converter to this DocumentReference, allowing you to use your own custom model objects with Firestore. When you call [setDoc()](./firestore_lite.md#setdoc_ee215ad), [getDoc()](./firestore_lite.md#getdoc_4569087), etc. with the returned DocumentReference instance, the provided converter will convert between Firestore data of type NewDbModelType and your custom type NewAppModelType. | | [withConverter(converter)](./firestore_.documentreference.md#documentreferencewithconverter) | | Removes the current converter. | @@ -96,6 +99,68 @@ The type of this Firestore reference. readonly type = "document"; ``` +## DocumentReference.fromJSON() + +Builds a `DocumentReference` instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson). + +Signature: + +```typescript +static fromJSON(firestore: Firestore, json: object): DocumentReference; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance the snapshot should be loaded for. | +| json | object | a JSON object represention of a DocumentReference instance | + +Returns: + +[DocumentReference](./firestore_.documentreference.md#documentreference_class) + +an instance of [DocumentReference](./firestore_.documentreference.md#documentreference_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + +## DocumentReference.fromJSON() + +Builds a `DocumentReference` instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson). + +Signature: + +```typescript +static fromJSON(firestore: Firestore, json: object, converter: FirestoreDataConverter): DocumentReference; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance the snapshot should be loaded for. | +| json | object | a JSON object represention of a DocumentReference instance | +| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<NewAppModelType, NewDbModelType> | Converts objects to and from Firestore. | + +Returns: + +[DocumentReference](./firestore_.documentreference.md#documentreference_class)<NewAppModelType, NewDbModelType> + +an instance of [DocumentReference](./firestore_.documentreference.md#documentreference_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + +## DocumentReference.toJSON() + +Returns a JSON-serializable representation of this `DocumentReference` instance. + +Signature: + +```typescript +toJSON(): object; +``` +Returns: + +object + +a JSON representation of this object. + ## DocumentReference.withConverter() Applies a custom data converter to this `DocumentReference`, allowing you to use your own custom model objects with Firestore. When you call [setDoc()](./firestore_lite.md#setdoc_ee215ad), [getDoc()](./firestore_lite.md#getdoc_4569087), etc. with the returned `DocumentReference` instance, the provided converter will convert between Firestore data of type `NewDbModelType` and your custom type `NewAppModelType`. diff --git a/docs-devsite/firestore_.documentsnapshot.md b/docs-devsite/firestore_.documentsnapshot.md index 476588b78c0..cece3f79deb 100644 --- a/docs-devsite/firestore_.documentsnapshot.md +++ b/docs-devsite/firestore_.documentsnapshot.md @@ -41,6 +41,7 @@ export declare class DocumentSnapshotObject. Returns undefined if the document doesn't exist.By default, serverTimestamp() values that have not yet been set to their final value will be returned as null. You can override this by passing an options object. | | [exists()](./firestore_.documentsnapshot.md#documentsnapshotexists) | | Returns whether or not the data exists. True if the document exists. | | [get(fieldPath, options)](./firestore_.documentsnapshot.md#documentsnapshotget) | | Retrieves the field specified by fieldPath. Returns undefined if the document or field doesn't exist.By default, a serverTimestamp() that has not yet been set to its final value will be returned as null. You can override this by passing an options object. | +| [toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson) | | Returns a JSON-serializable representation of this DocumentSnapshot instance. | ## DocumentSnapshot.(constructor) @@ -144,3 +145,18 @@ any The data at the specified field location or undefined if no such field exists in the document. +## DocumentSnapshot.toJSON() + +Returns a JSON-serializable representation of this `DocumentSnapshot` instance. + +Signature: + +```typescript +toJSON(): object; +``` +Returns: + +object + +a JSON representation of this object. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if this `DocumentSnapshot` has pending writes. + diff --git a/docs-devsite/firestore_.geopoint.md b/docs-devsite/firestore_.geopoint.md index d4264a39f5f..c88a5289c64 100644 --- a/docs-devsite/firestore_.geopoint.md +++ b/docs-devsite/firestore_.geopoint.md @@ -37,8 +37,9 @@ export declare class GeoPoint | Method | Modifiers | Description | | --- | --- | --- | +| [fromJSON(json)](./firestore_.geopoint.md#geopointfromjson) | static | Builds a GeoPoint instance from a JSON object created by [GeoPoint.toJSON()](./firestore_.geopoint.md#geopointtojson). | | [isEqual(other)](./firestore_.geopoint.md#geopointisequal) | | Returns true if this GeoPoint is equal to the provided one. | -| [toJSON()](./firestore_.geopoint.md#geopointtojson) | | Returns a JSON-serializable representation of this GeoPoint. | +| [toJSON()](./firestore_.geopoint.md#geopointtojson) | | Returns a JSON-serializable representation of this GeoPoint instance. | ## GeoPoint.(constructor) @@ -77,6 +78,28 @@ The longitude of this `GeoPoint` instance. get longitude(): number; ``` +## GeoPoint.fromJSON() + +Builds a `GeoPoint` instance from a JSON object created by [GeoPoint.toJSON()](./firestore_.geopoint.md#geopointtojson). + +Signature: + +```typescript +static fromJSON(json: object): GeoPoint; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| json | object | a JSON object represention of a GeoPoint instance | + +Returns: + +[GeoPoint](./firestore_.geopoint.md#geopoint_class) + +an instance of [GeoPoint](./firestore_.geopoint.md#geopoint_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + ## GeoPoint.isEqual() Returns true if this `GeoPoint` is equal to the provided one. @@ -101,7 +124,7 @@ true if this `GeoPoint` is equal to the provided one. ## GeoPoint.toJSON() -Returns a JSON-serializable representation of this GeoPoint. +Returns a JSON-serializable representation of this `GeoPoint` instance. Signature: @@ -109,9 +132,12 @@ Returns a JSON-serializable representation of this GeoPoint. toJSON(): { latitude: number; longitude: number; + type: string; }; ``` Returns: -{ latitude: number; longitude: number; } +{ latitude: number; longitude: number; type: string; } + +a JSON representation of this object. diff --git a/docs-devsite/firestore_.md b/docs-devsite/firestore_.md index 91d21e32708..5d237fcafea 100644 --- a/docs-devsite/firestore_.md +++ b/docs-devsite/firestore_.md @@ -19,6 +19,11 @@ https://github.com/firebase/firebase-js-sdk | [getFirestore(app)](./firestore_.md#getfirestore_cf608e1) | Returns the existing default [Firestore](./firestore_.firestore.md#firestore_class) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with default settings. | | [getFirestore(app, databaseId)](./firestore_.md#getfirestore_48de6cb) | (Public Preview) Returns the existing named [Firestore](./firestore_.firestore.md#firestore_class) instance that is associated with the provided [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface). If no instance exists, initializes a new instance with default settings. | | [initializeFirestore(app, settings, databaseId)](./firestore_.md#initializefirestore_fc7d200) | Initializes a new instance of [Firestore](./firestore_.firestore.md#firestore_class) with the provided settings. Can only be called before any other function, including [getFirestore()](./firestore_.md#getfirestore). If the custom settings are empty, this function is equivalent to calling [getFirestore()](./firestore_.md#getfirestore). | +| function(db, ...) | +| [documentSnapshotFromJSON(db, json)](./firestore_.md#documentsnapshotfromjson_a318ff2) | Builds a DocumentSnapshot instance from a JSON object created by [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). | +| [documentSnapshotFromJSON(db, json, converter)](./firestore_.md#documentsnapshotfromjson_ddb369d) | Builds a DocumentSnapshot instance from a JSON object created by [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). | +| [querySnapshotFromJSON(db, json)](./firestore_.md#querysnapshotfromjson_a318ff2) | Builds a QuerySnapshot instance from a JSON object created by [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). | +| [querySnapshotFromJSON(db, json, converter)](./firestore_.md#querysnapshotfromjson_ddb369d) | Builds a QuerySnapshot instance from a JSON object created by [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). | | function(firestore, ...) | | [clearIndexedDbPersistence(firestore)](./firestore_.md#clearindexeddbpersistence_231a8e0) | Clears the persistent storage. This includes pending writes and cached documents.Must be called while the [Firestore](./firestore_.firestore.md#firestore_class) instance is not started (after the app is terminated or when the app is first initialized). On startup, this function must be called before other functions (other than [initializeFirestore()](./firestore_.md#initializefirestore_fc7d200) or [getFirestore()](./firestore_.md#getfirestore))). If the [Firestore](./firestore_.firestore.md#firestore_class) instance is still running, the promise will be rejected with the error code of failed-precondition.Note: clearIndexedDbPersistence() is primarily intended to help write reliable tests that use Cloud Firestore. It uses an efficient mechanism for dropping existing data but does not attempt to securely overwrite or otherwise make cached data unrecoverable. For applications that are sensitive to the disclosure of cached data in between user sessions, we strongly recommend not enabling persistence at all. | | [collection(firestore, path, pathSegments)](./firestore_.md#collection_1eb4c23) | Gets a CollectionReference instance that refers to the collection at the specified absolute path. | @@ -32,6 +37,14 @@ https://github.com/firebase/firebase-js-sdk | [getPersistentCacheIndexManager(firestore)](./firestore_.md#getpersistentcacheindexmanager_231a8e0) | Returns the PersistentCache Index Manager used by the given Firestore object. The PersistentCacheIndexManager instance, or null if local persistent storage is not in use. | | [loadBundle(firestore, bundleData)](./firestore_.md#loadbundle_bec5b75) | Loads a Firestore bundle into the local cache. | | [namedQuery(firestore, name)](./firestore_.md#namedquery_6438876) | Reads a Firestore [Query](./firestore_.query.md#query_class) from local cache, identified by the given name.The named queries are packaged into bundles on the server side (along with resulting documents), and loaded to local cache using loadBundle. Once in local cache, use this method to extract a [Query](./firestore_.query.md#query_class) by name. | +| [onSnapshotResume(firestore, snapshotJson, onNext, onError, onCompletion, converter)](./firestore_.md#onsnapshotresume_7c84f5e) | Attaches a listener for QuerySnapshot events based on data generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson) You may either pass individual onNext and onError callbacks or pass a single observer object with next and error callbacks. The listener can be cancelled by calling the function that is returned when onSnapshot is called.NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. | +| [onSnapshotResume(firestore, snapshotJson, onNext, onError, onCompletion, converter)](./firestore_.md#onsnapshotresume_712362a) | Attaches a listener for DocumentSnapshot events based on data generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). You may either pass individual onNext and onError callbacks or pass a single observer object with next and error callbacks. The listener can be cancelled by calling the function that is returned when onSnapshot is called.NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. | +| [onSnapshotResume(firestore, snapshotJson, options, onNext, onError, onCompletion, converter)](./firestore_.md#onsnapshotresume_8807e6e) | Attaches a listener for QuerySnapshot events based on data generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). You may either pass individual onNext and onError callbacks or pass a single observer object with next and error callbacks. The listener can be cancelled by calling the function that is returned when onSnapshot is called.NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. | +| [onSnapshotResume(firestore, snapshotJson, options, onNext, onError, onCompletion, converter)](./firestore_.md#onsnapshotresume_301fcec) | Attaches a listener for DocumentSnapshot events based on data generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). You may either pass individual onNext and onError callbacks or pass a single observer object with next and error callbacks. The listener can be cancelled by calling the function that is returned when onSnapshot is called.NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. | +| [onSnapshotResume(firestore, snapshotJson, observer, converter)](./firestore_.md#onsnapshotresume_b8b5c9d) | Attaches a listener for QuerySnapshot events based on QuerySnapshot data generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). You may either pass individual onNext and onError callbacks or pass a single observer object with next and error callbacks. The listener can be cancelled by calling the function that is returned when onSnapshot is called.NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. | +| [onSnapshotResume(firestore, snapshotJson, observer, converter)](./firestore_.md#onsnapshotresume_9b75d28) | Attaches a listener for DocumentSnapshot events based on data generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson) You may either pass individual onNext and onError callbacks or pass a single observer object with next and error callbacks. The listener can be cancelled by calling the function that is returned when onSnapshot is called.NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. | +| [onSnapshotResume(firestore, snapshotJson, options, observer, converter)](./firestore_.md#onsnapshotresume_fb80adf) | Attaches a listener for QuerySnapshot events based on QuerySnapshot data generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson) You may either pass individual onNext and onError callbacks or pass a single observer object with next and error callbacks. The listener can be cancelled by calling the function that is returned when onSnapshot is called.NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. | +| [onSnapshotResume(firestore, snapshotJson, options, observer, converter)](./firestore_.md#onsnapshotresume_f76d912) | Attaches a listener for DocumentSnapshot events based on QuerySnapshot data generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson) You may either pass individual onNext and onError callbacks or pass a single observer object with next and error callbacks. The listener can be cancelled by calling the function that is returned when onSnapshot is called.NOTE: Although an onCompletion callback can be provided, it will never be called because the snapshot stream is never-ending. | | [onSnapshotsInSync(firestore, observer)](./firestore_.md#onsnapshotsinsync_2f0dfa4) | Attaches a listener for a snapshots-in-sync event. The snapshots-in-sync event indicates that all listeners affected by a given change have fired, even if a single server-generated change affects multiple listeners.NOTE: The snapshots-in-sync event only indicates that listeners are in sync with each other, but does not relate to whether those snapshots are in sync with the server. Use SnapshotMetadata in the individual listeners to determine if a snapshot is from the cache or the server. | | [onSnapshotsInSync(firestore, onSync)](./firestore_.md#onsnapshotsinsync_1901c06) | Attaches a listener for a snapshots-in-sync event. The snapshots-in-sync event indicates that all listeners affected by a given change have fired, even if a single server-generated change affects multiple listeners.NOTE: The snapshots-in-sync event only indicates that listeners are in sync with each other, but does not relate to whether those snapshots are in sync with the server. Use SnapshotMetadata in the individual listeners to determine if a snapshot is from the cache or the server. | | [runTransaction(firestore, updateFunction, options)](./firestore_.md#runtransaction_6f03ec4) | Executes the given updateFunction and then attempts to commit the changes applied within the transaction. If any document read within the transaction has changed, Cloud Firestore retries the updateFunction. If it fails to commit after 5 attempts, the transaction fails.The maximum number of writes allowed in a single transaction is 500. | @@ -298,6 +311,102 @@ export declare function initializeFirestore(app: FirebaseApp, settings: Firestor A newly initialized [Firestore](./firestore_.firestore.md#firestore_class) instance. +## function(db, ...) + +### documentSnapshotFromJSON(db, json) {:#documentsnapshotfromjson_a318ff2} + +Builds a `DocumentSnapshot` instance from a JSON object created by [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). + +Signature: + +```typescript +export declare function documentSnapshotFromJSON(db: Firestore, json: object): DocumentSnapshot; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| db | [Firestore](./firestore_.firestore.md#firestore_class) | | +| json | object | a JSON object represention of a DocumentSnapshot instance. | + +Returns: + +[DocumentSnapshot](./firestore_.documentsnapshot.md#documentsnapshot_class) + +an instance of [DocumentSnapshot](./firestore_.documentsnapshot.md#documentsnapshot_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + +### documentSnapshotFromJSON(db, json, converter) {:#documentsnapshotfromjson_ddb369d} + +Builds a `DocumentSnapshot` instance from a JSON object created by [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). + +Signature: + +```typescript +export declare function documentSnapshotFromJSON(db: Firestore, json: object, converter: FirestoreDataConverter): DocumentSnapshot; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| db | [Firestore](./firestore_.firestore.md#firestore_class) | | +| json | object | a JSON object represention of a DocumentSnapshot instance. | +| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<AppModelType, DbModelType> | Converts objects to and from Firestore. | + +Returns: + +[DocumentSnapshot](./firestore_.documentsnapshot.md#documentsnapshot_class)<AppModelType, DbModelType> + +an instance of [DocumentSnapshot](./firestore_.documentsnapshot.md#documentsnapshot_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + +### querySnapshotFromJSON(db, json) {:#querysnapshotfromjson_a318ff2} + +Builds a `QuerySnapshot` instance from a JSON object created by [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). + +Signature: + +```typescript +export declare function querySnapshotFromJSON(db: Firestore, json: object): QuerySnapshot; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| db | [Firestore](./firestore_.firestore.md#firestore_class) | | +| json | object | a JSON object represention of a QuerySnapshot instance. | + +Returns: + +[QuerySnapshot](./firestore_.querysnapshot.md#querysnapshot_class) + +an instance of [QuerySnapshot](./firestore_.querysnapshot.md#querysnapshot_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + +### querySnapshotFromJSON(db, json, converter) {:#querysnapshotfromjson_ddb369d} + +Builds a `QuerySnapshot` instance from a JSON object created by [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). + +Signature: + +```typescript +export declare function querySnapshotFromJSON(db: Firestore, json: object, converter: FirestoreDataConverter): QuerySnapshot; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| db | [Firestore](./firestore_.firestore.md#firestore_class) | | +| json | object | a JSON object represention of a QuerySnapshot instance. | +| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<AppModelType, DbModelType> | Converts objects to and from Firestore. | + +Returns: + +[QuerySnapshot](./firestore_.querysnapshot.md#querysnapshot_class)<AppModelType, DbModelType> + +an instance of [QuerySnapshot](./firestore_.querysnapshot.md#querysnapshot_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + ## function(firestore, ...) ### clearIndexedDbPersistence(firestore) {:#clearindexeddbpersistence_231a8e0} @@ -617,6 +726,250 @@ Promise<[Query](./firestore_.query.md#query_class) \| null> A `Promise` that is resolved with the Query or `null`. +### onSnapshotResume(firestore, snapshotJson, onNext, onError, onCompletion, converter) {:#onsnapshotresume_7c84f5e} + +Attaches a listener for `QuerySnapshot` events based on data generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson) You may either pass individual `onNext` and `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by calling the function that is returned when `onSnapshot` is called. + +NOTE: Although an `onCompletion` callback can be provided, it will never be called because the snapshot stream is never-ending. + +Signature: + +```typescript +export declare function onSnapshotResume(firestore: Firestore, snapshotJson: object, onNext: (snapshot: QuerySnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter): Unsubscribe; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance to enable the listener for. | +| snapshotJson | object | A JSON object generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). | +| onNext | (snapshot: [QuerySnapshot](./firestore_.querysnapshot.md#querysnapshot_class)<AppModelType, DbModelType>) => void | A callback to be called every time a new QuerySnapshot is available. | +| onError | (error: [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class)) => void | A callback to be called if the listen fails or is cancelled. No further callbacks will occur. | +| onCompletion | () => void | Can be provided, but will not be called since streams are never ending. | +| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<DbModelType> | An optional object that converts objects from Firestore before the onNext listener is invoked. | + +Returns: + +[Unsubscribe](./firestore_.unsubscribe.md#unsubscribe_interface) + +An unsubscribe function that can be called to cancel the snapshot listener. + +### onSnapshotResume(firestore, snapshotJson, onNext, onError, onCompletion, converter) {:#onsnapshotresume_712362a} + +Attaches a listener for `DocumentSnapshot` events based on data generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). You may either pass individual `onNext` and `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by calling the function that is returned when `onSnapshot` is called. + +NOTE: Although an `onCompletion` callback can be provided, it will never be called because the snapshot stream is never-ending. + +Signature: + +```typescript +export declare function onSnapshotResume(firestore: Firestore, snapshotJson: object, onNext: (snapshot: DocumentSnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter): Unsubscribe; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance to enable the listener for. | +| snapshotJson | object | A JSON object generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). | +| onNext | (snapshot: [DocumentSnapshot](./firestore_.documentsnapshot.md#documentsnapshot_class)<AppModelType, DbModelType>) => void | A callback to be called every time a new DocumentSnapshot is available. | +| onError | (error: [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class)) => void | A callback to be called if the listen fails or is cancelled. No further callbacks will occur. | +| onCompletion | () => void | Can be provided, but will not be called since streams are never ending. | +| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<DbModelType> | An optional object that converts objects from Firestore before the onNext listener is invoked. | + +Returns: + +[Unsubscribe](./firestore_.unsubscribe.md#unsubscribe_interface) + +An unsubscribe function that can be called to cancel the snapshot listener. + +### onSnapshotResume(firestore, snapshotJson, options, onNext, onError, onCompletion, converter) {:#onsnapshotresume_8807e6e} + +Attaches a listener for `QuerySnapshot` events based on data generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). You may either pass individual `onNext` and `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by calling the function that is returned when `onSnapshot` is called. + +NOTE: Although an `onCompletion` callback can be provided, it will never be called because the snapshot stream is never-ending. + +Signature: + +```typescript +export declare function onSnapshotResume(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, onNext: (snapshot: QuerySnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter): Unsubscribe; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance to enable the listener for. | +| snapshotJson | object | A JSON object generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). | +| options | [SnapshotListenOptions](./firestore_.snapshotlistenoptions.md#snapshotlistenoptions_interface) | Options controlling the listen behavior. | +| onNext | (snapshot: [QuerySnapshot](./firestore_.querysnapshot.md#querysnapshot_class)<AppModelType, DbModelType>) => void | A callback to be called every time a new QuerySnapshot is available. | +| onError | (error: [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class)) => void | A callback to be called if the listen fails or is cancelled. No further callbacks will occur. | +| onCompletion | () => void | Can be provided, but will not be called since streams are never ending. | +| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<DbModelType> | An optional object that converts objects from Firestore before the onNext listener is invoked. | + +Returns: + +[Unsubscribe](./firestore_.unsubscribe.md#unsubscribe_interface) + +An unsubscribe function that can be called to cancel the snapshot listener. + +### onSnapshotResume(firestore, snapshotJson, options, onNext, onError, onCompletion, converter) {:#onsnapshotresume_301fcec} + +Attaches a listener for `DocumentSnapshot` events based on data generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). You may either pass individual `onNext` and `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by calling the function that is returned when `onSnapshot` is called. + +NOTE: Although an `onCompletion` callback can be provided, it will never be called because the snapshot stream is never-ending. + +Signature: + +```typescript +export declare function onSnapshotResume(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, onNext: (snapshot: DocumentSnapshot) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter): Unsubscribe; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance to enable the listener for. | +| snapshotJson | object | A JSON object generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). | +| options | [SnapshotListenOptions](./firestore_.snapshotlistenoptions.md#snapshotlistenoptions_interface) | Options controlling the listen behavior. | +| onNext | (snapshot: [DocumentSnapshot](./firestore_.documentsnapshot.md#documentsnapshot_class)<AppModelType, DbModelType>) => void | A callback to be called every time a new DocumentSnapshot is available. | +| onError | (error: [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class)) => void | A callback to be called if the listen fails or is cancelled. No further callbacks will occur. | +| onCompletion | () => void | Can be provided, but will not be called since streams are never ending. | +| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<DbModelType> | An optional object that converts objects from Firestore before the onNext listener is invoked. | + +Returns: + +[Unsubscribe](./firestore_.unsubscribe.md#unsubscribe_interface) + +An unsubscribe function that can be called to cancel the snapshot listener. + +### onSnapshotResume(firestore, snapshotJson, observer, converter) {:#onsnapshotresume_b8b5c9d} + +Attaches a listener for `QuerySnapshot` events based on QuerySnapshot data generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). You may either pass individual `onNext` and `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by calling the function that is returned when `onSnapshot` is called. + +NOTE: Although an `onCompletion` callback can be provided, it will never be called because the snapshot stream is never-ending. + +Signature: + +```typescript +export declare function onSnapshotResume(firestore: Firestore, snapshotJson: object, observer: { + next: (snapshot: QuerySnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; +}, converter?: FirestoreDataConverter): Unsubscribe; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance to enable the listener for. | +| snapshotJson | object | A JSON object generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). | +| observer | { next: (snapshot: [QuerySnapshot](./firestore_.querysnapshot.md#querysnapshot_class)<AppModelType, DbModelType>) => void; error?: (error: [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class)) => void; complete?: () => void; } | A single object containing next and error callbacks. | +| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<DbModelType> | An optional object that converts objects from Firestore before the onNext listener is invoked. | + +Returns: + +[Unsubscribe](./firestore_.unsubscribe.md#unsubscribe_interface) + +An unsubscribe function that can be called to cancel the snapshot listener. + +### onSnapshotResume(firestore, snapshotJson, observer, converter) {:#onsnapshotresume_9b75d28} + +Attaches a listener for `DocumentSnapshot` events based on data generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson) You may either pass individual `onNext` and `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by calling the function that is returned when `onSnapshot` is called. + +NOTE: Although an `onCompletion` callback can be provided, it will never be called because the snapshot stream is never-ending. + +Signature: + +```typescript +export declare function onSnapshotResume(firestore: Firestore, snapshotJson: object, observer: { + next: (snapshot: DocumentSnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; +}, converter?: FirestoreDataConverter): Unsubscribe; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance to enable the listener for. | +| snapshotJson | object | A JSON object generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). | +| observer | { next: (snapshot: [DocumentSnapshot](./firestore_.documentsnapshot.md#documentsnapshot_class)<AppModelType, DbModelType>) => void; error?: (error: [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class)) => void; complete?: () => void; } | A single object containing next and error callbacks. | +| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<DbModelType> | An optional object that converts objects from Firestore before the onNext listener is invoked. | + +Returns: + +[Unsubscribe](./firestore_.unsubscribe.md#unsubscribe_interface) + +An unsubscribe function that can be called to cancel the snapshot listener. + +### onSnapshotResume(firestore, snapshotJson, options, observer, converter) {:#onsnapshotresume_fb80adf} + +Attaches a listener for `QuerySnapshot` events based on QuerySnapshot data generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson) You may either pass individual `onNext` and `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by calling the function that is returned when `onSnapshot` is called. + +NOTE: Although an `onCompletion` callback can be provided, it will never be called because the snapshot stream is never-ending. + +Signature: + +```typescript +export declare function onSnapshotResume(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, observer: { + next: (snapshot: QuerySnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; +}, converter?: FirestoreDataConverter): Unsubscribe; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance to enable the listener for. | +| snapshotJson | object | A JSON object generated by invoking [QuerySnapshot.toJSON()](./firestore_.querysnapshot.md#querysnapshottojson). | +| options | [SnapshotListenOptions](./firestore_.snapshotlistenoptions.md#snapshotlistenoptions_interface) | Options controlling the listen behavior. | +| observer | { next: (snapshot: [QuerySnapshot](./firestore_.querysnapshot.md#querysnapshot_class)<AppModelType, DbModelType>) => void; error?: (error: [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class)) => void; complete?: () => void; } | A single object containing next and error callbacks. | +| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<DbModelType> | An optional object that converts objects from Firestore before the onNext listener is invoked. | + +Returns: + +[Unsubscribe](./firestore_.unsubscribe.md#unsubscribe_interface) + +An unsubscribe function that can be called to cancel the snapshot listener. + +### onSnapshotResume(firestore, snapshotJson, options, observer, converter) {:#onsnapshotresume_f76d912} + +Attaches a listener for `DocumentSnapshot` events based on QuerySnapshot data generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson) You may either pass individual `onNext` and `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by calling the function that is returned when `onSnapshot` is called. + +NOTE: Although an `onCompletion` callback can be provided, it will never be called because the snapshot stream is never-ending. + +Signature: + +```typescript +export declare function onSnapshotResume(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, observer: { + next: (snapshot: DocumentSnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; +}, converter?: FirestoreDataConverter): Unsubscribe; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance to enable the listener for. | +| snapshotJson | object | A JSON object generated by invoking [DocumentSnapshot.toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson). | +| options | [SnapshotListenOptions](./firestore_.snapshotlistenoptions.md#snapshotlistenoptions_interface) | Options controlling the listen behavior. | +| observer | { next: (snapshot: [DocumentSnapshot](./firestore_.documentsnapshot.md#documentsnapshot_class)<AppModelType, DbModelType>) => void; error?: (error: [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class)) => void; complete?: () => void; } | A single object containing next and error callbacks. | +| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<DbModelType> | An optional object that converts objects from Firestore before the onNext listener is invoked. | + +Returns: + +[Unsubscribe](./firestore_.unsubscribe.md#unsubscribe_interface) + +An unsubscribe function that can be called to cancel the snapshot listener. + ### onSnapshotsInSync(firestore, observer) {:#onsnapshotsinsync_2f0dfa4} Attaches a listener for a snapshots-in-sync event. The snapshots-in-sync event indicates that all listeners affected by a given change have fired, even if a single server-generated change affects multiple listeners. diff --git a/docs-devsite/firestore_.querysnapshot.md b/docs-devsite/firestore_.querysnapshot.md index d9930c68d90..b574320c8c4 100644 --- a/docs-devsite/firestore_.querysnapshot.md +++ b/docs-devsite/firestore_.querysnapshot.md @@ -34,6 +34,7 @@ export declare class QuerySnapshotQuerySnapshot. | +| [toJSON()](./firestore_.querysnapshot.md#querysnapshottojson) | | Returns a JSON-serializable representation of this QuerySnapshot instance. | ## QuerySnapshot.docs @@ -126,3 +127,18 @@ forEach(callback: (result: QueryDocumentSnapshot) => void +## QuerySnapshot.toJSON() + +Returns a JSON-serializable representation of this `QuerySnapshot` instance. + +Signature: + +```typescript +toJSON(): object; +``` +Returns: + +object + +a JSON representation of this object. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if this `QuerySnapshot` has pending writes. + diff --git a/docs-devsite/firestore_.timestamp.md b/docs-devsite/firestore_.timestamp.md index 6f7a7dd011b..9d7282e5a2a 100644 --- a/docs-devsite/firestore_.timestamp.md +++ b/docs-devsite/firestore_.timestamp.md @@ -40,6 +40,7 @@ export declare class Timestamp | Method | Modifiers | Description | | --- | --- | --- | | [fromDate(date)](./firestore_.timestamp.md#timestampfromdate) | static | Creates a new timestamp from the given date. | +| [fromJSON(json)](./firestore_.timestamp.md#timestampfromjson) | static | Builds a Timestamp instance from a JSON object created by [Timestamp.toJSON()](./firestore_.timestamp.md#timestamptojson). | | [fromMillis(milliseconds)](./firestore_.timestamp.md#timestampfrommillis) | static | Creates a new timestamp from the given number of milliseconds. | | [isEqual(other)](./firestore_.timestamp.md#timestampisequal) | | Returns true if this Timestamp is equal to the provided one. | | [now()](./firestore_.timestamp.md#timestampnow) | static | Creates a new timestamp with the current date, with millisecond precision. | @@ -110,6 +111,26 @@ static fromDate(date: Date): Timestamp; A new `Timestamp` representing the same point in time as the given date. +## Timestamp.fromJSON() + +Builds a `Timestamp` instance from a JSON object created by [Timestamp.toJSON()](./firestore_.timestamp.md#timestamptojson). + +Signature: + +```typescript +static fromJSON(json: object): Timestamp; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| json | object | | + +Returns: + +[Timestamp](./firestore_.timestamp.md#timestamp_class) + ## Timestamp.fromMillis() Creates a new timestamp from the given number of milliseconds. @@ -194,11 +215,12 @@ Returns a JSON-serializable representation of this `Timestamp`. toJSON(): { seconds: number; nanoseconds: number; + type: string; }; ``` Returns: -{ seconds: number; nanoseconds: number; } +{ seconds: number; nanoseconds: number; type: string; } ## Timestamp.toMillis() diff --git a/docs-devsite/firestore_.vectorvalue.md b/docs-devsite/firestore_.vectorvalue.md index e35e96ec9ec..1fc4e2b35ab 100644 --- a/docs-devsite/firestore_.vectorvalue.md +++ b/docs-devsite/firestore_.vectorvalue.md @@ -24,8 +24,32 @@ export declare class VectorValue | Method | Modifiers | Description | | --- | --- | --- | +| [fromJSON(json)](./firestore_.vectorvalue.md#vectorvaluefromjson) | static | Builds a VectorValue instance from a JSON object created by [VectorValue.toJSON()](./firestore_.vectorvalue.md#vectorvaluetojson). | | [isEqual(other)](./firestore_.vectorvalue.md#vectorvalueisequal) | | Returns true if the two VectorValue values have the same raw number arrays, returns false otherwise. | | [toArray()](./firestore_.vectorvalue.md#vectorvaluetoarray) | | Returns a copy of the raw number array form of the vector. | +| [toJSON()](./firestore_.vectorvalue.md#vectorvaluetojson) | | Returns a JSON-serializable representation of this VectorValue instance. | + +## VectorValue.fromJSON() + +Builds a `VectorValue` instance from a JSON object created by [VectorValue.toJSON()](./firestore_.vectorvalue.md#vectorvaluetojson). + +Signature: + +```typescript +static fromJSON(json: object): VectorValue; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| json | object | a JSON object represention of a VectorValue instance. | + +Returns: + +[VectorValue](./firestore_.vectorvalue.md#vectorvalue_class) + +an instance of [VectorValue](./firestore_.vectorvalue.md#vectorvalue_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. ## VectorValue.isEqual() @@ -60,3 +84,18 @@ toArray(): number[]; number\[\] +## VectorValue.toJSON() + +Returns a JSON-serializable representation of this `VectorValue` instance. + +Signature: + +```typescript +toJSON(): object; +``` +Returns: + +object + +a JSON representation of this object. + diff --git a/docs-devsite/firestore_lite.bytes.md b/docs-devsite/firestore_lite.bytes.md index 51cfb0a9bf0..a26734f90bf 100644 --- a/docs-devsite/firestore_lite.bytes.md +++ b/docs-devsite/firestore_lite.bytes.md @@ -23,9 +23,11 @@ export declare class Bytes | Method | Modifiers | Description | | --- | --- | --- | | [fromBase64String(base64)](./firestore_lite.bytes.md#bytesfrombase64string) | static | Creates a new Bytes object from the given Base64 string, converting it to bytes. | +| [fromJSON(json)](./firestore_lite.bytes.md#bytesfromjson) | static | Builds a Bytes instance from a JSON object created by [Bytes.toJSON()](./firestore_.bytes.md#bytestojson). | | [fromUint8Array(array)](./firestore_lite.bytes.md#bytesfromuint8array) | static | Creates a new Bytes object from the given Uint8Array. | | [isEqual(other)](./firestore_lite.bytes.md#bytesisequal) | | Returns true if this Bytes object is equal to the provided one. | | [toBase64()](./firestore_lite.bytes.md#bytestobase64) | | Returns the underlying bytes as a Base64-encoded string. | +| [toJSON()](./firestore_lite.bytes.md#bytestojson) | | Returns a JSON-serializable representation of this Bytes instance. | | [toString()](./firestore_lite.bytes.md#bytestostring) | | Returns a string representation of the Bytes object. | | [toUint8Array()](./firestore_lite.bytes.md#bytestouint8array) | | Returns the underlying bytes in a new Uint8Array. | @@ -49,6 +51,28 @@ static fromBase64String(base64: string): Bytes; [Bytes](./firestore_lite.bytes.md#bytes_class) +## Bytes.fromJSON() + +Builds a `Bytes` instance from a JSON object created by [Bytes.toJSON()](./firestore_.bytes.md#bytestojson). + +Signature: + +```typescript +static fromJSON(json: object): Bytes; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| json | object | a JSON object represention of a Bytes instance | + +Returns: + +[Bytes](./firestore_lite.bytes.md#bytes_class) + +an instance of [Bytes](./firestore_.bytes.md#bytes_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + ## Bytes.fromUint8Array() Creates a new `Bytes` object from the given Uint8Array. @@ -106,6 +130,21 @@ string The Base64-encoded string created from the `Bytes` object. +## Bytes.toJSON() + +Returns a JSON-serializable representation of this `Bytes` instance. + +Signature: + +```typescript +toJSON(): object; +``` +Returns: + +object + +a JSON representation of this object. + ## Bytes.toString() Returns a string representation of the `Bytes` object. diff --git a/docs-devsite/firestore_lite.documentreference.md b/docs-devsite/firestore_lite.documentreference.md index 2239850b829..2a09e2e5964 100644 --- a/docs-devsite/firestore_lite.documentreference.md +++ b/docs-devsite/firestore_lite.documentreference.md @@ -33,6 +33,9 @@ export declare class DocumentReferencestatic | Builds a DocumentReference instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson). | +| [fromJSON(firestore, json, converter)](./firestore_lite.documentreference.md#documentreferencefromjson) | static | Builds a DocumentReference instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson). | +| [toJSON()](./firestore_lite.documentreference.md#documentreferencetojson) | | Returns a JSON-serializable representation of this DocumentReference instance. | | [withConverter(converter)](./firestore_lite.documentreference.md#documentreferencewithconverter) | | Applies a custom data converter to this DocumentReference, allowing you to use your own custom model objects with Firestore. When you call [setDoc()](./firestore_lite.md#setdoc_ee215ad), [getDoc()](./firestore_lite.md#getdoc_4569087), etc. with the returned DocumentReference instance, the provided converter will convert between Firestore data of type NewDbModelType and your custom type NewAppModelType. | | [withConverter(converter)](./firestore_lite.documentreference.md#documentreferencewithconverter) | | Removes the current converter. | @@ -96,6 +99,68 @@ The type of this Firestore reference. readonly type = "document"; ``` +## DocumentReference.fromJSON() + +Builds a `DocumentReference` instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson). + +Signature: + +```typescript +static fromJSON(firestore: Firestore, json: object): DocumentReference; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_lite.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance the snapshot should be loaded for. | +| json | object | a JSON object represention of a DocumentReference instance | + +Returns: + +[DocumentReference](./firestore_lite.documentreference.md#documentreference_class) + +an instance of [DocumentReference](./firestore_.documentreference.md#documentreference_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + +## DocumentReference.fromJSON() + +Builds a `DocumentReference` instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson). + +Signature: + +```typescript +static fromJSON(firestore: Firestore, json: object, converter: FirestoreDataConverter): DocumentReference; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| firestore | [Firestore](./firestore_lite.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance the snapshot should be loaded for. | +| json | object | a JSON object represention of a DocumentReference instance | +| converter | [FirestoreDataConverter](./firestore_lite.firestoredataconverter.md#firestoredataconverter_interface)<NewAppModelType, NewDbModelType> | Converts objects to and from Firestore. | + +Returns: + +[DocumentReference](./firestore_lite.documentreference.md#documentreference_class)<NewAppModelType, NewDbModelType> + +an instance of [DocumentReference](./firestore_.documentreference.md#documentreference_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + +## DocumentReference.toJSON() + +Returns a JSON-serializable representation of this `DocumentReference` instance. + +Signature: + +```typescript +toJSON(): object; +``` +Returns: + +object + +a JSON representation of this object. + ## DocumentReference.withConverter() Applies a custom data converter to this `DocumentReference`, allowing you to use your own custom model objects with Firestore. When you call [setDoc()](./firestore_lite.md#setdoc_ee215ad), [getDoc()](./firestore_lite.md#getdoc_4569087), etc. with the returned `DocumentReference` instance, the provided converter will convert between Firestore data of type `NewDbModelType` and your custom type `NewAppModelType`. diff --git a/docs-devsite/firestore_lite.geopoint.md b/docs-devsite/firestore_lite.geopoint.md index fdd760520c8..6b3396107d2 100644 --- a/docs-devsite/firestore_lite.geopoint.md +++ b/docs-devsite/firestore_lite.geopoint.md @@ -37,8 +37,9 @@ export declare class GeoPoint | Method | Modifiers | Description | | --- | --- | --- | +| [fromJSON(json)](./firestore_lite.geopoint.md#geopointfromjson) | static | Builds a GeoPoint instance from a JSON object created by [GeoPoint.toJSON()](./firestore_.geopoint.md#geopointtojson). | | [isEqual(other)](./firestore_lite.geopoint.md#geopointisequal) | | Returns true if this GeoPoint is equal to the provided one. | -| [toJSON()](./firestore_lite.geopoint.md#geopointtojson) | | Returns a JSON-serializable representation of this GeoPoint. | +| [toJSON()](./firestore_lite.geopoint.md#geopointtojson) | | Returns a JSON-serializable representation of this GeoPoint instance. | ## GeoPoint.(constructor) @@ -77,6 +78,28 @@ The longitude of this `GeoPoint` instance. get longitude(): number; ``` +## GeoPoint.fromJSON() + +Builds a `GeoPoint` instance from a JSON object created by [GeoPoint.toJSON()](./firestore_.geopoint.md#geopointtojson). + +Signature: + +```typescript +static fromJSON(json: object): GeoPoint; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| json | object | a JSON object represention of a GeoPoint instance | + +Returns: + +[GeoPoint](./firestore_lite.geopoint.md#geopoint_class) + +an instance of [GeoPoint](./firestore_.geopoint.md#geopoint_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. + ## GeoPoint.isEqual() Returns true if this `GeoPoint` is equal to the provided one. @@ -101,7 +124,7 @@ true if this `GeoPoint` is equal to the provided one. ## GeoPoint.toJSON() -Returns a JSON-serializable representation of this GeoPoint. +Returns a JSON-serializable representation of this `GeoPoint` instance. Signature: @@ -109,9 +132,12 @@ Returns a JSON-serializable representation of this GeoPoint. toJSON(): { latitude: number; longitude: number; + type: string; }; ``` Returns: -{ latitude: number; longitude: number; } +{ latitude: number; longitude: number; type: string; } + +a JSON representation of this object. diff --git a/docs-devsite/firestore_lite.timestamp.md b/docs-devsite/firestore_lite.timestamp.md index 506a6c66ade..0fb35ada682 100644 --- a/docs-devsite/firestore_lite.timestamp.md +++ b/docs-devsite/firestore_lite.timestamp.md @@ -40,6 +40,7 @@ export declare class Timestamp | Method | Modifiers | Description | | --- | --- | --- | | [fromDate(date)](./firestore_lite.timestamp.md#timestampfromdate) | static | Creates a new timestamp from the given date. | +| [fromJSON(json)](./firestore_lite.timestamp.md#timestampfromjson) | static | Builds a Timestamp instance from a JSON object created by [Timestamp.toJSON()](./firestore_.timestamp.md#timestamptojson). | | [fromMillis(milliseconds)](./firestore_lite.timestamp.md#timestampfrommillis) | static | Creates a new timestamp from the given number of milliseconds. | | [isEqual(other)](./firestore_lite.timestamp.md#timestampisequal) | | Returns true if this Timestamp is equal to the provided one. | | [now()](./firestore_lite.timestamp.md#timestampnow) | static | Creates a new timestamp with the current date, with millisecond precision. | @@ -110,6 +111,26 @@ static fromDate(date: Date): Timestamp; A new `Timestamp` representing the same point in time as the given date. +## Timestamp.fromJSON() + +Builds a `Timestamp` instance from a JSON object created by [Timestamp.toJSON()](./firestore_.timestamp.md#timestamptojson). + +Signature: + +```typescript +static fromJSON(json: object): Timestamp; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| json | object | | + +Returns: + +[Timestamp](./firestore_lite.timestamp.md#timestamp_class) + ## Timestamp.fromMillis() Creates a new timestamp from the given number of milliseconds. @@ -194,11 +215,12 @@ Returns a JSON-serializable representation of this `Timestamp`. toJSON(): { seconds: number; nanoseconds: number; + type: string; }; ``` Returns: -{ seconds: number; nanoseconds: number; } +{ seconds: number; nanoseconds: number; type: string; } ## Timestamp.toMillis() diff --git a/docs-devsite/firestore_lite.vectorvalue.md b/docs-devsite/firestore_lite.vectorvalue.md index 28eaf7f5f01..17c18e4c4ed 100644 --- a/docs-devsite/firestore_lite.vectorvalue.md +++ b/docs-devsite/firestore_lite.vectorvalue.md @@ -24,8 +24,32 @@ export declare class VectorValue | Method | Modifiers | Description | | --- | --- | --- | +| [fromJSON(json)](./firestore_lite.vectorvalue.md#vectorvaluefromjson) | static | Builds a VectorValue instance from a JSON object created by [VectorValue.toJSON()](./firestore_.vectorvalue.md#vectorvaluetojson). | | [isEqual(other)](./firestore_lite.vectorvalue.md#vectorvalueisequal) | | Returns true if the two VectorValue values have the same raw number arrays, returns false otherwise. | | [toArray()](./firestore_lite.vectorvalue.md#vectorvaluetoarray) | | Returns a copy of the raw number array form of the vector. | +| [toJSON()](./firestore_lite.vectorvalue.md#vectorvaluetojson) | | Returns a JSON-serializable representation of this VectorValue instance. | + +## VectorValue.fromJSON() + +Builds a `VectorValue` instance from a JSON object created by [VectorValue.toJSON()](./firestore_.vectorvalue.md#vectorvaluetojson). + +Signature: + +```typescript +static fromJSON(json: object): VectorValue; +``` + +#### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| json | object | a JSON object represention of a VectorValue instance. | + +Returns: + +[VectorValue](./firestore_lite.vectorvalue.md#vectorvalue_class) + +an instance of [VectorValue](./firestore_.vectorvalue.md#vectorvalue_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs. ## VectorValue.isEqual() @@ -60,3 +84,18 @@ toArray(): number[]; number\[\] +## VectorValue.toJSON() + +Returns a JSON-serializable representation of this `VectorValue` instance. + +Signature: + +```typescript +toJSON(): object; +``` +Returns: + +object + +a JSON representation of this object. + diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index ea969c6b94c..d05f032a910 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -89,9 +89,11 @@ export { DocumentChange, DocumentChangeType, DocumentSnapshot, + documentSnapshotFromJSON, FirestoreDataConverter, QueryDocumentSnapshot, QuerySnapshot, + querySnapshotFromJSON, snapshotEqual, SnapshotMetadata, SnapshotOptions @@ -161,6 +163,7 @@ export { getDocsFromServer, onSnapshot, onSnapshotsInSync, + onSnapshotResume, setDoc, updateDoc } from './api/reference_impl'; diff --git a/packages/firestore/src/api/reference_impl.ts b/packages/firestore/src/api/reference_impl.ts index e730fb40da7..8fa21a13e6d 100644 --- a/packages/firestore/src/api/reference_impl.ts +++ b/packages/firestore/src/api/reference_impl.ts @@ -17,6 +17,7 @@ import { getModularInstance } from '@firebase/util'; +import { loadBundle, namedQuery } from '../api/database'; import { CompleteFn, ErrorFn, @@ -59,14 +60,20 @@ import { parseUpdateVarargs } from '../lite-api/user_data_reader'; import { AbstractUserDataWriter } from '../lite-api/user_data_writer'; +import { DocumentKey } from '../model/document_key'; import { DeleteMutation, Mutation, Precondition } from '../model/mutation'; import { debugAssert } from '../util/assert'; import { ByteString } from '../util/byte_string'; -import { FirestoreError } from '../util/error'; +import { Code, FirestoreError } from '../util/error'; import { cast } from '../util/input_validation'; import { ensureFirestoreConfigured, Firestore } from './database'; -import { DocumentSnapshot, QuerySnapshot, SnapshotMetadata } from './snapshot'; +import { + DocumentSnapshot, + FirestoreDataConverter, + QuerySnapshot, + SnapshotMetadata +} from './snapshot'; /** * An options object that can be passed to {@link (onSnapshot:1)} and {@link @@ -483,12 +490,11 @@ export interface Unsubscribe { // integration tests /** - * Attaches a listener for `DocumentSnapshot` events. You may either pass - * individual `onNext` and `onError` callbacks or pass a single observer - * object with `next` and `error` callbacks. + * Attaches a listener for `DocumentSnapshot` events. You may either pass individual `onNext` and + * `onError` callbacks or pass a single observer object with `next` and `error` callbacks. * - * NOTE: Although an `onCompletion` callback can be provided, it will - * never be called because the snapshot stream is never-ending. + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. * * @param reference - A reference to the document to listen to. * @param observer - A single object containing `next` and `error` callbacks. @@ -504,12 +510,11 @@ export function onSnapshot( } ): Unsubscribe; /** - * Attaches a listener for `DocumentSnapshot` events. You may either pass - * individual `onNext` and `onError` callbacks or pass a single observer - * object with `next` and `error` callbacks. + * Attaches a listener for `DocumentSnapshot` events. You may either pass individual `onNext` and + * `onError` callbacks or pass a single observer object with `next` and `error` callbacks. * - * NOTE: Although an `onCompletion` callback can be provided, it will - * never be called because the snapshot stream is never-ending. + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. * * @param reference - A reference to the document to listen to. * @param options - Options controlling the listen behavior. @@ -527,22 +532,18 @@ export function onSnapshot( } ): Unsubscribe; /** - * Attaches a listener for `DocumentSnapshot` events. You may either pass - * individual `onNext` and `onError` callbacks or pass a single observer - * object with `next` and `error` callbacks. + * Attaches a listener for `DocumentSnapshot` events. You may either pass individual `onNext` and + * `onError` callbacks or pass a single observer object with `next` and `error` callbacks. * - * NOTE: Although an `onCompletion` callback can be provided, it will - * never be called because the snapshot stream is never-ending. + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. * * @param reference - A reference to the document to listen to. - * @param onNext - A callback to be called every time a new `DocumentSnapshot` - * is available. - * @param onError - A callback to be called if the listen fails or is - * cancelled. No further callbacks will occur. - * @param onCompletion - Can be provided, but will not be called since streams are - * never ending. - * @returns An unsubscribe function that can be called to cancel - * the snapshot listener. + * @param onNext - A callback to be called every time a new `DocumentSnapshot` is available. + * @param onError - A callback to be called if the listen fails or is cancelled. No further + * callbacks will occur. + * @param onCompletion - Can be provided, but will not be called since streams are never ending. + * @returns An unsubscribe function that can be called to cancel the snapshot listener. */ export function onSnapshot( reference: DocumentReference, @@ -551,23 +552,19 @@ export function onSnapshot( onCompletion?: () => void ): Unsubscribe; /** - * Attaches a listener for `DocumentSnapshot` events. You may either pass - * individual `onNext` and `onError` callbacks or pass a single observer - * object with `next` and `error` callbacks. + * Attaches a listener for `DocumentSnapshot` events. You may either pass individual `onNext` and + * `onError` callbacks or pass a single observer object with `next` and `error` callbacks. * - * NOTE: Although an `onCompletion` callback can be provided, it will - * never be called because the snapshot stream is never-ending. + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. * * @param reference - A reference to the document to listen to. * @param options - Options controlling the listen behavior. - * @param onNext - A callback to be called every time a new `DocumentSnapshot` - * is available. - * @param onError - A callback to be called if the listen fails or is - * cancelled. No further callbacks will occur. - * @param onCompletion - Can be provided, but will not be called since streams are - * never ending. - * @returns An unsubscribe function that can be called to cancel - * the snapshot listener. + * @param onNext - A callback to be called every time a new `DocumentSnapshot` is available. + * @param onError - A callback to be called if the listen fails or is cancelled. No further + * callbacks will occur. + * @param onCompletion - Can be provided, but will not be called since streams are never ending. + * @returns An unsubscribe function that can be called to cancel the snapshot listener. */ export function onSnapshot( reference: DocumentReference, @@ -577,18 +574,16 @@ export function onSnapshot( onCompletion?: () => void ): Unsubscribe; /** - * Attaches a listener for `QuerySnapshot` events. You may either pass - * individual `onNext` and `onError` callbacks or pass a single observer - * object with `next` and `error` callbacks. The listener can be cancelled by - * calling the function that is returned when `onSnapshot` is called. + * Attaches a listener for `QuerySnapshot` events. You may either pass individual `onNext` and + * `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The + * listener can be cancelled by calling the function that is returned when `onSnapshot` is called. * - * NOTE: Although an `onCompletion` callback can be provided, it will - * never be called because the snapshot stream is never-ending. + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. * * @param query - The query to listen to. * @param observer - A single object containing `next` and `error` callbacks. - * @returns An unsubscribe function that can be called to cancel - * the snapshot listener. + * @returns An unsubscribe function that can be called to cancel the snapshot listener. */ export function onSnapshot( query: Query, @@ -599,19 +594,17 @@ export function onSnapshot( } ): Unsubscribe; /** - * Attaches a listener for `QuerySnapshot` events. You may either pass - * individual `onNext` and `onError` callbacks or pass a single observer - * object with `next` and `error` callbacks. The listener can be cancelled by - * calling the function that is returned when `onSnapshot` is called. + * Attaches a listener for `QuerySnapshot` events. You may either pass individual `onNext` and + * `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The + * listener can be cancelled by calling the function that is returned when `onSnapshot` is called. * - * NOTE: Although an `onCompletion` callback can be provided, it will - * never be called because the snapshot stream is never-ending. + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. * * @param query - The query to listen to. * @param options - Options controlling the listen behavior. * @param observer - A single object containing `next` and `error` callbacks. - * @returns An unsubscribe function that can be called to cancel - * the snapshot listener. + * @returns An unsubscribe function that can be called to cancel the snapshot listener. */ export function onSnapshot( query: Query, @@ -623,23 +616,19 @@ export function onSnapshot( } ): Unsubscribe; /** - * Attaches a listener for `QuerySnapshot` events. You may either pass - * individual `onNext` and `onError` callbacks or pass a single observer - * object with `next` and `error` callbacks. The listener can be cancelled by - * calling the function that is returned when `onSnapshot` is called. + * Attaches a listener for `QuerySnapshot` events. You may either pass individual `onNext` and + * `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The + * listener can be cancelled by calling the function that is returned when `onSnapshot` is called. * - * NOTE: Although an `onCompletion` callback can be provided, it will - * never be called because the snapshot stream is never-ending. + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. * * @param query - The query to listen to. - * @param onNext - A callback to be called every time a new `QuerySnapshot` - * is available. - * @param onCompletion - Can be provided, but will not be called since streams are - * never ending. - * @param onError - A callback to be called if the listen fails or is - * cancelled. No further callbacks will occur. - * @returns An unsubscribe function that can be called to cancel - * the snapshot listener. + * @param onNext - A callback to be called every time a new `QuerySnapshot` is available. + * @param onCompletion - Can be provided, but will not be called since streams are never ending. + * @param onError - A callback to be called if the listen fails or is cancelled. No further + * callbacks will occur. + * @returns An unsubscribe function that can be called to cancel the snapshot listener. */ export function onSnapshot( query: Query, @@ -648,24 +637,20 @@ export function onSnapshot( onCompletion?: () => void ): Unsubscribe; /** - * Attaches a listener for `QuerySnapshot` events. You may either pass - * individual `onNext` and `onError` callbacks or pass a single observer - * object with `next` and `error` callbacks. The listener can be cancelled by - * calling the function that is returned when `onSnapshot` is called. + * Attaches a listener for `QuerySnapshot` events. You may either pass individual `onNext` and + * `onError` callbacks or pass a single observer object with `next` and `error` callbacks. The + * listener can be cancelled by calling the function that is returned when `onSnapshot` is called. * - * NOTE: Although an `onCompletion` callback can be provided, it will - * never be called because the snapshot stream is never-ending. + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. * * @param query - The query to listen to. * @param options - Options controlling the listen behavior. - * @param onNext - A callback to be called every time a new `QuerySnapshot` - * is available. - * @param onCompletion - Can be provided, but will not be called since streams are - * never ending. - * @param onError - A callback to be called if the listen fails or is - * cancelled. No further callbacks will occur. - * @returns An unsubscribe function that can be called to cancel - * the snapshot listener. + * @param onNext - A callback to be called every time a new `QuerySnapshot` is available. + * @param onCompletion - Can be provided, but will not be called since streams are never ending. + * @param onError - A callback to be called if the listen fails or is cancelled. No further + * callbacks will occur. + * @returns An unsubscribe function that can be called to cancel the snapshot listener. */ export function onSnapshot( query: Query, @@ -680,16 +665,15 @@ export function onSnapshot( | DocumentReference, ...args: unknown[] ): Unsubscribe { + // onSnapshot for Query or Document. reference = getModularInstance(reference); - let options: SnapshotListenOptions = { includeMetadataChanges: false, source: 'default' }; let currArg = 0; if (typeof args[currArg] === 'object' && !isPartialObserver(args[currArg])) { - options = args[currArg] as SnapshotListenOptions; - currArg++; + options = args[currArg++] as SnapshotListenOptions; } const internalOptions = { @@ -736,7 +720,6 @@ export function onSnapshot( firestore = cast(query.firestore, Firestore); internalQuery = query._query; const userDataWriter = new ExpUserDataWriter(firestore); - observer = { next: snapshot => { if (args[currArg]) { @@ -761,6 +744,340 @@ export function onSnapshot( ); } +/** + * Attaches a listener for `QuerySnapshot` events based on data generated by invoking + * {@link QuerySnapshot.toJSON} You may either pass individual `onNext` and `onError` callbacks or + * pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by + * calling the function that is returned when `onSnapshot` is called. + * + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. + * + * @param firestore - The {@link Firestore} instance to enable the listener for. + * @param snapshotJson - A JSON object generated by invoking {@link QuerySnapshot.toJSON}. + * @param onNext - A callback to be called every time a new `QuerySnapshot` is available. + * @param onError - A callback to be called if the listen fails or is cancelled. No further + * callbacks will occur. + * @param onCompletion - Can be provided, but will not be called since streams are never ending. + * @param converter - An optional object that converts objects from Firestore before the onNext + * listener is invoked. + * @returns An unsubscribe function that can be called to cancel the snapshot listener. + */ +export function onSnapshotResume< + AppModelType, + DbModelType extends DocumentData +>( + firestore: Firestore, + snapshotJson: object, + onNext: (snapshot: QuerySnapshot) => void, + onError?: (error: FirestoreError) => void, + onCompletion?: () => void, + converter?: FirestoreDataConverter +): Unsubscribe; +/** + * Attaches a listener for `DocumentSnapshot` events based on data generated by invoking + * {@link DocumentSnapshot.toJSON}. You may either pass individual `onNext` and `onError` callbacks or + * pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by + * calling the function that is returned when `onSnapshot` is called. + * + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. + * + * @param firestore - The {@link Firestore} instance to enable the listener for. + * @param snapshotJson - A JSON object generated by invoking {@link DocumentSnapshot.toJSON}. + * @param onNext - A callback to be called every time a new `DocumentSnapshot` is available. + * @param onError - A callback to be called if the listen fails or is cancelled. No further + * callbacks will occur. + * @param onCompletion - Can be provided, but will not be called since streams are + * never ending. + * @param converter - An optional object that converts objects from Firestore before the onNext + * listener is invoked. + * @returns An unsubscribe function that can be called to cancel the snapshot listener. + */ +export function onSnapshotResume< + AppModelType, + DbModelType extends DocumentData +>( + firestore: Firestore, + snapshotJson: object, + onNext: (snapshot: DocumentSnapshot) => void, + onError?: (error: FirestoreError) => void, + onCompletion?: () => void, + converter?: FirestoreDataConverter +): Unsubscribe; +/** + * Attaches a listener for `QuerySnapshot` events based on data generated by invoking + * {@link QuerySnapshot.toJSON}. You may either pass individual `onNext` and `onError` callbacks or + * pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by + * calling the function that is returned when `onSnapshot` is called. + * + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. + * + * @param firestore - The {@link Firestore} instance to enable the listener for. + * @param snapshotJson - A JSON object generated by invoking {@link QuerySnapshot.toJSON}. + * @param options - Options controlling the listen behavior. + * @param onNext - A callback to be called every time a new `QuerySnapshot` is available. + * @param onError - A callback to be called if the listen fails or is cancelled. No further + * callbacks will occur. + * @param onCompletion - Can be provided, but will not be called since streams are never ending. + * @param converter - An optional object that converts objects from Firestore before the onNext + * listener is invoked. + * @returns An unsubscribe function that can be called to cancel the snapshot listener. + */ +export function onSnapshotResume< + AppModelType, + DbModelType extends DocumentData +>( + firestore: Firestore, + snapshotJson: object, + options: SnapshotListenOptions, + onNext: (snapshot: QuerySnapshot) => void, + onError?: (error: FirestoreError) => void, + onCompletion?: () => void, + converter?: FirestoreDataConverter +): Unsubscribe; +/** + * Attaches a listener for `DocumentSnapshot` events based on data generated by invoking + * {@link DocumentSnapshot.toJSON}. You may either pass individual `onNext` and `onError` callbacks + * or pass a single observer object with `next` and `error` callbacks. The listener can be cancelled + * by calling the function that is returned when `onSnapshot` is called. + * + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. + * + * @param firestore - The {@link Firestore} instance to enable the listener for. + * @param snapshotJson - A JSON object generated by invoking {@link DocumentSnapshot.toJSON}. + * @param options - Options controlling the listen behavior. + * @param onNext - A callback to be called every time a new `DocumentSnapshot` is available. + * @param onError - A callback to be called if the listen fails or is cancelled. No further + * callbacks will occur. + * @param onCompletion - Can be provided, but will not be called since streams are never ending. + * @param converter - An optional object that converts objects from Firestore before the onNext + * listener is invoked. + * @returns An unsubscribe function that can be called to cancel + * the snapshot listener. + */ +export function onSnapshotResume< + AppModelType, + DbModelType extends DocumentData +>( + firestore: Firestore, + snapshotJson: object, + options: SnapshotListenOptions, + onNext: (snapshot: DocumentSnapshot) => void, + onError?: (error: FirestoreError) => void, + onCompletion?: () => void, + converter?: FirestoreDataConverter +): Unsubscribe; +/** + * Attaches a listener for `QuerySnapshot` events based on QuerySnapshot data generated by invoking + * {@link QuerySnapshot.toJSON}. You may either pass individual `onNext` and `onError` callbacks or + * pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by + * calling the function that is returned when `onSnapshot` is called. + * + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. + * + * @param firestore - The {@link Firestore} instance to enable the listener for. + * @param snapshotJson - A JSON object generated by invoking {@link QuerySnapshot.toJSON}. + * @param observer - A single object containing `next` and `error` callbacks. + * @param converter - An optional object that converts objects from Firestore before the onNext + * listener is invoked. + * @returns An unsubscribe function that can be called to cancel + * the snapshot listener. + */ +export function onSnapshotResume< + AppModelType, + DbModelType extends DocumentData +>( + firestore: Firestore, + snapshotJson: object, + observer: { + next: (snapshot: QuerySnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; + }, + converter?: FirestoreDataConverter +): Unsubscribe; +/** + * Attaches a listener for `DocumentSnapshot` events based on data generated by invoking + * {@link DocumentSnapshot.toJSON} You may either pass individual `onNext` and `onError` callbacks + * or pass a single observer object with `next` and `error` callbacks. The listener can be cancelled + * by calling the function that is returned when `onSnapshot` is called. + * + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. + * + * @param firestore - The {@link Firestore} instance to enable the listener for. + * @param snapshotJson - A JSON object generated by invoking {@link DocumentSnapshot.toJSON}. + * @param observer - A single object containing `next` and `error` callbacks. + * @param converter - An optional object that converts objects from Firestore before the onNext + * listener is invoked. + * @returns An unsubscribe function that can be called to cancel + * the snapshot listener. + */ +export function onSnapshotResume< + AppModelType, + DbModelType extends DocumentData +>( + firestore: Firestore, + snapshotJson: object, + observer: { + next: (snapshot: DocumentSnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; + }, + converter?: FirestoreDataConverter +): Unsubscribe; +/** + * Attaches a listener for `QuerySnapshot` events based on QuerySnapshot data generated by invoking + * {@link QuerySnapshot.toJSON} You may either pass individual `onNext` and `onError` callbacks or + * pass a single observer object with `next` and `error` callbacks. The listener can be cancelled by + * calling the function that is returned when `onSnapshot` is called. + * + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. + * + * @param firestore - The {@link Firestore} instance to enable the listener for. + * @param snapshotJson - A JSON object generated by invoking {@link QuerySnapshot.toJSON}. + * @param options - Options controlling the listen behavior. + * @param observer - A single object containing `next` and `error` callbacks. + * @param converter - An optional object that converts objects from Firestore before the onNext + * listener is invoked. + * @returns An unsubscribe function that can be called to cancel + * the snapshot listener. + */ +export function onSnapshotResume< + AppModelType, + DbModelType extends DocumentData +>( + firestore: Firestore, + snapshotJson: object, + options: SnapshotListenOptions, + observer: { + next: (snapshot: QuerySnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; + }, + converter?: FirestoreDataConverter +): Unsubscribe; +/** + * Attaches a listener for `DocumentSnapshot` events based on QuerySnapshot data generated by + * invoking {@link DocumentSnapshot.toJSON} You may either pass individual `onNext` and `onError` + * callbacks or pass a single observer object with `next` and `error` callbacks. The listener can be + * cancelled by calling the function that is returned when `onSnapshot` is called. + * + * NOTE: Although an `onCompletion` callback can be provided, it will never be called because the + * snapshot stream is never-ending. + * + * @param firestore - The {@link Firestore} instance to enable the listener for. + * @param snapshotJson - A JSON object generated by invoking {@link DocumentSnapshot.toJSON}. + * @param options - Options controlling the listen behavior. + * @param observer - A single object containing `next` and `error` callbacks. + * @param converter - An optional object that converts objects from Firestore before the onNext + * listener is invoked. + * @returns An unsubscribe function that can be called to cancel the snapshot listener. + */ +export function onSnapshotResume< + AppModelType, + DbModelType extends DocumentData +>( + firestore: Firestore, + snapshotJson: object, + options: SnapshotListenOptions, + observer: { + next: (snapshot: DocumentSnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; + }, + converter?: FirestoreDataConverter +): Unsubscribe; +export function onSnapshotResume< + AppModelType, + DbModelType extends DocumentData +>(reference: Firestore, snapshotJson: object, ...args: unknown[]): Unsubscribe { + const db = getModularInstance(reference); + const json = normalizeSnapshotJsonFields(snapshotJson); + if (json.error) { + throw new FirestoreError(Code.INVALID_ARGUMENT, json.error); + } + let curArg = 0; + let options: SnapshotListenOptions | undefined = undefined; + if (typeof args[curArg] === 'object' && !isPartialObserver(args[curArg])) { + options = args[curArg++] as SnapshotListenOptions; + } + + if (json.bundleSource === 'QuerySnapshot') { + let observer: { + next: (snapshot: QuerySnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; + } | null = null; + if (typeof args[curArg] === 'object' && isPartialObserver(args[curArg])) { + const userObserver = args[curArg++] as PartialObserver< + QuerySnapshot + >; + observer = { + next: userObserver.next!, + error: userObserver.error, + complete: userObserver.complete + }; + } else { + observer = { + next: args[curArg++] as ( + snapshot: QuerySnapshot + ) => void, + error: args[curArg++] as (error: FirestoreError) => void, + complete: args[curArg++] as () => void + }; + } + return onSnapshotQuerySnapshotBundle( + db, + json, + options, + observer!, + args[curArg] as FirestoreDataConverter + ); + } else if (json.bundleSource === 'DocumentSnapshot') { + let observer: { + next: (snapshot: DocumentSnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; + } | null = null; + if (typeof args[curArg] === 'object' && isPartialObserver(args[curArg])) { + const userObserver = args[curArg++] as PartialObserver< + DocumentSnapshot + >; + observer = { + next: userObserver.next!, + error: userObserver.error, + complete: userObserver.complete + }; + } else { + observer = { + next: args[curArg++] as ( + snapshot: DocumentSnapshot + ) => void, + error: args[curArg++] as (error: FirestoreError) => void, + complete: args[curArg++] as () => void + }; + } + return onSnapshotDocumentSnapshotBundle( + db, + json, + options, + observer!, + args[curArg] as FirestoreDataConverter + ); + } else { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + `unsupported bundle source: ${json.bundleSource}` + ); + } +} + // TODO(firestorexp): Make sure these overloads are tested via the Firestore // integration tests @@ -859,3 +1176,186 @@ function convertToDocSnapshot( ref.converter ); } + +/** + * Ensures the data required to construct an {@link onSnapshot} listener exist in a `snapshotJson` + * object that originates from {@link DocumentSnapshot.toJSON} or {@link Querysnapshot.toJSON}. The + * data is normalized into a typed object. + * + * @param snapshotJson - The JSON object that the app provided to {@link onSnapshot}. + * @returns A normalized object that contains all of the required bundle JSON fields. If + * {@link snapshotJson} doesn't contain the required fields, or if the fields exist as empty + * strings, then the {@link snapshotJson.error} field will be a non empty string. + * + * @internal + */ +function normalizeSnapshotJsonFields(snapshotJson: object): { + bundle: string; + bundleName: string; + bundleSource: string; + error?: string; +} { + const result: { + bundle: string; + bundleName: string; + bundleSource: string; + error?: string; + } = { + bundle: '', + bundleName: '', + bundleSource: '' + }; + const requiredKeys = ['bundle', 'bundleName', 'bundleSource']; + for (const key of requiredKeys) { + if (!(key in snapshotJson)) { + result.error = `snapshotJson missing required field: ${key}`; + break; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const value = (snapshotJson as any)[key]; + if (typeof value !== 'string') { + result.error = `snapshotJson field '${key}' must be a string.`; + break; + } + if (value.length === 0) { + result.error = `snapshotJson field '${key}' cannot be an empty string.`; + break; + } + if (key === 'bundle') { + result.bundle = value; + } else if (key === 'bundleName') { + result.bundleName = value; + } else if (key === 'bundleSource') { + result.bundleSource = value; + } + } + return result; +} + +/** + * Loads the bundle in a separate task and then invokes {@link onSnapshot} with a + * {@link DocumentReference} for the document in the bundle. + * + * @param firestore - The {@link Firestore} instance for the {@link onSnapshot} operation request. + * @param json - The JSON bundle to load, produced by {@link DocumentSnapshot.toJSON}. + * @param options - Options controlling the listen behavior. + * @param observer - A single object containing `next` and `error` callbacks. + * @param converter - An optional object that converts objects from Firestore before the onNext + * listener is invoked. + * @returns An unsubscribe function that can be called to cancel the snapshot + * listener. + * + * @internal + */ +function onSnapshotDocumentSnapshotBundle< + AppModelType, + DbModelType extends DocumentData +>( + db: Firestore, + json: { bundle: string; bundleName: string; bundleSource: string }, + options: SnapshotListenOptions | undefined, + observer: { + next: (snapshot: DocumentSnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; + }, + converter?: FirestoreDataConverter +): Unsubscribe { + let unsubscribed: boolean = false; + let internalUnsubscribe: Unsubscribe | undefined; + const loadTask = loadBundle(db, json.bundle); + loadTask + .then(() => { + if (!unsubscribed) { + const docReference = new DocumentReference( + db, + converter ? converter : null, + DocumentKey.fromPath(json.bundleName) + ); + internalUnsubscribe = onSnapshot( + docReference as DocumentReference, + options ? options : {}, + observer + ); + } + }) + .catch(e => { + if (observer.error) { + observer.error(e); + } + return () => {}; + }); + return () => { + if (unsubscribed) { + return; + } + unsubscribed = true; + if (internalUnsubscribe) { + internalUnsubscribe(); + } + }; +} + +/** + * Loads the bundle in a separate task and then invokes {@link onSnapshot} with a + * {@link Query} that represents the Query in the bundle. + * + * @param firestore - The {@link Firestore} instance for the {@link onSnapshot} operation request. + * @param json - The JSON bundle to load, produced by {@link QuerySnapshot.toJSON}. + * @param options - Options controlling the listen behavior. + * @param observer - A single object containing `next` and `error` callbacks. + * @param converter - An optional object that converts objects from Firestore before the onNext + * listener is invoked. + * @returns An unsubscribe function that can be called to cancel the snapshot + * listener. + * + * @internal + */ +function onSnapshotQuerySnapshotBundle< + AppModelType, + DbModelType extends DocumentData +>( + db: Firestore, + json: { bundle: string; bundleName: string; bundleSource: string }, + options: SnapshotListenOptions | undefined, + observer: { + next: (snapshot: QuerySnapshot) => void; + error?: (error: FirestoreError) => void; + complete?: () => void; + }, + converter?: FirestoreDataConverter +): Unsubscribe { + let unsubscribed: boolean = false; + let internalUnsubscribe: Unsubscribe | undefined; + const loadTask = loadBundle(db, json.bundle); + loadTask + .then(() => namedQuery(db, json.bundleName)) + .then(query => { + if (query && !unsubscribed) { + const realQuery: Query = (query as Query)!; + if (converter) { + realQuery.withConverter(converter); + } + internalUnsubscribe = onSnapshot( + query as Query, + options ? options : {}, + observer + ); + } + }) + .catch(e => { + if (observer.error) { + observer.error(e); + } + return () => {}; + }); + return () => { + if (unsubscribed) { + return; + } + unsubscribed = true; + if (internalUnsubscribe) { + internalUnsubscribe(); + } + }; +} diff --git a/packages/firestore/src/api/snapshot.ts b/packages/firestore/src/api/snapshot.ts index 669ac26cafe..c82add0642a 100644 --- a/packages/firestore/src/api/snapshot.ts +++ b/packages/firestore/src/api/snapshot.ts @@ -15,6 +15,8 @@ * limitations under the License. */ +import { BundleLoader } from '../core/bundle_impl'; +import { createBundleReaderSync } from '../core/firestore_client'; import { newQueryComparator } from '../core/query'; import { ChangeType, ViewSnapshot } from '../core/view_snapshot'; import { FieldPath } from '../lite-api/field_path'; @@ -26,6 +28,7 @@ import { SetOptions, WithFieldValue } from '../lite-api/reference'; +import { LiteUserDataWriter } from '../lite-api/reference_impl'; import { DocumentSnapshot as LiteDocumentSnapshot, fieldPathFromArgument, @@ -33,14 +36,30 @@ import { } from '../lite-api/snapshot'; import { UntypedFirestoreDataConverter } from '../lite-api/user_data_reader'; import { AbstractUserDataWriter } from '../lite-api/user_data_writer'; +import { fromBundledQuery } from '../local/local_serializer'; +import { documentKeySet } from '../model/collections'; import { Document } from '../model/document'; import { DocumentKey } from '../model/document_key'; +import { DocumentSet } from '../model/document_set'; +import { ResourcePath } from '../model/path'; +import { newSerializer } from '../platform/serializer'; +import { + buildQuerySnapshotJsonBundle, + buildDocumentSnapshotJsonBundle +} from '../platform/snapshot_to_json'; +import { fromDocument } from '../remote/serializer'; import { debugAssert, fail } from '../util/assert'; import { Code, FirestoreError } from '../util/error'; +// API extractor fails importing 'property' unless we also explicitly import 'Property'. +// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports-ts +import { Property, property, validateJSON } from '../util/json_validation'; +import { AutoId } from '../util/misc'; import { Firestore } from './database'; import { SnapshotListenOptions } from './reference_impl'; +const NOT_SUPPORTED = 'NOT SUPPORTED'; + /** * Converter used by `withConverter()` to transform user objects of type * `AppModelType` into Firestore data of type `DbModelType`. @@ -496,6 +515,148 @@ export class DocumentSnapshot< } return undefined; } + + static _jsonSchemaVersion: string = 'firestore/documentSnapshot/1.0'; + static _jsonSchema = { + type: property('string', DocumentSnapshot._jsonSchemaVersion), + bundleSource: property('string', 'DocumentSnapshot'), + bundleName: property('string'), + bundle: property('string') + }; + + /** + * Returns a JSON-serializable representation of this `DocumentSnapshot` instance. + * + * @returns a JSON representation of this object. Throws a {@link FirestoreError} if this + * `DocumentSnapshot` has pending writes. + */ + toJSON(): object { + if (this.metadata.hasPendingWrites) { + throw new FirestoreError( + Code.FAILED_PRECONDITION, + 'DocumentSnapshot.toJSON() attempted to serialize a document with pending writes. ' + + 'Await waitForPendingWrites() before invoking toJSON().' + ); + } + const document = this._document; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result: any = {}; + result['type'] = DocumentSnapshot._jsonSchemaVersion; + result['bundle'] = ''; + result['bundleSource'] = 'DocumentSnapshot'; + result['bundleName'] = this._key.toString(); + + if ( + !document || + !document.isValidDocument() || + !document.isFoundDocument() + ) { + return result; + } + const documentData = this._userDataWriter.convertObjectMap( + document.data.value.mapValue.fields, + 'previous' + ); + result['bundle'] = buildDocumentSnapshotJsonBundle( + this._firestore, + document, + documentData, + this.ref.path + ); + return result; + } +} + +/** + * Builds a `DocumentSnapshot` instance from a JSON object created by + * {@link DocumentSnapshot.toJSON}. + * + * @param firestore - The {@link Firestore} instance the snapshot should be loaded for. + * @param json - a JSON object represention of a `DocumentSnapshot` instance. + * @returns an instance of {@link DocumentSnapshot} if the JSON object could be + * parsed. Throws a {@link FirestoreError} if an error occurs. + */ +export function documentSnapshotFromJSON( + db: Firestore, + json: object +): DocumentSnapshot; +/** + * Builds a `DocumentSnapshot` instance from a JSON object created by + * {@link DocumentSnapshot.toJSON}. + * + * @param firestore - The {@link Firestore} instance the snapshot should be loaded for. + * @param json - a JSON object represention of a `DocumentSnapshot` instance. + * @param converter - Converts objects to and from Firestore. + * @returns an instance of {@link DocumentSnapshot} if the JSON object could be + * parsed. Throws a {@link FirestoreError} if an error occurs. + */ +export function documentSnapshotFromJSON< + AppModelType, + DbModelType extends DocumentData = DocumentData +>( + db: Firestore, + json: object, + converter: FirestoreDataConverter +): DocumentSnapshot; +export function documentSnapshotFromJSON< + AppModelType, + DbModelType extends DocumentData = DocumentData +>( + db: Firestore, + json: object, + converter?: FirestoreDataConverter +): DocumentSnapshot { + if (validateJSON(json, DocumentSnapshot._jsonSchema)) { + if (json.bundle === NOT_SUPPORTED) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'The provided JSON object was created in a client environment, which is not supported.' + ); + } + // Parse the bundle data. + const serializer = newSerializer(db._databaseId); + const bundleReader = createBundleReaderSync(json.bundle, serializer); + const elements = bundleReader.getElements(); + const bundleLoader: BundleLoader = new BundleLoader( + bundleReader.getMetadata(), + serializer + ); + for (const element of elements) { + bundleLoader.addSizedElement(element); + } + + // Ensure that we have the correct number of documents in the bundle. + const bundledDocuments = bundleLoader.documents; + if (bundledDocuments.length !== 1) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + `Expected bundle data to contain 1 document, but it contains ${bundledDocuments.length} documents.` + ); + } + + // Build out the internal document data. + const document = fromDocument(serializer, bundledDocuments[0].document!); + const documentKey = new DocumentKey( + ResourcePath.fromString(json.bundleName) + ); + + // Return the external facing DocumentSnapshot. + return new DocumentSnapshot( + db, + new LiteUserDataWriter(db), + documentKey, + document, + new SnapshotMetadata( + /* hasPendingWrites= */ false, + /* fromCache= */ false + ), + converter ? converter : null + ); + } + throw new FirestoreError( + Code.INTERNAL, + 'Unexpected error creating DocumentSnapshot from JSON.' + ); } /** @@ -651,6 +812,171 @@ export class QuerySnapshot< return this._cachedChanges; } + + static _jsonSchemaVersion: string = 'firestore/querySnapshot/1.0'; + static _jsonSchema = { + type: property('string', QuerySnapshot._jsonSchemaVersion), + bundleSource: property('string', 'QuerySnapshot'), + bundleName: property('string'), + bundle: property('string') + }; + + /** + * Returns a JSON-serializable representation of this `QuerySnapshot` instance. + * + * @returns a JSON representation of this object. Throws a {@link FirestoreError} if this + * `QuerySnapshot` has pending writes. + */ + toJSON(): object { + if (this.metadata.hasPendingWrites) { + throw new FirestoreError( + Code.FAILED_PRECONDITION, + 'QuerySnapshot.toJSON() attempted to serialize a document with pending writes. ' + + 'Await waitForPendingWrites() before invoking toJSON().' + ); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result: any = {}; + result['type'] = QuerySnapshot._jsonSchemaVersion; + result['bundleSource'] = 'QuerySnapshot'; + result['bundleName'] = AutoId.newId(); + + const databaseId = this._firestore._databaseId.database; + const projectId = this._firestore._databaseId.projectId; + const parent = `projects/${projectId}/databases/${databaseId}/documents`; + const documents: Document[] = []; + const documentData: DocumentData[] = []; + const paths: string[] = []; + + this.docs.forEach(doc => { + if (doc._document === null) { + return; + } + documents.push(doc._document); + documentData.push( + this._userDataWriter.convertObjectMap( + doc._document.data.value.mapValue.fields, + 'previous' + ) + ); + paths.push(doc.ref.path); + }); + result['bundle'] = buildQuerySnapshotJsonBundle( + this._firestore, + this.query._query, + result['bundleName'], + parent, + paths, + documents, + documentData + ); + return result; + } +} + +/** + * Builds a `QuerySnapshot` instance from a JSON object created by + * {@link QuerySnapshot.toJSON}. + * + * @param firestore - The {@link Firestore} instance the snapshot should be loaded for. + * @param json - a JSON object represention of a `QuerySnapshot` instance. + * @returns an instance of {@link QuerySnapshot} if the JSON object could be + * parsed. Throws a {@link FirestoreError} if an error occurs. + */ +export function querySnapshotFromJSON( + db: Firestore, + json: object +): QuerySnapshot; +/** + * Builds a `QuerySnapshot` instance from a JSON object created by + * {@link QuerySnapshot.toJSON}. + * + * @param firestore - The {@link Firestore} instance the snapshot should be loaded for. + * @param json - a JSON object represention of a `QuerySnapshot` instance. + * @param converter - Converts objects to and from Firestore. + * @returns an instance of {@link QuerySnapshot} if the JSON object could be + * parsed. Throws a {@link FirestoreError} if an error occurs. + */ +export function querySnapshotFromJSON< + AppModelType, + DbModelType extends DocumentData = DocumentData +>( + db: Firestore, + json: object, + converter: FirestoreDataConverter +): QuerySnapshot; +export function querySnapshotFromJSON< + AppModelType, + DbModelType extends DocumentData +>( + db: Firestore, + json: object, + converter?: FirestoreDataConverter +): QuerySnapshot { + if (validateJSON(json, QuerySnapshot._jsonSchema)) { + if (json.bundle === NOT_SUPPORTED) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'The provided JSON object was created in a client environment, which is not supported.' + ); + } + // Parse the bundle data. + const serializer = newSerializer(db._databaseId); + const bundleReader = createBundleReaderSync(json.bundle, serializer); + const elements = bundleReader.getElements(); + const bundleLoader: BundleLoader = new BundleLoader( + bundleReader.getMetadata(), + serializer + ); + for (const element of elements) { + bundleLoader.addSizedElement(element); + } + + if (bundleLoader.queries.length !== 1) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + `Snapshot data expected 1 query but found ${bundleLoader.queries.length} queries.` + ); + } + + // Create an internal Query object from the named query in the bundle. + const query = fromBundledQuery(bundleLoader.queries[0].bundledQuery!); + + // Construct the arrays of document data for the query. + const bundledDocuments = bundleLoader.documents; + let documentSet = new DocumentSet(); + bundledDocuments.map(bundledDocument => { + const document = fromDocument(serializer, bundledDocument.document!); + documentSet = documentSet.add(document); + }); + // Create a view snapshot of the query and documents. + const viewSnapshot = ViewSnapshot.fromInitialDocuments( + query, + documentSet, + documentKeySet() /* Zero mutated keys signifies no pending writes. */, + /* fromCache= */ false, + /* hasCachedResults= */ false + ); + + // Create an external Query object, required to construct the QuerySnapshot. + const externalQuery = new Query( + db, + converter ? converter : null, + query + ); + + // Return a new QuerySnapshot with all of the collected data. + return new QuerySnapshot( + db, + new LiteUserDataWriter(db), + externalQuery, + viewSnapshot + ); + } + throw new FirestoreError( + Code.INTERNAL, + 'Unexpected error creating QuerySnapshot from JSON.' + ); } /** Calculates the array of `DocumentChange`s for a given `ViewSnapshot`. */ diff --git a/packages/firestore/src/core/bundle_impl.ts b/packages/firestore/src/core/bundle_impl.ts index 9a42e43261f..b91933f1349 100644 --- a/packages/firestore/src/core/bundle_impl.ts +++ b/packages/firestore/src/core/bundle_impl.ts @@ -82,27 +82,42 @@ export class BundleConverterImpl implements BundleConverter { } /** - * A class to process the elements from a bundle, load them into local + * A class to process the elements from a bundle, and optionally load them into local * storage and provide progress update while loading. */ export class BundleLoader { /** The current progress of loading */ private progress: LoadBundleTaskProgress; /** Batched queries to be saved into storage */ - private queries: ProtoNamedQuery[] = []; + private _queries: ProtoNamedQuery[] = []; /** Batched documents to be saved into storage */ - private documents: BundledDocuments = []; + private _documents: BundledDocuments = []; /** The collection groups affected by this bundle. */ private collectionGroups = new Set(); constructor( private bundleMetadata: ProtoBundleMetadata, - private localStore: LocalStore, private serializer: JsonProtoSerializer ) { this.progress = bundleInitialProgress(bundleMetadata); } + /** + * Returns the named queries that have been parsed from the SizeBundleElements added by + * calling {@link adSizedElement}. + */ + get queries(): ProtoNamedQuery[] { + return this._queries; + } + + /** + * Returns the BundledDocuments that have been parsed from the SizeBundleElements added by + * calling {@link addSizedElement}. + */ + get documents(): BundledDocuments { + return this._documents; + } + /** * Adds an element from the bundle to the loader. * @@ -117,9 +132,9 @@ export class BundleLoader { let documentsLoaded = this.progress.documentsLoaded; if (element.payload.namedQuery) { - this.queries.push(element.payload.namedQuery); + this._queries.push(element.payload.namedQuery); } else if (element.payload.documentMetadata) { - this.documents.push({ metadata: element.payload.documentMetadata }); + this._documents.push({ metadata: element.payload.documentMetadata }); if (!element.payload.documentMetadata.exists) { ++documentsLoaded; } @@ -133,12 +148,12 @@ export class BundleLoader { this.collectionGroups.add(path.get(path.length - 2)); } else if (element.payload.document) { debugAssert( - this.documents.length > 0 && - this.documents[this.documents.length - 1].metadata.name === + this._documents.length > 0 && + this._documents[this._documents.length - 1].metadata.name === element.payload.document.name, 'The document being added does not match the stored metadata.' ); - this.documents[this.documents.length - 1].document = + this._documents[this._documents.length - 1].document = element.payload.document; ++documentsLoaded; } @@ -176,26 +191,28 @@ export class BundleLoader { /** * Update the progress to 'Success' and return the updated progress. */ - async complete(): Promise { + async completeAndStoreAsync( + localStore: LocalStore + ): Promise { debugAssert( - this.documents[this.documents.length - 1]?.metadata.exists !== true || - !!this.documents[this.documents.length - 1].document, + this._documents[this._documents.length - 1]?.metadata.exists !== true || + !!this._documents[this._documents.length - 1].document, 'Bundled documents end with a document metadata element instead of a document.' ); debugAssert(!!this.bundleMetadata.id, 'Bundle ID must be set.'); const changedDocs = await localStoreApplyBundledDocuments( - this.localStore, + localStore, new BundleConverterImpl(this.serializer), - this.documents, + this._documents, this.bundleMetadata.id! ); const queryDocumentMap = this.getQueryDocumentMapping(this.documents); - for (const q of this.queries) { + for (const q of this._queries) { await localStoreSaveNamedQuery( - this.localStore, + localStore, q, queryDocumentMap.get(q.name!) ); diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index e43060d229d..d04f37a3d7b 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -53,8 +53,9 @@ import { JsonProtoSerializer } from '../remote/serializer'; import { debugAssert } from '../util/assert'; import { AsyncObserver } from '../util/async_observer'; import { AsyncQueue, wrapInUserErrorIfRecoverable } from '../util/async_queue'; -import { BundleReader } from '../util/bundle_reader'; +import { BundleReader, BundleReaderSync } from '../util/bundle_reader'; import { newBundleReader } from '../util/bundle_reader_impl'; +import { newBundleReaderSync } from '../util/bundle_reader_sync_impl'; import { Code, FirestoreError } from '../util/error'; import { logDebug, logWarn } from '../util/log'; import { AutoId } from '../util/misc'; @@ -351,7 +352,7 @@ async function ensureOfflineComponents( return client._offlineComponents!; } -async function ensureOnlineComponents( +export async function ensureOnlineComponents( client: FirestoreClient ): Promise { if (!client._onlineComponents) { @@ -833,6 +834,13 @@ function createBundleReader( return newBundleReader(toByteStreamReader(content), serializer); } +export function createBundleReaderSync( + bundleData: string, + serializer: JsonProtoSerializer +): BundleReaderSync { + return newBundleReaderSync(bundleData, serializer); +} + export function firestoreClientSetIndexConfiguration( client: FirestoreClient, indexes: FieldIndex[] diff --git a/packages/firestore/src/core/sync_engine_impl.ts b/packages/firestore/src/core/sync_engine_impl.ts index 404d4663a47..f00acb5a4ee 100644 --- a/packages/firestore/src/core/sync_engine_impl.ts +++ b/packages/firestore/src/core/sync_engine_impl.ts @@ -1697,11 +1697,7 @@ async function loadBundleImpl( task._updateProgress(bundleInitialProgress(metadata)); - const loader = new BundleLoader( - metadata, - syncEngine.localStore, - reader.serializer - ); + const loader = new BundleLoader(metadata, reader.serializer); let element = await reader.nextElement(); while (element) { debugAssert( @@ -1716,7 +1712,7 @@ async function loadBundleImpl( element = await reader.nextElement(); } - const result = await loader.complete(); + const result = await loader.completeAndStoreAsync(syncEngine.localStore); await syncEngineEmitNewSnapsAndNotifyLocalStore( syncEngine, result.changedDocs, diff --git a/packages/firestore/src/lite-api/bytes.ts b/packages/firestore/src/lite-api/bytes.ts index ef16bc54463..225ad7918af 100644 --- a/packages/firestore/src/lite-api/bytes.ts +++ b/packages/firestore/src/lite-api/bytes.ts @@ -17,6 +17,9 @@ import { ByteString } from '../util/byte_string'; import { Code, FirestoreError } from '../util/error'; +// API extractor fails importing property unless we also explicitly import Property. +// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports-ts +import { Property, property, validateJSON } from '../util/json_validation'; /** * An immutable object representing an array of bytes. @@ -91,4 +94,39 @@ export class Bytes { isEqual(other: Bytes): boolean { return this._byteString.isEqual(other._byteString); } + + static _jsonSchemaVersion: string = 'firestore/bytes/1.0'; + static _jsonSchema = { + type: property('string', Bytes._jsonSchemaVersion), + bytes: property('string') + }; + + /** + * Returns a JSON-serializable representation of this `Bytes` instance. + * + * @returns a JSON representation of this object. + */ + toJSON(): object { + return { + type: Bytes._jsonSchemaVersion, + bytes: this.toBase64() + }; + } + + /** + * Builds a `Bytes` instance from a JSON object created by {@link Bytes.toJSON}. + * + * @param json a JSON object represention of a `Bytes` instance + * @returns an instance of {@link Bytes} if the JSON object could be parsed. Throws a + * {@link FirestoreError} if an error occurs. + */ + static fromJSON(json: object): Bytes { + if (validateJSON(json, Bytes._jsonSchema)) { + return Bytes.fromBase64String(json.bytes); + } + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'Unexpected error creating Bytes from JSON.' + ); + } } diff --git a/packages/firestore/src/lite-api/geo_point.ts b/packages/firestore/src/lite-api/geo_point.ts index 3e2944cde29..75194049f89 100644 --- a/packages/firestore/src/lite-api/geo_point.ts +++ b/packages/firestore/src/lite-api/geo_point.ts @@ -16,6 +16,9 @@ */ import { Code, FirestoreError } from '../util/error'; +// API extractor fails importing 'property' unless we also explicitly import 'Property'. +// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports-ts +import { Property, property, validateJSON } from '../util/json_validation'; import { primitiveComparator } from '../util/misc'; /** @@ -79,11 +82,6 @@ export class GeoPoint { return this._lat === other._lat && this._long === other._long; } - /** Returns a JSON-serializable representation of this GeoPoint. */ - toJSON(): { latitude: number; longitude: number } { - return { latitude: this._lat, longitude: this._long }; - } - /** * Actually private to JS consumers of our API, so this function is prefixed * with an underscore. @@ -94,4 +92,41 @@ export class GeoPoint { primitiveComparator(this._long, other._long) ); } + + static _jsonSchemaVersion: string = 'firestore/geoPoint/1.0'; + static _jsonSchema = { + type: property('string', GeoPoint._jsonSchemaVersion), + latitude: property('number'), + longitude: property('number') + }; + + /** + * Returns a JSON-serializable representation of this `GeoPoint` instance. + * + * @returns a JSON representation of this object. + */ + toJSON(): { latitude: number; longitude: number; type: string } { + return { + latitude: this._lat, + longitude: this._long, + type: GeoPoint._jsonSchemaVersion + }; + } + + /** + * Builds a `GeoPoint` instance from a JSON object created by {@link GeoPoint.toJSON}. + * + * @param json a JSON object represention of a `GeoPoint` instance + * @returns an instance of {@link GeoPoint} if the JSON object could be parsed. Throws a + * {@link FirestoreError} if an error occurs. + */ + static fromJSON(json: object): GeoPoint { + if (validateJSON(json, GeoPoint._jsonSchema)) { + return new GeoPoint(json.latitude, json.longitude); + } + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'Unexpected error creating GeoPoint from JSON.' + ); + } } diff --git a/packages/firestore/src/lite-api/reference.ts b/packages/firestore/src/lite-api/reference.ts index 26ae2fbd433..f38dad9a078 100644 --- a/packages/firestore/src/lite-api/reference.ts +++ b/packages/firestore/src/lite-api/reference.ts @@ -32,6 +32,9 @@ import { validateDocumentPath, validateNonEmptyArgument } from '../util/input_validation'; +// API extractor fails importing property unless we also explicitly import Property. +// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports-ts +import { Property, property, validateJSON } from '../util/json_validation'; import { AutoId } from '../util/misc'; import { Firestore } from './database'; @@ -278,6 +281,73 @@ export class DocumentReference< this._key ); } + + static _jsonSchemaVersion: string = 'firestore/documentReference/1.0'; + static _jsonSchema = { + type: property('string', DocumentReference._jsonSchemaVersion), + referencePath: property('string') + }; + + /** + * Returns a JSON-serializable representation of this `DocumentReference` instance. + * + * @returns a JSON representation of this object. + */ + toJSON(): object { + return { + type: DocumentReference._jsonSchemaVersion, + referencePath: this._key.toString() + }; + } + + /** + * Builds a `DocumentReference` instance from a JSON object created by + * {@link DocumentReference.toJSON}. + * + * @param firestore - The {@link Firestore} instance the snapshot should be loaded for. + * @param json a JSON object represention of a `DocumentReference` instance + * @returns an instance of {@link DocumentReference} if the JSON object could be parsed. Throws a + * {@link FirestoreError} if an error occurs. + */ + static fromJSON(firestore: Firestore, json: object): DocumentReference; + /** + * Builds a `DocumentReference` instance from a JSON object created by + * {@link DocumentReference.toJSON}. + * + * @param firestore - The {@link Firestore} instance the snapshot should be loaded for. + * @param json a JSON object represention of a `DocumentReference` instance + * @param converter - Converts objects to and from Firestore. + * @returns an instance of {@link DocumentReference} if the JSON object could be parsed. Throws a + * {@link FirestoreError} if an error occurs. + */ + static fromJSON< + NewAppModelType = DocumentData, + NewDbModelType extends DocumentData = DocumentData + >( + firestore: Firestore, + json: object, + converter: FirestoreDataConverter + ): DocumentReference; + static fromJSON< + NewAppModelType = DocumentData, + NewDbModelType extends DocumentData = DocumentData + >( + firestore: Firestore, + json: object, + converter?: FirestoreDataConverter + ): DocumentReference { + if (validateJSON(json, DocumentReference._jsonSchema)) { + return new DocumentReference( + firestore, + converter ? converter : null, + new DocumentKey(ResourcePath.fromString(json.referencePath)) + ); + } + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'Unexpected error creating Bytes from JSON.' + ); + } } /** diff --git a/packages/firestore/src/lite-api/timestamp.ts b/packages/firestore/src/lite-api/timestamp.ts index e3d945aaf30..48c514feca8 100644 --- a/packages/firestore/src/lite-api/timestamp.ts +++ b/packages/firestore/src/lite-api/timestamp.ts @@ -16,6 +16,9 @@ */ import { Code, FirestoreError } from '../util/error'; +// API extractor fails importing 'property' unless we also explicitly import 'Property'. +// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports-ts +import { Property, property, validateJSON } from '../util/json_validation'; import { primitiveComparator } from '../util/misc'; // The earliest date supported by Firestore timestamps (0001-01-01T00:00:00Z). @@ -174,9 +177,35 @@ export class Timestamp { ); } - /** Returns a JSON-serializable representation of this `Timestamp`. */ - toJSON(): { seconds: number; nanoseconds: number } { - return { seconds: this.seconds, nanoseconds: this.nanoseconds }; + static _jsonSchemaVersion: string = 'firestore/timestamp/1.0'; + static _jsonSchema = { + type: property('string', Timestamp._jsonSchemaVersion), + seconds: property('number'), + nanoseconds: property('number') + }; + + /** + * Returns a JSON-serializable representation of this `Timestamp`. + */ + toJSON(): { seconds: number; nanoseconds: number; type: string } { + return { + type: Timestamp._jsonSchemaVersion, + seconds: this.seconds, + nanoseconds: this.nanoseconds + }; + } + + /** + * Builds a `Timestamp` instance from a JSON object created by {@link Timestamp.toJSON}. + */ + static fromJSON(json: object): Timestamp { + if (validateJSON(json, Timestamp._jsonSchema)) { + return new Timestamp(json.seconds, json.nanoseconds); + } + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'Unexpected error creating Timestamp from JSON.' + ); } /** diff --git a/packages/firestore/src/lite-api/user_data_reader.ts b/packages/firestore/src/lite-api/user_data_reader.ts index aa5f9eeb5bf..a3022be627e 100644 --- a/packages/firestore/src/lite-api/user_data_reader.ts +++ b/packages/firestore/src/lite-api/user_data_reader.ts @@ -780,7 +780,7 @@ export function parseData( } } -function parseObject( +export function parseObject( obj: Dict, context: ParseContextImpl ): { mapValue: ProtoMapValue } { diff --git a/packages/firestore/src/lite-api/vector_value.ts b/packages/firestore/src/lite-api/vector_value.ts index 9ac9753fef5..c48feaeff3b 100644 --- a/packages/firestore/src/lite-api/vector_value.ts +++ b/packages/firestore/src/lite-api/vector_value.ts @@ -16,6 +16,10 @@ */ import { isPrimitiveArrayEqual } from '../util/array'; +import { Code, FirestoreError } from '../util/error'; +// API extractor fails importing 'property' unless we also explicitly import 'Property'. +// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports-ts +import { Property, property, validateJSON } from '../util/json_validation'; /** * Represents a vector type in Firestore documents. @@ -48,4 +52,48 @@ export class VectorValue { isEqual(other: VectorValue): boolean { return isPrimitiveArrayEqual(this._values, other._values); } + + static _jsonSchemaVersion: string = 'firestore/vectorValue/1.0'; + static _jsonSchema = { + type: property('string', VectorValue._jsonSchemaVersion), + vectorValues: property('object') + }; + + /** + * Returns a JSON-serializable representation of this `VectorValue` instance. + * + * @returns a JSON representation of this object. + */ + toJSON(): object { + return { + type: VectorValue._jsonSchemaVersion, + vectorValues: this._values + }; + } + + /** + * Builds a `VectorValue` instance from a JSON object created by {@link VectorValue.toJSON}. + * + * @param json a JSON object represention of a `VectorValue` instance. + * @returns an instance of {@link VectorValue} if the JSON object could be parsed. Throws a + * {@link FirestoreError} if an error occurs. + */ + static fromJSON(json: object): VectorValue { + if (validateJSON(json, VectorValue._jsonSchema)) { + if ( + Array.isArray(json.vectorValues) && + json.vectorValues.every(element => typeof element === 'number') + ) { + return new VectorValue(json.vectorValues); + } + throw new FirestoreError( + Code.INVALID_ARGUMENT, + "Expected 'vectorValues' field to be a number array" + ); + } + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'Unexpected error creating Timestamp from JSON.' + ); + } } diff --git a/packages/firestore/src/platform/browser/snapshot_to_json.ts b/packages/firestore/src/platform/browser/snapshot_to_json.ts new file mode 100644 index 00000000000..37c1a0b556d --- /dev/null +++ b/packages/firestore/src/platform/browser/snapshot_to_json.ts @@ -0,0 +1,43 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** Return the Platform-specific build JSON bundle implementations. */ +import { Firestore } from '../../api/database'; +import { Query } from '../../core/query'; +import { DocumentData } from '../../lite-api/reference'; +import { Document } from '../../model/document'; + +export function buildDocumentSnapshotJsonBundle( + db: Firestore, + document: Document, + docData: DocumentData, + path: string +): string { + return 'NOT SUPPORTED'; +} + +export function buildQuerySnapshotJsonBundle( + db: Firestore, + query: Query, + bundleName: string, + parent: string, + paths: string[], + docs: Document[], + documentData: DocumentData[] +): string { + return 'NOT SUPPORTED'; +} diff --git a/packages/firestore/src/platform/browser_lite/snapshot_to_json.ts b/packages/firestore/src/platform/browser_lite/snapshot_to_json.ts new file mode 100644 index 00000000000..4012dc496b2 --- /dev/null +++ b/packages/firestore/src/platform/browser_lite/snapshot_to_json.ts @@ -0,0 +1,18 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from '../browser/snapshot_to_json'; diff --git a/packages/firestore/src/platform/node/snapshot_to_json.ts b/packages/firestore/src/platform/node/snapshot_to_json.ts new file mode 100644 index 00000000000..61987fbbc3c --- /dev/null +++ b/packages/firestore/src/platform/node/snapshot_to_json.ts @@ -0,0 +1,84 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** Return the Platform-specific build JSON bundle implementations. */ +import { Firestore } from '../../api/database'; +import { Query } from '../../core/query'; +import { DocumentData } from '../../lite-api/reference'; +import { Document } from '../../model/document'; +import { + BundleBuilder, + DocumentSnapshotBundleData, + QuerySnapshotBundleData +} from '../../util/bundle_builder_impl'; +import { AutoId } from '../../util/misc'; + +export function buildDocumentSnapshotJsonBundle( + db: Firestore, + document: Document, + docData: DocumentData, + path: string +): string { + const builder: BundleBuilder = new BundleBuilder(db, AutoId.newId()); + builder.addBundleDocument( + documentToDocumentSnapshotBundleData(path, docData, document) + ); + return builder.build(); +} + +export function buildQuerySnapshotJsonBundle( + db: Firestore, + query: Query, + bundleName: string, + parent: string, + paths: string[], + docs: Document[], + documentData: DocumentData[] +): string { + const docBundleDataArray: DocumentSnapshotBundleData[] = []; + for (let i = 0; i < docs.length; i++) { + docBundleDataArray.push( + documentToDocumentSnapshotBundleData(paths[i], documentData[i], docs[i]) + ); + } + const bundleData: QuerySnapshotBundleData = { + name: bundleName, + query, + parent, + docBundleDataArray + }; + const builder: BundleBuilder = new BundleBuilder(db, bundleName); + builder.addBundleQuery(bundleData); + return builder.build(); +} + +// Formats Document data for bundling a DocumentSnapshot. +function documentToDocumentSnapshotBundleData( + path: string, + documentData: DocumentData, + document: Document +): DocumentSnapshotBundleData { + return { + documentData, + documentKey: document.mutableCopy().key, + documentPath: path, + documentExists: true, + createdTime: document.createTime.toTimestamp(), + readTime: document.readTime.toTimestamp(), + versionTime: document.version.toTimestamp() + }; +} diff --git a/packages/firestore/src/platform/node_lite/snapshot_to_json.ts b/packages/firestore/src/platform/node_lite/snapshot_to_json.ts new file mode 100644 index 00000000000..ba6bbb8424b --- /dev/null +++ b/packages/firestore/src/platform/node_lite/snapshot_to_json.ts @@ -0,0 +1,18 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from '../node/snapshot_to_json'; diff --git a/packages/firestore/src/platform/rn/snapshot_to_json.ts b/packages/firestore/src/platform/rn/snapshot_to_json.ts new file mode 100644 index 00000000000..551f586d20e --- /dev/null +++ b/packages/firestore/src/platform/rn/snapshot_to_json.ts @@ -0,0 +1,21 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { + buildDocumentSnapshotJsonBundle, + buildQuerySnapshotJsonBundle +} from '../browser/snapshot_to_json'; diff --git a/packages/firestore/src/platform/rn_lite/snapshot_to_json.ts b/packages/firestore/src/platform/rn_lite/snapshot_to_json.ts new file mode 100644 index 00000000000..709509c8a4e --- /dev/null +++ b/packages/firestore/src/platform/rn_lite/snapshot_to_json.ts @@ -0,0 +1,18 @@ +/** + * @license + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { toByteStreamReader } from '../browser/byte_stream_reader'; diff --git a/packages/firestore/src/platform/snapshot_to_json.ts b/packages/firestore/src/platform/snapshot_to_json.ts new file mode 100644 index 00000000000..1eae948eb45 --- /dev/null +++ b/packages/firestore/src/platform/snapshot_to_json.ts @@ -0,0 +1,62 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Firestore } from '../api/database'; +import { Query } from '../core/query'; +import { DocumentData } from '../lite-api/reference'; +import { Document } from '../model/document'; + +// This file is only used under ts-node. +// eslint-disable-next-line @typescript-eslint/no-require-imports +const platform = require(`./${ + process.env.TEST_PLATFORM ?? 'node' +}/snapshot_to_json`); + +/** + * Constructs the bundle data for a DocumentSnapshot used in its toJSON serialization. + */ +export function buildDocumentSnapshotJsonBundle( + db: Firestore, + document: Document, + docData: DocumentData, + path: string +): string { + return platform.buildDocumentSnapshotJsonBundle(db, document, docData, path); +} + +/** + * Constructs the bundle data for a QuerySnapshot used in its toJSON serialization. + */ +export function buildQuerySnapshotJsonBundle( + db: Firestore, + query: Query, + bundleName: string, + parent: string, + paths: string[], + docs: Document[], + documentData: DocumentData[] +): string { + return platform.buildQuerySnapshotJsonBundle( + db, + query, + bundleName, + parent, + paths, + docs, + documentData + ); +} diff --git a/packages/firestore/src/remote/serializer.ts b/packages/firestore/src/remote/serializer.ts index aabdb263c1a..830875f5e1b 100644 --- a/packages/firestore/src/remote/serializer.ts +++ b/packages/firestore/src/remote/serializer.ts @@ -226,7 +226,10 @@ export function toTimestamp( } } -function fromTimestamp(date: ProtoTimestamp): Timestamp { +/** + * Returns a Timestamp typed object given protobuf timestamp value. + */ +export function fromTimestamp(date: ProtoTimestamp): Timestamp { const timestamp = normalizeTimestamp(date); return new Timestamp(timestamp.seconds, timestamp.nanos); } diff --git a/packages/firestore/src/util/bundle_builder_impl.ts b/packages/firestore/src/util/bundle_builder_impl.ts new file mode 100644 index 00000000000..dc94ebca495 --- /dev/null +++ b/packages/firestore/src/util/bundle_builder_impl.ts @@ -0,0 +1,284 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + JsonProtoSerializer, + fromTimestamp, + toName, + toQueryTarget, + toTimestamp +} from '../../src/remote/serializer'; +import { encoder } from '../../test/unit/util/bundle_data'; +import { Firestore } from '../api/database'; +import { DatabaseId } from '../core/database_info'; +import { Query, queryToTarget } from '../core/query'; +import { DocumentData } from '../lite-api/reference'; +import { Timestamp } from '../lite-api/timestamp'; +import { + parseObject, + UserDataReader, + UserDataSource +} from '../lite-api/user_data_reader'; +import { DocumentKey } from '../model/document_key'; +import { + BundledDocumentMetadata as ProtoBundledDocumentMetadata, + BundleElement as ProtoBundleElement, + BundleMetadata as ProtoBundleMetadata, + NamedQuery as ProtoNamedQuery +} from '../protos/firestore_bundle_proto'; +import { + Document as ProtoDocument, + Document +} from '../protos/firestore_proto_api'; + +const BUNDLE_VERSION = 1; + +/** + * Builds a Firestore data bundle with results from the given document and query snapshots. + */ +export class BundleBuilder { + // Resulting documents for the bundle, keyed by full document path. + private documents: Map = new Map(); + // Named queries saved in the bundle, keyed by query name. + private namedQueries: Map = new Map(); + + // The latest read time among all bundled documents and queries. + private latestReadTime = new Timestamp(0, 0); + + // Database identifier which is part of the serialized bundle. + private databaseId: DatabaseId; + + // Tools to convert public data types into their serialized form. + private readonly serializer: JsonProtoSerializer; + private readonly userDataReader: UserDataReader; + + constructor(private firestore: Firestore, readonly bundleId: string) { + this.databaseId = firestore._databaseId; + + // useProto3Json is true because the objects will be serialized to JSON string + // before being written to the bundle buffer. + this.serializer = new JsonProtoSerializer( + this.databaseId, + /*useProto3Json=*/ true + ); + + this.userDataReader = new UserDataReader( + this.databaseId, + true, + this.serializer + ); + } + + /** + * Adds data from a DocumentSnapshot to the bundle. + * @internal + * @param docBundleData A DocumentSnapshotBundleData containing information from the + * DocumentSnapshot. Note we cannot accept a DocumentSnapshot directly due to a circular + * dependency error. + * @param queryName The name of the QuerySnapshot if this document is part of a Query. + */ + addBundleDocument( + docBundleData: DocumentSnapshotBundleData, + queryName?: string + ): void { + const originalDocument = this.documents.get(docBundleData.documentPath); + const originalQueries = originalDocument?.metadata.queries; + const docReadTime: Timestamp | undefined = docBundleData.readTime; + const origDocReadTime: Timestamp | null = !!originalDocument?.metadata + .readTime + ? fromTimestamp(originalDocument.metadata.readTime) + : null; + + const neitherHasReadTime: boolean = !docReadTime && origDocReadTime == null; + const docIsNewer: boolean = + docReadTime !== undefined && + (origDocReadTime == null || origDocReadTime < docReadTime); + if (neitherHasReadTime || docIsNewer) { + // Store document. + this.documents.set(docBundleData.documentPath, { + document: this.toBundleDocument(docBundleData), + metadata: { + name: toName(this.serializer, docBundleData.documentKey), + readTime: !!docReadTime + ? toTimestamp(this.serializer, docReadTime) // Convert Timestamp to proto format. + : undefined, + exists: docBundleData.documentExists + } + }); + } + if (docReadTime && docReadTime > this.latestReadTime) { + this.latestReadTime = docReadTime; + } + // Update `queries` to include both original and `queryName`. + if (queryName) { + const newDocument = this.documents.get(docBundleData.documentPath)!; + newDocument.metadata.queries = originalQueries || []; + newDocument.metadata.queries!.push(queryName); + } + } + + /** + * Adds data from a QuerySnapshot to the bundle. + * @internal + * @param docBundleData A QuerySnapshotBundleData containing information from the + * QuerySnapshot. Note we cannot accept a QuerySnapshot directly due to a circular + * dependency error. + */ + addBundleQuery(queryBundleData: QuerySnapshotBundleData): void { + if (this.namedQueries.has(queryBundleData.name)) { + throw new Error(`Query name conflict: ${name} has already been added.`); + } + let latestReadTime = new Timestamp(0, 0); + for (const docBundleData of queryBundleData.docBundleDataArray) { + this.addBundleDocument(docBundleData, queryBundleData.name); + if (docBundleData.readTime && docBundleData.readTime > latestReadTime) { + latestReadTime = docBundleData.readTime; + } + } + const queryTarget = toQueryTarget( + this.serializer, + queryToTarget(queryBundleData.query) + ); + const bundledQuery = { + parent: queryBundleData.parent, + structuredQuery: queryTarget.queryTarget.structuredQuery + }; + this.namedQueries.set(queryBundleData.name, { + name: queryBundleData.name, + bundledQuery, + readTime: toTimestamp(this.serializer, latestReadTime) + }); + } + + /** + * Convert data from a DocumentSnapshot into the serialized form within a bundle. + * @private + * @internal + * @param docBundleData a DocumentSnapshotBundleData containing the data required to + * serialize a document. + */ + private toBundleDocument( + docBundleData: DocumentSnapshotBundleData + ): ProtoDocument { + // a parse context is typically used for validating and parsing user data, but in this + // case we are using it internally to convert DocumentData to Proto3 JSON + const context = this.userDataReader.createContext( + UserDataSource.ArrayArgument, + 'internal toBundledDocument' + ); + const proto3Fields = parseObject(docBundleData.documentData, context); + + return { + name: toName(this.serializer, docBundleData.documentKey), + fields: proto3Fields.mapValue.fields, + updateTime: toTimestamp(this.serializer, docBundleData.versionTime), + createTime: toTimestamp(this.serializer, docBundleData.createdTime) + }; + } + + /** + * Converts a IBundleElement to a Buffer whose content is the length prefixed JSON representation + * of the element. + * @private + * @internal + * @param bundleElement A ProtoBundleElement that is expected to be Proto3 JSON compatible. + */ + private lengthPrefixedString(bundleElement: ProtoBundleElement): string { + const str = JSON.stringify(bundleElement); + // TODO: it's not ideal to have to re-encode all of these strings multiple times + // It may be more performant to return a UInt8Array that is concatenated to other + // UInt8Arrays instead of returning and concatenating strings and then + // converting the full string to UInt8Array. + const l = encoder.encode(str).byteLength; + return `${l}${str}`; + } + + /** + * Construct a serialized string containing document and query information that has previously + * been added to the BundleBuilder through the addBundleDocument and addBundleQuery methods. + * @internal + */ + build(): string { + let bundleString = ''; + + for (const namedQuery of this.namedQueries.values()) { + bundleString += this.lengthPrefixedString({ namedQuery }); + } + + for (const bundledDocument of this.documents.values()) { + const documentMetadata: ProtoBundledDocumentMetadata = + bundledDocument.metadata; + + bundleString += this.lengthPrefixedString({ documentMetadata }); + // Write to the bundle if document exists. + const document = bundledDocument.document; + if (document) { + bundleString += this.lengthPrefixedString({ document }); + } + } + + const metadata: ProtoBundleMetadata = { + id: this.bundleId, + createTime: toTimestamp(this.serializer, this.latestReadTime), + version: BUNDLE_VERSION, + totalDocuments: this.documents.size, + // TODO: it's not ideal to have to re-encode all of these strings multiple times + totalBytes: encoder.encode(bundleString).length + }; + // Prepends the metadata element to the bundleBuffer: `bundleBuffer` is the second argument to `Buffer.concat`. + bundleString = this.lengthPrefixedString({ metadata }) + bundleString; + + return bundleString; + } +} + +/** + * Interface for an object that contains data required to bundle a DocumentSnapshot. + * @internal + */ +export interface DocumentSnapshotBundleData { + documentData: DocumentData; + documentKey: DocumentKey; + documentPath: string; + documentExists: boolean; + createdTime: Timestamp; + readTime?: Timestamp; + versionTime: Timestamp; +} + +/** + * Interface for an object that contains data required to bundle a QuerySnapshot. + * @internal + */ +export interface QuerySnapshotBundleData { + name: string; + query: Query; + parent: string; + docBundleDataArray: DocumentSnapshotBundleData[]; +} + +/** + * Convenient class to hold both the metadata and the actual content of a document to be bundled. + * @private + * @internal + */ +class BundledDocument { + constructor( + readonly metadata: ProtoBundledDocumentMetadata, + readonly document?: Document + ) {} +} diff --git a/packages/firestore/src/util/bundle_reader.ts b/packages/firestore/src/util/bundle_reader.ts index 6ebfb2d5e8e..cca1c61a538 100644 --- a/packages/firestore/src/util/bundle_reader.ts +++ b/packages/firestore/src/util/bundle_reader.ts @@ -65,3 +65,24 @@ export interface BundleReader { */ nextElement(): Promise; } + +/** + * A class representing a synchronized bundle reader. + * + * Takes a bundle string buffer, parses the data, and provides accessors to the data contained + * within it. + */ +export interface BundleReaderSync { + serializer: JsonProtoSerializer; + + /** + * Returns the metadata of the bundle. + */ + getMetadata(): BundleMetadata; + + /** + * Returns BundleElements parsed from the bundle. Returns an empty array if no bundle elements + * exist. + */ + getElements(): SizedBundleElement[]; +} diff --git a/packages/firestore/src/util/bundle_reader_sync_impl.ts b/packages/firestore/src/util/bundle_reader_sync_impl.ts new file mode 100644 index 00000000000..9379bb5a5a7 --- /dev/null +++ b/packages/firestore/src/util/bundle_reader_sync_impl.ts @@ -0,0 +1,129 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BundleMetadata } from '../protos/firestore_bundle_proto'; +import { JsonProtoSerializer } from '../remote/serializer'; +import { Code, FirestoreError } from '../util/error'; + +import { BundleReaderSync, SizedBundleElement } from './bundle_reader'; + +/** + * A class that can parse a bundle form the string serialization of a bundle. + */ +export class BundleReaderSyncImpl implements BundleReaderSync { + private metadata: BundleMetadata; + private elements: SizedBundleElement[]; + private cursor: number; + constructor( + private bundleData: string, + readonly serializer: JsonProtoSerializer + ) { + this.cursor = 0; + this.elements = []; + + let element = this.nextElement(); + if (element && element.isBundleMetadata()) { + this.metadata = element as BundleMetadata; + } else { + throw new Error(`The first element of the bundle is not a metadata object, it is + ${JSON.stringify(element?.payload)}`); + } + + do { + element = this.nextElement(); + if (element !== null) { + this.elements.push(element); + } + } while (element !== null); + } + + /* Returns the parsed metadata of the bundle. */ + getMetadata(): BundleMetadata { + return this.metadata; + } + + /* Returns the DocumentSnapshot or NamedQuery elements of the bundle. */ + getElements(): SizedBundleElement[] { + return this.elements; + } + + /** + * Parses the next element of the bundle. + * + * @returns a SizedBundleElement representation of the next element in the bundle, or null if + * no more elements exist. + */ + private nextElement(): SizedBundleElement | null { + if (this.cursor === this.bundleData.length) { + return null; + } + const length: number = this.readLength(); + const jsonString = this.readJsonString(length); + return new SizedBundleElement(JSON.parse(jsonString), length); + } + + /** + * Reads from a specified position from the bundleData string, for a specified + * number of bytes. + * + * @param length how many characters to read. + * @returns a string parsed from the bundle. + */ + private readJsonString(length: number): string { + if (this.cursor + length > this.bundleData.length) { + throw new FirestoreError( + Code.INTERNAL, + 'Reached the end of bundle when more is expected.' + ); + } + const result = this.bundleData.slice(this.cursor, (this.cursor += length)); + return result; + } + + /** + * Reads from the current cursor until the first '{'. + * + * @returns A string to integer represention of the parsed value. + * @throws An {@link Error} if the cursor has reached the end of the stream, since lengths + * prefix bundle objects. + */ + private readLength(): number { + const startIndex = this.cursor; + let curIndex = this.cursor; + while (curIndex < this.bundleData.length) { + if (this.bundleData[curIndex] === '{') { + if (curIndex === startIndex) { + throw new Error('First character is a bracket and not a number'); + } + this.cursor = curIndex; + return Number(this.bundleData.slice(startIndex, curIndex)); + } + curIndex++; + } + throw new Error('Reached the end of bundle when more is expected.'); + } +} + +/** + * Creates an instance of BundleReader without exposing the BundleReaderSyncImpl class type. + */ +export function newBundleReaderSync( + bundleData: string, + serializer: JsonProtoSerializer +): BundleReaderSync { + return new BundleReaderSyncImpl(bundleData, serializer); +} diff --git a/packages/firestore/src/util/json_validation.ts b/packages/firestore/src/util/json_validation.ts new file mode 100644 index 00000000000..771a7f91ef3 --- /dev/null +++ b/packages/firestore/src/util/json_validation.ts @@ -0,0 +1,142 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { isPlainObject } from '../util/input_validation'; + +import { Code, FirestoreError } from './error'; + +/** + * A list of data types Firestore objects may serialize in their toJSON implemenetations. + * @private + * @internal + */ +export type JsonTypeDesc = + | 'object' + | 'string' + | 'number' + | 'boolean' + | 'null' + | 'undefined'; + +/** + * An association of JsonTypeDesc values to their native types. + * @private + * @internal + */ +export type TSType = T extends 'object' + ? object + : T extends 'string' + ? string + : T extends 'number' + ? number + : T extends 'boolean' + ? boolean + : T extends 'null' + ? null + : T extends 'undefined' + ? undefined + : never; + +/** + * The representation of a JSON object property name and its type value. + * @private + * @internal + */ +export interface Property { + value?: TSType; + typeString: JsonTypeDesc; +} + +/** + * A type Firestore data types may use to define the fields used in their JSON serialization. + * @private + * @internal + */ +export interface JsonSchema { + [key: string]: Property; +} + +/** + * Associates the JSON property type to the native type and sets them to be Required. + * @private + * @internal + */ +export type Json = { + [K in keyof T]: Required['value']; +}; + +/** + * Helper function to define a JSON schema {@link Property}. + * @private + * @internal + */ +export function property( + typeString: T, + optionalValue?: TSType +): Property { + const result: Property = { + typeString + }; + if (optionalValue) { + result.value = optionalValue; + } + return result; +} + +/** + * Validates the JSON object based on the provided schema, and narrows the type to the provided + * JSON schema. + * @private + * @internal + * + * @param json A JSON object to validate. + * @param scheme a {@link JsonSchema} that defines the properties to validate. + * @returns true if the JSON schema exists within the object. Throws a FirestoreError otherwise. + */ +export function validateJSON( + json: object, + schema: S +): json is Json { + if (!isPlainObject(json)) { + throw new FirestoreError(Code.INVALID_ARGUMENT, 'JSON must be an object'); + } + let error: string | undefined = undefined; + for (const key in schema) { + if (schema[key]) { + const typeString = schema[key].typeString; + const value: { value: unknown } | undefined = + 'value' in schema[key] ? { value: schema[key].value } : undefined; + if (!(key in json)) { + error = `JSON missing required field: '${key}'`; + break; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const fieldValue = (json as any)[key]; + if (typeString && typeof fieldValue !== typeString) { + error = `JSON field '${key}' must be a ${typeString}.`; + break; + } else if (value !== undefined && fieldValue !== value.value) { + error = `Expected '${key}' field to equal '${value.value}'`; + break; + } + } + } + if (error) { + throw new FirestoreError(Code.INVALID_ARGUMENT, error); + } + return true; +} diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index 9675e02efeb..b63c03a4f62 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -16,7 +16,7 @@ */ import { deleteApp } from '@firebase/app'; -import { Deferred } from '@firebase/util'; +import { Deferred, isNode } from '@firebase/util'; import { expect, use } from 'chai'; import chaiAsPromised from 'chai-as-promised'; @@ -33,6 +33,7 @@ import { DocumentData, documentId, DocumentSnapshot, + documentSnapshotFromJSON, enableIndexedDbPersistence, enableNetwork, getDoc, @@ -42,6 +43,7 @@ import { initializeFirestore, limit, onSnapshot, + onSnapshotResume, onSnapshotsInSync, orderBy, query, @@ -66,6 +68,7 @@ import { newTestApp, FirestoreError, QuerySnapshot, + querySnapshotFromJSON, vector, getDocsFromServer } from '../util/firebase_export'; @@ -1206,6 +1209,429 @@ apiDescribe('Database', persistence => { }); }); + it('DocumentSnapshot events for snapshot created by a bundle', async () => { + if (isNode()) { + const initialData = { a: 1 }; + const finalData = { a: 2 }; + await withTestDocAndInitialData( + persistence, + initialData, + async (docRef, db) => { + const doc = await getDoc(docRef); + const accumulator = new EventsAccumulator(); + const unsubscribe = onSnapshotResume( + db, + doc.toJSON(), + accumulator.storeEvent + ); + await accumulator + .awaitEvent() + .then(snap => { + console.error('DEDB accumulator event 1'); + expect(snap.exists()).to.be.true; + expect(snap.data()).to.deep.equal(initialData); + }) + .then(() => setDoc(docRef, finalData)) + .then(() => accumulator.awaitEvent()) + .then(snap => { + expect(snap.exists()).to.be.true; + expect(snap.data()).to.deep.equal(finalData); + }); + unsubscribe(); + } + ); + } + }); + + it('DocumentSnapshot updated doc events in snapshot created by a bundle accumulator', async () => { + if (isNode()) { + const initialData = { a: 1 }; + const finalData = { a: 2 }; + await withTestDocAndInitialData( + persistence, + initialData, + async (docRef, db) => { + const doc = await getDoc(docRef); + const accumulator = new EventsAccumulator(); + const unsubscribe = onSnapshotResume( + db, + doc.toJSON(), + accumulator.storeEvent + ); + await accumulator + .awaitEvent() + .then(snap => { + expect(snap.exists()).to.be.true; + expect(snap.data()).to.deep.equal(initialData); + }) + .then(() => setDoc(docRef, finalData)) + .then(() => accumulator.awaitEvent()) + .then(snap => { + expect(snap.exists()).to.be.true; + expect(snap.data()).to.deep.equal(finalData); + }); + unsubscribe(); + } + ); + } + }); + + it('DocumentSnapshot observer events for snapshot created by a bundle', async () => { + if (isNode()) { + const initialData = { a: 1 }; + const finalData = { a: 2 }; + await withTestDocAndInitialData( + persistence, + initialData, + async (docRef, db) => { + const doc = await getDoc(docRef); + const accumulator = new EventsAccumulator(); + const unsubscribe = onSnapshotResume(db, doc.toJSON(), { + next: accumulator.storeEvent + }); + await accumulator + .awaitEvent() + .then(snap => { + expect(snap.exists()).to.be.true; + expect(snap.data()).to.deep.equal(initialData); + }) + .then(() => setDoc(docRef, finalData)) + .then(() => accumulator.awaitEvent()) + .then(snap => { + expect(snap.exists()).to.be.true; + expect(snap.data()).to.deep.equal(finalData); + }); + unsubscribe(); + } + ); + } + }); + + it('DocumentSnapshot error events for snapshot created by a bundle', async () => { + return withTestDb(persistence, async db => { + const json = { + bundle: 'BadData', + bundleName: 'bundleName', + bundleSource: 'DocumentSnapshot' + }; + const deferred = new Deferred(); + const unsubscribe = onSnapshotResume( + db, + json, + ds => { + expect(ds).to.not.exist; + deferred.resolve(); + }, + err => { + expect(err.name).to.exist; + expect(err.message).to.exist; + deferred.resolve(); + } + ); + await deferred.promise; + unsubscribe(); + }); + }); + + it('DocumentSnapshot observer error events for snapshot created by a bundle', async () => { + return withTestDb(persistence, async db => { + const json = { + bundle: 'BadData', + bundleName: 'bundleName', + bundleSource: 'QuerySnapshot' + }; + const deferred = new Deferred(); + const unsubscribe = onSnapshotResume(db, json, { + next: ds => { + expect(ds).to.not.exist; + deferred.resolve(); + }, + error: err => { + expect(err.name).to.exist; + expect(err.message).to.exist; + deferred.resolve(); + } + }); + await deferred.promise; + unsubscribe(); + }); + }); + + it('DocumentSnapshot updated doc events in snapshot created by fromJSON bundle', async () => { + if (isNode()) { + const initialData = { a: 1 }; + const finalData = { a: 2 }; + await withTestDocAndInitialData( + persistence, + initialData, + async (docRef, db) => { + const doc = await getDoc(docRef); + const fromJsonDoc = documentSnapshotFromJSON(db, doc.toJSON()); + const accumulator = new EventsAccumulator(); + const unsubscribe = onSnapshotResume( + db, + fromJsonDoc.toJSON(), + accumulator.storeEvent + ); + await accumulator + .awaitEvent() + .then(snap => { + expect(snap.exists()).to.be.true; + expect(snap.data()).to.deep.equal(initialData); + }) + .then(() => setDoc(docRef, finalData)) + .then(() => accumulator.awaitEvent()) + .then(snap => { + expect(snap.exists()).to.be.true; + expect(snap.data()).to.deep.equal(finalData); + }); + unsubscribe(); + } + ); + } + }); + + it('DocumentSnapshot updated doc events in snapshot created by fromJSON doc ref', async () => { + if (isNode()) { + const initialData = { a: 1 }; + const finalData = { a: 2 }; + await withTestDocAndInitialData( + persistence, + initialData, + async (docRef, db) => { + const doc = await getDoc(docRef); + const fromJsonDoc = documentSnapshotFromJSON(db, doc.toJSON()); + const accumulator = new EventsAccumulator(); + const unsubscribe = onSnapshot( + fromJsonDoc.ref, + accumulator.storeEvent + ); + await accumulator + .awaitEvent() + .then(snap => { + expect(snap.exists()).to.be.true; + expect(snap.data()).to.deep.equal(initialData); + }) + .then(() => setDoc(docRef, finalData)) + .then(() => accumulator.awaitEvent()) + .then(snap => { + expect(snap.exists()).to.be.true; + expect(snap.data()).to.deep.equal(finalData); + }); + unsubscribe(); + } + ); + } + }); + + it('Querysnapshot events for snapshot created by a bundle', async () => { + if (isNode()) { + const testDocs = { + a: { foo: 1 }, + b: { bar: 2 } + }; + await withTestCollection(persistence, testDocs, async (coll, db) => { + const querySnap = await getDocs(query(coll, orderBy(documentId()))); + const accumulator = new EventsAccumulator(); + const unsubscribe = onSnapshotResume( + db, + querySnap.toJSON(), + accumulator.storeEvent + ); + await accumulator.awaitEvent().then(snap => { + expect(snap.docs).not.to.be.null; + expect(snap.docs.length).to.equal(2); + expect(snap.docs[0].data()).to.deep.equal(testDocs.a); + expect(snap.docs[1].data()).to.deep.equal(testDocs.b); + }); + unsubscribe(); + }); + } + }); + + it('Querysnapshot observer events for snapshot created by a bundle', async () => { + if (isNode()) { + const testDocs = { + a: { foo: 1 }, + b: { bar: 2 } + }; + await withTestCollection(persistence, testDocs, async (coll, db) => { + const querySnap = await getDocs(query(coll, orderBy(documentId()))); + const accumulator = new EventsAccumulator(); + const unsubscribe = onSnapshotResume(db, querySnap.toJSON(), { + next: accumulator.storeEvent + }); + await accumulator.awaitEvent().then(snap => { + expect(snap.docs).not.to.be.null; + expect(snap.docs.length).to.equal(2); + expect(snap.docs[0].data()).to.deep.equal(testDocs.a); + expect(snap.docs[1].data()).to.deep.equal(testDocs.b); + }); + unsubscribe(); + }); + } + }); + + it('QuerySnapshot error events for snapshot created by a bundle', async () => { + return withTestDb(persistence, async db => { + const json = { + bundle: 'BadData', + bundleName: 'bundleName', + bundleSource: 'QuerySnapshot' + }; + const deferred = new Deferred(); + const unsubscribe = onSnapshotResume( + db, + json, + qs => { + expect(qs).to.not.exist; + deferred.resolve(); + }, + err => { + expect(err.name).to.exist; + expect(err.message).to.exist; + deferred.resolve(); + } + ); + await deferred.promise; + unsubscribe(); + }); + }); + + it('QuerySnapshot observer error events for snapshot created by a bundle', async () => { + return withTestDb(persistence, async db => { + const json = { + bundle: 'BadData', + bundleName: 'bundleName', + bundleSource: 'QuerySnapshot' + }; + const deferred = new Deferred(); + const unsubscribe = onSnapshotResume(db, json, { + next: qs => { + expect(qs).to.not.exist; + deferred.resolve(); + }, + error: err => { + expect(err.name).to.exist; + expect(err.message).to.exist; + deferred.resolve(); + } + }); + await deferred.promise; + unsubscribe(); + }); + }); + + it('QuerySnapshot updated doc events in snapshot created by a bundle', async () => { + if (isNode()) { + const testDocs = { + a: { foo: 1 }, + b: { bar: 2 } + }; + await withTestCollection(persistence, testDocs, async (coll, db) => { + const querySnap = await getDocs(query(coll, orderBy(documentId()))); + const refForDocA = querySnap.docs[0].ref; + const accumulator = new EventsAccumulator(); + const unsubscribe = onSnapshotResume( + db, + querySnap.toJSON(), + accumulator.storeEvent + ); + await accumulator + .awaitEvent() + .then(snap => { + expect(snap.docs).not.to.be.null; + expect(snap.docs.length).to.equal(2); + expect(snap.docs[0].data()).to.deep.equal(testDocs.a); + expect(snap.docs[1].data()).to.deep.equal(testDocs.b); + }) + .then(() => setDoc(refForDocA, { foo: 0 })) + .then(() => accumulator.awaitEvent()) + .then(snap => { + expect(snap.docs).not.to.be.null; + expect(snap.docs.length).to.equal(2); + expect(snap.docs[0].data()).to.deep.equal({ foo: 0 }); + expect(snap.docs[1].data()).to.deep.equal(testDocs.b); + }); + unsubscribe(); + }); + } + }); + + it('QuerySnapshot updated doc events in snapshot created by fromJSON ', async () => { + if (isNode()) { + const testDocs = { + a: { foo: 1 }, + b: { bar: 2 } + }; + await withTestCollection(persistence, testDocs, async (coll, db) => { + const querySnap = await getDocs(query(coll, orderBy(documentId()))); + const querySnapFromJson = querySnapshotFromJSON(db, querySnap.toJSON()); + const refForDocA = querySnapFromJson.docs[0].ref; + const accumulator = new EventsAccumulator(); + + const unsubscribe = onSnapshotResume( + db, + querySnapFromJson.toJSON(), + accumulator.storeEvent + ); + await accumulator + .awaitEvent() + .then(snap => { + expect(snap.docs).not.to.be.null; + expect(snap.docs.length).to.equal(2); + expect(snap.docs[0].data()).to.deep.equal(testDocs.a); + expect(snap.docs[1].data()).to.deep.equal(testDocs.b); + }) + .then(() => setDoc(refForDocA, { foo: 0 })) + .then(() => accumulator.awaitEvent()) + .then(snap => { + expect(snap.docs).not.to.be.null; + expect(snap.docs.length).to.equal(2); + expect(snap.docs[0].data()).to.deep.equal({ foo: 0 }); + expect(snap.docs[1].data()).to.deep.equal(testDocs.b); + }); + unsubscribe(); + }); + } + }); + + it('QuerySnapshot updated doc events in snapshot created by fromJSON query ref', async () => { + if (isNode()) { + const testDocs = { + a: { foo: 1 }, + b: { bar: 2 } + }; + await withTestCollection(persistence, testDocs, async (coll, db) => { + const querySnap = await getDocs(query(coll, orderBy(documentId()))); + const querySnapFromJson = querySnapshotFromJSON(db, querySnap.toJSON()); + const refForDocA = querySnapFromJson.docs[0].ref; + const accumulator = new EventsAccumulator(); + const unsubscribe = onSnapshot( + querySnapFromJson.query, + accumulator.storeEvent + ); + await accumulator + .awaitEvent() + .then(snap => { + expect(snap.docs).not.to.be.null; + expect(snap.docs.length).to.equal(2); + expect(snap.docs[0].data()).to.deep.equal(testDocs.a); + expect(snap.docs[1].data()).to.deep.equal(testDocs.b); + }) + .then(() => setDoc(refForDocA, { foo: 0 })) + .then(() => accumulator.awaitEvent()) + .then(snap => { + expect(snap.docs).not.to.be.null; + expect(snap.docs.length).to.equal(2); + expect(snap.docs[0].data()).to.deep.equal({ foo: 0 }); + expect(snap.docs[1].data()).to.deep.equal(testDocs.b); + }); + unsubscribe(); + }); + } + }); + it('Metadata only changes are not fired when no options provided', () => { return withTestDoc(persistence, docRef => { const secondUpdateFound = new Deferred(); diff --git a/packages/firestore/test/integration/api/query.test.ts b/packages/firestore/test/integration/api/query.test.ts index 0f3c1c82a2d..a12c843bf26 100644 --- a/packages/firestore/test/integration/api/query.test.ts +++ b/packages/firestore/test/integration/api/query.test.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import { isNode } from '@firebase/util'; import { expect } from 'chai'; import { addEqualityMatcher } from '../../util/equality_matcher'; @@ -37,9 +38,11 @@ import { endAt, endBefore, GeoPoint, + getDocFromCache, getDocs, limit, limitToLast, + loadBundle, onSnapshot, or, orderBy, @@ -74,6 +77,47 @@ import { captureExistenceFilterMismatches } from '../util/testing_hooks_util'; apiDescribe('Queries', persistence => { addEqualityMatcher(); + it('QuerySnapshot.toJSON bundle getDocFromCache', async () => { + if (isNode()) { + let path: string | null = null; + let jsonBundle: object | null = null; + const testDocs = { + a: { k: 'a' }, + b: { k: 'b' }, + c: { k: 'c' } + }; + // Write an initial document in an isolated Firestore instance so it's not stored in the cache. + await withTestCollection(persistence, testDocs, async collection => { + await getDocs(query(collection)).then(querySnapshot => { + expect(querySnapshot.docs.length).to.equal(3); + // Find the path to a known doc. + querySnapshot.docs.forEach(docSnapshot => { + if (docSnapshot.ref.path.endsWith('a')) { + path = docSnapshot.ref.path; + } + }); + expect(path).to.not.be.null; + jsonBundle = querySnapshot.toJSON(); + }); + }); + expect(jsonBundle).to.not.be.null; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const json = (jsonBundle as any).bundle; + expect(json).to.exist; + expect(json.length).to.be.greaterThan(0); + + if (path !== null) { + await withTestDb(persistence, async db => { + const docRef = doc(db, path!); + await loadBundle(db, json); + const docSnap = await getDocFromCache(docRef); + expect(docSnap.exists); + expect(docSnap.data()).to.deep.equal(testDocs.a); + }); + } + } + }); + it('can issue limit queries', () => { const testDocs = { a: { k: 'a' }, diff --git a/packages/firestore/test/lite/integration.test.ts b/packages/firestore/test/lite/integration.test.ts index 780db5f4f9c..7fb7eafcb1e 100644 --- a/packages/firestore/test/lite/integration.test.ts +++ b/packages/firestore/test/lite/integration.test.ts @@ -423,6 +423,20 @@ describe('getDoc()', () => { expect(docSnap.exists()).to.be.true; }); }); + + it('can get doc with a deserialized reference', () => { + return withTestDocAndInitialData({ val: 1 }, async docRef => { + const docSnap = await getDoc(docRef); + expect(docSnap.exists()).to.be.true; + const json = docRef.toJSON(); + const deserializedDocRef = DocumentReference.fromJSON( + docSnap._firestore, + json + ); + const docSnap2 = await getDoc(deserializedDocRef); + expect(docSnap2.exists()).to.be.true; + }); + }); }); /** diff --git a/packages/firestore/test/unit/api/bytes.test.ts b/packages/firestore/test/unit/api/bytes.test.ts index afc37400d8e..8fa8919b1e2 100644 --- a/packages/firestore/test/unit/api/bytes.test.ts +++ b/packages/firestore/test/unit/api/bytes.test.ts @@ -55,4 +55,52 @@ describe('Bytes', () => { expectEqual(blob(1, 2, 3), blob(1, 2, 3)); expectNotEqual(blob(1, 2, 3), blob(4, 5, 6)); }); + + it('fromJSON reconstructs the value from toJSON', () => { + const bytes = Bytes.fromUint8Array(new Uint8Array([0, 1, 2, 3, 4, 5])); + expect(() => { + Bytes.fromJSON(bytes.toJSON()); + }).to.not.throw; + expect(Bytes.fromJSON(bytes.toJSON()).isEqual(bytes)).to.be.true; + }); + + it('fromJSON parameter order does not matter', () => { + const type = 'firestore/bytes/1.0'; + const bytes = 'AA=='; + expect(() => { + Bytes.fromJSON({ bytes, type }); + }).to.not.throw; + expect(() => { + Bytes.fromJSON({ type, bytes }); + }).to.not.throw; + }); + + it('toJSON -> fromJSON bytes comparison', () => { + Object.keys(base64Mappings).forEach(base64Str => { + const bytesToSerialize = Bytes.fromBase64String(base64Str); + const deserializedBytes = Bytes.fromJSON(bytesToSerialize.toJSON()); + expectEqual(bytesToSerialize, deserializedBytes); + const expectedUint8Array = base64Mappings[base64Str]; + const actualUint8Array = deserializedBytes.toUint8Array(); + expect(actualUint8Array.length).to.equal(expectedUint8Array.length); + for (let i = 0; i < actualUint8Array.length; i++) { + expect(actualUint8Array[i]).to.equal(expectedUint8Array[i]); + } + }); + }); + + it('fromJSON misisng fields throws', () => { + expect(() => { + Bytes.fromJSON({ type: 'firestore/bytes/1.0' /* missing bytes data */ }); + }).to.throw; + expect(() => { + Bytes.fromJSON({ bytes: 'AA==' /* missing type */ }); + }).to.throw; + expect(() => { + Bytes.fromJSON({ type: 1, bytes: 'AA==' }); + }).to.throw; + expect(() => { + Bytes.fromJSON({ type: 'firestore/bytes/1.0', bytes: 1 }); + }).to.throw; + }); }); diff --git a/packages/firestore/test/unit/api/database.test.ts b/packages/firestore/test/unit/api/database.test.ts index 46e4c65f180..7f8ce10ffd7 100644 --- a/packages/firestore/test/unit/api/database.test.ts +++ b/packages/firestore/test/unit/api/database.test.ts @@ -15,10 +15,17 @@ * limitations under the License. */ +import { isNode } from '@firebase/util'; import { expect } from 'chai'; import { + DocumentReference, + DocumentSnapshot, + documentSnapshotFromJSON, + QuerySnapshot, + querySnapshotFromJSON, connectFirestoreEmulator, + loadBundle, refEqual, snapshotEqual, queryEqual @@ -29,12 +36,22 @@ import { collectionReference, documentReference, documentSnapshot, + firestore, newTestFirestore, query, querySnapshot } from '../../util/api_helpers'; import { keys } from '../../util/helpers'; +describe('Bundle', () => { + it('loadBundle does not throw with an empty bundle string)', async () => { + const db = newTestFirestore(); + expect(async () => { + await loadBundle(db, ''); + }).to.not.throw; + }); +}); + describe('CollectionReference', () => { it('support equality checking with isEqual()', () => { expect(refEqual(collectionReference('foo'), collectionReference('foo'))).to @@ -61,6 +78,151 @@ describe('DocumentReference', () => { it('JSON.stringify() does not throw', () => { JSON.stringify(documentReference('foo/bar')); }); + + it('toJSON() does not throw', () => { + expect(() => { + documentReference('foo/bar').toJSON(); + }).to.not.throw; + }); + + it('toJSON() includes correct JSON fields', () => { + const docRef = documentReference('foo/bar'); + const json = docRef.toJSON(); + expect(json).to.deep.equal({ + type: 'firestore/documentReference/1.0', + referencePath: 'foo/bar' + }); + }); + + it('fromJSON() throws with invalid data', () => { + const db = newTestFirestore(); + expect(() => { + DocumentReference.fromJSON(db, {}); + }).to.throw("JSON missing required field: 'type'"); + }); + + it('fromJSON() throws with missing type data', () => { + const db = newTestFirestore(); + expect(() => { + documentSnapshotFromJSON(db, { + bundleSource: 'DocumentSnapshot', + bundleName: 'test name', + bundle: 'test bundle' + }); + }).to.throw("JSON missing required field: 'type'"); + }); + + it('fromJSON() throws with invalid type data', () => { + const db = newTestFirestore(); + expect(() => { + documentSnapshotFromJSON(db, { + type: 1, + bundleSource: 'DocumentSnapshot', + bundleName: 'test name', + bundle: 'test bundle' + }); + }).to.throw("JSON field 'type' must be a string"); + }); + + it('fromJSON() throws with missing bundleSource', () => { + const db = newTestFirestore(); + expect(() => { + documentSnapshotFromJSON(db, { + type: DocumentSnapshot._jsonSchemaVersion, + bundleName: 'test name', + bundle: 'test bundle' + }); + }).to.throw("JSON missing required field: 'bundleSource'"); + }); + + it('fromJSON() throws with invalid bundleSource type', () => { + const db = newTestFirestore(); + expect(() => { + documentSnapshotFromJSON(db, { + type: DocumentSnapshot._jsonSchemaVersion, + bundleSource: 1, + bundleName: 'test name', + bundle: 'test bundle' + }); + }).to.throw("JSON field 'bundleSource' must be a string"); + }); + + it('fromJSON() throws with invalid bundleSource value', () => { + const db = newTestFirestore(); + expect(() => { + documentSnapshotFromJSON(db, { + type: DocumentSnapshot._jsonSchemaVersion, + bundleSource: 'QuerySnapshot', + bundleName: 'test name', + bundle: 'test bundle' + }); + }).to.throw("Expected 'bundleSource' field to equal 'DocumentSnapshot'"); + }); + + it('fromJSON() throws with missing bundleName', () => { + const db = newTestFirestore(); + expect(() => { + documentSnapshotFromJSON(db, { + type: DocumentSnapshot._jsonSchemaVersion, + bundleSource: 'DocumentSnapshot', + bundle: 'test bundle' + }); + }).to.throw("JSON missing required field: 'bundleName'"); + }); + + it('fromJSON() throws with invalid bundleName', () => { + const db = newTestFirestore(); + expect(() => { + documentSnapshotFromJSON(db, { + type: DocumentSnapshot._jsonSchemaVersion, + bundleSource: 'DocumentSnapshot', + bundleName: 1, + bundle: 'test bundle' + }); + }).to.throw("JSON field 'bundleName' must be a string"); + }); + + it('fromJSON() throws with missing bundle', () => { + const db = newTestFirestore(); + expect(() => { + documentSnapshotFromJSON(db, { + type: DocumentSnapshot._jsonSchemaVersion, + bundleSource: 'DocumentSnapshot', + bundleName: 'test name' + }); + }).to.throw("JSON missing required field: 'bundle'"); + }); + + it('fromJSON() throws with invalid bundle', () => { + const db = newTestFirestore(); + expect(() => { + documentSnapshotFromJSON(db, { + type: DocumentSnapshot._jsonSchemaVersion, + bundleSource: 'DocumentSnapshot', + bundleName: 'test name', + bundle: 1 + }); + }).to.throw("JSON field 'bundle' must be a string"); + }); + + it('fromJSON() does not throw', () => { + const db = newTestFirestore(); + const docRef = documentReference('foo/bar'); + const json = docRef.toJSON(); + expect(() => { + DocumentReference.fromJSON(db, json); + }).to.not.throw; + }); + + it('fromJSON() equals original docRef', () => { + const db = newTestFirestore(); + const docRef = documentReference('foo/bar'); + const json = docRef.toJSON(); + const deserializedDocRef = DocumentReference.fromJSON(db, json); + expect(docRef.id).to.equal(deserializedDocRef.id); + expect(docRef.path).to.equal(deserializedDocRef.path); + expect(docRef.toJSON()).to.deep.equal(deserializedDocRef.toJSON()); + }); }); describe('DocumentSnapshot', () => { @@ -107,6 +269,108 @@ describe('DocumentSnapshot', () => { it('JSON.stringify() does not throw', () => { JSON.stringify(documentSnapshot('foo/bar', { a: 1 }, true)); }); + + it('toJSON returns a bundle', () => { + const snapshotJson = documentSnapshot( + 'foo/bar', + { a: 1 }, + /*fromCache=*/ true + ).toJSON(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const json = snapshotJson as any; + expect(json.bundle).to.exist; + expect(json.bundle.length).to.be.greaterThan(0); + }); + + it('toJSON returns a bundle containing NOT_SUPPORTED in non-node environments', () => { + if (!isNode()) { + const snapshotJson = documentSnapshot( + 'foo/bar', + { a: 1 }, + /*fromCache=*/ true + ).toJSON(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const json = snapshotJson as any; + expect(json.bundle).to.exist; + expect(json.bundle).to.equal('NOT SUPPORTED'); + } + }); + + it('toJSON returns an empty bundle when there are no documents', () => { + if (isNode()) { + const snapshotJson = documentSnapshot( + 'foo/bar', + /*data=*/ null, + /*fromCache=*/ true + ).toJSON(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const json = snapshotJson as any; + expect(json.bundle).to.exist; + expect(json.bundle.length).to.equal(0); + } + }); + + it('toJSON throws when there are pending writes', () => { + expect(() => { + documentSnapshot( + 'foo/bar', + {}, + /*fromCache=*/ true, + /*hasPendingWrites=*/ true + ).toJSON(); + }).to.throw( + `DocumentSnapshot.toJSON() attempted to serialize a document with pending writes. ` + + `Await waitForPendingWrites() before invoking toJSON().` + ); + }); + + it('fromJSON throws when parsing client-side toJSON result', () => { + if (!isNode()) { + const docSnap = documentSnapshot( + 'foo/bar', + { a: 1 }, + /*fromCache=*/ true + ); + expect(() => { + documentSnapshotFromJSON(docSnap._firestore, docSnap.toJSON()); + }).to.throw; + } + }); + + it('fromJSON parses toJSON result', () => { + if (isNode()) { + const docSnap = documentSnapshot( + 'foo/bar', + { a: 1 }, + /*fromCache=*/ true + ); + expect(() => { + documentSnapshotFromJSON(docSnap._firestore, docSnap.toJSON()); + }).to.not.throw; + } + }); + + it('fromJSON produces valid snapshot data.', () => { + if (isNode()) { + const docSnap = documentSnapshot( + 'foo/bar', + { a: 1 }, + /*fromCache=*/ true + ); + const db = firestore(); + const docSnapFromJSON = documentSnapshotFromJSON(db, docSnap.toJSON()); + expect(docSnapFromJSON).to.exist; + const data = docSnapFromJSON.data(); + expect(docSnapFromJSON).to.not.be.undefined; + expect(docSnapFromJSON).to.not.be.null; + if (data) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect((data as any).a).to.exist; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect((data as any).a).to.equal(1); + } + } + }); }); describe('Query', () => { @@ -229,6 +493,259 @@ describe('QuerySnapshot', () => { querySnapshot('foo', {}, { a: { a: 1 } }, keys(), false, false) ); }); + + it('toJSON returns a bundle', () => { + const snapshotJson = querySnapshot( + 'foo', + {}, + { a: { a: 1 } }, + keys(), // An empty set of mutaded document keys signifies that there are no pending writes. + false, + false + ).toJSON(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const json = snapshotJson as any; + expect(json.bundle).to.exist; + expect(json.bundle.length).to.be.greaterThan(0); + }); + + it('toJSON returns a bundle containing NOT_SUPPORTED in non-node environments', () => { + if (!isNode()) { + const snapshotJson = querySnapshot( + 'foo', + {}, + { a: { a: 1 } }, + keys(), // An empty set of mutaded document keys signifies that there are no pending writes. + false, + false + ).toJSON(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const json = snapshotJson as any; + expect(json.bundle).to.exist; + expect(json.bundle).to.equal('NOT SUPPORTED'); + } + }); + + it('toJSON returns a bundle when there are no documents', () => { + if (isNode()) { + const snapshotJson = querySnapshot( + 'foo', + {}, + {}, + keys(), + false, + false + ).toJSON(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const json = snapshotJson as any; + expect(json.bundle).to.exist; + expect(json.bundle.length).to.be.greaterThan(0); + } + }); + + it('toJSON throws when there are pending writes', () => { + expect(() => + querySnapshot( + 'foo', + {}, + { a: { a: 1 } }, + keys('foo/a'), // A non empty set of mutated keys signifies pending writes. + false, + false + ).toJSON() + ).to.throw( + `QuerySnapshot.toJSON() attempted to serialize a document with pending writes. ` + + `Await waitForPendingWrites() before invoking toJSON().` + ); + }); + + it('fromJSON() throws with invalid data', () => { + const db = newTestFirestore(); + expect(() => { + querySnapshotFromJSON(db, {}); + }).to.throw("JSON missing required field: 'type'"); + }); + + it('fromJSON() throws with missing type data', () => { + const db = newTestFirestore(); + expect(() => { + querySnapshotFromJSON(db, { + bundleSource: 'QuerySnapshot', + bundleName: 'test name', + bundle: 'test bundle' + }); + }).to.throw("JSON missing required field: 'type'"); + }); + + it('fromJSON() throws with invalid type data', () => { + const db = newTestFirestore(); + expect(() => { + querySnapshotFromJSON(db, { + type: 1, + bundleSource: 'QuerySnapshot', + bundleName: 'test name', + bundle: 'test bundle' + }); + }).to.throw("JSON field 'type' must be a string"); + }); + + it('fromJSON() throws with missing bundle source data', () => { + const db = newTestFirestore(); + expect(() => { + querySnapshotFromJSON(db, { + type: QuerySnapshot._jsonSchemaVersion, + bundleName: 'test name', + bundle: 'test bundle' + }); + }).to.throw("JSON missing required field: 'bundleSource'"); + }); + + it('fromJSON() throws with invalid bundleSource type', () => { + const db = newTestFirestore(); + expect(() => { + querySnapshotFromJSON(db, { + type: QuerySnapshot._jsonSchemaVersion, + bundleSource: 1, + bundleName: 'test name', + bundle: 'test bundle' + }); + }).to.throw("JSON field 'bundleSource' must be a string"); + }); + + it('fromJSON() throws with invalid bundleSource value', () => { + const db = newTestFirestore(); + expect(() => { + querySnapshotFromJSON(db, { + type: QuerySnapshot._jsonSchemaVersion, + bundleSource: 'DocumentSnapshot', + bundleName: 'test name', + bundle: 'test bundle' + }); + }).to.throw("Expected 'bundleSource' field to equal 'QuerySnapshot'"); + }); + + it('fromJSON() throws with missing bundleName', () => { + const db = newTestFirestore(); + expect(() => { + querySnapshotFromJSON(db, { + type: QuerySnapshot._jsonSchemaVersion, + bundleSource: 'QuerySnapshot', + bundle: 'test bundle' + }); + }).to.throw("JSON missing required field: 'bundleName'"); + }); + + it('fromJSON() throws with invalid bundleName', () => { + const db = newTestFirestore(); + expect(() => { + querySnapshotFromJSON(db, { + type: QuerySnapshot._jsonSchemaVersion, + bundleSource: 'QuerySnapshot', + bundleName: 1, + bundle: 'test bundle' + }); + }).to.throw("JSON field 'bundleName' must be a string"); + }); + + it('fromJSON() throws with missing bundle field', () => { + const db = newTestFirestore(); + expect(() => { + querySnapshotFromJSON(db, { + type: QuerySnapshot._jsonSchemaVersion, + bundleSource: 'QuerySnapshot', + bundleName: 'test name' + }); + }).to.throw("JSON missing required field: 'bundle'"); + }); + + it('fromJSON() throws with invalid bundle field', () => { + const db = newTestFirestore(); + expect(() => { + querySnapshotFromJSON(db, { + type: QuerySnapshot._jsonSchemaVersion, + bundleSource: 'QuerySnapshot', + bundleName: 'test name', + bundle: 1 + }); + }).to.throw("JSON field 'bundle' must be a string"); + }); + + it('fromJSON does not throw', () => { + if (isNode()) { + const snapshot = querySnapshot( + 'foo', + {}, + { + a: { a: 1 }, + b: { bar: 2 } + }, + keys(), // An empty set of mutaded document keys signifies that there are no pending writes. + false, + false + ); + const db = firestore(); + expect(() => { + querySnapshotFromJSON(db, snapshot.toJSON()); + }).to.not.throw; + } + }); + + it('fromJSON produces valid snapshot data', () => { + if (isNode()) { + const snapshot = querySnapshot( + 'foo', + {}, + { + a: { a: 1 }, + b: { bar: 2 } + }, + keys(), // An empty set of mutaded document keys signifies that there are no pending writes. + false, + false + ); + const db = firestore(); + const querySnap = querySnapshotFromJSON(db, snapshot.toJSON()); + expect(querySnap).to.exist; + if (querySnap !== undefined) { + const docs = querySnap.docs; + expect(docs).to.not.be.undefined; + expect(docs).to.not.be.null; + if (docs) { + expect(docs.length).to.equal(2); + if (docs.length === 2) { + let docData = docs[0].data(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let data = docData as any; + expect(data.a).to.exist; + expect(data.a).to.equal(1); + + docData = docs[1].data(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data = docData as any; + expect(data.bar).to.exist; + expect(data.bar).to.equal(2); + } + } + } + } + }); + + it('fromJSON throws when parsing client-side toJSON result', () => { + if (!isNode()) { + const querySnap = querySnapshot( + 'foo', + {}, + { a: { a: 1 } }, + keys(), // An empty set of mutaded document keys signifies that there are no pending writes. + false, + false + ); + const json = querySnap.toJSON(); + expect(() => { + querySnapshotFromJSON(querySnap._firestore, json); + }).to.throw; + } + }); }); describe('SnapshotMetadata', () => { diff --git a/packages/firestore/test/unit/api/geo_point.test.ts b/packages/firestore/test/unit/api/geo_point.test.ts index f2cdb4c27f5..dc744ceb63f 100644 --- a/packages/firestore/test/unit/api/geo_point.test.ts +++ b/packages/firestore/test/unit/api/geo_point.test.ts @@ -105,15 +105,96 @@ describe('GeoPoint', () => { it('serializes to JSON', () => { expect(new GeoPoint(1, 2).toJSON()).to.deep.equal({ latitude: 1, - longitude: 2 + longitude: 2, + 'type': GeoPoint._jsonSchemaVersion }); expect(new GeoPoint(0, 0).toJSON()).to.deep.equal({ latitude: 0, - longitude: 0 + longitude: 0, + 'type': GeoPoint._jsonSchemaVersion }); expect(new GeoPoint(90, 180).toJSON()).to.deep.equal({ latitude: 90, - longitude: 180 + longitude: 180, + 'type': GeoPoint._jsonSchemaVersion }); }); + it('fromJSON does not throw', () => { + const geoPoint = new GeoPoint(1, 2); + expect(() => { + GeoPoint.fromJSON(geoPoint.toJSON()); + }).to.not.throw; + }); + + it('fromJSON reconstructs seconds and nanoseconds', () => { + const geoPoint = new GeoPoint(1, 2); + const deserializedGeoPoint = GeoPoint.fromJSON(geoPoint.toJSON()); + expect(deserializedGeoPoint).to.exist; + expect(geoPoint.latitude).to.equal(deserializedGeoPoint.latitude); + expect(geoPoint.longitude).to.equal(deserializedGeoPoint.longitude); + }); + + it('toJSON -> fromJSON timestamp comparison', () => { + const geoPoint = new GeoPoint(1, 2); + const deserializedGeoPoint = GeoPoint.fromJSON(geoPoint.toJSON()); + expect(deserializedGeoPoint.isEqual(geoPoint)).to.be.true; + }); + + it('fromJSON parameter order does not matter', () => { + const type = 'firestore/geopoint/1.0'; + const latitude = 90; + const longitude = 180; + const control = new GeoPoint(90, 180); + expect(() => { + expect(GeoPoint.fromJSON({ latitude, longitude, type }).isEqual(control)) + .to.be.true; + }).to.not.throw; + expect(() => { + expect(GeoPoint.fromJSON({ longitude, type, latitude }).isEqual(control)) + .to.be.true; + }).to.not.throw; + expect(() => { + expect(GeoPoint.fromJSON({ type, latitude, longitude }).isEqual(control)) + .to.be.true; + }).to.not.throw; + expect(() => { + expect(GeoPoint.fromJSON({ latitude, type, longitude }).isEqual(control)) + .to.be.true; + }).to.not.throw; + }); + + it('fromJSON missing fields throws', () => { + const type = 'firestore/geopoint/1.0'; + const latitude = 90; + const longitude = 180; + + expect(() => { + GeoPoint.fromJSON({ type, latitude }); + }).to.throw; + expect(() => { + GeoPoint.fromJSON({ type, longitude }); + }).to.throw; + expect(() => { + GeoPoint.fromJSON({ latitude, longitude }); + }).to.throw; + }); + + it('fromJSON field errant field type throws', () => { + const type = 'firestore/geopoint/1.0'; + const latitude = 90; + const longitude = 180; + + expect(() => { + GeoPoint.fromJSON({ type, latitude, longitude: 'wrong' }); + }).to.throw; + expect(() => { + GeoPoint.fromJSON({ type, longitude, latitude: 'wrong' }); + }).to.throw; + expect(() => { + GeoPoint.fromJSON({ latitude, longitude, type: 1 }); + }).to.throw; + expect(() => { + GeoPoint.fromJSON({ latitude, longitude, type: 'firestore/wrong/1.0' }); + }).to.throw; + }); }); diff --git a/packages/firestore/test/unit/api/timestamp.test.ts b/packages/firestore/test/unit/api/timestamp.test.ts index ef883f33a92..81773520698 100644 --- a/packages/firestore/test/unit/api/timestamp.test.ts +++ b/packages/firestore/test/unit/api/timestamp.test.ts @@ -143,15 +143,101 @@ describe('Timestamp', () => { it('serializes to JSON', () => { expect(new Timestamp(123, 456).toJSON()).to.deep.equal({ seconds: 123, - nanoseconds: 456 + nanoseconds: 456, + type: 'firestore/timestamp/1.0' }); expect(new Timestamp(0, 0).toJSON()).to.deep.equal({ seconds: 0, - nanoseconds: 0 + nanoseconds: 0, + type: 'firestore/timestamp/1.0' }); expect(new Timestamp(-123, 456).toJSON()).to.deep.equal({ seconds: -123, - nanoseconds: 456 + nanoseconds: 456, + type: 'firestore/timestamp/1.0' }); }); + + it('fromJSON does not throw', () => { + const timestamp = new Timestamp(123, 456); + expect(() => { + Timestamp.fromJSON(timestamp.toJSON()); + }).to.not.throw; + }); + + it('fromJSON reconstructs seconds and nanoseconds', () => { + const timestamp = new Timestamp(123, 456); + const deserializedTimestamp = Timestamp.fromJSON(timestamp.toJSON()); + expect(deserializedTimestamp).to.exist; + expect(timestamp.nanoseconds).to.equal(deserializedTimestamp.nanoseconds); + expect(timestamp.seconds).to.equal(deserializedTimestamp.seconds); + }); + + it('toJSON -> fromJSON timestamp comparison', () => { + const timestamp = new Timestamp(123, 456); + const deserializedTimestamp = Timestamp.fromJSON(timestamp.toJSON()); + expect(deserializedTimestamp.isEqual(timestamp)).to.be.true; + }); + + it('fromJSON parameter order does not matter', () => { + const type = 'firestore/timestamp/1.0'; + const seconds = 123; + const nanoseconds = 456; + const control = new Timestamp(seconds, nanoseconds); + expect(() => { + expect( + Timestamp.fromJSON({ seconds, nanoseconds, type }).isEqual(control) + ).to.be.true; + }).to.not.throw; + expect(() => { + expect( + Timestamp.fromJSON({ nanoseconds, type, seconds }).isEqual(control) + ).to.be.true; + }).to.not.throw; + expect(() => { + expect( + Timestamp.fromJSON({ type, seconds, nanoseconds }).isEqual(control) + ).to.be.true; + }).to.not.throw; + expect(() => { + expect( + Timestamp.fromJSON({ seconds, type, nanoseconds }).isEqual(control) + ).to.be.true; + }).to.not.throw; + }); + + it('fromJSON missing fields throws', () => { + const type = 'firestore/timestamp/1.0'; + const seconds = 123; + const nanoseconds = 456; + + expect(() => { + Timestamp.fromJSON({ type, seconds }); + }).to.throw; + expect(() => { + Timestamp.fromJSON({ type, nanoseconds }); + }).to.throw; + expect(() => { + Timestamp.fromJSON({ seconds, nanoseconds }); + }).to.throw; + }); + + it('fromJSON field errant field type throws', () => { + const type = 'firestore/timestamp/1.0'; + const seconds = 123; + const nanoseconds = 456; + + expect(() => { + Timestamp.fromJSON({ type, seconds, nanoseconds: 'wrong' }); + }).to.throw; + expect(() => { + Timestamp.fromJSON({ type, nanoseconds, seconds: 'wrong' }); + }).to.throw; + expect(() => { + Timestamp.fromJSON({ seconds, nanoseconds, type: 1 }); + }).to.throw; + expect(() => { + Timestamp.fromJSON({ seconds, nanoseconds, type: 'firestore/wrong/1.0' }); + }).to.throw; + }); }); diff --git a/packages/firestore/test/unit/api/vector_value.test.ts b/packages/firestore/test/unit/api/vector_value.test.ts new file mode 100644 index 00000000000..c0944a64934 --- /dev/null +++ b/packages/firestore/test/unit/api/vector_value.test.ts @@ -0,0 +1,73 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; + +import { VectorValue } from '../../../src'; + +describe('VectorValue', () => { + it('fromJSON reconstructs the value from toJSON', () => { + const num: number[] = [1, 2, 3]; + const vectorValue = new VectorValue(num); + const json = vectorValue.toJSON(); + const parsedVectorValue = VectorValue.fromJSON(json); + expect(vectorValue.isEqual(parsedVectorValue)).to.be.true; + }); + + it('fromJSON parameter order does not matter', () => { + const type = VectorValue._jsonSchemaVersion; + const vectorValues = [1, 2, 3]; + const control = new VectorValue(vectorValues); + + expect(() => { + expect(VectorValue.fromJSON({ vectorValues, type }).isEqual(control)).to + .be.true; + }).to.not.throw; + expect(() => { + expect(VectorValue.fromJSON({ type, vectorValues }).isEqual(control)).to + .be.true; + }).to.not.throw; + }); + + it('fromJSON empty array does not throw', () => { + const type = VectorValue._jsonSchemaVersion; + const vectorValues = [1, 2, 3]; + expect(() => { + VectorValue.fromJSON({ type, vectorValues }); + }).to.not.throw; + }); + + it('fromJSON missing fields throws', () => { + const type = VectorValue._jsonSchemaVersion; + const vectorValues = [1, 2, 3]; + expect(() => { + VectorValue.fromJSON({ type /* missing data */ }); + }).to.throw; + expect(() => { + VectorValue.fromJSON({ vectorValues /* missing type */ }); + }).to.throw; + expect(() => { + VectorValue.fromJSON({ type: 1, vectorValues }); + }).to.throw; + expect(() => { + VectorValue.fromJSON({ type: 'firestore/bytes/1.0', vectorValues }); + }).to.throw; + expect(() => { + VectorValue.fromJSON({ type, vectorValues: 'not a number' }); + }); + }); +}); diff --git a/packages/firestore/test/unit/local/indexeddb_persistence.test.ts b/packages/firestore/test/unit/local/indexeddb_persistence.test.ts index 1240c977cee..9fa872101b1 100644 --- a/packages/firestore/test/unit/local/indexeddb_persistence.test.ts +++ b/packages/firestore/test/unit/local/indexeddb_persistence.test.ts @@ -316,10 +316,14 @@ describe('IndexedDbSchema: createOrUpgradeDb', () => { lastRemoteSnapshotVersion: { seconds: 1, nanoseconds: 1 }, targetCount: 1 }; + const timestamp = SnapshotVersion.min().toTimestamp(); const resetTargetGlobal: DbTargetGlobal = { highestTargetId: 0, highestListenSequenceNumber: 0, - lastRemoteSnapshotVersion: SnapshotVersion.min().toTimestamp(), + lastRemoteSnapshotVersion: { + seconds: timestamp.seconds, + nanoseconds: timestamp.nanoseconds + }, targetCount: 0 }; diff --git a/packages/firestore/test/util/api_helpers.ts b/packages/firestore/test/util/api_helpers.ts index 762b5258a29..d248c9213b5 100644 --- a/packages/firestore/test/util/api_helpers.ts +++ b/packages/firestore/test/util/api_helpers.ts @@ -79,8 +79,10 @@ export function documentReference(path: string): DocumentReference { export function documentSnapshot( path: string, data: JsonObject | null, - fromCache: boolean + fromCache: boolean, + hasPendingWrites?: boolean ): DocumentSnapshot { + hasPendingWrites = !!hasPendingWrites; const db = firestore(); const userDataWriter = new ExpUserDataWriter(db); if (data) { @@ -89,7 +91,7 @@ export function documentSnapshot( userDataWriter, key(path), doc(path, 1, data), - new SnapshotMetadata(/* hasPendingWrites= */ false, fromCache), + new SnapshotMetadata(hasPendingWrites, fromCache), /* converter= */ null ); } else { @@ -98,7 +100,7 @@ export function documentSnapshot( userDataWriter, key(path), null, - new SnapshotMetadata(/* hasPendingWrites= */ false, fromCache), + new SnapshotMetadata(hasPendingWrites, fromCache), /* converter= */ null ); } From 86155b3c8f3974f8d777232625108c14f924e035 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Mon, 23 Jun 2025 16:29:30 -0400 Subject: [PATCH 74/77] chore(Firestore) add changeset for PR #8926 (#9115) Add the missing changset for PR #8926. --- .changeset/moody-comics-speak.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/moody-comics-speak.md diff --git a/.changeset/moody-comics-speak.md b/.changeset/moody-comics-speak.md new file mode 100644 index 00000000000..9a178a6605b --- /dev/null +++ b/.changeset/moody-comics-speak.md @@ -0,0 +1,8 @@ +--- +'@firebase/firestore': minor +'firebase': minor +--- + +Added support for Firestore result types to be serialized with `toJSON` and then deserialized with `fromJSON` methods on the objects. + +Addeed support to resume `onSnapshot` listeners in the CSR phase based on serialized `DataSnapshot`s and `QuerySnapshot`s built in the SSR phase. From 3d44792f14f3df265162d06e2acdf3cad0c2ef86 Mon Sep 17 00:00:00 2001 From: Raymond Lam Date: Thu, 26 Jun 2025 10:19:40 -0700 Subject: [PATCH 75/77] Update Fireperf logging to use sendBeacon only if the payload is under the 64KB limit (#9120) * Update Fireperf logging to use sendBeacon only if the payload is under the 64KB limit for most browsers. - For the flush, attempt to use sendBeacon with a low number of events incase sendBeacon is also used by other libraries. * Add changeset and fix format * Add additional comments * Put max flush size behind remote config flag --- .changeset/nervous-needles-sit.md | 5 + .../services/remote_config_service.test.ts | 7 +- .../src/services/remote_config_service.ts | 10 ++ .../src/services/settings_service.ts | 4 + .../src/services/transport_service.test.ts | 152 +++++++++++++++++- .../src/services/transport_service.ts | 96 +++++++---- 6 files changed, 243 insertions(+), 31 deletions(-) create mode 100644 .changeset/nervous-needles-sit.md diff --git a/.changeset/nervous-needles-sit.md b/.changeset/nervous-needles-sit.md new file mode 100644 index 00000000000..77594e263b0 --- /dev/null +++ b/.changeset/nervous-needles-sit.md @@ -0,0 +1,5 @@ +--- +'@firebase/performance': patch +--- + +Fix bug where events are not sent if they exceed sendBeacon payload limit diff --git a/packages/performance/src/services/remote_config_service.test.ts b/packages/performance/src/services/remote_config_service.test.ts index 78f1b1b4462..49b8893140f 100644 --- a/packages/performance/src/services/remote_config_service.test.ts +++ b/packages/performance/src/services/remote_config_service.test.ts @@ -40,7 +40,8 @@ describe('Performance Monitoring > remote_config_service', () => { "fpr_log_endpoint_url":"https://firebaselogging.test.com",\ "fpr_log_transport_key":"pseudo-transport-key",\ "fpr_log_source":"2","fpr_vc_network_request_sampling_rate":"0.250000",\ - "fpr_vc_session_sampling_rate":"0.250000","fpr_vc_trace_sampling_rate":"0.500000"},\ + "fpr_vc_session_sampling_rate":"0.250000","fpr_vc_trace_sampling_rate":"0.500000", + "fpr_log_max_flush_size":"10"},\ "state":"UPDATE"}`; const PROJECT_ID = 'project1'; const APP_ID = '1:23r:web:fewq'; @@ -80,6 +81,7 @@ describe('Performance Monitoring > remote_config_service', () => { settingsService.loggingEnabled = false; settingsService.networkRequestsSamplingRate = 1; settingsService.tracesSamplingRate = 1; + settingsService.logMaxFlushSize = 40; } // parameterized beforeEach. Should be called at beginning of each test. @@ -150,6 +152,7 @@ describe('Performance Monitoring > remote_config_service', () => { expect(SettingsService.getInstance().tracesSamplingRate).to.equal( TRACE_SAMPLING_RATE ); + expect(SettingsService.getInstance().logMaxFlushSize).to.equal(10); }); it('does not call remote config if a valid config is in local storage', async () => { @@ -190,6 +193,7 @@ describe('Performance Monitoring > remote_config_service', () => { expect(SettingsService.getInstance().tracesSamplingRate).to.equal( TRACE_SAMPLING_RATE ); + expect(SettingsService.getInstance().logMaxFlushSize).to.equal(10); }); it('does not change the default config if call to RC fails', async () => { @@ -207,6 +211,7 @@ describe('Performance Monitoring > remote_config_service', () => { await getConfig(performanceController, IID); expect(SettingsService.getInstance().loggingEnabled).to.equal(false); + expect(SettingsService.getInstance().logMaxFlushSize).to.equal(40); }); it('uses secondary configs if the response does not have all the fields', async () => { diff --git a/packages/performance/src/services/remote_config_service.ts b/packages/performance/src/services/remote_config_service.ts index 13787e2b693..1641b3b953b 100644 --- a/packages/performance/src/services/remote_config_service.ts +++ b/packages/performance/src/services/remote_config_service.ts @@ -38,6 +38,7 @@ interface SecondaryConfig { transportKey?: string; tracesSamplingRate?: number; networkRequestsSamplingRate?: number; + logMaxFlushSize?: number; } // These values will be used if the remote config object is successfully @@ -56,6 +57,7 @@ interface RemoteConfigTemplate { fpr_vc_network_request_sampling_rate?: string; fpr_vc_trace_sampling_rate?: string; fpr_vc_session_sampling_rate?: string; + fpr_log_max_flush_size?: string; } /* eslint-enable camelcase */ @@ -221,6 +223,14 @@ function processConfig( settingsServiceInstance.tracesSamplingRate = DEFAULT_CONFIGS.tracesSamplingRate; } + + if (entries.fpr_log_max_flush_size) { + settingsServiceInstance.logMaxFlushSize = Number( + entries.fpr_log_max_flush_size + ); + } else if (DEFAULT_CONFIGS.logMaxFlushSize) { + settingsServiceInstance.logMaxFlushSize = DEFAULT_CONFIGS.logMaxFlushSize; + } // Set the per session trace and network logging flags. settingsServiceInstance.logTraceAfterSampling = shouldLogAfterSampling( settingsServiceInstance.tracesSamplingRate diff --git a/packages/performance/src/services/settings_service.ts b/packages/performance/src/services/settings_service.ts index 83e08bd53d5..dbaed32a537 100644 --- a/packages/performance/src/services/settings_service.ts +++ b/packages/performance/src/services/settings_service.ts @@ -54,6 +54,10 @@ export class SettingsService { // TTL of config retrieved from remote config in hours. configTimeToLive = 12; + // The max number of events to send during a flush. This number is kept low to since Chrome has a + // shared payload limit for all sendBeacon calls in the same nav context. + logMaxFlushSize = 40; + getFlTransportFullUrl(): string { return this.flTransportEndpointUrl.concat('?key=', this.transportKey); } diff --git a/packages/performance/src/services/transport_service.test.ts b/packages/performance/src/services/transport_service.test.ts index 124ce1f415b..4f46205958d 100644 --- a/packages/performance/src/services/transport_service.test.ts +++ b/packages/performance/src/services/transport_service.test.ts @@ -21,7 +21,8 @@ import sinonChai from 'sinon-chai'; import { transportHandler, setupTransportService, - resetTransportService + resetTransportService, + flushQueuedEvents } from './transport_service'; import { SettingsService } from './settings_service'; @@ -88,14 +89,15 @@ describe('Firebase Performance > transport_service', () => { expect(fetchStub).to.not.have.been.called; }); - it('sends up to the maximum event limit in one request', async () => { + it('sends up to the maximum event limit in one request if payload is under 64 KB', async () => { // Arrange const setting = SettingsService.getInstance(); const flTransportFullUrl = setting.flTransportEndpointUrl + '?key=' + setting.transportKey; // Act - // Generate 1020 events, which should be dispatched in two batches (1000 events and 20 events). + // Generate 1020 events with small payloads, which should be dispatched in two batches + // (1000 events and 20 events). for (let i = 0; i < 1020; i++) { testTransportHandler('event' + i); } @@ -134,6 +136,58 @@ describe('Firebase Performance > transport_service', () => { expect(fetchStub).to.not.have.been.called; }); + it('sends fetch if payload is above 64 KB', async () => { + // Arrange + const setting = SettingsService.getInstance(); + const flTransportFullUrl = + setting.flTransportEndpointUrl + '?key=' + setting.transportKey; + fetchStub.resolves( + new Response('{}', { + status: 200, + headers: { 'Content-type': 'application/json' } + }) + ); + + // Act + // Generate 1020 events with a large payload. The total size of the payload will be > 65 KB + const payload = 'a'.repeat(300); + for (let i = 0; i < 1020; i++) { + testTransportHandler(payload + i); + } + // Wait for first and second event dispatch to happen. + clock.tick(INITIAL_SEND_TIME_DELAY_MS); + // This is to resolve the floating promise chain in transport service. + await Promise.resolve().then().then().then(); + clock.tick(DEFAULT_SEND_INTERVAL_MS); + + // Assert + // Expects the first logRequest which contains first 1000 events. + const firstLogRequest = generateLogRequest('5501'); + for (let i = 0; i < MAX_EVENT_COUNT_PER_REQUEST; i++) { + firstLogRequest['log_event'].push({ + 'source_extension_json_proto3': payload + i, + 'event_time_ms': '1' + }); + } + expect(fetchStub).calledWith(flTransportFullUrl, { + method: 'POST', + body: JSON.stringify(firstLogRequest) + }); + // Expects the second logRequest which contains remaining 20 events; + const secondLogRequest = generateLogRequest('15501'); + for (let i = 0; i < 20; i++) { + secondLogRequest['log_event'].push({ + 'source_extension_json_proto3': + payload + (MAX_EVENT_COUNT_PER_REQUEST + i), + 'event_time_ms': '1' + }); + } + expect(sendBeaconStub).calledWith( + flTransportFullUrl, + JSON.stringify(secondLogRequest) + ); + }); + it('falls back to fetch if sendBeacon fails.', async () => { sendBeaconStub.returns(false); fetchStub.resolves( @@ -147,6 +201,98 @@ describe('Firebase Performance > transport_service', () => { expect(fetchStub).to.have.been.calledOnce; }); + it('flushes the queue with multiple sendBeacons in batches of 40', async () => { + // Arrange + const setting = SettingsService.getInstance(); + const flTransportFullUrl = + setting.flTransportEndpointUrl + '?key=' + setting.transportKey; + fetchStub.resolves( + new Response('{}', { + status: 200, + headers: { 'Content-type': 'application/json' } + }) + ); + + const payload = 'a'.repeat(300); + // Act + // Generate 80 events + for (let i = 0; i < 80; i++) { + testTransportHandler(payload + i); + } + + flushQueuedEvents(); + + // Assert + const firstLogRequest = generateLogRequest('1'); + const secondLogRequest = generateLogRequest('1'); + for (let i = 0; i < 40; i++) { + firstLogRequest['log_event'].push({ + 'source_extension_json_proto3': payload + (i + 40), + 'event_time_ms': '1' + }); + secondLogRequest['log_event'].push({ + 'source_extension_json_proto3': payload + i, + 'event_time_ms': '1' + }); + } + expect(sendBeaconStub).calledWith( + flTransportFullUrl, + JSON.stringify(firstLogRequest) + ); + expect(sendBeaconStub).calledWith( + flTransportFullUrl, + JSON.stringify(secondLogRequest) + ); + expect(fetchStub).to.not.have.been.called; + }); + + it('flushes the queue with fetch for sendBeacons that failed', async () => { + // Arrange + const setting = SettingsService.getInstance(); + const flTransportFullUrl = + setting.flTransportEndpointUrl + '?key=' + setting.transportKey; + fetchStub.resolves( + new Response('{}', { + status: 200, + headers: { 'Content-type': 'application/json' } + }) + ); + + const payload = 'a'.repeat(300); + // Act + // Generate 80 events + for (let i = 0; i < 80; i++) { + testTransportHandler(payload + i); + } + sendBeaconStub.onCall(0).returns(true); + sendBeaconStub.onCall(1).returns(false); + flushQueuedEvents(); + + // Assert + const firstLogRequest = generateLogRequest('1'); + const secondLogRequest = generateLogRequest('1'); + for (let i = 40; i < 80; i++) { + firstLogRequest['log_event'].push({ + 'source_extension_json_proto3': payload + i, + 'event_time_ms': '1' + }); + } + for (let i = 0; i < 40; i++) { + secondLogRequest['log_event'].push({ + 'source_extension_json_proto3': payload + i, + 'event_time_ms': '1' + }); + } + expect(sendBeaconStub).calledWith( + flTransportFullUrl, + JSON.stringify(firstLogRequest) + ); + expect(fetchStub).calledWith(flTransportFullUrl, { + method: 'POST', + body: JSON.stringify(secondLogRequest) + }); + }); + function generateLogRequest(requestTimeMs: string): any { return { 'request_time_ms': requestTimeMs, diff --git a/packages/performance/src/services/transport_service.ts b/packages/performance/src/services/transport_service.ts index 8577fd3a128..46c9930210a 100644 --- a/packages/performance/src/services/transport_service.ts +++ b/packages/performance/src/services/transport_service.ts @@ -24,6 +24,11 @@ const INITIAL_SEND_TIME_DELAY_MS = 5.5 * 1000; const MAX_EVENT_COUNT_PER_REQUEST = 1000; const DEFAULT_REMAINING_TRIES = 3; +// Most browsers have a max payload of 64KB for sendbeacon/keep alive payload. +const MAX_SEND_BEACON_PAYLOAD_SIZE = 65536; + +const TEXT_ENCODER = new TextEncoder(); + let remainingTries = DEFAULT_REMAINING_TRIES; interface BatchEvent { @@ -90,14 +95,31 @@ function dispatchQueueEvents(): void { // for next attempt. const staged = queue.splice(0, MAX_EVENT_COUNT_PER_REQUEST); + const data = buildPayload(staged); + + postToFlEndpoint(data) + .then(() => { + remainingTries = DEFAULT_REMAINING_TRIES; + }) + .catch(() => { + // If the request fails for some reason, add the events that were attempted + // back to the primary queue to retry later. + queue = [...staged, ...queue]; + remainingTries--; + consoleLogger.info(`Tries left: ${remainingTries}.`); + processQueue(DEFAULT_SEND_INTERVAL_MS); + }); +} + +function buildPayload(events: BatchEvent[]): string { /* eslint-disable camelcase */ // We will pass the JSON serialized event to the backend. - const log_event: Log[] = staged.map(evt => ({ + const log_event: Log[] = events.map(evt => ({ source_extension_json_proto3: evt.message, event_time_ms: String(evt.eventTime) })); - const data: TransportBatchLogFormat = { + const transportBatchLog: TransportBatchLogFormat = { request_time_ms: String(Date.now()), client_info: { client_type: 1, // 1 is JS @@ -108,32 +130,27 @@ function dispatchQueueEvents(): void { }; /* eslint-enable camelcase */ - postToFlEndpoint(data) - .then(() => { - remainingTries = DEFAULT_REMAINING_TRIES; - }) - .catch(() => { - // If the request fails for some reason, add the events that were attempted - // back to the primary queue to retry later. - queue = [...staged, ...queue]; - remainingTries--; - consoleLogger.info(`Tries left: ${remainingTries}.`); - processQueue(DEFAULT_SEND_INTERVAL_MS); - }); + return JSON.stringify(transportBatchLog); } -function postToFlEndpoint(data: TransportBatchLogFormat): Promise { +/** Sends to Firelog. Atempts to use sendBeacon otherwsise uses fetch. */ +function postToFlEndpoint(body: string): Promise { const flTransportFullUrl = SettingsService.getInstance().getFlTransportFullUrl(); - const body = JSON.stringify(data); - - return navigator.sendBeacon && navigator.sendBeacon(flTransportFullUrl, body) - ? Promise.resolve() - : fetch(flTransportFullUrl, { - method: 'POST', - body, - keepalive: true - }).then(); + const size = TEXT_ENCODER.encode(body).length; + + if ( + size <= MAX_SEND_BEACON_PAYLOAD_SIZE && + navigator.sendBeacon && + navigator.sendBeacon(flTransportFullUrl, body) + ) { + return Promise.resolve(); + } else { + return fetch(flTransportFullUrl, { + method: 'POST', + body + }); + } } function addToQueue(evt: BatchEvent): void { @@ -159,11 +176,36 @@ export function transportHandler( } /** - * Force flush the queued events. Useful at page unload time to ensure all - * events are uploaded. + * Force flush the queued events. Useful at page unload time to ensure all events are uploaded. + * Flush will attempt to use sendBeacon to send events async and defaults back to fetch as soon as a + * sendBeacon fails. Firefox */ export function flushQueuedEvents(): void { + const flTransportFullUrl = + SettingsService.getInstance().getFlTransportFullUrl(); + while (queue.length > 0) { - dispatchQueueEvents(); + // Send the last events first to prioritize page load traces + const staged = queue.splice(-SettingsService.getInstance().logMaxFlushSize); + const body = buildPayload(staged); + + if ( + navigator.sendBeacon && + navigator.sendBeacon(flTransportFullUrl, body) + ) { + continue; + } else { + queue = [...queue, ...staged]; + break; + } + } + if (queue.length > 0) { + const body = buildPayload(queue); + fetch(flTransportFullUrl, { + method: 'POST', + body + }).catch(() => { + consoleLogger.info(`Failed flushing queued events.`); + }); } } From b6404540c2fe630b02be3b1be57c20dd5d0376e4 Mon Sep 17 00:00:00 2001 From: Mila <107142260+milaGGL@users.noreply.github.com> Date: Fri, 27 Jun 2025 18:31:51 -0400 Subject: [PATCH 76/77] Add decimal128 to new types (#344) * add decimal128 * add new tests * format * resolve comments * update comments * refactor compareNumbers * copy paste Quadruple code * resolve comments 1 * make Quadruple value in decimal128 private --- common/api-review/firestore-lite.api.md | 8 + common/api-review/firestore.api.md | 8 + packages/firestore/lite/index.ts | 2 + packages/firestore/src/api.ts | 2 + .../src/index/firestore_index_value_writer.ts | 98 +- .../src/lite-api/decimal128_value.ts | 47 + .../src/lite-api/user_data_reader.ts | 18 +- .../src/lite-api/user_data_writer.ts | 18 +- .../src/model/transform_operation.ts | 4 +- packages/firestore/src/model/values.ts | 272 +++-- packages/firestore/src/util/quadruple.ts | 275 +++++ .../firestore/src/util/quadruple_builder.ts | 1067 +++++++++++++++++ .../test/integration/api/database.test.ts | 336 +++++- .../test/integration/api/type.test.ts | 74 ++ .../firestore/test/lite/integration.test.ts | 4 + .../firestore_index_value_writer.test.ts | 106 +- .../test/unit/local/index_manager.test.ts | 214 +++- .../unit/local/local_store_indexeddb.test.ts | 274 ++++- .../test/unit/model/document.test.ts | 7 +- .../test/unit/model/object_value.test.ts | 24 +- .../firestore/test/unit/model/values.test.ts | 83 +- .../test/unit/remote/serializer.helper.ts | 4 +- packages/firestore/tsconfig.json | 3 +- 23 files changed, 2686 insertions(+), 262 deletions(-) create mode 100644 packages/firestore/src/lite-api/decimal128_value.ts create mode 100644 packages/firestore/src/util/quadruple.ts create mode 100644 packages/firestore/src/util/quadruple_builder.ts diff --git a/common/api-review/firestore-lite.api.md b/common/api-review/firestore-lite.api.md index f0203c034b3..7d2098e6f11 100644 --- a/common/api-review/firestore-lite.api.md +++ b/common/api-review/firestore-lite.api.md @@ -136,6 +136,14 @@ export function connectFirestoreEmulator(firestore: Firestore, host: string, por // @public export function count(): AggregateField; +// @public +export class Decimal128Value { + constructor(value: string); + isEqual(other: Decimal128Value): boolean; + // (undocumented) + readonly stringValue: string; + } + // @public export function deleteDoc(reference: DocumentReference): Promise; diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 90137f78b00..2c7b3879c73 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -142,6 +142,14 @@ export function connectFirestoreEmulator(firestore: Firestore, host: string, por // @public export function count(): AggregateField; +// @public +export class Decimal128Value { + constructor(value: string); + isEqual(other: Decimal128Value): boolean; + // (undocumented) + readonly stringValue: string; + } + // @public export function deleteAllPersistentCacheIndexes(indexManager: PersistentCacheIndexManager): void; diff --git a/packages/firestore/lite/index.ts b/packages/firestore/lite/index.ts index 7eee71a9893..2a0799d4d8e 100644 --- a/packages/firestore/lite/index.ts +++ b/packages/firestore/lite/index.ts @@ -143,6 +143,8 @@ export { VectorValue } from '../src/lite-api/vector_value'; export { Int32Value } from '../src/lite-api/int32_value'; +export { Decimal128Value } from '../src/lite-api/decimal128_value'; + export { RegexValue } from '../src/lite-api/regex_value'; export { BsonBinaryData } from '../src/lite-api/bson_binary_data'; diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index b9d14923bcd..442ef30007f 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -180,6 +180,8 @@ export { VectorValue } from './lite-api/vector_value'; export { Int32Value } from './lite-api/int32_value'; +export { Decimal128Value } from './lite-api/decimal128_value'; + export { RegexValue } from './lite-api/regex_value'; export { BsonBinaryData } from './lite-api/bson_binary_data'; diff --git a/packages/firestore/src/index/firestore_index_value_writer.ts b/packages/firestore/src/index/firestore_index_value_writer.ts index 5a5f04c9988..f306b21c13a 100644 --- a/packages/firestore/src/index/firestore_index_value_writer.ts +++ b/packages/firestore/src/index/firestore_index_value_writer.ts @@ -31,9 +31,16 @@ import { MapRepresentation, RESERVED_REGEX_PATTERN_KEY, RESERVED_REGEX_OPTIONS_KEY, - RESERVED_INT32_KEY + RESERVED_INT32_KEY, + RESERVED_DECIMAL128_KEY } from '../model/values'; -import { ArrayValue, MapValue, Value } from '../protos/firestore_proto_api'; +import { + ArrayValue, + MapValue, + Value, + Timestamp, + LatLng +} from '../protos/firestore_proto_api'; import { fail } from '../util/assert'; import { isNegativeZero } from '../util/types'; @@ -106,26 +113,10 @@ export class FirestoreIndexValueWriter { this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER); encoder.writeNumber(normalizeNumber(indexValue.integerValue)); } else if ('doubleValue' in indexValue) { - const n = normalizeNumber(indexValue.doubleValue); - if (isNaN(n)) { - this.writeValueTypeLabel(encoder, INDEX_TYPE_NAN); - } else { - this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER); - if (isNegativeZero(n)) { - // -0.0, 0 and 0.0 are all considered the same - encoder.writeNumber(0.0); - } else { - encoder.writeNumber(n); - } - } + const doubleValue = normalizeNumber(indexValue.doubleValue); + this.writeIndexDouble(doubleValue, encoder); } else if ('timestampValue' in indexValue) { - let timestamp = indexValue.timestampValue!; - this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP); - if (typeof timestamp === 'string') { - timestamp = normalizeTimestamp(timestamp); - } - encoder.writeString(`${timestamp.seconds || ''}`); - encoder.writeNumber(timestamp.nanos || 0); + this.writeIndexTimestamp(indexValue.timestampValue!, encoder); } else if ('stringValue' in indexValue) { this.writeIndexString(indexValue.stringValue!, encoder); this.writeTruncationMarker(encoder); @@ -136,10 +127,7 @@ export class FirestoreIndexValueWriter { } else if ('referenceValue' in indexValue) { this.writeIndexEntityRef(indexValue.referenceValue!, encoder); } else if ('geoPointValue' in indexValue) { - const geoPoint = indexValue.geoPointValue!; - this.writeValueTypeLabel(encoder, INDEX_TYPE_GEOPOINT); - encoder.writeNumber(geoPoint.latitude || 0); - encoder.writeNumber(geoPoint.longitude || 0); + this.writeIndexGeoPoint(indexValue.geoPointValue!, encoder); } else if ('mapValue' in indexValue) { const type = detectMapRepresentation(indexValue); if (type === MapRepresentation.INTERNAL_MAX) { @@ -159,12 +147,14 @@ export class FirestoreIndexValueWriter { } else if (type === MapRepresentation.BSON_OBJECT_ID) { this.writeIndexBsonObjectId(indexValue.mapValue!, encoder); } else if (type === MapRepresentation.INT32) { - this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER); - encoder.writeNumber( - normalizeNumber( - indexValue.mapValue!.fields![RESERVED_INT32_KEY]!.integerValue! - ) + this.writeIndexInt32(indexValue.mapValue!, encoder); + } else if (type === MapRepresentation.DECIMAL128) { + // Double and Decimal128 sort the same + // Decimal128 is written as double with precision lost + const parsedValue = parseFloat( + indexValue.mapValue!.fields![RESERVED_DECIMAL128_KEY]!.stringValue! ); + this.writeIndexDouble(parsedValue, encoder); } else { this.writeIndexMap(indexValue.mapValue!, encoder); this.writeTruncationMarker(encoder); @@ -192,6 +182,54 @@ export class FirestoreIndexValueWriter { encoder.writeString(stringIndexValue); } + private writeIndexDouble( + double: number, + encoder: DirectionalIndexByteEncoder + ): void { + if (isNaN(double)) { + this.writeValueTypeLabel(encoder, INDEX_TYPE_NAN); + } else { + this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER); + if (isNegativeZero(double)) { + // -0.0, 0 and 0.0 are all considered the same + encoder.writeNumber(0.0); + } else { + encoder.writeNumber(double); + } + } + } + + private writeIndexInt32( + mapValue: MapValue, + encoder: DirectionalIndexByteEncoder + ): void { + this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER); + encoder.writeNumber( + normalizeNumber(mapValue.fields![RESERVED_INT32_KEY]!.integerValue!) + ); + } + + private writeIndexTimestamp( + timestamp: Timestamp, + encoder: DirectionalIndexByteEncoder + ): void { + this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP); + if (typeof timestamp === 'string') { + timestamp = normalizeTimestamp(timestamp); + } + encoder.writeString(`${timestamp.seconds || ''}`); + encoder.writeNumber(timestamp.nanos || 0); + } + + private writeIndexGeoPoint( + geoPoint: LatLng, + encoder: DirectionalIndexByteEncoder + ): void { + this.writeValueTypeLabel(encoder, INDEX_TYPE_GEOPOINT); + encoder.writeNumber(geoPoint.latitude || 0); + encoder.writeNumber(geoPoint.longitude || 0); + } + private writeIndexMap( mapIndexValue: MapValue, encoder: DirectionalIndexByteEncoder diff --git a/packages/firestore/src/lite-api/decimal128_value.ts b/packages/firestore/src/lite-api/decimal128_value.ts new file mode 100644 index 00000000000..ef420238273 --- /dev/null +++ b/packages/firestore/src/lite-api/decimal128_value.ts @@ -0,0 +1,47 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Quadruple } from '../util/quadruple'; + +/** + * Represents a 128-bit decimal type in Firestore documents. + * + * @class Decimal128Value + */ +export class Decimal128Value { + readonly stringValue: string; + private value: Quadruple; + + constructor(value: string) { + this.stringValue = value; + this.value = Quadruple.fromString(value); + } + + /** + * Returns true if this `Decimal128Value` is equal to the provided one. + * + * @param other - The `Decimal128Value` to compare against. + * @return 'true' if this `Decimal128Value` is equal to the provided one. + */ + isEqual(other: Decimal128Value): boolean { + // Firestore considers +0 and -0 to be equal. + if (this.value.isZero() && other.value.isZero()) { + return true; + } + return this.value.compareTo(other.value) === 0; + } +} diff --git a/packages/firestore/src/lite-api/user_data_reader.ts b/packages/firestore/src/lite-api/user_data_reader.ts index 63b0b99db90..b0fd7772bc1 100644 --- a/packages/firestore/src/lite-api/user_data_reader.ts +++ b/packages/firestore/src/lite-api/user_data_reader.ts @@ -55,7 +55,8 @@ import { RESERVED_BSON_TIMESTAMP_INCREMENT_KEY, RESERVED_BSON_BINARY_KEY, RESERVED_MIN_KEY, - RESERVED_MAX_KEY + RESERVED_MAX_KEY, + RESERVED_DECIMAL128_KEY } from '../model/values'; import { newSerializer } from '../platform/serializer'; import { @@ -80,6 +81,7 @@ import { BsonObjectId } from './bson_object_Id'; import { BsonTimestamp } from './bson_timestamp'; import { Bytes } from './bytes'; import { Firestore } from './database'; +import { Decimal128Value } from './decimal128_value'; import { FieldPath } from './field_path'; import { FieldValue } from './field_value'; import { GeoPoint } from './geo_point'; @@ -936,6 +938,8 @@ function parseScalarValue( return parseBsonObjectId(value); } else if (value instanceof Int32Value) { return parseInt32Value(value); + } else if (value instanceof Decimal128Value) { + return parseDecimal128Value(value); } else if (value instanceof BsonTimestamp) { return parseBsonTimestamp(value); } else if (value instanceof BsonBinaryData) { @@ -1045,6 +1049,17 @@ export function parseInt32Value(value: Int32Value): ProtoValue { return { mapValue }; } +export function parseDecimal128Value(value: Decimal128Value): ProtoValue { + const mapValue: ProtoMapValue = { + fields: { + [RESERVED_DECIMAL128_KEY]: { + stringValue: value.stringValue + } + } + }; + return { mapValue }; +} + export function parseBsonTimestamp(value: BsonTimestamp): ProtoValue { const mapValue: ProtoMapValue = { fields: { @@ -1105,6 +1120,7 @@ function looksLikeJsonObject(input: unknown): boolean { !(input instanceof MinKey) && !(input instanceof MaxKey) && !(input instanceof Int32Value) && + !(input instanceof Decimal128Value) && !(input instanceof RegexValue) && !(input instanceof BsonObjectId) && !(input instanceof BsonTimestamp) && diff --git a/packages/firestore/src/lite-api/user_data_writer.ts b/packages/firestore/src/lite-api/user_data_writer.ts index ba9ade1c207..92ba958608b 100644 --- a/packages/firestore/src/lite-api/user_data_writer.ts +++ b/packages/firestore/src/lite-api/user_data_writer.ts @@ -41,7 +41,10 @@ import { RESERVED_BSON_TIMESTAMP_KEY, RESERVED_BSON_TIMESTAMP_SECONDS_KEY, typeOrder, - VECTOR_MAP_VECTORS_KEY + VECTOR_MAP_VECTORS_KEY, + RESERVED_DECIMAL128_KEY, + isInt32Value, + isDecimal128Value } from '../model/values'; import { ApiClientObjectMap, @@ -61,6 +64,7 @@ import { forEach } from '../util/obj'; import { BsonBinaryData } from './bson_binary_data'; import { BsonObjectId } from './bson_object_Id'; import { BsonTimestamp } from './bson_timestamp'; +import { Decimal128Value } from './decimal128_value'; import { GeoPoint } from './geo_point'; import { Int32Value } from './int32_value'; import { MaxKey } from './max_key'; @@ -89,7 +93,11 @@ export abstract class AbstractUserDataWriter { return value.booleanValue!; case TypeOrder.NumberValue: if ('mapValue' in value) { - return this.convertToInt32Value(value.mapValue!); + if (isInt32Value(value)) { + return this.convertToInt32Value(value.mapValue!); + } else if (isDecimal128Value(value)) { + return this.convertToDecimal128Value(value.mapValue!); + } } return normalizeNumber(value.integerValue || value.doubleValue); case TypeOrder.TimestampValue: @@ -215,6 +223,12 @@ export abstract class AbstractUserDataWriter { return new Int32Value(value); } + private convertToDecimal128Value(mapValue: ProtoMapValue): Decimal128Value { + const value = + mapValue!.fields?.[RESERVED_DECIMAL128_KEY]?.stringValue ?? ''; + return new Decimal128Value(value); + } + private convertGeoPoint(value: ProtoLatLng): GeoPoint { return new GeoPoint( normalizeNumber(value.latitude), diff --git a/packages/firestore/src/model/transform_operation.ts b/packages/firestore/src/model/transform_operation.ts index 07f6df94366..01ce05d3f82 100644 --- a/packages/firestore/src/model/transform_operation.ts +++ b/packages/firestore/src/model/transform_operation.ts @@ -23,7 +23,7 @@ import { arrayEquals } from '../util/misc'; import { normalizeNumber } from './normalize'; import { serverTimestamp } from './server_timestamps'; -import { isArray, isInteger, isNumber, valueEquals } from './values'; +import { isArray, isIntegerValue, isNumber, valueEquals } from './values'; /** Used to represent a field transform on a mutation. */ export class TransformOperation { @@ -205,7 +205,7 @@ export function applyNumericIncrementTransformOperationToLocalView( previousValue )!; const sum = asNumber(baseValue) + asNumber(transform.operand); - if (isInteger(baseValue) && isInteger(transform.operand)) { + if (isIntegerValue(baseValue) && isIntegerValue(transform.operand)) { return toInteger(sum); } else { return toDouble(transform.serializer, sum); diff --git a/packages/firestore/src/model/values.ts b/packages/firestore/src/model/values.ts index fca9c34b9ea..01902ff8b5d 100644 --- a/packages/firestore/src/model/values.ts +++ b/packages/firestore/src/model/values.ts @@ -30,6 +30,7 @@ import { primitiveComparator } from '../util/misc'; import { forEach, objectSize } from '../util/obj'; +import { Quadruple } from '../util/quadruple'; import { isNegativeZero } from '../util/types'; import { DocumentKey } from './document_key'; @@ -59,6 +60,8 @@ export const RESERVED_BSON_OBJECT_ID_KEY = '__oid__'; export const RESERVED_INT32_KEY = '__int__'; +export const RESERVED_DECIMAL128_KEY = '__decimal128__'; + export const RESERVED_BSON_TIMESTAMP_KEY = '__request_timestamp__'; export const RESERVED_BSON_TIMESTAMP_SECONDS_KEY = 'seconds'; export const RESERVED_BSON_TIMESTAMP_INCREMENT_KEY = 'increment'; @@ -168,6 +171,7 @@ export enum MapRepresentation { REGEX = 'regexValue', BSON_OBJECT_ID = 'bsonObjectIdValue', INT32 = 'int32Value', + DECIMAL128 = 'decimal128Value', BSON_TIMESTAMP = 'bsonTimestampValue', BSON_BINARY = 'bsonBinaryValue', MIN_KEY = 'minKeyValue', @@ -178,6 +182,27 @@ export enum MapRepresentation { REGULAR_MAP = 'regularMapValue' } +const TYPE_BASED_REPRESENTATIONS: Record = { + [RESERVED_VECTOR_KEY]: MapRepresentation.VECTOR, + [RESERVED_MAX_KEY]: MapRepresentation.INTERNAL_MAX, + [RESERVED_SERVER_TIMESTAMP_KEY]: MapRepresentation.SERVER_TIMESTAMP +}; + +const BSON_REPRESENTATIONS: Record = { + [RESERVED_REGEX_KEY]: MapRepresentation.REGEX, + [RESERVED_BSON_OBJECT_ID_KEY]: MapRepresentation.BSON_OBJECT_ID, + [RESERVED_INT32_KEY]: MapRepresentation.INT32, + [RESERVED_DECIMAL128_KEY]: MapRepresentation.DECIMAL128, + [RESERVED_BSON_TIMESTAMP_KEY]: MapRepresentation.BSON_TIMESTAMP, + [RESERVED_BSON_BINARY_KEY]: MapRepresentation.BSON_BINARY, + [RESERVED_MIN_KEY]: MapRepresentation.MIN_KEY, + [RESERVED_MAX_KEY]: MapRepresentation.MAX_KEY +}; + +const BSON_TYPE_REPRESENTATIONS = new Set( + Object.values(BSON_REPRESENTATIONS) +); + /** Extracts the backend's type order for the provided value. */ export function typeOrder(value: Value): TypeOrder { if ('nullValue' in value) { @@ -212,6 +237,7 @@ export function typeOrder(value: Value): TypeOrder { case MapRepresentation.BSON_OBJECT_ID: return TypeOrder.BsonObjectIdValue; case MapRepresentation.INT32: + case MapRepresentation.DECIMAL128: return TypeOrder.NumberValue; case MapRepresentation.BSON_TIMESTAMP: return TypeOrder.BsonTimestampValue; @@ -321,10 +347,11 @@ function blobEquals(left: Value, right: Value): boolean { } export function numberEquals(left: Value, right: Value): boolean { - if ( + if (isDecimal128Value(left) && isDecimal128Value(right)) { + return compareQuadruples(left, right) === 0; + } else if ( ('integerValue' in left && 'integerValue' in right) || - (detectMapRepresentation(left) === MapRepresentation.INT32 && - detectMapRepresentation(right) === MapRepresentation.INT32) + (isInt32Value(left) && isInt32Value(right)) ) { return extractNumber(left) === extractNumber(right); } else if ('doubleValue' in left && 'doubleValue' in right) { @@ -431,7 +458,7 @@ export function valueCompare(left: Value, right: Value): number { export function extractNumber(value: Value): number { let numberValue; - if (detectMapRepresentation(value) === MapRepresentation.INT32) { + if (isInt32Value(value)) { numberValue = value.mapValue!.fields![RESERVED_INT32_KEY].integerValue!; } else { numberValue = value.integerValue || value.doubleValue; @@ -439,23 +466,53 @@ export function extractNumber(value: Value): number { return normalizeNumber(numberValue); } +function getDecimal128StringValue(value: Value): string { + return value.mapValue!.fields![RESERVED_DECIMAL128_KEY].stringValue!; +} + function compareNumbers(left: Value, right: Value): number { + // If either number is Decimal128, we cast both to wider (128-bit) representation, and compare those. + if (isDecimal128Value(left) || isDecimal128Value(right)) { + return compareQuadruples(left, right); + } + const leftNumber = extractNumber(left); const rightNumber = extractNumber(right); - if (leftNumber < rightNumber) { - return -1; - } else if (leftNumber > rightNumber) { + // one or both numbers are NaN. + if (isNaN(leftNumber)) { + return isNaN(rightNumber) ? 0 : -1; + } else if (isNaN(rightNumber)) { return 1; - } else if (leftNumber === rightNumber) { + } + + return primitiveComparator(leftNumber, rightNumber); +} + +function compareQuadruples(left: Value, right: Value): number { + const leftQuadruple = convertNumberToQuadruple(left); + const rightQuadruple = convertNumberToQuadruple(right); + + // Firestore considers +0 and -0 to be equal. + if (leftQuadruple.isZero() && rightQuadruple.isZero()) { return 0; + } + + // NaN sorts equal to itself and before any other number. + if (leftQuadruple.isNaN()) { + return rightQuadruple.isNaN() ? 0 : -1; + } else if (rightQuadruple.isNaN()) { + return 1; + } + + return leftQuadruple.compareTo(rightQuadruple); +} + +function convertNumberToQuadruple(value: Value): Quadruple { + if (isDecimal128Value(value)) { + return Quadruple.fromString(getDecimal128StringValue(value)); } else { - // one or both are NaN. - if (isNaN(leftNumber)) { - return isNaN(rightNumber) ? 0 : -1; - } else { - return 1; - } + return Quadruple.fromNumber(extractNumber(value)); } } @@ -758,7 +815,9 @@ export function estimateByteSize(value: Value): number { case TypeOrder.BooleanValue: return 4; case TypeOrder.NumberValue: - // TODO(Mila/BSON): return 16 if the value is 128 decimal value + if (isDecimal128Value(value)) { + return 16; + } return 8; case TypeOrder.TimestampValue: // Timestamps are made up of two distinct numbers (seconds + nanoseconds) @@ -819,22 +878,48 @@ export function refValue(databaseId: DatabaseId, key: DocumentKey): Value { } /** Returns true if `value` is an IntegerValue . */ -export function isInteger( +export function isIntegerValue( value?: Value | null ): value is { integerValue: string | number } { return !!value && 'integerValue' in value; } /** Returns true if `value` is a DoubleValue. */ -export function isDouble( +export function isDoubleValue( value?: Value | null ): value is { doubleValue: string | number } { return !!value && 'doubleValue' in value; } +export function isDecimal128Value(value: Value): boolean { + if (!value.mapValue?.fields) { + return false; + } + + const fields = value.mapValue.fields; + return ( + objectSize(fields) === 1 && + fields[RESERVED_DECIMAL128_KEY] && + !!fields[RESERVED_DECIMAL128_KEY].stringValue + ); +} + +export function isInt32Value(value: Value): boolean { + if (!value.mapValue?.fields) { + return false; + } + + const fields = value.mapValue.fields; + return ( + objectSize(fields) === 1 && + fields[RESERVED_INT32_KEY] && + !!fields[RESERVED_INT32_KEY].integerValue + ); +} + /** Returns true if `value` is either an IntegerValue or a DoubleValue. */ export function isNumber(value?: Value | null): boolean { - return isInteger(value) || isDouble(value); + return isIntegerValue(value) || isDoubleValue(value); } /** Returns true if `value` is an ArrayValue. */ @@ -859,10 +944,16 @@ export function isNullValue( } /** Returns true if `value` is NaN. */ -export function isNanValue( - value?: Value | null -): value is { doubleValue: 'NaN' | number } { - return !!value && 'doubleValue' in value && isNaN(Number(value.doubleValue)); +export function isNanValue(value: Value): boolean { + if (isDoubleValue(value) && isNaN(Number(value.doubleValue))) { + return true; + } + + if (isDecimal128Value(value) && getDecimal128StringValue(value) === 'NaN') { + return true; + } + + return false; } /** Returns true if `value` is a MapValue. */ @@ -872,65 +963,32 @@ export function isMapValue( return !!value && 'mapValue' in value; } +export function isBsonType(value: Value): boolean { + return BSON_TYPE_REPRESENTATIONS.has(detectMapRepresentation(value)); +} + export function detectMapRepresentation(value: Value): MapRepresentation { - if (!value || !value.mapValue || !value.mapValue.fields) { - return MapRepresentation.REGULAR_MAP; // Not a special map type + if (!value.mapValue?.fields) { + return MapRepresentation.REGULAR_MAP; } const fields = value.mapValue.fields; // Check for type-based mappings - const type = fields[TYPE_KEY]?.stringValue; - if (type) { - const typeMap: Record = { - [RESERVED_VECTOR_KEY]: MapRepresentation.VECTOR, - [RESERVED_MAX_KEY]: MapRepresentation.INTERNAL_MAX, - [RESERVED_SERVER_TIMESTAMP_KEY]: MapRepresentation.SERVER_TIMESTAMP - }; - if (typeMap[type]) { - return typeMap[type]; - } - } - - if (objectSize(fields) !== 1) { - // All BSON types have 1 key in the map. To improve performance, we can - // return early if the number of keys in the map is not 1. - return MapRepresentation.REGULAR_MAP; + const typeString = fields[TYPE_KEY]?.stringValue; + if (typeString && TYPE_BASED_REPRESENTATIONS[typeString]) { + return TYPE_BASED_REPRESENTATIONS[typeString]; } - // Check for BSON-related mappings - const bsonMap: Record = { - [RESERVED_REGEX_KEY]: MapRepresentation.REGEX, - [RESERVED_BSON_OBJECT_ID_KEY]: MapRepresentation.BSON_OBJECT_ID, - [RESERVED_INT32_KEY]: MapRepresentation.INT32, - [RESERVED_BSON_TIMESTAMP_KEY]: MapRepresentation.BSON_TIMESTAMP, - [RESERVED_BSON_BINARY_KEY]: MapRepresentation.BSON_BINARY, - [RESERVED_MIN_KEY]: MapRepresentation.MIN_KEY, - [RESERVED_MAX_KEY]: MapRepresentation.MAX_KEY - }; - - for (const key in bsonMap) { - if (fields[key]) { - return bsonMap[key]; - } + // For BSON-related mappings, they typically have a single, unique key. + if (objectSize(fields) === 1) { + const keys = Object.keys(fields); + return BSON_REPRESENTATIONS[keys[0]]; } return MapRepresentation.REGULAR_MAP; } -export function isBsonType(value: Value): boolean { - const bsonTypes = new Set([ - MapRepresentation.REGEX, - MapRepresentation.BSON_OBJECT_ID, - MapRepresentation.INT32, - MapRepresentation.BSON_TIMESTAMP, - MapRepresentation.BSON_BINARY, - MapRepresentation.MIN_KEY, - MapRepresentation.MAX_KEY - ]); - return bsonTypes.has(detectMapRepresentation(value)); -} - /** Creates a deep copy of `source`. */ export function deepClone(source: Value): Value { if (source.geoPointValue) { @@ -980,25 +1038,28 @@ export function valuesGetLowerBound(value: Value): Value { return { arrayValue: {} }; } else if ('mapValue' in value) { const type = detectMapRepresentation(value); - if (type === MapRepresentation.VECTOR) { - return MIN_VECTOR_VALUE; - } else if (type === MapRepresentation.BSON_OBJECT_ID) { - return MIN_BSON_OBJECT_ID_VALUE; - } else if (type === MapRepresentation.BSON_TIMESTAMP) { - return MIN_BSON_TIMESTAMP_VALUE; - } else if (type === MapRepresentation.BSON_BINARY) { - return MIN_BSON_BINARY_VALUE; - } else if (type === MapRepresentation.REGEX) { - return MIN_REGEX_VALUE; - } else if (type === MapRepresentation.INT32) { - // int32Value is treated the same as integerValue and doubleValue - return { doubleValue: NaN }; - } else if (type === MapRepresentation.MIN_KEY) { - return MIN_KEY_VALUE; - } else if (type === MapRepresentation.MAX_KEY) { - return MAX_KEY_VALUE; + switch (type) { + case MapRepresentation.VECTOR: + return MIN_VECTOR_VALUE; + case MapRepresentation.BSON_OBJECT_ID: + return MIN_BSON_OBJECT_ID_VALUE; + case MapRepresentation.BSON_TIMESTAMP: + return MIN_BSON_TIMESTAMP_VALUE; + case MapRepresentation.BSON_BINARY: + return MIN_BSON_BINARY_VALUE; + case MapRepresentation.REGEX: + return MIN_REGEX_VALUE; + case MapRepresentation.INT32: + case MapRepresentation.DECIMAL128: + // Int32Value and Decimal128Value are treated the same as integerValue and doubleValue + return { doubleValue: NaN }; + case MapRepresentation.MIN_KEY: + return MIN_KEY_VALUE; + case MapRepresentation.MAX_KEY: + return MAX_KEY_VALUE; + default: + return { mapValue: {} }; } - return { mapValue: {} }; } else { return fail(0x8c66, 'Invalid value type', { value }); } @@ -1026,25 +1087,28 @@ export function valuesGetUpperBound(value: Value): Value { return MIN_VECTOR_VALUE; } else if ('mapValue' in value) { const type = detectMapRepresentation(value); - if (type === MapRepresentation.VECTOR) { - return { mapValue: {} }; - } else if (type === MapRepresentation.BSON_OBJECT_ID) { - return { geoPointValue: { latitude: -90, longitude: -180 } }; - } else if (type === MapRepresentation.BSON_TIMESTAMP) { - return { stringValue: '' }; - } else if (type === MapRepresentation.BSON_BINARY) { - return refValue(DatabaseId.empty(), DocumentKey.empty()); - } else if (type === MapRepresentation.REGEX) { - return { arrayValue: {} }; - } else if (type === MapRepresentation.INT32) { - // int32Value is treated the same as integerValue and doubleValue - return { timestampValue: { seconds: Number.MIN_SAFE_INTEGER } }; - } else if (type === MapRepresentation.MIN_KEY) { - return { booleanValue: false }; - } else if (type === MapRepresentation.MAX_KEY) { - return INTERNAL_MAX_VALUE; + switch (type) { + case MapRepresentation.VECTOR: + return { mapValue: {} }; + case MapRepresentation.BSON_OBJECT_ID: + return { geoPointValue: { latitude: -90, longitude: -180 } }; + case MapRepresentation.BSON_TIMESTAMP: + return { stringValue: '' }; + case MapRepresentation.BSON_BINARY: + return refValue(DatabaseId.empty(), DocumentKey.empty()); + case MapRepresentation.REGEX: + return { arrayValue: {} }; + case MapRepresentation.INT32: + case MapRepresentation.DECIMAL128: + // Int32Value and Decimal128Value are treated the same as integerValue and doubleValue + return { timestampValue: { seconds: Number.MIN_SAFE_INTEGER } }; + case MapRepresentation.MIN_KEY: + return { booleanValue: false }; + case MapRepresentation.MAX_KEY: + return INTERNAL_MAX_VALUE; + default: + return MAX_KEY_VALUE; } - return MAX_KEY_VALUE; } else { return fail(0xf207, 'Invalid value type', { value }); } diff --git a/packages/firestore/src/util/quadruple.ts b/packages/firestore/src/util/quadruple.ts new file mode 100644 index 00000000000..5c3501106a1 --- /dev/null +++ b/packages/firestore/src/util/quadruple.ts @@ -0,0 +1,275 @@ +/** + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { QuadrupleBuilder } from './quadruple_builder'; + +/** + * @private + * @internal + */ +export class Quadruple { + constructor( + negative: boolean, + biasedExponent: number, + mantHi: bigint, + mantLo: bigint + ) { + this.negative = negative; + this.biasedExponent = biasedExponent; + this.mantHi = mantHi; + this.mantLo = mantLo; + } + // The fields containing the value of the instance + negative: boolean; + biasedExponent: number; + mantHi: bigint; + mantLo: bigint; + static #exponentOfInfinity = Number(QuadrupleBuilder.EXPONENT_OF_INFINITY); + static positiveZero: Quadruple = new Quadruple(false, 0, 0n, 0n); + static negativeZero: Quadruple = new Quadruple(true, 0, 0n, 0n); + static NaN: Quadruple = new Quadruple( + false, + Quadruple.#exponentOfInfinity, + 1n << 63n, + 0n + ); + static negativeInfinity: Quadruple = new Quadruple( + true, + Quadruple.#exponentOfInfinity, + 0n, + 0n + ); + static positiveInfinity: Quadruple = new Quadruple( + false, + Quadruple.#exponentOfInfinity, + 0n, + 0n + ); + static #minLong: Quadruple = new Quadruple(true, Quadruple.#bias(63), 0n, 0n); + static #positiveOne: Quadruple = new Quadruple( + false, + Quadruple.#bias(0), + 0n, + 0n + ); + static #negativeOne: Quadruple = new Quadruple( + true, + Quadruple.#bias(0), + 0n, + 0n + ); + /** Return the (unbiased) exponent of this {@link Quadruple}. */ + exponent(): number { + return this.biasedExponent - QuadrupleBuilder.EXPONENT_BIAS; + } + /** Return true if this {@link Quadruple} is -0 or +0 */ + isZero(): boolean { + return ( + this.biasedExponent === 0 && this.mantHi === 0n && this.mantLo === 0n + ); + } + /** Return true if this {@link Quadruple} is -infinity or +infinity */ + isInfinite(): boolean { + return ( + this.biasedExponent === Quadruple.#exponentOfInfinity && + this.mantHi === 0n && + this.mantLo === 0n + ); + } + /** Return true if this {@link Quadruple} is a NaN. */ + isNaN(): boolean { + return ( + this.biasedExponent === Quadruple.#exponentOfInfinity && + !(this.mantHi === 0n && this.mantLo === 0n) + ); + } + /** Compare two quadruples, with -0 < 0, and all NaNs equal and larger than all numbers. */ + compareTo(other: Quadruple): number { + if (this.isNaN()) { + return other.isNaN() ? 0 : 1; + } + if (other.isNaN()) { + return -1; + } + let lessThan; + let greaterThan; + if (this.negative) { + if (!other.negative) { + return -1; + } + lessThan = 1; + greaterThan = -1; + } else { + if (other.negative) { + return 1; + } + lessThan = -1; + greaterThan = 1; + } + if (this.biasedExponent < other.biasedExponent) { + return lessThan; + } + if (this.biasedExponent > other.biasedExponent) { + return greaterThan; + } + if (this.mantHi < other.mantHi) { + return lessThan; + } + if (this.mantHi > other.mantHi) { + return greaterThan; + } + if (this.mantLo < other.mantLo) { + return lessThan; + } + if (this.mantLo > other.mantLo) { + return greaterThan; + } + return 0; + } + debug(): string { + return ( + (this.negative ? '+' : '-') + + Quadruple.#hex(this.mantHi) + + Quadruple.#hex(this.mantLo) + + '*2^' + + this.exponent() + ); + } + static #hex(n: bigint): string { + return n.toString(16).padStart(16, '0'); + } + static fromNumber(value: number): Quadruple { + if (isNaN(value)) { + return Quadruple.NaN; + } + if (!isFinite(value)) { + return value < 0 + ? Quadruple.negativeInfinity + : Quadruple.positiveInfinity; + } + if (value === 0) { + // -0 === 0 and Math.sign(-0) = -0, so can't be used to distinguish 0 and -0. + // But 1/-0=-infinity, and 1/0=infinity, and Math.sign does "work" on infinity. + return Math.sign(1 / value) > 0 + ? Quadruple.positiveZero + : Quadruple.negativeZero; + } + const array = new DataView(new ArrayBuffer(8)); + array.setFloat64(0, value); + const bits = array.getBigUint64(0); + let mantHi = BigInt.asUintN(64, bits << 12n); + let exponent = Number(bits >> 52n) & 0x7ff; + if (exponent === 0) { + // subnormal - mantHi cannot be zero as that means value===+/-0 + const leadingZeros = QuadrupleBuilder.clz64(mantHi); + mantHi = leadingZeros < 63 ? mantHi << BigInt(leadingZeros + 1) : 0n; + exponent = -leadingZeros; + } + return new Quadruple( + value < 0, + Quadruple.#bias(exponent - 1023), + mantHi, + 0n + ); + } + /** + * Converts a decimal number to a {@link Quadruple}. The supported format (no whitespace allowed) + * is: + * + *
      + *
    • NaN for Quadruple.NaN + *
    • Infinity or +Infinity for Quadruple.POSITIVE_INFINITY + *
    • -Infinity for Quadruple.NEGATIVE_INFINITY + *
    • regular expression: [+-]?[0-9]*(.[0-9]*)?([eE][+-]?[0-9]+)? - the exponent cannot be more + * than 9 digits, and the whole string cannot be empty + *
    + */ + static fromString(s: string): Quadruple { + if (s === 'NaN') { + return Quadruple.NaN; + } + if (s === '-Infinity') { + return Quadruple.negativeInfinity; + } + if (s === 'Infinity' || s === '+Infinity') { + return Quadruple.positiveInfinity; + } + const digits: number[] = new Array(s.length).fill(0); + let i = 0; + let j = 0; + let exponent = 0; + let negative = false; + if (s[i] === '-') { + negative = true; + i++; + } else if (s[i] === '+') { + i++; + } + while (Quadruple.#isDigit(s, i)) { + digits[j++] = Quadruple.#digit(s, i++); + } + if (s[i] === '.') { + const decimal = ++i; + while (Quadruple.#isDigit(s, i)) { + digits[j++] = Quadruple.#digit(s, i++); + } + exponent = decimal - i; + } + if (s[i] === 'e' || s[i] === 'E') { + let exponentValue = 0; + i++; + let exponentSign = 1; + if (s[i] === '-') { + exponentSign = -1; + i++; + } else if (s[i] === '+') { + i++; + } + const firstExponent = i; + while (Quadruple.#isDigit(s, i)) { + exponentValue = exponentValue * 10 + Quadruple.#digit(s, i++); + if (i - firstExponent > 9) { + throw new Error('Exponent too large ' + s); + } + } + if (i === firstExponent) { + throw new Error('Invalid number ' + s); + } + exponent += exponentValue * exponentSign; + } + if (j === 0 || i !== s.length) { + throw new Error('Invalid number ' + s); + } + const parsed = QuadrupleBuilder.parseDecimal(digits.slice(0, j), exponent); + return new Quadruple( + negative, + parsed.exponent, + parsed.mantHi, + parsed.mantLo + ); + } + static #isDigit(s: string, i: number): boolean { + const cp = s.codePointAt(i); + return cp !== undefined && cp >= 48 && cp <= 57; + } + static #digit(s: string, i: number): number { + return s.codePointAt(i)! - 48; + } + static #bias(exponent: number): number { + return exponent + QuadrupleBuilder.EXPONENT_BIAS; + } +} diff --git a/packages/firestore/src/util/quadruple_builder.ts b/packages/firestore/src/util/quadruple_builder.ts new file mode 100644 index 00000000000..5d5d69bbf84 --- /dev/null +++ b/packages/firestore/src/util/quadruple_builder.ts @@ -0,0 +1,1067 @@ +/** + * Copyright 2021 M.Vokhmentsev + * + * @license + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint-disable */ + +/** + * @private + * @internal + */ +export class QuadrupleBuilder { + static parseDecimal(digits: number[], exp10: number): QuadrupleBuilder { + let q = new QuadrupleBuilder(); + q.parse(digits, exp10); + return q; + } + // The fields containing the value of the instance + exponent: number = 0; + mantHi: bigint = 0n; + mantLo: bigint = 0n; + // 2^192 = 6.277e57, so the 58-th digit after point may affect the result + static MAX_MANTISSA_LENGTH = 59; + // Max value of the decimal exponent, corresponds to EXPONENT_OF_MAX_VALUE + static MAX_EXP10 = 646456993; + // Min value of the decimal exponent, corresponds to EXPONENT_OF_MIN_NORMAL + static MIN_EXP10 = -646457032; + // (2^63) / 10 =~ 9.223372e17 + static TWO_POW_63_DIV_10 = 922337203685477580.0; + // Just for convenience: 0x8000_0000_0000_0000L + static HIGH_BIT = 0x8000_0000_0000_0000n; + // Just for convenience: 0x8000_0000L, 2^31 + static POW_2_31 = 2147483648.0; + // Just for convenience: 0x0000_0000_FFFF_FFFFL + static LOWER_32_BITS = 0x0000_0000_ffff_ffffn; + // Just for convenience: 0xFFFF_FFFF_0000_0000L; + static HIGHER_32_BITS = 0xffff_ffff_0000_0000n; + // Approximate value of log2(10) + static LOG2_10 = Math.log(10) / Math.log(2); + // Approximate value of log2(e) + static LOG2_E = 1 / Math.log(2.0); + // The value of the exponent (biased) corresponding to {@code 1.0 == 2^0}; equals to 2_147_483_647 + // ({@code 0x7FFF_FFFF}). + static EXPONENT_BIAS = 0x7fff_ffff; + // The value of the exponent (biased), corresponding to {@code Infinity}, {@code _Infinty}, and + // {@code NaN} + static EXPONENT_OF_INFINITY = 0xffff_ffffn; + // An array of positive powers of two, each value consists of 4 longs: decimal exponent and 3 x 64 + // bits of mantissa, divided by ten Used to find an arbitrary power of 2 (by powerOfTwo(long exp)) + static POS_POWERS_OF_2: bigint[][] = [ + // 0: 2^0 = 1 = 0.1e1 + [ + 1n, + 0x1999_9999_9999_9999n, + 0x9999_9999_9999_9999n, + 0x9999_9999_9999_999an + ], // 1: 2^(2^0) = 2^1 = 2 = 0.2e1 + [ + 1n, + 0x3333_3333_3333_3333n, + 0x3333_3333_3333_3333n, + 0x3333_3333_3333_3334n + ], // *** + // 2: 2^(2^1) = 2^2 = 4 = 0.4e1 + [ + 1n, + 0x6666_6666_6666_6666n, + 0x6666_6666_6666_6666n, + 0x6666_6666_6666_6667n + ], // *** + // 3: 2^(2^2) = 2^4 = 16 = 0.16e2 + [ + 2n, + 0x28f5_c28f_5c28_f5c2n, + 0x8f5c_28f5_c28f_5c28n, + 0xf5c2_8f5c_28f5_c290n + ], // *** + // 4: 2^(2^3) = 2^8 = 256 = 0.256e3 + [ + 3n, + 0x4189_374b_c6a7_ef9dn, + 0xb22d_0e56_0418_9374n, + 0xbc6a_7ef9_db22_d0e6n + ], // *** + // 5: 2^(2^4) = 2^16 = 65536 = 0.65536e5 + [ + 5n, + 0xa7c5_ac47_1b47_8423n, + 0x0fcf_80dc_3372_1d53n, + 0xcddd_6e04_c059_2104n + ], // 6: 2^(2^5) = 2^32 = 4294967296 = 0.4294967296e10 + [ + 10n, + 0x6df3_7f67_5ef6_eadfn, + 0x5ab9_a207_2d44_268dn, + 0x97df_837e_6748_956en + ], // 7: 2^(2^6) = 2^64 = 18446744073709551616 = 0.18446744073709551616e20 + [ + 20n, + 0x2f39_4219_2484_46ban, + 0xa23d_2ec7_29af_3d61n, + 0x0607_aa01_67dd_94cbn + ], // 8: 2^(2^7) = 2^128 = 340282366920938463463374607431768211456 = + // 0.340282366920938463463374607431768211456e39 + [ + 39n, + 0x571c_bec5_54b6_0dbbn, + 0xd5f6_4baf_0506_840dn, + 0x451d_b70d_5904_029bn + ], // 9: 2^(2^8) = 2^256 = + // 1.1579208923731619542357098500868790785326998466564056403945758401E+77 = + // 0.11579208923731619542357098500868790785326998466564056403945758401e78 + [ + 78n, + 0x1da4_8ce4_68e7_c702n, + 0x6520_247d_3556_476dn, + 0x1469_caf6_db22_4cfan + ], // *** + // 10: 2^(2^9) = 2^512 = + // 1.3407807929942597099574024998205846127479365820592393377723561444E+154 = + // 0.13407807929942597099574024998205846127479365820592393377723561444e155 + [ + 155n, + 0x2252_f0e5_b397_69dcn, + 0x9ae2_eea3_0ca3_ade0n, + 0xeeaa_3c08_dfe8_4e30n + ], // 11: 2^(2^10) = 2^1024 = + // 1.7976931348623159077293051907890247336179769789423065727343008116E+308 = + // 0.17976931348623159077293051907890247336179769789423065727343008116e309 + [ + 309n, + 0x2e05_5c9a_3f6b_a793n, + 0x1658_3a81_6eb6_0a59n, + 0x22c4_b082_6cf1_ebf7n + ], // 12: 2^(2^11) = 2^2048 = + // 3.2317006071311007300714876688669951960444102669715484032130345428E+616 = + // 0.32317006071311007300714876688669951960444102669715484032130345428e617 + [ + 617n, + 0x52bb_45e9_cf23_f17fn, + 0x7688_c076_06e5_0364n, + 0xb344_79aa_9d44_9a57n + ], // 13: 2^(2^12) = 2^4096 = + // 1.0443888814131525066917527107166243825799642490473837803842334833E+1233 = + // 0.10443888814131525066917527107166243825799642490473837803842334833e1234 + [ + 1234n, + 0x1abc_81c8_ff5f_846cn, + 0x8f5e_3c98_53e3_8c97n, + 0x4506_0097_f3bf_9296n + ], // 14: 2^(2^13) = 2^8192 = + // 1.0907481356194159294629842447337828624482641619962326924318327862E+2466 = + // 0.10907481356194159294629842447337828624482641619962326924318327862e2467 + [ + 2467n, + 0x1bec_53b5_10da_a7b4n, + 0x4836_9ed7_7dbb_0eb1n, + 0x3b05_587b_2187_b41en + ], // 15: 2^(2^14) = 2^16384 = + // 1.1897314953572317650857593266280071307634446870965102374726748212E+4932 = + // 0.11897314953572317650857593266280071307634446870965102374726748212e4933 + [ + 4933n, + 0x1e75_063a_5ba9_1326n, + 0x8abf_b8e4_6001_6ae3n, + 0x2800_8702_d29e_8a3cn + ], // 16: 2^(2^15) = 2^32768 = + // 1.4154610310449547890015530277449516013481307114723881672343857483E+9864 = + // 0.14154610310449547890015530277449516013481307114723881672343857483e9865 + [ + 9865n, + 0x243c_5d8b_b5c5_fa55n, + 0x40c6_d248_c588_1915n, + 0x4c0f_d99f_d5be_fc22n + ], // 17: 2^(2^16) = 2^65536 = + // 2.0035299304068464649790723515602557504478254755697514192650169737E+19728 = + // 0.20035299304068464649790723515602557504478254755697514192650169737e19729 + [ + 19729n, + 0x334a_5570_c3f4_ef3cn, + 0xa13c_36c4_3f97_9c90n, + 0xda7a_c473_555f_b7a8n + ], // 18: 2^(2^17) = 2^131072 = + // 4.0141321820360630391660606060388767343771510270414189955825538065E+39456 = + // 0.40141321820360630391660606060388767343771510270414189955825538065e39457 + [ + 39457n, + 0x66c3_0444_5dd9_8f3bn, + 0xa8c2_93a2_0e47_a41bn, + 0x4c5b_03dc_1260_4964n + ], // 19: 2^(2^18) = 2^262144 = + // 1.6113257174857604736195721184520050106440238745496695174763712505E+78913 = + // 0.16113257174857604736195721184520050106440238745496695174763712505e78914 + [ + 78914n, + 0x293f_fbf5_fb02_8cc4n, + 0x89d3_e5ff_4423_8406n, + 0x369a_339e_1bfe_8c9bn + ], // 20: 2^(2^19) = 2^524288 = + // 2.5963705678310007761265964957268828277447343763484560463573654868E+157826 = + // 0.25963705678310007761265964957268828277447343763484560463573654868e157827 + [ + 157827n, + 0x4277_92fb_b68e_5d20n, + 0x7b29_7cd9_fc15_4b62n, + 0xf091_4211_4aa9_a20cn + ], // 21: 2^(2^20) = 2^1048576 = + // 6.7411401254990734022690651047042454376201859485326882846944915676E+315652 = + // 0.67411401254990734022690651047042454376201859485326882846944915676e315653 + [ + 315653n, + 0xac92_bc65_ad5c_08fcn, + 0x00be_eb11_5a56_6c19n, + 0x4ba8_82d8_a462_2437n + ], // 22: 2^(2^21) = 2^2097152 = + // 4.5442970191613663099961595907970650433180103994591456270882095573E+631305 = + // 0.45442970191613663099961595907970650433180103994591456270882095573e631306 + [ + 631306n, + 0x7455_8144_0f92_e80en, + 0x4da8_22cf_7f89_6f41n, + 0x509d_5986_7816_4ecdn + ], // 23: 2^(2^22) = 2^4194304 = + // 2.0650635398358879243991194945816501695274360493029670347841664177E+1262611 = + // 0.20650635398358879243991194945816501695274360493029670347841664177e1262612 + [ + 1262612n, + 0x34dd_99b4_c695_23a5n, + 0x64bc_2e8f_0d8b_1044n, + 0xb03b_1c96_da5d_d349n + ], // 24: 2^(2^23) = 2^8388608 = + // 4.2644874235595278724327289260856157547554200794957122157246170406E+2525222 = + // 0.42644874235595278724327289260856157547554200794957122157246170406e2525223 + [ + 2525223n, + 0x6d2b_bea9_d6d2_5a08n, + 0xa0a4_606a_88e9_6b70n, + 0x1820_63bb_c2fe_8520n + ], // 25: 2^(2^24) = 2^16777216 = + // 1.8185852985697380078927713277749906189248596809789408311078112486E+5050445 = + // 0.18185852985697380078927713277749906189248596809789408311078112486e5050446 + [ + 5050446n, + 0x2e8e_47d6_3bfd_d6e3n, + 0x2b55_fa89_76ea_a3e9n, + 0x1a6b_9d30_8641_2a73n + ], // 26: 2^(2^25) = 2^33554432 = + // 3.3072524881739831340558051919726975471129152081195558970611353362E+10100890 = + // 0.33072524881739831340558051919726975471129152081195558970611353362e10100891 + [ + 10100891n, + 0x54aa_68ef_a1d7_19dfn, + 0xd850_5806_612c_5c8fn, + 0xad06_8837_fee8_b43an + ], // 27: 2^(2^26) = 2^67108864 = + // 1.0937919020533002449982468634925923461910249420785622990340704603E+20201781 = + // 0.10937919020533002449982468634925923461910249420785622990340704603e20201782 + [ + 20201782n, + 0x1c00_464c_cb7b_ae77n, + 0x9e38_7778_4c77_982cn, + 0xd94a_f3b6_1717_404fn + ], // 28: 2^(2^27) = 2^134217728 = + // 1.1963807249973763567102377630870670302911237824129274789063323723E+40403562 = + // 0.11963807249973763567102377630870670302911237824129274789063323723e40403563 + [ + 40403563n, + 0x1ea0_99c8_be2b_6cd0n, + 0x8bfb_6d53_9fa5_0466n, + 0x6d3b_c37e_69a8_4218n + ], // 29: 2^(2^28) = 2^268435456 = + // 1.4313268391452478724777126233530788980596273340675193575004129517E+80807124 = + // 0.14313268391452478724777126233530788980596273340675193575004129517e80807125 + [ + 80807125n, + 0x24a4_57f4_66ce_8d18n, + 0xf2c8_f3b8_1bc6_bb59n, + 0xa78c_7576_92e0_2d49n + ], // 30: 2^(2^29) = 2^536870912 = + // 2.0486965204575262773910959587280218683219330308711312100181276813E+161614248 = + // 0.20486965204575262773910959587280218683219330308711312100181276813e161614249 + [ + 161614249n, + 0x3472_5667_7aba_6b53n, + 0x3fbf_90d3_0611_a67cn, + 0x1e03_9d87_e0bd_b32bn + ], // 31: 2^(2^30) = 2^1073741824 = + // 4.1971574329347753848087162337676781412761959309467052555732924370E+323228496 = + // 0.41971574329347753848087162337676781412761959309467052555732924370e323228497 + [ + 323228497n, + 0x6b72_7daf_0fd3_432an, + 0x71f7_1121_f9e4_200fn, + 0x8fcd_9942_d486_c10cn + ], // 32: 2^(2^31) = 2^2147483648 = + // 1.7616130516839633532074931497918402856671115581881347960233679023E+646456993 = + // 0.17616130516839633532074931497918402856671115581881347960233679023e646456994 + [ + 646456994n, + 0x2d18_e844_84d9_1f78n, + 0x4079_bfe7_829d_ec6fn, + 0x2155_1643_e365_abc6n + ] + ]; + // An array of negative powers of two, each value consists of 4 longs: decimal exponent and 3 x 64 + // bits of mantissa, divided by ten. Used to find an arbitrary power of 2 (by powerOfTwo(long exp)) + static NEG_POWERS_OF_2: bigint[][] = [ + // v18 + // 0: 2^0 = 1 = 0.1e1 + [ + 1n, + 0x1999_9999_9999_9999n, + 0x9999_9999_9999_9999n, + 0x9999_9999_9999_999an + ], // 1: 2^-(2^0) = 2^-1 = 0.5 = 0.5e0 + [ + 0n, + 0x8000_0000_0000_0000n, + 0x0000_0000_0000_0000n, + 0x0000_0000_0000_0000n + ], // 2: 2^-(2^1) = 2^-2 = 0.25 = 0.25e0 + // {0, 0x4000_0000_0000_0000L, 0x0000_0000_0000_0000L, 0x0000_0000_0000_0000L}, + [ + 0n, + 0x4000_0000_0000_0000n, + 0x0000_0000_0000_0000n, + 0x0000_0000_0000_0001n + ], // *** + // 3: 2^-(2^2) = 2^-4 = 0.0625 = 0.625e-1 + [ + -1n, + 0xa000_0000_0000_0000n, + 0x0000_0000_0000_0000n, + 0x0000_0000_0000_0000n + ], // 4: 2^-(2^3) = 2^-8 = 0.00390625 = 0.390625e-2 + [ + -2n, + 0x6400_0000_0000_0000n, + 0x0000_0000_0000_0000n, + 0x0000_0000_0000_0000n + ], // 5: 2^-(2^4) = 2^-16 = 0.0000152587890625 = 0.152587890625e-4 + [ + -4n, + 0x2710_0000_0000_0000n, + 0x0000_0000_0000_0000n, + 0x0000_0000_0000_0001n + ], // *** + // 6: 2^-(2^5) = 2^-32 = 2.3283064365386962890625E-10 = 0.23283064365386962890625e-9 + [ + -9n, + 0x3b9a_ca00_0000_0000n, + 0x0000_0000_0000_0000n, + 0x0000_0000_0000_0001n + ], // *** + // 7: 2^-(2^6) = 2^-64 = 5.42101086242752217003726400434970855712890625E-20 = + // 0.542101086242752217003726400434970855712890625e-19 + [ + -19n, + 0x8ac7_2304_89e8_0000n, + 0x0000_0000_0000_0000n, + 0x0000_0000_0000_0000n + ], // 8: 2^-(2^7) = 2^-128 = + // 2.9387358770557187699218413430556141945466638919302188037718792657E-39 = + // 0.29387358770557187699218413430556141945466638919302188037718792657e-38 + [ + -38n, + 0x4b3b_4ca8_5a86_c47an, + 0x098a_2240_0000_0000n, + 0x0000_0000_0000_0001n + ], // *** + // 9: 2^-(2^8) = 2^-256 = + // 8.6361685550944446253863518628003995711160003644362813850237034700E-78 = + // 0.86361685550944446253863518628003995711160003644362813850237034700e-77 + [ + -77n, + 0xdd15_fe86_affa_d912n, + 0x49ef_0eb7_13f3_9eben, + 0xaa98_7b6e_6fd2_a002n + ], // 10: 2^-(2^9) = 2^-512 = + // 7.4583407312002067432909653154629338373764715346004068942715183331E-155 = + // 0.74583407312002067432909653154629338373764715346004068942715183331e-154 + [ + -154n, + 0xbeee_fb58_4aff_8603n, + 0xaafb_550f_facf_d8fan, + 0x5ca4_7e4f_88d4_5371n + ], // 11: 2^-(2^10) = 2^-1024 = + // 5.5626846462680034577255817933310101605480399511558295763833185421E-309 = + // 0.55626846462680034577255817933310101605480399511558295763833185421e-308 + [ + -308n, + 0x8e67_9c2f_5e44_ff8fn, + 0x570f_09ea_a7ea_7648n, + 0x5961_db50_c6d2_b888n + ], // *** + // 12: 2^-(2^11) = 2^-2048 = + // 3.0943460473825782754801833699711978538925563038849690459540984582E-617 = + // 0.30943460473825782754801833699711978538925563038849690459540984582e-616 + [ + -616n, + 0x4f37_1b33_99fc_2ab0n, + 0x8170_041c_9feb_05aan, + 0xc7c3_4344_7c75_bcf6n + ], // 13: 2^-(2^12) = 2^-4096 = + // 9.5749774609521853579467310122804202420597417413514981491308464986E-1234 = + // 0.95749774609521853579467310122804202420597417413514981491308464986e-1233 + [ + -1233n, + 0xf51e_9281_7901_3fd3n, + 0xde4b_d12c_de4d_985cn, + 0x4a57_3ca6_f94b_ff14n + ], // 14: 2^-(2^13) = 2^-8192 = + // 9.1680193377742358281070619602424158297818248567928361864131947526E-2467 = + // 0.91680193377742358281070619602424158297818248567928361864131947526e-2466 + [ + -2466n, + 0xeab3_8812_7bcc_aff7n, + 0x1667_6391_42b9_fbaen, + 0x775e_c999_5e10_39fbn + ], // 15: 2^-(2^14) = 2^-16384 = + // 8.4052578577802337656566945433043815064951983621161781002720680748E-4933 = + // 0.84052578577802337656566945433043815064951983621161781002720680748e-4932 + [ + -4932n, + 0xd72c_b2a9_5c7e_f6ccn, + 0xe81b_f1e8_25ba_7515n, + 0xc2fe_b521_d6cb_5dcdn + ], // 16: 2^-(2^15) = 2^-32768 = + // 7.0648359655776364427774021878587184537374439102725065590941425796E-9865 = + // 0.70648359655776364427774021878587184537374439102725065590941425796e-9864 + [ + -9864n, + 0xb4dc_1be6_6045_02dcn, + 0xd491_079b_8eef_6535n, + 0x578d_3965_d24d_e84dn + ], // *** + // 17: 2^-(2^16) = 2^-65536 = + // 4.9911907220519294656590574792132451973746770423207674161425040336E-19729 = + // 0.49911907220519294656590574792132451973746770423207674161425040336e-19728 + [ + -19728n, + 0x7fc6_447b_ee60_ea43n, + 0x2548_da5c_8b12_5b27n, + 0x5f42_d114_2f41_d349n + ], // *** + // 18: 2^-(2^17) = 2^-131072 = + // 2.4911984823897261018394507280431349807329035271689521242878455599E-39457 = + // 0.24911984823897261018394507280431349807329035271689521242878455599e-39456 + [ + -39456n, + 0x3fc6_5180_f88a_f8fbn, + 0x6a69_15f3_8334_9413n, + 0x063c_3708_b6ce_b291n + ], // *** + // 19: 2^-(2^18) = 2^-262144 = + // 6.2060698786608744707483205572846793091942192651991171731773832448E-78914 = + // 0.62060698786608744707483205572846793091942192651991171731773832448e-78913 + [ + -78913n, + 0x9ee0_197c_8dcd_55bfn, + 0x2b2b_9b94_2c38_f4a2n, + 0x0f8b_a634_e9c7_06aen + ], // 20: 2^-(2^19) = 2^-524288 = + // 3.8515303338821801176537443725392116267291403078581314096728076497E-157827 = + // 0.38515303338821801176537443725392116267291403078581314096728076497e-157826 + [ + -157826n, + 0x6299_63a2_5b8b_2d79n, + 0xd00b_9d22_86f7_0876n, + 0xe970_0470_0c36_44fcn + ], // *** + // 21: 2^-(2^20) = 2^-1048576 = + // 1.4834285912814577854404052243709225888043963245995136935174170977E-315653 = + // 0.14834285912814577854404052243709225888043963245995136935174170977e-315652 + [ + -315652n, + 0x25f9_cc30_8cee_f4f3n, + 0x40f1_9543_911a_4546n, + 0xa2cd_3894_52cf_c366n + ], // 22: 2^-(2^21) = 2^-2097152 = + // 2.2005603854312903332428997579002102976620485709683755186430397089E-631306 = + // 0.22005603854312903332428997579002102976620485709683755186430397089e-631305 + [ + -631305n, + 0x3855_97b0_d47e_76b8n, + 0x1b9f_67e1_03bf_2329n, + 0xc311_9848_5959_85f7n + ], // 23: 2^-(2^22) = 2^-4194304 = + // 4.8424660099295090687215589310713586524081268589231053824420510106E-1262612 = + // 0.48424660099295090687215589310713586524081268589231053824420510106e-1262611 + [ + -1262611n, + 0x7bf7_95d2_76c1_2f66n, + 0x66a6_1d62_a446_659an, + 0xa1a4_d73b_ebf0_93d5n + ], // *** + // 24: 2^-(2^23) = 2^-8388608 = + // 2.3449477057322620222546775527242476219043877555386221929831430440E-2525223 = + // 0.23449477057322620222546775527242476219043877555386221929831430440e-2525222 + [ + -2525222n, + 0x3c07_d96a_b1ed_7799n, + 0xcb73_55c2_2cc0_5ac0n, + 0x4ffc_0ab7_3b1f_6a49n + ], // *** + // 25: 2^-(2^24) = 2^-16777216 = + // 5.4987797426189993226257377747879918011694025935111951649826798628E-5050446 = + // 0.54987797426189993226257377747879918011694025935111951649826798628e-5050445 + [ + -5050445n, + 0x8cc4_cd8c_3ede_fb9an, + 0x6c8f_f86a_90a9_7e0cn, + 0x166c_fddb_f98b_71bfn + ], // *** + // 26: 2^-(2^25) = 2^-33554432 = + // 3.0236578657837068435515418409027857523343464783010706819696074665E-10100891 = + // 0.30236578657837068435515418409027857523343464783010706819696074665e-10100890 + [ + -10100890n, + 0x4d67_d81c_c88e_1228n, + 0x1d7c_fb06_666b_79b3n, + 0x7b91_6728_aaa4_e70dn + ], // *** + // 27: 2^-(2^26) = 2^-67108864 = + // 9.1425068893156809483320844568740945600482370635012633596231964471E-20201782 = + // 0.91425068893156809483320844568740945600482370635012633596231964471e-20201781 + [ + -20201781n, + 0xea0c_5549_4e7a_552dn, + 0xb88c_b948_4bb8_6c61n, + 0x8d44_893c_610b_b7dfn + ], // *** + // 28: 2^-(2^27) = 2^-134217728 = + // 8.3585432221184688810803924874542310018191301711943564624682743545E-40403563 = + // 0.83585432221184688810803924874542310018191301711943564624682743545e-40403562 + [ + -40403562n, + 0xd5fa_8c82_1ec0_c24an, + 0xa80e_46e7_64e0_f8b0n, + 0xa727_6bfa_432f_ac7en + ], // 29: 2^-(2^28) = 2^-268435456 = + // 6.9865244796022595809958912202005005328020601847785697028605460277E-80807125 = + // 0.69865244796022595809958912202005005328020601847785697028605460277e-80807124 + [ + -80807124n, + 0xb2da_e307_426f_6791n, + 0xc970_b82f_58b1_2918n, + 0x0472_592f_7f39_190en + ], // 30: 2^-(2^29) = 2^-536870912 = + // 4.8811524304081624052042871019605298977947353140996212667810837790E-161614249 = + // 0.48811524304081624052042871019605298977947353140996212667810837790e-161614248 + // {-161614248, 0x7cf5_1edd_8a15_f1c9L, 0x656d_ab34_98f8_e697L, 0x12da_a2a8_0e53_c809L}, + [ + -161614248n, + 0x7cf5_1edd_8a15_f1c9n, + 0x656d_ab34_98f8_e697n, + 0x12da_a2a8_0e53_c807n + ], // 31: 2^-(2^30) = 2^-1073741824 = + // 2.3825649048879510732161697817326745204151961255592397879550237608E-323228497 = + // 0.23825649048879510732161697817326745204151961255592397879550237608e-323228496 + [ + -323228496n, + 0x3cfe_609a_b588_3c50n, + 0xbec8_b5d2_2b19_8871n, + 0xe184_7770_3b46_22b4n + ], // 32: 2^-(2^31) = 2^-2147483648 = + // 5.6766155260037313438164181629489689531186932477276639365773003794E-646456994 = + // 0.56766155260037313438164181629489689531186932477276639365773003794e-646456993 + [ + -646456993n, + 0x9152_447b_9d7c_da9an, + 0x3b4d_3f61_10d7_7aadn, + 0xfa81_bad1_c394_adb4n + ] + ]; + // Buffers used internally + // The order of words in the arrays is big-endian: the highest part is in buff[0] (in buff[1] for + // buffers of 10 words) + + buffer4x64B: bigint[] = new Array(4).fill(0n); + buffer6x32A: bigint[] = new Array(6).fill(0n); + buffer6x32B: bigint[] = new Array(6).fill(0n); + buffer6x32C: bigint[] = new Array(6).fill(0n); + buffer12x32: bigint[] = new Array(12).fill(0n); + parse(digits: number[], exp10: number): void { + exp10 += digits.length - 1; // digits is viewed as x.yyy below. + this.exponent = 0; + this.mantHi = 0n; + this.mantLo = 0n; + // Finds numeric value of the decimal mantissa + let mantissa: bigint[] = this.buffer6x32C; + let exp10Corr: number = this.parseMantissa(digits, mantissa); + if (exp10Corr == 0 && this.isEmpty(mantissa)) { + // Mantissa == 0 + return; + } + // takes account of the point position in the mant string and possible carry as a result of + // round-up (like 9.99e1 -> 1.0e2) + exp10 += exp10Corr; + if (exp10 < QuadrupleBuilder.MIN_EXP10) { + return; + } + if (exp10 > QuadrupleBuilder.MAX_EXP10) { + this.exponent = Number(QuadrupleBuilder.EXPONENT_OF_INFINITY); + return; + } + let exp2: number = this.findBinaryExponent(exp10, mantissa); + // Finds binary mantissa and possible exponent correction. Fills the fields. + this.findBinaryMantissa(exp10, exp2, mantissa); + } + parseMantissa(digits: number[], mantissa: bigint[]): number { + for (let i = 0; i < 6; i++) { + mantissa[i] = 0n; + } + // Skip leading zeroes + let firstDigit: number = 0; + while (firstDigit < digits.length && digits[firstDigit] == 0) { + firstDigit += 1; + } + if (firstDigit == digits.length) { + return 0; // All zeroes + } + let expCorr: number = -firstDigit; + // Limit the string length to avoid unnecessary fuss + if (digits.length - firstDigit > QuadrupleBuilder.MAX_MANTISSA_LENGTH) { + let carry: boolean = digits[QuadrupleBuilder.MAX_MANTISSA_LENGTH] >= 5; // The highest digit to be truncated + let truncated: number[] = new Array( + QuadrupleBuilder.MAX_MANTISSA_LENGTH + ).fill(0); + for (let i = 0; i < QuadrupleBuilder.MAX_MANTISSA_LENGTH; i++) { + truncated[i] = digits[i + firstDigit]; + } + if (carry) { + // Round-up: add carry + expCorr += this.addCarry(truncated); // May add an extra digit in front of it (99..99 -> 100) + } + digits = truncated; + firstDigit = 0; + } + for (let i = digits.length - 1; i >= firstDigit; i--) { + // digits, starting from the last + mantissa[0] |= BigInt(digits[i]) << 32n; + this.divBuffBy10(mantissa); + } + return expCorr; + } + // Divides the unpacked value stored in the given buffer by 10 + // @param buffer contains the unpacked value to divide (32 least significant bits are used) + divBuffBy10(buffer: bigint[]): void { + let maxIdx: number = buffer.length; + // big/endian + for (let i = 0; i < maxIdx; i++) { + let r: bigint = buffer[i] % 10n; + buffer[i] = buffer[i] / 10n; + if (i + 1 < maxIdx) { + buffer[i + 1] += r << 32n; + } + } + } + // Checks if the buffer is empty (contains nothing but zeros) + // @param buffer the buffer to check + // @return {@code true} if the buffer is empty, {@code false} otherwise + isEmpty(buffer: bigint[]): boolean { + for (let i = 0; i < buffer.length; i++) { + if (buffer[i] != 0n) { + return false; + } + } + return true; + } + // Adds one to a decimal number represented as a sequence of decimal digits. propagates carry as + // needed, so that {@code addCarryTo("6789") = "6790", addCarryTo("9999") = "10000"} etc. + // @return 1 if an additional higher "1" was added in front of the number as a result of + // rounding-up, 0 otherwise + addCarry(digits: number[]): number { + for (let i = digits.length - 1; i >= 0; i--) { + // starting with the lowest digit + let c: number = digits[i]; + if (c == 9) { + digits[i] = 0; + } else { + digits[i] = digits[i] + 1; + return 0; + } + } + digits[0] = 1; + return 1; + } + // Finds binary exponent, using decimal exponent and mantissa.
    + // exp2 = exp10 * log2(10) + log2(mant)
    + // @param exp10 decimal exponent + // @param mantissa array of longs containing decimal mantissa (divided by 10) + // @return found value of binary exponent + findBinaryExponent(exp10: number, mantissa: bigint[]): number { + let mant10: bigint = (mantissa[0] << 31n) | (mantissa[1] >> 1n); // Higher 63 bits of the mantissa, in range + // 0x0CC..CCC -- 0x7FF..FFF (2^63/10 -- 2^63-1) + // decimal value of the mantissa in range 1.0..9.9999... + let mant10d: number = Number(mant10) / QuadrupleBuilder.TWO_POW_63_DIV_10; + return Math.floor( + Number(exp10) * QuadrupleBuilder.LOG2_10 + this.log2(mant10d) + ); // Binary exponent + } + // Calculates log2 of the given x + // @param x argument that can't be 0 + // @return the value of log2(x) + log2(x: number): number { + // x can't be 0 + return QuadrupleBuilder.LOG2_E * Math.log(x); + } + findBinaryMantissa(exp10: number, exp2: number, mantissa: bigint[]): void { + // pow(2, -exp2): division by 2^exp2 is multiplication by 2^(-exp2) actually + let powerOf2: bigint[] = this.buffer4x64B; + this.powerOfTwo(-exp2, powerOf2); + let product: bigint[] = this.buffer12x32; // use it for the product (M * 10^E / 2^e) + this.multUnpacked6x32byPacked(mantissa, powerOf2, product); // product in buff_12x32 + this.multBuffBy10(product); // "Quasidecimals" are numbers divided by 10 + // The powerOf2[0] is stored as an unsigned value + if (BigInt(powerOf2[0]) != BigInt(-exp10)) { + // For some combinations of exp2 and exp10, additional multiplication needed + // (see mant2_from_M_E_e.xls) + this.multBuffBy10(product); + } + // compensate possible inaccuracy of logarithms used to compute exp2 + exp2 += this.normalizeMant(product); + exp2 += QuadrupleBuilder.EXPONENT_BIAS; // add bias + // For subnormal values, exp2 <= 0. We just return 0 for them, as they are + // far from any range we are interested in. + if (exp2 <= 0) { + return; + } + exp2 += this.roundUp(product); // round up, may require exponent correction + if (BigInt(exp2) >= QuadrupleBuilder.EXPONENT_OF_INFINITY) { + this.exponent = Number(QuadrupleBuilder.EXPONENT_OF_INFINITY); + } else { + this.exponent = Number(exp2); + this.mantHi = ((product[0] << 32n) + product[1]) & 0xffffffffffffffffn; + this.mantLo = ((product[2] << 32n) + product[3]) & 0xffffffffffffffffn; + } + } + // Calculates the required power and returns the result in the quasidecimal format (an array of + // longs, where result[0] is the decimal exponent of the resulting value, and result[1] -- + // result[3] contain 192 bits of the mantissa divided by ten (so that 8 looks like + //
    {@code {1, 0xCCCC_.._CCCCL, 0xCCCC_.._CCCCL, 0xCCCC_.._CCCDL}}}
    + // uses arrays buffer4x64B, buffer6x32A, buffer6x32B, buffer12x32, + // @param exp the power to raise 2 to + // @param power (result) the value of {@code2^exp} + powerOfTwo(exp: number, power: bigint[]): void { + if (exp == 0) { + this.array_copy(QuadrupleBuilder.POS_POWERS_OF_2[0], power); + return; + } + // positive powers of 2 (2^0, 2^1, 2^2, 2^4, 2^8 ... 2^(2^31) ) + let powers: bigint[][] = QuadrupleBuilder.POS_POWERS_OF_2; + if (exp < 0) { + exp = -exp; + powers = QuadrupleBuilder.NEG_POWERS_OF_2; // positive powers of 2 (2^0, 2^-1, 2^-2, 2^-4, 2^-8 ... 2^30) + } + // 2^31 = 0x8000_0000L; a single bit that will be shifted right at every iteration + let currPowOf2: number = QuadrupleBuilder.POW_2_31; + let idx: number = 32; // Index in the table of powers + let first_power: boolean = true; + // if exp = b31 * 2^31 + b30 * 2^30 + .. + b0 * 2^0, where b0..b31 are the values of the bits in + // exp, then 2^exp = 2^b31 * 2^b30 ... * 2^b0. Find the product, using a table of powers of 2. + while (exp > 0) { + if (exp >= currPowOf2) { + // the current bit in the exponent is 1 + if (first_power) { + // 4 longs, power[0] -- decimal (?) exponent, power[1..3] -- 192 bits of mantissa + this.array_copy(powers[idx], power); + first_power = false; + } else { + // Multiply by the corresponding power of 2 + this.multPacked3x64_AndAdjustExponent(power, powers[idx], power); + } + exp -= currPowOf2; + } + idx -= 1; + currPowOf2 = currPowOf2 * 0.5; // Note: this is exact + } + } + // Copies from into to. + array_copy(source: bigint[], dest: bigint[]): void { + for (let i = 0; i < dest.length; i++) { + dest[i] = source[i]; + } + } + // Multiplies two quasidecimal numbers contained in buffers of 3 x 64 bits with exponents, puts + // the product to buffer4x64B
    + // and returns it. Both each of the buffers and the product contain 4 longs - exponent and 3 x 64 + // bits of mantissa. If the higher word of mantissa of the product is less than + // 0x1999_9999_9999_9999L (i.e. mantissa is less than 0.1) multiplies mantissa by 10 and adjusts + // the exponent respectively. + multPacked3x64_AndAdjustExponent( + factor1: bigint[], + factor2: bigint[], + result: bigint[] + ): void { + this.multPacked3x64_simply(factor1, factor2, this.buffer12x32); + let expCorr: number = this.correctPossibleUnderflow(this.buffer12x32); + this.pack_6x32_to_3x64(this.buffer12x32, result); + // result[0] is a signed int64 value stored in an uint64 + result[0] = factor1[0] + factor2[0] + BigInt(expCorr); // product.exp = f1.exp + f2.exp + } + // Multiplies mantissas of two packed quasidecimal values (each is an array of 4 longs, exponent + + // 3 x 64 bits of mantissa) Returns the product as unpacked buffer of 12 x 32 (12 x 32 bits of + // product) + // uses arrays buffer6x32A, buffer6x32B + // @param factor1 an array of longs containing factor 1 as packed quasidecimal + // @param factor2 an array of longs containing factor 2 as packed quasidecimal + // @param result an array of 12 longs filled with the product of mantissas + multPacked3x64_simply( + factor1: bigint[], + factor2: bigint[], + result: bigint[] + ): void { + for (let i = 0; i < result.length; i++) { + result[i] = 0n; + } + // TODO2 19.01.16 21:23:06 for the next version -- rebuild the table of powers to make the + // numbers unpacked, to avoid packing/unpacking + this.unpack_3x64_to_6x32(factor1, this.buffer6x32A); + this.unpack_3x64_to_6x32(factor2, this.buffer6x32B); + for (let i = 6 - 1; i >= 0; i--) { + // compute partial 32-bit products + for (let j = 6 - 1; j >= 0; j--) { + let part: bigint = this.buffer6x32A[i] * this.buffer6x32B[j]; + result[j + i + 1] = + (result[j + i + 1] + (part & QuadrupleBuilder.LOWER_32_BITS)) & + 0xffffffffffffffffn; + result[j + i] = (result[j + i] + (part >> 32n)) & 0xffffffffffffffffn; + } + } + // Carry higher bits of the product to the lower bits of the next word + for (let i = 12 - 1; i >= 1; i--) { + result[i - 1] = + (result[i - 1] + (result[i] >> 32n)) & 0xffffffffffffffffn; + result[i] &= QuadrupleBuilder.LOWER_32_BITS; + } + } + // Corrects possible underflow of the decimal mantissa, passed in in the {@code mantissa}, by + // multiplying it by a power of ten. The corresponding value to adjust the decimal exponent is + // returned as the result + // @param mantissa a buffer containing the mantissa to be corrected + // @return a corrective (addition) that is needed to adjust the decimal exponent of the number + correctPossibleUnderflow(mantissa: bigint[]): number { + let expCorr: number = 0; + while (this.isLessThanOne(mantissa)) { + // Underflow + this.multBuffBy10(mantissa); + expCorr -= 1; + } + return expCorr; + } + // Checks if the unpacked quasidecimal value held in the given buffer is less than one (in this + // format, one is represented as { 0x1999_9999L, 0x9999_9999L, 0x9999_9999L,...} + // @param buffer a buffer containing the value to check + // @return {@code true}, if the value is less than one + isLessThanOne(buffer: bigint[]): boolean { + if (buffer[0] < 0x1999_9999n) { + return true; + } + if (buffer[0] > 0x1999_9999n) { + return false; + } + // A note regarding the coverage: + // Multiplying a 128-bit number by another 192-bit number, + // as well as multiplying of two 192-bit numbers, + // can never produce 320 (or 384 bits, respectively) of 0x1999_9999L, 0x9999_9999L, + for (let i = 1; i < buffer.length; i++) { + // so this loop can't be covered entirely + if (buffer[i] < 0x9999_9999n) { + return true; + } + if (buffer[i] > 0x9999_9999n) { + return false; + } + } + // and it can never reach this point in real life. + return false; // Still Java requires the return statement here. + } + // Multiplies unpacked 192-bit value by a packed 192-bit factor
    + // uses static arrays buffer6x32B + // @param factor1 a buffer containing unpacked quasidecimal mantissa (6 x 32 bits) + // @param factor2 an array of 4 longs containing packed quasidecimal power of two + // @param product a buffer of at least 12 longs to hold the product + multUnpacked6x32byPacked( + factor1: bigint[], + factor2: bigint[], + product: bigint[] + ): void { + for (let i = 0; i < product.length; i++) { + product[i] = 0n; + } + let unpacked2: bigint[] = this.buffer6x32B; + this.unpack_3x64_to_6x32(factor2, unpacked2); // It's the powerOf2, with exponent in 0'th word + let maxFactIdx: number = factor1.length; + for (let i = maxFactIdx - 1; i >= 0; i--) { + // compute partial 32-bit products + for (let j = maxFactIdx - 1; j >= 0; j--) { + let part: bigint = factor1[i] * unpacked2[j]; + product[j + i + 1] = + (product[j + i + 1] + (part & QuadrupleBuilder.LOWER_32_BITS)) & + 0xffffffffffffffffn; + product[j + i] = (product[j + i] + (part >> 32n)) & 0xffffffffffffffffn; + } + } + // Carry higher bits of the product to the lower bits of the next word + for (let i = 12 - 1; i >= 1; i--) { + product[i - 1] = + (product[i - 1] + (product[i] >> 32n)) & 0xffffffffffffffffn; + product[i] &= QuadrupleBuilder.LOWER_32_BITS; + } + } + // Multiplies the unpacked value stored in the given buffer by 10 + // @param buffer contains the unpacked value to multiply (32 least significant bits are used) + multBuffBy10(buffer: bigint[]): void { + let maxIdx: number = buffer.length - 1; + buffer[0] &= QuadrupleBuilder.LOWER_32_BITS; + buffer[maxIdx] *= 10n; + for (let i = maxIdx - 1; i >= 0; i--) { + buffer[i] = + (buffer[i] * 10n + (buffer[i + 1] >> 32n)) & 0xffffffffffffffffn; + buffer[i + 1] &= QuadrupleBuilder.LOWER_32_BITS; + } + } + // Makes sure that the (unpacked) mantissa is normalized, + // i.e. buff[0] contains 1 in bit 32 (the implied integer part) and higher 32 of mantissa in bits 31..0, + // and buff[1]..buff[4] contain other 96 bits of mantissa in their lower halves: + //
    0x0000_0001_XXXX_XXXXL, 0x0000_0000_XXXX_XXXXL...
    + // If necessary, divides the mantissa by appropriate power of 2 to make it normal. + // @param mantissa a buffer containing unpacked mantissa + // @return if the mantissa was not normal initially, a correction that should be added to the result's exponent, or 0 otherwise + normalizeMant(mantissa: bigint[]): number { + let expCorr: number = 31 - QuadrupleBuilder.clz64(mantissa[0]); + if (expCorr != 0) { + this.divBuffByPower2(mantissa, expCorr); + } + return expCorr; + } + // Rounds up the contents of the unpacked buffer to 128 bits by adding unity one bit lower than + // the lowest of these 128 bits. If carry propagates up to bit 33 of buff[0], shifts the buffer + // rightwards to keep it normalized. + // @param mantissa the buffer to get rounded + // @return 1 if the buffer was shifted, 0 otherwise + roundUp(mantissa: bigint[]): number { + // due to the limited precision of the power of 2, a number with exactly half LSB in its + // mantissa + // (i.e that would have 0x8000_0000_0000_0000L in bits 128..191 if it were computed precisely), + // after multiplication by this power of 2, may get erroneous bits 185..191 (counting from the + // MSB), + // taking a value from + // 0xXXXX_XXXX_XXXX_XXXXL 0xXXXX_XXXX_XXXX_XXXXL 0x7FFF_FFFF_FFFF_FFD8L. + // to + // 0xXXXX_XXXX_XXXX_XXXXL 0xXXXX_XXXX_XXXX_XXXXL 0x8000_0000_0000_0014L, or something alike. + // To round it up, we first add + // 0x0000_0000_0000_0000L 0x0000_0000_0000_0000L 0x0000_0000_0000_0028L, to turn it into + // 0xXXXX_XXXX_XXXX_XXXXL 0xXXXX_XXXX_XXXX_XXXXL 0x8000_0000_0000_00XXL, + // and then add + // 0x0000_0000_0000_0000L 0x0000_0000_0000_0000L 0x8000_0000_0000_0000L, to provide carry to + // higher bits. + this.addToBuff(mantissa, 5, 100n); // to compensate possible inaccuracy + this.addToBuff(mantissa, 4, 0x8000_0000n); // round-up, if bits 128..159 >= 0x8000_0000L + if ((mantissa[0] & (QuadrupleBuilder.HIGHER_32_BITS << 1n)) != 0n) { + // carry's got propagated beyond the highest bit + this.divBuffByPower2(mantissa, 1); + return 1; + } + return 0; + } + // converts 192 most significant bits of the mantissa of a number from an unpacked quasidecimal + // form (where 32 least significant bits only used) to a packed quasidecimal form (where buff[0] + // contains the exponent and buff[1]..buff[3] contain 3 x 64 = 192 bits of mantissa) + // @param unpackedMant a buffer of at least 6 longs containing an unpacked value + // @param result a buffer of at least 4 long to hold the packed value + // @return packedQD192 with words 1..3 filled with the packed mantissa. packedQD192[0] is not + // affected. + pack_6x32_to_3x64(unpackedMant: bigint[], result: bigint[]): void { + result[1] = (unpackedMant[0] << 32n) + unpackedMant[1]; + result[2] = (unpackedMant[2] << 32n) + unpackedMant[3]; + result[3] = (unpackedMant[4] << 32n) + unpackedMant[5]; + } + // Unpacks the mantissa of a 192-bit quasidecimal (4 longs: exp10, mantHi, mantMid, mantLo) to a + // buffer of 6 longs, where the least significant 32 bits of each long contains respective 32 bits + // of the mantissa + // @param qd192 array of 4 longs containing the number to unpack + // @param buff_6x32 buffer of 6 long to hold the unpacked mantissa + unpack_3x64_to_6x32(qd192: bigint[], buff_6x32: bigint[]): void { + buff_6x32[0] = qd192[1] >> 32n; + buff_6x32[1] = qd192[1] & QuadrupleBuilder.LOWER_32_BITS; + buff_6x32[2] = qd192[2] >> 32n; + buff_6x32[3] = qd192[2] & QuadrupleBuilder.LOWER_32_BITS; + buff_6x32[4] = qd192[3] >> 32n; + buff_6x32[5] = qd192[3] & QuadrupleBuilder.LOWER_32_BITS; + } + // Divides the contents of the buffer by 2^exp2
    + // (shifts the buffer rightwards by exp2 if the exp2 is positive, and leftwards if it's negative), + // keeping it unpacked (only lower 32 bits of each element are used, except the buff[0] whose + // higher half is intended to contain integer part) + // @param buffer the buffer to divide + // @param exp2 the exponent of the power of two to divide by, expected to be + divBuffByPower2(buffer: bigint[], exp2: number): void { + let maxIdx: number = buffer.length - 1; + let backShift: bigint = BigInt(32 - Math.abs(exp2)); + if (exp2 > 0) { + // Shift to the right + let exp2Shift: bigint = BigInt(exp2); + for (let i = maxIdx + 1 - 1; i >= 1; i--) { + buffer[i] = + (buffer[i] >> exp2Shift) | + ((buffer[i - 1] << backShift) & QuadrupleBuilder.LOWER_32_BITS); + } + buffer[0] = buffer[0] >> exp2Shift; // Preserve the high half of buff[0] + } else if (exp2 < 0) { + // Shift to the left + let exp2Shift: bigint = BigInt(-exp2); + buffer[0] = + ((buffer[0] << exp2Shift) | (buffer[1] >> backShift)) & + 0xffffffffffffffffn; // Preserve the high half of buff[0] + for (let i = 1; i < maxIdx; i++) { + buffer[i] = + (((buffer[i] << exp2Shift) & QuadrupleBuilder.LOWER_32_BITS) | + (buffer[i + 1] >> backShift)) & + 0xffffffffffffffffn; + } + buffer[maxIdx] = + (buffer[maxIdx] << exp2Shift) & QuadrupleBuilder.LOWER_32_BITS; + } + } + // Adds the summand to the idx'th word of the unpacked value stored in the buffer + // and propagates carry as necessary + // @param buff the buffer to add the summand to + // @param idx the index of the element to which the summand is to be added + // @param summand the summand to add to the idx'th element of the buffer + addToBuff(buff: bigint[], idx: number, summand: bigint): void { + let maxIdx: number = idx; + buff[maxIdx] = (buff[maxIdx] + summand) & 0xffffffffffffffffn; // Big-endian, the lowest word + for (let i = maxIdx + 1 - 1; i >= 1; i--) { + // from the lowest word upwards, except the highest + if ((buff[i] & QuadrupleBuilder.HIGHER_32_BITS) != 0n) { + buff[i] &= QuadrupleBuilder.LOWER_32_BITS; + buff[i - 1] += 1n; + } else { + break; + } + } + } + static clz64(x: bigint): number { + let high = Number(x >> 32n); + return high == 0 + ? 32 + Math.clz32(Number(BigInt.asUintN(32, x))) + : Math.clz32(high); + } +} diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index ce5a3d34eae..76475f23b4e 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -77,7 +77,8 @@ import { MaxKey, MinKey, RegexValue, - BsonTimestamp + BsonTimestamp, + Decimal128Value } from '../util/firebase_export'; import { apiDescribe, @@ -2724,6 +2725,7 @@ apiDescribe('Database', persistence => { binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), objectId: new BsonObjectId('507f191e810c19729de860ea'), int32: new Int32Value(1), + decimal128: new Decimal128Value('1.2e3'), min: MinKey.instance(), max: MaxKey.instance(), regex: new RegexValue('^foo', 'i') @@ -2746,6 +2748,9 @@ apiDescribe('Database', persistence => { .isEqual(new BsonObjectId('507f191e810c19729de860ea')) ).to.be.true; expect(snapshot.get('int32').isEqual(new Int32Value(2))).to.be.true; + expect( + snapshot.get('decimal128').isEqual(new Decimal128Value('1.2e3')) + ).to.be.true; expect(snapshot.get('min') === MinKey.instance()).to.be.true; expect(snapshot.get('max') === MaxKey.instance()).to.be.true; expect( @@ -2777,6 +2782,7 @@ apiDescribe('Database', persistence => { binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), objectId: new BsonObjectId('507f191e810c19729de860ea'), int32: new Int32Value(1), + decimal128: new Decimal128Value('1.2e3'), regex: new RegexValue('^foo', 'i'), timestamp: new BsonTimestamp(1, 2), min: MinKey.instance(), @@ -2795,6 +2801,9 @@ apiDescribe('Database', persistence => { .isEqual(new BsonObjectId('507f191e810c19729de860ea')) ).to.be.true; expect(snapshot.get('int32').isEqual(new Int32Value(1))).to.be.true; + expect( + snapshot.get('decimal128').isEqual(new Decimal128Value('1.2e3')) + ).to.be.true; expect(snapshot.get('regex').isEqual(new RegexValue('^foo', 'i'))).to .be.true; expect(snapshot.get('timestamp').isEqual(new BsonTimestamp(1, 2))).to @@ -2879,6 +2888,7 @@ apiDescribe('Database', persistence => { ); let snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ testDocs['c'], testDocs['b'] @@ -2911,6 +2921,126 @@ apiDescribe('Database', persistence => { ); }); + it('can filter and order Decimal128 values', async () => { + const testDocs = { + a: { key: new Decimal128Value('-1.2e3') }, + b: { key: new Decimal128Value('0') }, + c: { key: new Decimal128Value('1.2e3') }, + d: { key: new Decimal128Value('NaN') }, + e: { key: new Decimal128Value('-Infinity') }, + f: { key: new Decimal128Value('Infinity') } + }; + return withTestProjectIdAndCollectionSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + testDocs, + async coll => { + // Populate the cache with all docs first + await getDocs(coll); + + let orderedQuery = query( + coll, + where('key', '>', new Decimal128Value('-1.2e3')), + orderBy('key', 'desc') + ); + + let snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['f'], + testDocs['c'], + testDocs['b'] + ]); + + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + + orderedQuery = query( + coll, + where('key', '!=', new Decimal128Value('0.0')), + orderBy('key', 'desc') + ); + + snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['f'], + testDocs['c'], + testDocs['a'], + testDocs['e'], + testDocs['d'] + ]); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + + orderedQuery = query( + coll, + where('key', '>', new Decimal128Value('-1.2e-3')), + orderBy('key', 'desc') + ); + + snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['f'], + testDocs['c'], + testDocs['b'] + ]); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + + orderedQuery = query( + coll, + where('key', '!=', new Decimal128Value('NaN')) + ); + snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['e'], + testDocs['a'], + testDocs['b'], + testDocs['c'], + testDocs['f'] + ]); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + + orderedQuery = query( + coll, + where('key', 'not-in', [ + new Decimal128Value('1.2e3'), + new Decimal128Value('Infinity'), + new Decimal128Value('NaN') + ]), + orderBy('key', 'desc') + ); + // Note: server is sending NaN incorrectly, but the SDK NotInFilter + // `matches` function gracefully handles it and removes the incorrect + // doc "d". + snapshot = await getDocs(orderedQuery); + expect(toDataArray(snapshot)).to.deep.equal([ + testDocs['b'], + testDocs['a'], + testDocs['e'] + ]); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + } + ); + }); + it('can filter and order Timestamp values', async () => { const testDocs = { a: { key: new BsonTimestamp(1, 1) }, @@ -3303,6 +3433,154 @@ apiDescribe('Database', persistence => { ); }); + it('can filter and order numerical values ', async () => { + const testDocs = { + a: { key: new Decimal128Value('-1.2e3') }, // -1200 + b: { key: new Int32Value(0) }, + c: { key: new Decimal128Value('1') }, + d: { key: new Int32Value(1) }, + e: { key: 1 }, + f: { key: 1.0 }, + g: { key: new Decimal128Value('1.2e-3') }, // 0.0012 + h: { key: new Int32Value(2) }, + i: { key: new Decimal128Value('NaN') }, + j: { key: new Decimal128Value('-Infinity') }, + k: { key: NaN }, + l: { key: Infinity } + }; + + return withTestProjectIdAndCollectionSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + testDocs, + async coll => { + // Pre-populate the cache with all docs + await getDocs(coll); + + let orderedQuery = query(coll, orderBy('key', 'desc')); + let snapshot = await getDocs(orderedQuery); + expect(toIds(snapshot)).to.deep.equal([ + 'l', // Infinity + 'h', // 2 + 'f', // 1.0 + 'e', // 1 + 'd', // 1 + 'c', // 1 + 'g', // 0.0012 + 'b', // 0 + 'a', // -1200 + 'j', // -Infinity + 'k', // NaN + 'i' // NaN + ]); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + + orderedQuery = query( + coll, + orderBy('key', 'desc'), + where('key', '!=', new Decimal128Value('1.0')) + ); + snapshot = await getDocs(orderedQuery); + expect(toIds(snapshot)).to.deep.equal([ + 'l', + 'h', + 'g', + 'b', + 'a', + 'j', + 'k', + 'i' + ]); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + + orderedQuery = query( + coll, + orderBy('key', 'desc'), + where('key', '==', 1) + ); + snapshot = await getDocs(orderedQuery); + expect(toIds(snapshot)).to.deep.equal(['f', 'e', 'd', 'c']); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + } + ); + }); + + it('decimal128 values with no 2s complement representation', async () => { + const testDocs = { + a: { key: new Decimal128Value('-1.1e-3') }, // -0.0011 + b: { key: new Decimal128Value('1.1') }, + c: { key: 1.1 }, + d: { key: 1.0 }, + e: { key: new Decimal128Value('1.1e-3') } // 0.0011 + }; + + return withTestProjectIdAndCollectionSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + testDocs, + async coll => { + // Pre-populate the cache with all docs + await getDocs(coll); + + let orderedQuery = query( + coll, + where('key', '==', new Decimal128Value('1.1')) + ); + let snapshot = await getDocs(orderedQuery); + expect(toIds(snapshot)).to.deep.equal(['b']); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + + orderedQuery = query( + coll, + where('key', '!=', new Decimal128Value('1.1')) + ); + snapshot = await getDocs(orderedQuery); + expect(toIds(snapshot)).to.deep.equal(['a', 'e', 'd', 'c']); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + + orderedQuery = query(coll, where('key', '==', 1.1)); + snapshot = await getDocs(orderedQuery); + expect(toIds(snapshot)).to.deep.equal(['c']); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + + orderedQuery = query(coll, where('key', '!=', 1.1)); + snapshot = await getDocs(orderedQuery); + expect(toIds(snapshot)).to.deep.equal(['a', 'e', 'd', 'b']); + await assertSDKQueryResultsConsistentWithBackend( + orderedQuery, + testDocs, + toIds(snapshot) + ); + } + ); + }); + it('can listen to documents with bson types', async () => { const testDocs = { a: { key: MaxKey.instance() }, @@ -3310,7 +3588,8 @@ apiDescribe('Database', persistence => { c: { key: new BsonTimestamp(1, 2) }, d: { key: new BsonObjectId('507f191e810c19729de860ea') }, e: { key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }, - f: { key: new RegexValue('^foo', 'i') } + f: { key: new RegexValue('^foo', 'i') }, + g: { key: new Decimal128Value('1.2e3') } }; return withTestProjectIdAndCollectionSettings( persistence, @@ -3326,6 +3605,7 @@ apiDescribe('Database', persistence => { let listenSnapshot = await storeEvent.awaitEvent(); expect(toDataArray(listenSnapshot)).to.deep.equal([ testDocs['b'], + testDocs['g'], testDocs['c'], testDocs['e'], testDocs['d'], @@ -3334,11 +3614,12 @@ apiDescribe('Database', persistence => { ]); const newData = { key: new Int32Value(2) }; - await setDoc(doc(coll, 'g'), newData); + await setDoc(doc(coll, 'h'), newData); listenSnapshot = await storeEvent.awaitEvent(); expect(toDataArray(listenSnapshot)).to.deep.equal([ testDocs['b'], newData, + testDocs['g'], testDocs['c'], testDocs['e'], testDocs['d'], @@ -3351,7 +3632,8 @@ apiDescribe('Database', persistence => { ); }); - // TODO(Mila/BSON): Skip the runTransaction tests against nightly when running on browsers. remove when it is supported by prod + // TODO(Mila/BSON): Skip the runTransaction tests against nightly when running on browsers. + // Run this test when BSON type is supported by prod // eslint-disable-next-line no-restricted-properties it.skip('can run transactions on documents with bson types', async () => { const testDocs = { @@ -3398,18 +3680,19 @@ apiDescribe('Database', persistence => { e: { key: new Int32Value(1) }, f: { key: 2.0 }, g: { key: 3 }, - h: { key: new Timestamp(100, 123456000) }, - i: { key: new BsonTimestamp(1, 2) }, - j: { key: 'string' }, - k: { key: Bytes.fromUint8Array(new Uint8Array([0, 1, 255])) }, - l: { key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }, - n: { key: new BsonObjectId('507f191e810c19729de860ea') }, - o: { key: new GeoPoint(0, 0) }, - p: { key: new RegexValue('^foo', 'i') }, - q: { key: [1, 2] }, - r: { key: vector([1, 2]) }, - s: { key: { a: 1 } }, - t: { key: MaxKey.instance() } + h: { key: new Decimal128Value('1.2e3') }, + i: { key: new Timestamp(100, 123456000) }, + j: { key: new BsonTimestamp(1, 2) }, + k: { key: 'string' }, + l: { key: Bytes.fromUint8Array(new Uint8Array([0, 1, 255])) }, + m: { key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }, + o: { key: new BsonObjectId('507f191e810c19729de860ea') }, + p: { key: new GeoPoint(0, 0) }, + q: { key: new RegexValue('^foo', 'i') }, + r: { key: [1, 2] }, + s: { key: vector([1, 2]) }, + t: { key: { a: 1 } }, + u: { key: MaxKey.instance() } }; return withTestProjectIdAndCollectionSettings( @@ -3420,8 +3703,8 @@ apiDescribe('Database', persistence => { async coll => { // TODO(Mila/BSON): remove after prod supports bson, and use `ref` helper function instead const docRef = doc(coll, 'doc'); - await setDoc(doc(coll, 'm'), { key: docRef }); - testDocs['m'] = { key: docRef }; + await setDoc(doc(coll, 'n'), { key: docRef }); + testDocs['n'] = { key: docRef }; const orderedQuery = query(coll, orderBy('key', 'desc')); await assertSDKQueryResultsConsistentWithBackend( @@ -3429,6 +3712,7 @@ apiDescribe('Database', persistence => { orderedQuery, testDocs, [ + 'u', 't', 's', 'r', @@ -3462,9 +3746,12 @@ apiDescribe('Database', persistence => { c: { key: new Int32Value(1) }, d: { key: new Int32Value(-1) }, e: { key: new Int32Value(0) }, - f: { key: new BsonTimestamp(1, 1) }, - g: { key: new BsonTimestamp(2, 1) }, - h: { key: new BsonTimestamp(1, 2) }, + f: { key: new Decimal128Value('-1.2e3') }, + g: { key: new Decimal128Value('0.0') }, + h: { key: new Decimal128Value('1.2e3') }, + t: { key: new BsonTimestamp(1, 1) }, + u: { key: new BsonTimestamp(2, 1) }, + v: { key: new BsonTimestamp(1, 2) }, i: { key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }, j: { key: new BsonBinaryData(1, new Uint8Array([1, 1, 4])) }, k: { key: new BsonBinaryData(2, new Uint8Array([1, 0, 0])) }, @@ -3492,12 +3779,15 @@ apiDescribe('Database', persistence => { [ 'r', 's', + 'f', 'd', 'e', + 'g', 'c', - 'f', 'h', - 'g', + 't', + 'v', + 'u', 'j', 'i', 'k', diff --git a/packages/firestore/test/integration/api/type.test.ts b/packages/firestore/test/integration/api/type.test.ts index 2f7cb7f9295..f47e99b0185 100644 --- a/packages/firestore/test/integration/api/type.test.ts +++ b/packages/firestore/test/integration/api/type.test.ts @@ -25,6 +25,7 @@ import { BsonTimestamp, Bytes, collection, + Decimal128Value, doc, DocumentData, DocumentReference, @@ -305,6 +306,41 @@ apiDescribe('Firestore', persistence => { ); }); + it('can read and write decimal128 fields', () => { + return withTestDbsSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + 1, + async dbs => { + await expectRoundtripWithoutTransaction(dbs[0], { + decimalSciPositive: new Decimal128Value('1.2e3'), + decimalSciNegative: new Decimal128Value('-2.5e-2'), + decimalSciPositiveCapE: new Decimal128Value('1.2345E+5'), + decimalSciNegativeCapE: new Decimal128Value('-9.876E-3'), + decimalIntPositive: new Decimal128Value('12345'), + decimalIntNegative: new Decimal128Value('-67890'), + decimalFloatPositive: new Decimal128Value('123.456'), + decimalFloatNegative: new Decimal128Value('-789.012'), + decimalZeroFloat: new Decimal128Value('0.0'), + decimalZeroInt: new Decimal128Value('0'), + decimalPrecisePositive: new Decimal128Value( + '0.1234567890123456789012345678901234' + ), + decimalLargePositive: new Decimal128Value( + '1234567890123456789012345678901234' + ), + decimalPreciseNegative: new Decimal128Value( + '-0.1234567890123456789012345678901234' + ), + decimalLargeNegative: new Decimal128Value( + '-1234567890123456789012345678901234' + ) + }); + } + ); + }); + it('can read and write bsonTimestamp fields', () => { return withTestDbsSettings( persistence, @@ -359,6 +395,8 @@ apiDescribe('Firestore', persistence => { new BsonBinaryData(1, new Uint8Array([1, 2, 3])), new BsonObjectId('507f191e810c19729de860ea'), new Int32Value(1), + new Decimal128Value('1.2e3'), + new BsonTimestamp(1, 2), MinKey.instance(), MaxKey.instance(), new RegexValue('^foo', 'i') @@ -380,6 +418,8 @@ apiDescribe('Firestore', persistence => { binary: new BsonBinaryData(1, new Uint8Array([1, 2, 3])), objectId: new BsonObjectId('507f191e810c19729de860ea'), int32: new Int32Value(1), + decimal128: new Decimal128Value('1.2e3'), + bsonTimestamp: new BsonTimestamp(1, 2), min: MinKey.instance(), max: MaxKey.instance(), regex: new RegexValue('^foo', 'i') @@ -419,6 +459,39 @@ apiDescribe('Firestore', persistence => { ); }); + it('invalid decimal128 gets rejected', async () => { + return withTestProjectIdAndCollectionSettings( + persistence, + NIGHTLY_PROJECT_ID, + settings, + {}, + async coll => { + const docRef = doc(coll, 'test-doc'); + let errorMessage; + try { + await setDoc(docRef, { key: new Decimal128Value('') }); + } catch (err) { + errorMessage = (err as FirestoreError)?.message; + } + expect(errorMessage).to.contains('Invalid number'); + + try { + await setDoc(docRef, { key: new Decimal128Value('1 23. 4') }); + } catch (err) { + errorMessage = (err as FirestoreError)?.message; + } + expect(errorMessage).to.contains('Invalid number 1 23. 4'); + + try { + await setDoc(docRef, { key: new Decimal128Value('abc') }); + } catch (err) { + errorMessage = (err as FirestoreError)?.message; + } + expect(errorMessage).to.contains('Invalid number abc'); + } + ); + }); + it('invalid BSON timestamp gets rejected', async () => { return withTestProjectIdAndCollectionSettings( persistence, @@ -525,6 +598,7 @@ apiDescribe('Firestore', persistence => { booleanValue: { key: true }, nanValue: { key: NaN }, int32Value: { key: new Int32Value(1) }, + decimal128Value: { key: new Decimal128Value('1.2e3') }, doubleValue: { key: 2.0 }, integerValue: { key: 3 }, timestampValue: { key: new Timestamp(100, 123456000) }, diff --git a/packages/firestore/test/lite/integration.test.ts b/packages/firestore/test/lite/integration.test.ts index 25f372bcb95..df44d5d965b 100644 --- a/packages/firestore/test/lite/integration.test.ts +++ b/packages/firestore/test/lite/integration.test.ts @@ -38,6 +38,7 @@ import { initializeFirestore, terminate } from '../../src/lite-api/database'; +import { Decimal128Value } from '../../src/lite-api/decimal128_value'; import { FieldPath } from '../../src/lite-api/field_path'; import { FieldValue } from '../../src/lite-api/field_value'; import { @@ -2976,6 +2977,7 @@ describe.skip('BSON types', () => { const ref = await addDoc(coll, { objectId: new BsonObjectId('507f191e810c19729de860ea'), int32: new Int32Value(1), + decimal128: new Decimal128Value('1.2e3'), min: MinKey.instance(), max: MaxKey.instance(), regex: new RegexValue('^foo', 'i') @@ -2998,6 +3000,8 @@ describe.skip('BSON types', () => { .isEqual(new BsonObjectId('507f191e810c19729de860ea')) ).to.be.true; expect(snap1.get('int32').isEqual(new Int32Value(2))).to.be.true; + expect(snap1.get('decimal128').isEqual(new Decimal128Value('1.2e3'))).to + .be.true; expect(snap1.get('min') === MinKey.instance()).to.be.true; expect(snap1.get('max') === MaxKey.instance()).to.be.true; expect( diff --git a/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts index 907881c262c..1a1d795d719 100644 --- a/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts +++ b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts @@ -21,6 +21,7 @@ import { IndexByteEncoder } from '../../../src/index/index_byte_encoder'; import { BsonBinaryData } from '../../../src/lite-api/bson_binary_data'; import { BsonObjectId } from '../../../src/lite-api/bson_object_Id'; import { BsonTimestamp } from '../../../src/lite-api/bson_timestamp'; +import { Decimal128Value } from '../../../src/lite-api/decimal128_value'; import { Int32Value } from '../../../src/lite-api/int32_value'; import { RegexValue } from '../../../src/lite-api/regex_value'; import { Timestamp } from '../../../src/lite-api/timestamp'; @@ -31,7 +32,8 @@ import { parseMinKey, parseBsonObjectId, parseRegexValue, - parseBsonTimestamp + parseBsonTimestamp, + parseDecimal128Value } from '../../../src/lite-api/user_data_reader'; import { IndexKind } from '../../../src/model/field_index'; import type { Value } from '../../../src/protos/firestore_proto_api'; @@ -554,6 +556,108 @@ describe('Firestore Index Value Writer', () => { ).to.equal(1); }); + it('can compare BSON Decimal128', () => { + const value1 = { + mapValue: { + fields: { + '__decimal128__': { stringValue: '-1.2e3' } + } + } + }; + const value2 = { + mapValue: { + fields: { + '__decimal128__': { stringValue: '1.2e3' } + } + } + }; + const value3 = parseDecimal128Value(new Decimal128Value('-1.2e3')); + const value4 = parseDecimal128Value(new Decimal128Value('1.2e3')); + + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value1, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value1, value1, IndexKind.ASCENDING) + ).to.equal(0); + + expect( + compareIndexEncodedValues(value3, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value3, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value3, value1, IndexKind.ASCENDING) + ).to.equal(0); + + expect( + compareIndexEncodedValues(value4, value1, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value4, value2, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value4, value3, IndexKind.ASCENDING) + ).to.equal(1); + }); + + it('can compare BSON Decimal128 special cases', () => { + const value1 = { + mapValue: { + fields: { + '__decimal128__': { stringValue: 'NaN' } + } + } + }; + const value2 = { + mapValue: { + fields: { + '__decimal128__': { stringValue: '-Infinity' } + } + } + }; + const value3 = parseDecimal128Value(new Decimal128Value('NaN')); + const value4 = parseDecimal128Value(new Decimal128Value('Infinity')); + + // order should be: NaNs are equal, and less than -Infinity + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value1, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value1, value3, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value4, IndexKind.ASCENDING) + ).to.equal(-1); + + expect( + compareIndexEncodedValues(value2, value2, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value2, value3, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value2, value4, IndexKind.ASCENDING) + ).to.equal(-1); + + expect( + compareIndexEncodedValues(value3, value4, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value4, value3, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value4, value4, IndexKind.ASCENDING) + ).to.equal(0); + }); + it('can compare BSON MinKey', () => { const value1 = { mapValue: { diff --git a/packages/firestore/test/unit/local/index_manager.test.ts b/packages/firestore/test/unit/local/index_manager.test.ts index b6af448b2db..6ca4bd7cdeb 100644 --- a/packages/firestore/test/unit/local/index_manager.test.ts +++ b/packages/firestore/test/unit/local/index_manager.test.ts @@ -22,6 +22,7 @@ import { BsonObjectId, BsonTimestamp, Bytes, + Decimal128Value, GeoPoint, Int32Value, MaxKey, @@ -2153,6 +2154,140 @@ describe('IndexedDbIndexManager', async () => { await verifyResults(q); }); + it('can index Decimal128 fields', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) + ); + await addDoc('coll/doc1', { + key: new Decimal128Value('-1.2e3') + }); + await addDoc('coll/doc2', { + key: new Decimal128Value('0.0') + }); + await addDoc('coll/doc3', { + key: new Decimal128Value('1.2e3') + }); + const fieldIndexes = await indexManager.getFieldIndexes('coll'); + expect(fieldIndexes).to.have.length(1); + + let q = queryWithAddedOrderBy(query('coll'), orderBy('key')); + await verifyResults(q, 'coll/doc1', 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '==', new Decimal128Value('-1200')) + ); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '!=', new Decimal128Value('0')) + ); + await verifyResults(q, 'coll/doc1', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>=', new Decimal128Value('-0')) + ); + await verifyResults(q, 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<=', new Decimal128Value('-0.0')) + ); + await verifyResults(q, 'coll/doc1', 'coll/doc2'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', new Decimal128Value('1.2e-3')) + ); + await verifyResults(q, 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', new Decimal128Value('-1.2e-3')) + ); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', new Decimal128Value('1.2e3')) + ); + await verifyResults(q); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', new Decimal128Value('-1.2e3')) + ); + await verifyResults(q); + }); + + it('indexes Decimal128 fields with precision loss', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) + ); + await addDoc('coll/doc1', { + key: new Decimal128Value('-0.1234567890123456789') // will be rounded to -0.12345678901234568 + }); + await addDoc('coll/doc2', { + key: new Decimal128Value('0') + }); + await addDoc('coll/doc3', { + key: new Decimal128Value('0.1234567890123456789') // will be rounded to 0.12345678901234568 + }); + const fieldIndexes = await indexManager.getFieldIndexes('coll'); + expect(fieldIndexes).to.have.length(1); + + let q = queryWithAddedOrderBy(query('coll'), orderBy('key')); + await verifyResults(q, 'coll/doc1', 'coll/doc2', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '==', new Decimal128Value('0.1234567890123456789')) + ); + await verifyResults(q, 'coll/doc3'); + + // Mismatch behaviour caused by rounding error. Firestore fetches the doc3 from indexDB as + // doc3 rounds to the same number, even though the actual number in doc3 is different + q = queryWithAddedFilter( + query('coll'), + filter('key', '==', new Decimal128Value('0.12345678901234568')) + ); + await verifyResults(q, 'coll/doc3'); + + // Operations that doesn't go up to 17 decimal digits of precision wouldn't be affected by + // this rounding errors. + q = queryWithAddedFilter( + query('coll'), + filter('key', '!=', new Decimal128Value('0.0')) + ); + await verifyResults(q, 'coll/doc1', 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>=', new Decimal128Value('1.23e-1')) + ); + await verifyResults(q, 'coll/doc3'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<=', new Decimal128Value('-1.23e-1')) + ); + await verifyResults(q, 'coll/doc1'); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '>', new Decimal128Value('1.2e3')) + ); + await verifyResults(q); + + q = queryWithAddedFilter( + query('coll'), + filter('key', '<', new Decimal128Value('-1.2e3')) + ); + await verifyResults(q); + }); + it('can index regex fields', async () => { await indexManager.addFieldIndex( fieldIndex('coll', { fields: [['key', IndexKind.ASCENDING]] }) @@ -2372,37 +2507,44 @@ describe('IndexedDbIndexManager', async () => { key: new Int32Value(2) }); await addDoc('coll/doc3', { - key: new Int32Value(1) + key: new Int32Value(-1) }); await addDoc('coll/doc4', { - key: new BsonTimestamp(1, 2) + key: new Decimal128Value('1.2e3') }); await addDoc('coll/doc5', { - key: new BsonTimestamp(1, 1) + key: new Decimal128Value('-0.0') }); await addDoc('coll/doc6', { - key: new BsonBinaryData(1, new Uint8Array([1, 2, 4])) + key: new BsonTimestamp(1, 2) }); await addDoc('coll/doc7', { - key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) + key: new BsonTimestamp(1, 1) }); + await addDoc('coll/doc8', { - key: new BsonObjectId('507f191e810c19729de860eb') + key: new BsonBinaryData(1, new Uint8Array([1, 2, 4])) }); await addDoc('coll/doc9', { + key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) + }); + await addDoc('coll/doc10', { + key: new BsonObjectId('507f191e810c19729de860eb') + }); + await addDoc('coll/doc11', { key: new BsonObjectId('507f191e810c19729de860ea') }); - await addDoc('coll/doc10', { + await addDoc('coll/doc12', { key: new RegexValue('a', 'm') }); - await addDoc('coll/doc11', { + await addDoc('coll/doc13', { key: new RegexValue('a', 'i') }); - await addDoc('coll/doc12', { + await addDoc('coll/doc14', { key: MaxKey.instance() }); @@ -2412,18 +2554,20 @@ describe('IndexedDbIndexManager', async () => { const q = queryWithAddedOrderBy(query('coll'), orderBy('key', 'desc')); await verifyResults( q, - 'coll/doc12', - 'coll/doc10', - 'coll/doc11', - 'coll/doc8', - 'coll/doc9', - 'coll/doc6', - 'coll/doc7', - 'coll/doc4', - 'coll/doc5', - 'coll/doc2', - 'coll/doc3', - 'coll/doc1' + 'coll/doc14', // maxKey + 'coll/doc12', // regex m + 'coll/doc13', // regex i + 'coll/doc10', // objectId eb + 'coll/doc11', // objectId ea + 'coll/doc8', // binary [1,2,4] + 'coll/doc9', // binary [1,2,3] + 'coll/doc6', // timestamp 1,2 + 'coll/doc7', // timestamp 1,1 + 'coll/doc4', // Number decimal128 1200 + 'coll/doc2', // Number int32 2 + 'coll/doc5', // Number decimal128 -0.0 + 'coll/doc3', // Number int32 -1 + 'coll/doc1' // minKey ); }); }); @@ -2454,42 +2598,45 @@ describe('IndexedDbIndexManager', async () => { key: 3 }); await addDoc('coll/doc8', { - key: new Timestamp(100, 123456000) + key: new Decimal128Value('1.2e3') }); await addDoc('coll/doc9', { - key: new BsonTimestamp(1, 2) + key: new Timestamp(100, 123456000) }); await addDoc('coll/doc10', { - key: 'string' + key: new BsonTimestamp(1, 2) }); await addDoc('coll/doc11', { - key: Bytes.fromUint8Array(new Uint8Array([0, 1, 255])) as Bytes + key: 'string' }); await addDoc('coll/doc12', { - key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) + key: Bytes.fromUint8Array(new Uint8Array([0, 1, 255])) as Bytes }); await addDoc('coll/doc13', { - key: ref('coll/doc') + key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }); await addDoc('coll/doc14', { - key: new BsonObjectId('507f191e810c19729de860ea') + key: ref('coll/doc') }); await addDoc('coll/doc15', { - key: new GeoPoint(0, 1) + key: new BsonObjectId('507f191e810c19729de860ea') }); await addDoc('coll/doc16', { - key: new RegexValue('^foo', 'i') + key: new GeoPoint(0, 1) }); await addDoc('coll/doc17', { - key: [1, 2] + key: new RegexValue('^foo', 'i') }); await addDoc('coll/doc18', { - key: vector([1, 2]) + key: [1, 2] }); await addDoc('coll/doc19', { - key: { a: 1 } + key: vector([1, 2]) }); await addDoc('coll/doc20', { + key: { a: 1 } + }); + await addDoc('coll/doc21', { key: MaxKey.instance() }); @@ -2499,6 +2646,7 @@ describe('IndexedDbIndexManager', async () => { const q = queryWithAddedOrderBy(query('coll'), orderBy('key', 'desc')); await verifyResults( q, + 'coll/doc21', 'coll/doc20', 'coll/doc19', 'coll/doc18', diff --git a/packages/firestore/test/unit/local/local_store_indexeddb.test.ts b/packages/firestore/test/unit/local/local_store_indexeddb.test.ts index 5f68684d193..176322e5da3 100644 --- a/packages/firestore/test/unit/local/local_store_indexeddb.test.ts +++ b/packages/firestore/test/unit/local/local_store_indexeddb.test.ts @@ -28,7 +28,8 @@ import { Int32Value, RegexValue, MaxKey, - MinKey + MinKey, + Decimal128Value } from '../../../src'; import { User } from '../../../src/auth/user'; import { BundleConverterImpl } from '../../../src/core/bundle_impl'; @@ -1325,6 +1326,188 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { test.assertQueryReturned(query_, 'coll/a'); }); + it('Indexes BSON Decimal128 fields', async () => { + const index = fieldIndex('coll', { + id: 1, + fields: [['key', IndexKind.ASCENDING]] + }); + await test.configureFieldsIndexes(index); + await test.writeMutations( + setMutation('coll/a', { key: new Decimal128Value('-1.2e3') }), + setMutation('coll/b', { key: new Decimal128Value('0') }), + setMutation('coll/c', { key: new Decimal128Value('1.2e3') }) + ); + await test.backfillIndexes(); + + let query_ = query('coll', orderBy('key', 'asc')); + await test.executeQuery(query_); + test.assertOverlaysRead(3, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b', 'coll/c'); + + query_ = query('coll', filter('key', '==', new Decimal128Value('-1200'))); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/a').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a'); + + query_ = query('coll', filter('key', '!=', new Decimal128Value('0.0'))); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/c'); + + query_ = query('coll', filter('key', '>=', new Decimal128Value('-0'))); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/b', 'coll/c'); + + // This will fail if the negative 0s are not converted to positive 0 in `writeIndexValueAux` + // function + query_ = query('coll', filter('key', '<=', new Decimal128Value('-0.0'))); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query('coll', filter('key', '>', new Decimal128Value('1.2e3'))); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0); + test.assertQueryReturned(query_); + + query_ = query('coll', filter('key', '<', new Decimal128Value('-1.2e3'))); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0); + test.assertQueryReturned(query_); + + query_ = query( + 'coll', + filter('key', 'in', [ + new Decimal128Value('-1.2e3'), + new Decimal128Value('0') + ]) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b'); + + query_ = query( + 'coll', + filter('key', 'not-in', [ + new Decimal128Value('-1200'), + new Decimal128Value('0.0') + ]) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/c'); + }); + + it('Indexes BSON Decimal128 fields with precision loss', async () => { + const index = fieldIndex('coll', { + id: 1, + fields: [['key', IndexKind.ASCENDING]] + }); + await test.configureFieldsIndexes(index); + await test.writeMutations( + setMutation('coll/a', { + key: new Decimal128Value('-0.1234567890123456789') + }), // will be rounded to -0.12345678901234568 + setMutation('coll/b', { key: new Decimal128Value('0') }), + setMutation('coll/c', { + key: new Decimal128Value('0.1234567890123456789') + }) // will be rounded to 0.12345678901234568 + ); + await test.backfillIndexes(); + + let query_ = query('coll', orderBy('key', 'asc')); + await test.executeQuery(query_); + test.assertOverlaysRead(3, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/b').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/b', 'coll/c'); + + query_ = query( + 'coll', + filter('key', '==', new Decimal128Value('0.1234567890123456789')) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/c'); + + // Mismatch behavior caused by rounding error. Firestore fetches the doc3 from IndexedDb as + // doc3 rounds to the same number, but, it is not presented on the final query result. + query_ = query( + 'coll', + filter('key', '==', new Decimal128Value('0.12345678901234568')) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_); + + // Operations that doesn't go up to 17 decimal digits of precision wouldn't be affected by + // this rounding errors. + query_ = query('coll', filter('key', '!=', new Decimal128Value('0.0'))); + await test.executeQuery(query_); + test.assertOverlaysRead(2, 0, { + [key('coll/a').toString()]: MutationType.Set, + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a', 'coll/c'); + + query_ = query( + 'coll', + filter('key', '>=', new Decimal128Value('1.23e-1')) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/c').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/c'); + + query_ = query( + 'coll', + filter('key', '<=', new Decimal128Value('-1.23e-1')) + ); + await test.executeQuery(query_); + test.assertOverlaysRead(1, 0, { + [key('coll/a').toString()]: MutationType.Set + }); + test.assertQueryReturned(query_, 'coll/a'); + + query_ = query('coll', filter('key', '>', new Decimal128Value('1.2e3'))); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0); + test.assertQueryReturned(query_); + + query_ = query('coll', filter('key', '<', new Decimal128Value('-1.2e3'))); + await test.executeQuery(query_); + test.assertOverlaysRead(0, 0); + test.assertQueryReturned(query_); + }); + it('Indexes BSON Regex fields', async () => { const index = fieldIndex('coll', { id: 1, @@ -1576,30 +1759,32 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { await test.writeMutations( setMutation('coll/a', { key: MinKey.instance() }), setMutation('coll/b', { key: new Int32Value(2) }), - setMutation('coll/c', { key: new Int32Value(1) }), - setMutation('coll/d', { key: new BsonTimestamp(1000, 1001) }), - setMutation('coll/e', { key: new BsonTimestamp(1000, 1000) }), - setMutation('coll/f', { + setMutation('coll/c', { key: new Int32Value(-1) }), + setMutation('coll/d', { key: new Decimal128Value('1.2e3') }), + setMutation('coll/e', { key: new Decimal128Value('-0') }), + setMutation('coll/f', { key: new BsonTimestamp(1000, 1001) }), + setMutation('coll/g', { key: new BsonTimestamp(1000, 1000) }), + setMutation('coll/h', { key: new BsonBinaryData(1, new Uint8Array([1, 2, 4])) }), - setMutation('coll/g', { + setMutation('coll/i', { key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }), - setMutation('coll/h', { + setMutation('coll/j', { key: new BsonObjectId('507f191e810c19729de860eb') }), - setMutation('coll/i', { + setMutation('coll/k', { key: new BsonObjectId('507f191e810c19729de860ea') }), - setMutation('coll/j', { key: new RegexValue('^bar', 'm') }), - setMutation('coll/k', { key: new RegexValue('^bar', 'i') }), - setMutation('coll/l', { key: MaxKey.instance() }) + setMutation('coll/l', { key: new RegexValue('^bar', 'm') }), + setMutation('coll/m', { key: new RegexValue('^bar', 'i') }), + setMutation('coll/n', { key: MaxKey.instance() }) ); await test.backfillIndexes(); const query_ = query('coll', orderBy('key', 'desc')); await test.executeQuery(query_); - test.assertOverlaysRead(12, 0, { + test.assertOverlaysRead(14, 0, { [key('coll/a').toString()]: MutationType.Set, [key('coll/b').toString()]: MutationType.Set, [key('coll/c').toString()]: MutationType.Set, @@ -1611,22 +1796,26 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { [key('coll/i').toString()]: MutationType.Set, [key('coll/j').toString()]: MutationType.Set, [key('coll/k').toString()]: MutationType.Set, - [key('coll/l').toString()]: MutationType.Set + [key('coll/l').toString()]: MutationType.Set, + [key('coll/m').toString()]: MutationType.Set, + [key('coll/n').toString()]: MutationType.Set }); test.assertQueryReturned( query_, - 'coll/l', - 'coll/j', - 'coll/k', - 'coll/h', - 'coll/i', - 'coll/f', - 'coll/g', - 'coll/d', - 'coll/e', - 'coll/b', - 'coll/c', - 'coll/a' + 'coll/n', // maxKey + 'coll/l', // regex m + 'coll/m', // regex i + 'coll/j', // objectId eb + 'coll/k', // objectId ea + 'coll/h', // binary [1,2,4] + 'coll/i', // binary [1,2,3] + 'coll/f', // timestamp 1000,1001 + 'coll/g', // timestamp 1000,1000 + 'coll/d', // Number decimal128 1200 + 'coll/b', // Number int32 2 + 'coll/e', // Number decimal128 -0.0 + 'coll/c', // Number int32 -1 + 'coll/a' // minKey ); }); @@ -1645,29 +1834,30 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { setMutation('coll/e', { key: new Int32Value(1) }), setMutation('coll/f', { key: 2.0 }), setMutation('coll/g', { key: 3 }), - setMutation('coll/h', { key: new Timestamp(100, 123456000) }), - setMutation('coll/i', { key: new BsonTimestamp(1, 2) }), - setMutation('coll/j', { key: 'string' }), - setMutation('coll/k', { key: blob(1, 2, 3) }), - setMutation('coll/l', { + setMutation('coll/h', { key: new Decimal128Value('1.2e3') }), + setMutation('coll/i', { key: new Timestamp(100, 123456000) }), + setMutation('coll/j', { key: new BsonTimestamp(1, 2) }), + setMutation('coll/k', { key: 'string' }), + setMutation('coll/l', { key: blob(1, 2, 3) }), + setMutation('coll/m', { key: new BsonBinaryData(1, new Uint8Array([1, 2, 3])) }), - setMutation('coll/m', { key: ref('foo/bar') }), - setMutation('coll/n', { + setMutation('coll/n', { key: ref('foo/bar') }), + setMutation('coll/o', { key: new BsonObjectId('507f191e810c19729de860ea') }), - setMutation('coll/o', { key: new GeoPoint(1, 2) }), - setMutation('coll/p', { key: new RegexValue('^bar', 'm') }), - setMutation('coll/q', { key: [2, 'foo'] }), - setMutation('coll/r', { key: vector([1, 2, 3]) }), - setMutation('coll/s', { key: { bar: 1, foo: 2 } }), - setMutation('coll/t', { key: MaxKey.instance() }) + setMutation('coll/p', { key: new GeoPoint(1, 2) }), + setMutation('coll/q', { key: new RegexValue('^bar', 'm') }), + setMutation('coll/r', { key: [2, 'foo'] }), + setMutation('coll/s', { key: vector([1, 2, 3]) }), + setMutation('coll/t', { key: { bar: 1, foo: 2 } }), + setMutation('coll/u', { key: MaxKey.instance() }) ); await test.backfillIndexes(); const query_ = query('coll', orderBy('key', 'asc')); await test.executeQuery(query_); - test.assertOverlaysRead(20, 0, { + test.assertOverlaysRead(21, 0, { [key('coll/a').toString()]: MutationType.Set, [key('coll/b').toString()]: MutationType.Set, [key('coll/c').toString()]: MutationType.Set, @@ -1687,7 +1877,8 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { [key('coll/q').toString()]: MutationType.Set, [key('coll/r').toString()]: MutationType.Set, [key('coll/s').toString()]: MutationType.Set, - [key('coll/t').toString()]: MutationType.Set + [key('coll/t').toString()]: MutationType.Set, + [key('coll/u').toString()]: MutationType.Set }); test.assertQueryReturned( query_, @@ -1710,7 +1901,8 @@ describe('LocalStore w/ IndexedDB Persistence (Non generic)', () => { 'coll/q', 'coll/r', 'coll/s', - 'coll/t' + 'coll/t', + 'coll/u' ); }); }); diff --git a/packages/firestore/test/unit/model/document.test.ts b/packages/firestore/test/unit/model/document.test.ts index f67e9d971a0..472176633fc 100644 --- a/packages/firestore/test/unit/model/document.test.ts +++ b/packages/firestore/test/unit/model/document.test.ts @@ -21,6 +21,7 @@ import { BsonBinaryData, BsonObjectId, BsonTimestamp, + Decimal128Value, Int32Value, MaxKey, MinKey, @@ -61,7 +62,8 @@ describe('Document', () => { min: MinKey.instance(), max: MaxKey.instance(), regex: new RegexValue('a', 'b'), - int32: new Int32Value(1) + int32: new Int32Value(1), + decimal128: new Decimal128Value('1.2e3') }; const document = doc('rooms/Eros', 1, data); @@ -74,7 +76,8 @@ describe('Document', () => { min: MinKey.instance(), max: MaxKey.instance(), regex: new RegexValue('a', 'b'), - int32: new Int32Value(1) + int32: new Int32Value(1), + decimal128: new Decimal128Value('1.2e3') }) ); expect(value).not.to.equal(data); diff --git a/packages/firestore/test/unit/model/object_value.test.ts b/packages/firestore/test/unit/model/object_value.test.ts index 40b18893e68..b2df554490f 100644 --- a/packages/firestore/test/unit/model/object_value.test.ts +++ b/packages/firestore/test/unit/model/object_value.test.ts @@ -24,7 +24,8 @@ import { RegexValue, Int32Value, MaxKey, - MinKey + MinKey, + Decimal128Value } from '../../../src'; import { vector } from '../../../src/lite-api/field_value_impl'; import { extractFieldMask, ObjectValue } from '../../../src/model/object_value'; @@ -44,7 +45,8 @@ describe('ObjectValue', () => { min: MinKey.instance(), max: MaxKey.instance(), regex: new RegexValue('a', 'b'), - int32: new Int32Value(1) + int32: new Int32Value(1), + decimal128: new Decimal128Value('1.2e3') } }); @@ -84,6 +86,9 @@ describe('ObjectValue', () => { expect(typeOrder(objValue.field(field('bson.int32'))!)).to.equal( TypeOrder.NumberValue ); + expect(typeOrder(objValue.field(field('bson.decimal128'))!)).to.equal( + TypeOrder.NumberValue + ); expect(objValue.field(field('foo.a.b'))).to.be.null; expect(objValue.field(field('bar'))).to.be.null; @@ -108,7 +113,8 @@ describe('ObjectValue', () => { min: MinKey.instance(), max: MaxKey.instance(), regex: new RegexValue('a', 'b'), - int32: new Int32Value(1) + int32: new Int32Value(1), + decimal128: new Decimal128Value('1.2e3') }) ); expect(objValue.field(field('bson.objectId'))!).to.deep.equal( @@ -132,6 +138,9 @@ describe('ObjectValue', () => { expect(objValue.field(field('bson.int32'))!).to.deep.equal( wrap(new Int32Value(1)) ); + expect(objValue.field(field('bson.decimal128'))!).to.deep.equal( + wrap(new Decimal128Value('1.2e3')) + ); }); it('can overwrite existing fields', () => { @@ -248,6 +257,7 @@ describe('ObjectValue', () => { objValue.set(field('timestamp'), wrap(new BsonTimestamp(1, 2))); objValue.set(field('regex'), wrap(new RegexValue('a', 'b'))); objValue.set(field('int32'), wrap(new Int32Value(1))); + objValue.set(field('decimal128'), wrap(new Decimal128Value('1.2e3'))); objValue.set(field('min'), wrap(MinKey.instance())); objValue.set(field('max'), wrap(MaxKey.instance())); @@ -257,6 +267,7 @@ describe('ObjectValue', () => { timestamp: new BsonTimestamp(1, 2), regex: new RegexValue('a', 'b'), int32: new Int32Value(1), + decimal128: new Decimal128Value('1.2e3'), min: MinKey.instance(), max: MaxKey.instance() }); @@ -285,6 +296,7 @@ describe('ObjectValue', () => { timestamp: new BsonTimestamp(1, 2), regex: new RegexValue('a', 'b'), int32: new Int32Value(1), + decimal128: new Decimal128Value('1.2e3'), min: null, max: MaxKey.instance(), foo: { @@ -306,7 +318,8 @@ describe('ObjectValue', () => { min: MinKey.instance(), max: MaxKey.instance(), regex: new RegexValue('a', 'b'), - int32: new Int32Value(1) + int32: new Int32Value(1), + decimal128: new Decimal128Value('1.2e3') } }); const expectedMask = mask( @@ -322,7 +335,8 @@ describe('ObjectValue', () => { 'bar.min', 'bar.max', 'bar.regex', - 'bar.int32' + 'bar.int32', + 'bar.decimal128' ); const actualMask = extractFieldMask(objValue.value.mapValue); expect(actualMask.isEqual(expectedMask)).to.be.true; diff --git a/packages/firestore/test/unit/model/values.test.ts b/packages/firestore/test/unit/model/values.test.ts index dce8f1e123c..e4d45cb8e95 100644 --- a/packages/firestore/test/unit/model/values.test.ts +++ b/packages/firestore/test/unit/model/values.test.ts @@ -26,7 +26,8 @@ import { RegexValue, Int32Value, MaxKey, - MinKey + MinKey, + Decimal128Value } from '../../../src'; import { DatabaseId } from '../../../src/core/database_info'; import { vector } from '../../../src/lite-api/field_value_impl'; @@ -49,7 +50,8 @@ import { MIN_BSON_BINARY_VALUE, MIN_KEY_VALUE, MIN_REGEX_VALUE, - MIN_BSON_OBJECT_ID_VALUE + MIN_BSON_OBJECT_ID_VALUE, + RESERVED_DECIMAL128_KEY } from '../../../src/model/values'; import * as api from '../../../src/protos/firestore_proto_api'; import { primitiveComparator } from '../../../src/util/misc'; @@ -124,6 +126,7 @@ describe('Values', () => { wrap(new BsonObjectId('123456789012')) ], [wrap(new Int32Value(255)), wrap(new Int32Value(255))], + [wrap(new Decimal128Value('1.2e3')), wrap(new Decimal128Value('1.2e3'))], [wrap(MaxKey.instance()), wrap(MaxKey.instance())] ]; expectEqualitySets(values, (v1, v2) => valueEquals(v1, v2)); @@ -171,32 +174,61 @@ describe('Values', () => { [wrap(true)], // numbers - [wrap(NaN)], - [wrap(-Infinity)], + [wrap(NaN), wrap(new Decimal128Value('NaN'))], + [wrap(-Infinity), wrap(new Decimal128Value('-Infinity'))], [wrap(-Number.MAX_VALUE)], - [wrap(Number.MIN_SAFE_INTEGER - 1)], + [ + wrap(Number.MIN_SAFE_INTEGER - 1), + wrap(new Decimal128Value('-9007199254740992')) + ], [wrap(Number.MIN_SAFE_INTEGER)], - // 64-bit and 32-bit integers order together numerically. - [{ integerValue: -2147483648 }, wrap(new Int32Value(-2147483648))], - [wrap(-1.1)], + // 64-bit,32-bit integers and 128 decimal numbers order together numerically. + [ + { integerValue: -2147483648 }, + wrap(new Int32Value(-2147483648)), + wrap(new Decimal128Value('-2147483648')), + wrap(new Decimal128Value('-2.147483648e9')) + ], + [wrap(-1.5), wrap(new Decimal128Value('-1.5'))], // Integers, Int32Values and Doubles order the same. - [{ integerValue: -1 }, { doubleValue: -1 }, wrap(new Int32Value(-1))], + [ + { integerValue: -1 }, + { doubleValue: -1 }, + wrap(new Int32Value(-1)), + wrap(new Decimal128Value('-1')), + wrap(new Decimal128Value('-1.0')) + ], [wrap(-Number.MIN_VALUE)], // zeros all compare the same. [ { integerValue: 0 }, { doubleValue: 0 }, { doubleValue: -0 }, - wrap(new Int32Value(0)) + wrap(new Int32Value(0)), + wrap(new Decimal128Value('0')), + wrap(new Decimal128Value('0.0')), + wrap(new Decimal128Value('-0')), + wrap(new Decimal128Value('-0.0')), + wrap(new Decimal128Value('+0')), + wrap(new Decimal128Value('+0.0')) ], [wrap(Number.MIN_VALUE)], - [{ integerValue: 1 }, { doubleValue: 1.0 }, wrap(new Int32Value(1))], - [wrap(1.1)], - [wrap(new Int32Value(2))], - [wrap(new Int32Value(2147483647))], + [ + { integerValue: 1 }, + { doubleValue: 1.0 }, + wrap(new Int32Value(1)), + wrap(new Decimal128Value('1')), + wrap(new Decimal128Value('1.0')) + ], + [wrap(1.5), wrap(new Decimal128Value('1.5'))], + [wrap(new Int32Value(2)), wrap(new Decimal128Value('2'))], + [ + wrap(new Int32Value(2147483647)), + wrap(new Decimal128Value('2.147483647e9')) + ], [wrap(Number.MAX_SAFE_INTEGER)], [wrap(Number.MAX_SAFE_INTEGER + 1)], - [wrap(Infinity)], + [wrap(Infinity), wrap(new Decimal128Value('Infinity'))], // timestamps [wrap(date1)], @@ -425,6 +457,13 @@ describe('Values', () => { expectedByteSize: 8, elements: [wrap(new Int32Value(1)), wrap(new Int32Value(2147483647))] }, + { + expectedByteSize: 16, + elements: [ + wrap(new Decimal128Value('1.2e3')), + wrap(new Decimal128Value('1234567890.1234567890123456')) + ] + }, { expectedByteSize: 16, elements: [ @@ -503,9 +542,15 @@ describe('Values', () => { valuesGetLowerBound({ mapValue: { fields: { [RESERVED_INT32_KEY]: { integerValue: 0 } } } }), + valuesGetLowerBound({ + mapValue: { + fields: { [RESERVED_DECIMAL128_KEY]: { stringValue: '0' } } + } + }), wrap(NaN) ], [wrap(Number.NEGATIVE_INFINITY)], + [wrap(0), wrap(new Int32Value(0)), wrap(new Decimal128Value('0.0'))], [wrap(Number.MIN_VALUE)], // dates @@ -622,7 +667,10 @@ describe('Values', () => { [valuesGetUpperBound({ booleanValue: false })], // numbers - [wrap(new Int32Value(2147483647))], //largest int32 value + [ + wrap(new Int32Value(2147483647)), + wrap(new Decimal128Value('2147483647')) + ], //largest int32 value [wrap(Number.MAX_SAFE_INTEGER)], [wrap(Number.POSITIVE_INFINITY)], [valuesGetUpperBound({ doubleValue: NaN })], @@ -727,6 +775,9 @@ describe('Values', () => { '{__request_timestamp__:{increment:2,seconds:1}}' ); expect(canonicalId(wrap(new Int32Value(1)))).to.equal('{__int__:1}'); + expect(canonicalId(wrap(new Decimal128Value('-1.2e3')))).to.equal( + '{__decimal128__:-1.2e3}' + ); expect( canonicalId(wrap(new BsonBinaryData(1, new Uint8Array([1, 2, 3])))) ).to.equal('{__binary__:AQECAw==}'); diff --git a/packages/firestore/test/unit/remote/serializer.helper.ts b/packages/firestore/test/unit/remote/serializer.helper.ts index 24d7b039d0c..7aa0321e418 100644 --- a/packages/firestore/test/unit/remote/serializer.helper.ts +++ b/packages/firestore/test/unit/remote/serializer.helper.ts @@ -24,6 +24,7 @@ import { BsonObjectId, BsonTimestamp, Bytes, + Decimal128Value, DocumentReference, GeoPoint, increment, @@ -580,7 +581,8 @@ export function serializerTest( MinKey.instance(), MaxKey.instance(), new RegexValue('a', 'b'), - new Int32Value(1) + new Int32Value(1), + new Decimal128Value('1.2e3') ]; for (const example of examples) { diff --git a/packages/firestore/tsconfig.json b/packages/firestore/tsconfig.json index d53852fa79a..f25cf0b153d 100644 --- a/packages/firestore/tsconfig.json +++ b/packages/firestore/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "../../config/tsconfig.base.json", "compilerOptions": { - "outDir": "dist" + "outDir": "dist", + "target": "es2020" }, "exclude": ["scripts/**/*", "dist/**/*"] } From 60fd0763fd16e22c183bf8cc8492935f4788a507 Mon Sep 17 00:00:00 2001 From: Ehsan Nasiri Date: Fri, 27 Jun 2025 16:12:52 -0700 Subject: [PATCH 77/77] Fix merge conflicts. --- .../test/integration/api/database.test.ts | 15 ++++++++++++++- .../firestore/test/integration/api/type.test.ts | 3 ++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index c0502489e0f..c709c520a23 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -3137,7 +3137,8 @@ apiDescribe('Database', persistence => { const NIGHTLY_PROJECT_ID = 'firestore-sdk-nightly'; const settings = { ...DEFAULT_SETTINGS, - host: 'test-firestore.sandbox.googleapis.com' + host: 'test-firestore.sandbox.googleapis.com', + databaseId: '(default)' }; it('can write and read BSON types', async () => { @@ -3379,6 +3380,7 @@ apiDescribe('Database', persistence => { ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3399,6 +3401,7 @@ apiDescribe('Database', persistence => { testDocs['d'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3417,6 +3420,7 @@ apiDescribe('Database', persistence => { testDocs['b'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3435,6 +3439,7 @@ apiDescribe('Database', persistence => { testDocs['f'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3459,6 +3464,7 @@ apiDescribe('Database', persistence => { testDocs['e'] ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3901,6 +3907,7 @@ apiDescribe('Database', persistence => { 'i' // NaN ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3923,6 +3930,7 @@ apiDescribe('Database', persistence => { 'i' ]); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3936,6 +3944,7 @@ apiDescribe('Database', persistence => { snapshot = await getDocs(orderedQuery); expect(toIds(snapshot)).to.deep.equal(['f', 'e', 'd', 'c']); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3969,6 +3978,7 @@ apiDescribe('Database', persistence => { let snapshot = await getDocs(orderedQuery); expect(toIds(snapshot)).to.deep.equal(['b']); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3981,6 +3991,7 @@ apiDescribe('Database', persistence => { snapshot = await getDocs(orderedQuery); expect(toIds(snapshot)).to.deep.equal(['a', 'e', 'd', 'c']); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3990,6 +4001,7 @@ apiDescribe('Database', persistence => { snapshot = await getDocs(orderedQuery); expect(toIds(snapshot)).to.deep.equal(['c']); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) @@ -3999,6 +4011,7 @@ apiDescribe('Database', persistence => { snapshot = await getDocs(orderedQuery); expect(toIds(snapshot)).to.deep.equal(['a', 'e', 'd', 'b']); await assertSDKQueryResultsConsistentWithBackend( + coll, orderedQuery, testDocs, toIds(snapshot) diff --git a/packages/firestore/test/integration/api/type.test.ts b/packages/firestore/test/integration/api/type.test.ts index f47e99b0185..97df0672af9 100644 --- a/packages/firestore/test/integration/api/type.test.ts +++ b/packages/firestore/test/integration/api/type.test.ts @@ -247,7 +247,8 @@ apiDescribe('Firestore', persistence => { const NIGHTLY_PROJECT_ID = 'firestore-sdk-nightly'; const settings = { ...DEFAULT_SETTINGS, - host: 'test-firestore.sandbox.googleapis.com' + host: 'test-firestore.sandbox.googleapis.com', + databaseId: '(default)' }; it('can read and write minKey fields', () => {