@firestore-emulator/server
Version:
This package is the implementation of the Firestore emulator. It is a Node.js
646 lines (551 loc) • 17.5 kB
text/typescript
import type { Value as v1Value } from "@firestore-emulator/proto/dist/google/firestore/v1/document";
import { NullValue } from "@firestore-emulator/proto/dist/google/protobuf/struct";
export type ValueObjectType = ReturnType<typeof v1Value.prototype.toObject>;
export interface FirestoreStateDocumentBaseField {
eq(other: FirestoreStateDocumentFields): boolean;
gt(other: FirestoreStateDocumentFields): boolean;
gte(other: FirestoreStateDocumentFields): boolean;
lt(other: FirestoreStateDocumentFields): boolean;
lte(other: FirestoreStateDocumentFields): boolean;
toJSON(): { type: string; value: unknown };
toV1ValueObject(): ValueObjectType;
}
export class FirestoreStateDocumentStringField
implements FirestoreStateDocumentBaseField
{
type = "string_value" as const;
constructor(readonly value: string) {}
toJSON() {
return { type: this.type, value: this.value };
}
toV1ValueObject(): ValueObjectType {
return { string_value: this.value };
}
eq(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentStringField &&
this.value === other.value
);
}
lt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentStringField &&
this.value < other.value
);
}
lte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentStringField &&
this.value <= other.value
);
}
gt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentStringField &&
this.value > other.value
);
}
gte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentStringField &&
this.value >= other.value
);
}
}
export class FirestoreStateDocumentNullField
implements FirestoreStateDocumentBaseField
{
type = "null_value" as const;
value = null;
toJSON() {
return { type: this.type, value: null } as const;
}
toV1ValueObject(): ValueObjectType {
return { null_value: NullValue.NULL_VALUE };
}
eq(other: FirestoreStateDocumentFields): boolean {
return other.type === this.type;
}
lt(_other: FirestoreStateDocumentFields): boolean {
return false;
}
lte(_other: FirestoreStateDocumentFields): boolean {
return false;
}
gt(_other: FirestoreStateDocumentFields): boolean {
return false;
}
gte(_other: FirestoreStateDocumentFields): boolean {
return false;
}
}
export class FirestoreStateDocumentBooleanField
implements FirestoreStateDocumentBaseField
{
type = "boolean_value" as const;
constructor(readonly value: boolean) {}
toJSON() {
return { type: this.type, value: this.value };
}
toV1ValueObject(): ValueObjectType {
return { boolean_value: this.value };
}
eq(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentBooleanField &&
this.value === other.value
);
}
lt(_other: FirestoreStateDocumentFields): boolean {
return false;
}
lte(_other: FirestoreStateDocumentFields): boolean {
return false;
}
gt(_other: FirestoreStateDocumentFields): boolean {
return false;
}
gte(_other: FirestoreStateDocumentFields): boolean {
return false;
}
}
export class FirestoreStateDocumentIntegerField
implements FirestoreStateDocumentBaseField
{
type = "integer_value" as const;
constructor(readonly value: number) {
if (!Number.isInteger(value)) {
throw new Error(`value must be integer. value=${value}`);
}
}
toJSON() {
return { type: this.type, value: this.value };
}
toV1ValueObject(): ValueObjectType {
return { integer_value: this.value };
}
eq(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentIntegerField &&
this.value === other.value
);
}
lt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentIntegerField &&
this.value < other.value
);
}
lte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentIntegerField &&
this.value <= other.value
);
}
gt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentIntegerField &&
this.value > other.value
);
}
gte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentIntegerField &&
this.value >= other.value
);
}
add(
other: FirestoreStateDocumentIntegerField,
): FirestoreStateDocumentIntegerField;
add(
other: FirestoreStateDocumentDoubleField,
): FirestoreStateDocumentDoubleField;
add(
other:
| FirestoreStateDocumentIntegerField
| FirestoreStateDocumentDoubleField,
): FirestoreStateDocumentIntegerField | FirestoreStateDocumentDoubleField {
if (other instanceof FirestoreStateDocumentIntegerField) {
return new FirestoreStateDocumentIntegerField(this.value + other.value);
}
if (other instanceof FirestoreStateDocumentDoubleField) {
return new FirestoreStateDocumentDoubleField(this.value + other.value);
}
throw new Error(`unsupported type. other=${other}`);
}
}
export class FirestoreStateDocumentDoubleField
implements FirestoreStateDocumentBaseField
{
type = "double_value" as const;
constructor(readonly value: number) {}
toJSON() {
return { type: this.type, value: this.value };
}
toV1ValueObject(): ValueObjectType {
return { double_value: this.value };
}
eq(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentDoubleField &&
this.value === other.value
);
}
lt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentDoubleField &&
this.value < other.value
);
}
lte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentDoubleField &&
this.value <= other.value
);
}
gt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentDoubleField &&
this.value > other.value
);
}
gte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentDoubleField &&
this.value >= other.value
);
}
add(
other:
| FirestoreStateDocumentIntegerField
| FirestoreStateDocumentDoubleField,
): FirestoreStateDocumentDoubleField {
if (other instanceof FirestoreStateDocumentIntegerField) {
return new FirestoreStateDocumentDoubleField(this.value + other.value);
}
if (other instanceof FirestoreStateDocumentDoubleField) {
return new FirestoreStateDocumentDoubleField(this.value + other.value);
}
throw new Error(`unsupported type. other=${other}`);
}
}
export class FirestoreStateDocumentTimestampField
implements FirestoreStateDocumentBaseField
{
type = "timestamp_value" as const;
constructor(readonly value: { nanos: number; seconds: number }) {}
static fromDate(date: Date) {
return new FirestoreStateDocumentTimestampField({
nanos: (date.getTime() % 1000) * 1000000,
seconds: Math.floor(date.getTime() / 1000),
});
}
toJSON() {
return { type: this.type, value: this.value } as const;
}
toV1ValueObject(): ValueObjectType {
return { timestamp_value: this.value };
}
eq(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentTimestampField &&
this.value.seconds === other.value.seconds &&
this.value.nanos === other.value.nanos
);
}
lt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentTimestampField &&
(this.value.seconds < other.value.seconds ||
(this.value.seconds === other.value.seconds &&
this.value.nanos < other.value.nanos))
);
}
lte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentTimestampField &&
(this.value.seconds < other.value.seconds ||
(this.value.seconds === other.value.seconds &&
this.value.nanos <= other.value.nanos))
);
}
gt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentTimestampField &&
(this.value.seconds > other.value.seconds ||
(this.value.seconds === other.value.seconds &&
this.value.nanos > other.value.nanos))
);
}
gte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentTimestampField &&
(this.value.seconds > other.value.seconds ||
(this.value.seconds === other.value.seconds &&
this.value.nanos >= other.value.nanos))
);
}
}
export class FirestoreStateDocumentBytesField
implements FirestoreStateDocumentBaseField
{
type = "bytes_value" as const;
constructor(readonly value: Uint8Array) {}
toJSON() {
return { type: this.type, value: this.value } as const;
}
toV1ValueObject(): ValueObjectType {
return { bytes_value: this.value };
}
eq(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentBytesField &&
this.value.toString() === other.value.toString()
);
}
lt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentBytesField &&
this.value.toString() < other.value.toString()
);
}
lte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentBytesField &&
this.value.toString() <= other.value.toString()
);
}
gt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentBytesField &&
this.value.toString() > other.value.toString()
);
}
gte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentBytesField &&
this.value.toString() >= other.value.toString()
);
}
}
export class FirestoreStateDocumentReferenceField
implements FirestoreStateDocumentBaseField
{
type = "reference_value" as const;
constructor(readonly value: string) {}
toJSON() {
return { type: this.type, value: this.value };
}
toV1ValueObject(): ValueObjectType {
return { reference_value: this.value };
}
eq(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentReferenceField &&
this.value === other.value
);
}
lt(_other: FirestoreStateDocumentFields): boolean {
return false;
}
lte(_other: FirestoreStateDocumentFields): boolean {
return false;
}
gt(_other: FirestoreStateDocumentFields): boolean {
return false;
}
gte(_other: FirestoreStateDocumentFields): boolean {
return false;
}
}
export class FirestoreStateDocumentGeoPointField
implements FirestoreStateDocumentBaseField
{
type = "geo_point_value" as const;
constructor(readonly value: { latitude: number; longitude: number }) {}
toJSON() {
return { type: this.type, value: this.value };
}
toV1ValueObject(): ValueObjectType {
return { geo_point_value: this.value };
}
eq(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentGeoPointField &&
this.value.latitude === other.value.latitude &&
this.value.longitude === other.value.longitude
);
}
lt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentGeoPointField &&
(this.value.latitude < other.value.latitude ||
this.value.longitude < other.value.longitude)
);
}
lte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentGeoPointField &&
(this.value.latitude <= other.value.latitude ||
this.value.longitude <= other.value.longitude)
);
}
gt(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentGeoPointField &&
(this.value.latitude > other.value.latitude ||
this.value.longitude > other.value.longitude)
);
}
gte(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentGeoPointField &&
(this.value.latitude >= other.value.latitude ||
this.value.longitude >= other.value.longitude)
);
}
}
export class FirestoreStateDocumentArrayField
implements FirestoreStateDocumentBaseField
{
type = "array_value" as const;
constructor(readonly value: FirestoreStateDocumentFields[]) {}
toJSON(): { type: string; value: unknown } {
return {
type: this.type,
value: this.value.map((v) => v.toJSON()),
};
}
toV1ValueObject(): ValueObjectType {
return {
array_value: { values: this.value.map((v) => v.toV1ValueObject()) },
};
}
eq(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentArrayField &&
this.value.length === other.value.length &&
this.value.every((v, i) => {
const item = other.value[i];
if (!item) return false;
return item.eq(v);
})
);
}
lt(_other: FirestoreStateDocumentFields): boolean {
return false;
}
lte(_other: FirestoreStateDocumentFields): boolean {
return false;
}
gt(_other: FirestoreStateDocumentFields): boolean {
return false;
}
gte(_other: FirestoreStateDocumentFields): boolean {
return false;
}
}
export class FirestoreStateDocumentMapField
implements FirestoreStateDocumentBaseField
{
type = "map_value" as const;
constructor(readonly value: Record<string, FirestoreStateDocumentFields>) {}
toJSON(): { type: string; value: unknown } {
return {
type: this.type,
value: Object.fromEntries(
Object.entries(this.value).map(([k, v]) => [k, v.toJSON()]),
),
};
}
toV1ValueObject(): ValueObjectType {
return {
map_value: {
fields: Object.fromEntries(
Object.entries(this.value).map(([k, v]) => [k, v.toV1ValueObject()]),
),
},
};
}
eq(other: FirestoreStateDocumentFields): boolean {
return (
other instanceof FirestoreStateDocumentMapField &&
Object.keys(this.value).length === Object.keys(other.value).length &&
Object.entries(this.value).every(([k, v]) => {
const item = other.value[k];
if (!item) return false;
return item.eq(v);
})
);
}
lt(_other: FirestoreStateDocumentFields): boolean {
return false;
}
lte(_other: FirestoreStateDocumentFields): boolean {
return false;
}
gt(_other: FirestoreStateDocumentFields): boolean {
return false;
}
gte(_other: FirestoreStateDocumentFields): boolean {
return false;
}
}
export type FirestoreStateDocumentFields =
| FirestoreStateDocumentStringField
| FirestoreStateDocumentNullField
| FirestoreStateDocumentBooleanField
| FirestoreStateDocumentIntegerField
| FirestoreStateDocumentDoubleField
| FirestoreStateDocumentTimestampField
| FirestoreStateDocumentBytesField
| FirestoreStateDocumentReferenceField
| FirestoreStateDocumentGeoPointField
| FirestoreStateDocumentArrayField
| FirestoreStateDocumentMapField;
export const convertV1DocumentField = (
field: v1Value,
): FirestoreStateDocumentFields => {
if (field.has_string_value)
return new FirestoreStateDocumentStringField(field.string_value);
if (field.has_null_value) return new FirestoreStateDocumentNullField();
if (field.has_boolean_value)
return new FirestoreStateDocumentBooleanField(field.boolean_value);
if (field.has_integer_value)
return new FirestoreStateDocumentIntegerField(field.integer_value);
if (field.has_double_value)
return new FirestoreStateDocumentDoubleField(field.double_value);
if (field.has_timestamp_value)
return new FirestoreStateDocumentTimestampField({
nanos: field.timestamp_value.nanos,
seconds: field.timestamp_value.seconds,
});
if (field.has_bytes_value)
return new FirestoreStateDocumentBytesField(field.bytes_value);
if (field.has_reference_value)
return new FirestoreStateDocumentReferenceField(field.reference_value);
if (field.has_geo_point_value)
return new FirestoreStateDocumentGeoPointField({
latitude: field.geo_point_value.latitude,
longitude: field.geo_point_value.longitude,
});
if (field.has_array_value)
return new FirestoreStateDocumentArrayField(
field.array_value.values.map(convertV1DocumentField),
);
if (field.has_map_value)
return new FirestoreStateDocumentMapField(
Object.fromEntries(
Array.from(field.map_value.fields.entries()).map(([k, v]) => [
k,
convertV1DocumentField(v),
]),
),
);
throw new Error(`unknown field type. field=${JSON.stringify(field)}`);
};
export const convertV1Value = (
value: v1Value,
): ReturnType<typeof v1Value.prototype.toObject> => {
return convertV1DocumentField(value).toV1ValueObject();
};