UNPKG

@firestore-emulator/server

Version:

This package is the implementation of the Firestore emulator. It is a Node.js

1 lines 21.6 kB
{"version":3,"sources":["../../src/FirestoreState/mask.ts","../../src/FirestoreState/field.ts"],"sourcesContent":["import { produce } from \"immer\";\n\nimport {\n type FirestoreStateDocumentFields,\n FirestoreStateDocumentMapField,\n} from \"./field\";\n\n/**\n * If the key contains a \".\", it will be wrapped with \"\\`\" characters.\n * For example, if the key is `user.name`, it will be transformed into `` `user.name` ``.\n */\nfunction escapeKey(key: string): string {\n return key.includes(\".\") ? `\\`${key}\\`` : key;\n}\n\nfunction getNestedUpdateMask(updateMask: string[], key: string): string[] {\n const escapedKey = escapeKey(key);\n return updateMask\n .filter((m) => m.startsWith(`${escapedKey}.`))\n .map((m) => m.substring(escapedKey.length + 1));\n}\n\nfunction isUpdatable(updateMask: string[], key: string): boolean {\n return updateMask.length === 0 || updateMask.includes(escapeKey(key));\n}\n\nexport function updateFields(\n current: Record<string, FirestoreStateDocumentFields>,\n fields: Record<string, FirestoreStateDocumentFields>,\n updateMask: string[],\n): Record<string, FirestoreStateDocumentFields> {\n return produce(current, (draft) => {\n for (const [key, field] of Object.entries(fields)) {\n const updatable = isUpdatable(updateMask, key);\n const nestedUpdateMask = getNestedUpdateMask(updateMask, key);\n if (updatable) {\n draft[key] = field;\n } else if (\n field.type === \"map_value\" &&\n (draft[key] == null || draft[key].type === \"map_value\")\n ) {\n draft[key] = new FirestoreStateDocumentMapField(\n updateFields(draft[key]?.value ?? {}, field.value, nestedUpdateMask),\n );\n }\n }\n });\n}\n","import type { Value as v1Value } from \"@firestore-emulator/proto/dist/google/firestore/v1/document\";\nimport { NullValue } from \"@firestore-emulator/proto/dist/google/protobuf/struct\";\n\nexport type ValueObjectType = ReturnType<typeof v1Value.prototype.toObject>;\nexport interface FirestoreStateDocumentBaseField {\n eq(other: FirestoreStateDocumentFields): boolean;\n gt(other: FirestoreStateDocumentFields): boolean;\n gte(other: FirestoreStateDocumentFields): boolean;\n lt(other: FirestoreStateDocumentFields): boolean;\n lte(other: FirestoreStateDocumentFields): boolean;\n toJSON(): { type: string; value: unknown };\n toV1ValueObject(): ValueObjectType;\n}\n\nexport class FirestoreStateDocumentStringField\n implements FirestoreStateDocumentBaseField\n{\n type = \"string_value\" as const;\n constructor(readonly value: string) {}\n\n toJSON() {\n return { type: this.type, value: this.value };\n }\n\n toV1ValueObject(): ValueObjectType {\n return { string_value: this.value };\n }\n\n eq(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentStringField &&\n this.value === other.value\n );\n }\n\n lt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentStringField &&\n this.value < other.value\n );\n }\n\n lte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentStringField &&\n this.value <= other.value\n );\n }\n\n gt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentStringField &&\n this.value > other.value\n );\n }\n\n gte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentStringField &&\n this.value >= other.value\n );\n }\n}\n\nexport class FirestoreStateDocumentNullField\n implements FirestoreStateDocumentBaseField\n{\n type = \"null_value\" as const;\n value = null;\n toJSON() {\n return { type: this.type, value: null } as const;\n }\n\n toV1ValueObject(): ValueObjectType {\n return { null_value: NullValue.NULL_VALUE };\n }\n\n eq(other: FirestoreStateDocumentFields): boolean {\n return other.type === this.type;\n }\n\n lt(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n lte(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n gt(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n gte(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n}\n\nexport class FirestoreStateDocumentBooleanField\n implements FirestoreStateDocumentBaseField\n{\n type = \"boolean_value\" as const;\n constructor(readonly value: boolean) {}\n\n toJSON() {\n return { type: this.type, value: this.value };\n }\n\n toV1ValueObject(): ValueObjectType {\n return { boolean_value: this.value };\n }\n\n eq(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentBooleanField &&\n this.value === other.value\n );\n }\n\n lt(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n lte(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n gt(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n gte(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n}\n\nexport class FirestoreStateDocumentIntegerField\n implements FirestoreStateDocumentBaseField\n{\n type = \"integer_value\" as const;\n constructor(readonly value: number) {\n if (!Number.isInteger(value)) {\n throw new Error(`value must be integer. value=${value}`);\n }\n }\n\n toJSON() {\n return { type: this.type, value: this.value };\n }\n\n toV1ValueObject(): ValueObjectType {\n return { integer_value: this.value };\n }\n\n eq(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentIntegerField &&\n this.value === other.value\n );\n }\n\n lt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentIntegerField &&\n this.value < other.value\n );\n }\n\n lte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentIntegerField &&\n this.value <= other.value\n );\n }\n\n gt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentIntegerField &&\n this.value > other.value\n );\n }\n\n gte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentIntegerField &&\n this.value >= other.value\n );\n }\n\n add(\n other: FirestoreStateDocumentIntegerField,\n ): FirestoreStateDocumentIntegerField;\n add(\n other: FirestoreStateDocumentDoubleField,\n ): FirestoreStateDocumentDoubleField;\n add(\n other:\n | FirestoreStateDocumentIntegerField\n | FirestoreStateDocumentDoubleField,\n ): FirestoreStateDocumentIntegerField | FirestoreStateDocumentDoubleField {\n if (other instanceof FirestoreStateDocumentIntegerField) {\n return new FirestoreStateDocumentIntegerField(this.value + other.value);\n }\n if (other instanceof FirestoreStateDocumentDoubleField) {\n return new FirestoreStateDocumentDoubleField(this.value + other.value);\n }\n throw new Error(`unsupported type. other=${other}`);\n }\n}\n\nexport class FirestoreStateDocumentDoubleField\n implements FirestoreStateDocumentBaseField\n{\n type = \"double_value\" as const;\n constructor(readonly value: number) {}\n\n toJSON() {\n return { type: this.type, value: this.value };\n }\n\n toV1ValueObject(): ValueObjectType {\n return { double_value: this.value };\n }\n\n eq(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentDoubleField &&\n this.value === other.value\n );\n }\n\n lt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentDoubleField &&\n this.value < other.value\n );\n }\n\n lte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentDoubleField &&\n this.value <= other.value\n );\n }\n\n gt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentDoubleField &&\n this.value > other.value\n );\n }\n\n gte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentDoubleField &&\n this.value >= other.value\n );\n }\n\n add(\n other:\n | FirestoreStateDocumentIntegerField\n | FirestoreStateDocumentDoubleField,\n ): FirestoreStateDocumentDoubleField {\n if (other instanceof FirestoreStateDocumentIntegerField) {\n return new FirestoreStateDocumentDoubleField(this.value + other.value);\n }\n if (other instanceof FirestoreStateDocumentDoubleField) {\n return new FirestoreStateDocumentDoubleField(this.value + other.value);\n }\n throw new Error(`unsupported type. other=${other}`);\n }\n}\n\nexport class FirestoreStateDocumentTimestampField\n implements FirestoreStateDocumentBaseField\n{\n type = \"timestamp_value\" as const;\n constructor(readonly value: { nanos: number; seconds: number }) {}\n\n static fromDate(date: Date) {\n return new FirestoreStateDocumentTimestampField({\n nanos: (date.getTime() % 1000) * 1000000,\n seconds: Math.floor(date.getTime() / 1000),\n });\n }\n\n toJSON() {\n return { type: this.type, value: this.value } as const;\n }\n\n toV1ValueObject(): ValueObjectType {\n return { timestamp_value: this.value };\n }\n\n eq(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentTimestampField &&\n this.value.seconds === other.value.seconds &&\n this.value.nanos === other.value.nanos\n );\n }\n\n lt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentTimestampField &&\n (this.value.seconds < other.value.seconds ||\n (this.value.seconds === other.value.seconds &&\n this.value.nanos < other.value.nanos))\n );\n }\n\n lte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentTimestampField &&\n (this.value.seconds < other.value.seconds ||\n (this.value.seconds === other.value.seconds &&\n this.value.nanos <= other.value.nanos))\n );\n }\n\n gt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentTimestampField &&\n (this.value.seconds > other.value.seconds ||\n (this.value.seconds === other.value.seconds &&\n this.value.nanos > other.value.nanos))\n );\n }\n\n gte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentTimestampField &&\n (this.value.seconds > other.value.seconds ||\n (this.value.seconds === other.value.seconds &&\n this.value.nanos >= other.value.nanos))\n );\n }\n}\n\nexport class FirestoreStateDocumentBytesField\n implements FirestoreStateDocumentBaseField\n{\n type = \"bytes_value\" as const;\n constructor(readonly value: Uint8Array) {}\n\n toJSON() {\n return { type: this.type, value: this.value } as const;\n }\n\n toV1ValueObject(): ValueObjectType {\n return { bytes_value: this.value };\n }\n\n eq(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentBytesField &&\n this.value.toString() === other.value.toString()\n );\n }\n\n lt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentBytesField &&\n this.value.toString() < other.value.toString()\n );\n }\n\n lte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentBytesField &&\n this.value.toString() <= other.value.toString()\n );\n }\n\n gt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentBytesField &&\n this.value.toString() > other.value.toString()\n );\n }\n\n gte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentBytesField &&\n this.value.toString() >= other.value.toString()\n );\n }\n}\n\nexport class FirestoreStateDocumentReferenceField\n implements FirestoreStateDocumentBaseField\n{\n type = \"reference_value\" as const;\n constructor(readonly value: string) {}\n\n toJSON() {\n return { type: this.type, value: this.value };\n }\n\n toV1ValueObject(): ValueObjectType {\n return { reference_value: this.value };\n }\n\n eq(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentReferenceField &&\n this.value === other.value\n );\n }\n\n lt(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n lte(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n gt(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n gte(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n}\n\nexport class FirestoreStateDocumentGeoPointField\n implements FirestoreStateDocumentBaseField\n{\n type = \"geo_point_value\" as const;\n constructor(readonly value: { latitude: number; longitude: number }) {}\n\n toJSON() {\n return { type: this.type, value: this.value };\n }\n\n toV1ValueObject(): ValueObjectType {\n return { geo_point_value: this.value };\n }\n\n eq(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentGeoPointField &&\n this.value.latitude === other.value.latitude &&\n this.value.longitude === other.value.longitude\n );\n }\n\n lt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentGeoPointField &&\n (this.value.latitude < other.value.latitude ||\n this.value.longitude < other.value.longitude)\n );\n }\n\n lte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentGeoPointField &&\n (this.value.latitude <= other.value.latitude ||\n this.value.longitude <= other.value.longitude)\n );\n }\n\n gt(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentGeoPointField &&\n (this.value.latitude > other.value.latitude ||\n this.value.longitude > other.value.longitude)\n );\n }\n\n gte(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentGeoPointField &&\n (this.value.latitude >= other.value.latitude ||\n this.value.longitude >= other.value.longitude)\n );\n }\n}\n\nexport class FirestoreStateDocumentArrayField\n implements FirestoreStateDocumentBaseField\n{\n type = \"array_value\" as const;\n constructor(readonly value: FirestoreStateDocumentFields[]) {}\n\n toJSON(): { type: string; value: unknown } {\n return {\n type: this.type,\n value: this.value.map((v) => v.toJSON()),\n };\n }\n\n toV1ValueObject(): ValueObjectType {\n return {\n array_value: { values: this.value.map((v) => v.toV1ValueObject()) },\n };\n }\n\n eq(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentArrayField &&\n this.value.length === other.value.length &&\n this.value.every((v, i) => {\n const item = other.value[i];\n if (!item) return false;\n return item.eq(v);\n })\n );\n }\n\n lt(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n lte(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n gt(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n gte(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n}\n\nexport class FirestoreStateDocumentMapField\n implements FirestoreStateDocumentBaseField\n{\n type = \"map_value\" as const;\n constructor(readonly value: Record<string, FirestoreStateDocumentFields>) {}\n\n toJSON(): { type: string; value: unknown } {\n return {\n type: this.type,\n value: Object.fromEntries(\n Object.entries(this.value).map(([k, v]) => [k, v.toJSON()]),\n ),\n };\n }\n\n toV1ValueObject(): ValueObjectType {\n return {\n map_value: {\n fields: Object.fromEntries(\n Object.entries(this.value).map(([k, v]) => [k, v.toV1ValueObject()]),\n ),\n },\n };\n }\n\n eq(other: FirestoreStateDocumentFields): boolean {\n return (\n other instanceof FirestoreStateDocumentMapField &&\n Object.keys(this.value).length === Object.keys(other.value).length &&\n Object.entries(this.value).every(([k, v]) => {\n const item = other.value[k];\n if (!item) return false;\n return item.eq(v);\n })\n );\n }\n\n lt(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n lte(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n gt(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n\n gte(_other: FirestoreStateDocumentFields): boolean {\n return false;\n }\n}\n\nexport type FirestoreStateDocumentFields =\n | FirestoreStateDocumentStringField\n | FirestoreStateDocumentNullField\n | FirestoreStateDocumentBooleanField\n | FirestoreStateDocumentIntegerField\n | FirestoreStateDocumentDoubleField\n | FirestoreStateDocumentTimestampField\n | FirestoreStateDocumentBytesField\n | FirestoreStateDocumentReferenceField\n | FirestoreStateDocumentGeoPointField\n | FirestoreStateDocumentArrayField\n | FirestoreStateDocumentMapField;\n\nexport const convertV1DocumentField = (\n field: v1Value,\n): FirestoreStateDocumentFields => {\n if (field.has_string_value)\n return new FirestoreStateDocumentStringField(field.string_value);\n if (field.has_null_value) return new FirestoreStateDocumentNullField();\n if (field.has_boolean_value)\n return new FirestoreStateDocumentBooleanField(field.boolean_value);\n if (field.has_integer_value)\n return new FirestoreStateDocumentIntegerField(field.integer_value);\n if (field.has_double_value)\n return new FirestoreStateDocumentDoubleField(field.double_value);\n if (field.has_timestamp_value)\n return new FirestoreStateDocumentTimestampField({\n nanos: field.timestamp_value.nanos,\n seconds: field.timestamp_value.seconds,\n });\n if (field.has_bytes_value)\n return new FirestoreStateDocumentBytesField(field.bytes_value);\n if (field.has_reference_value)\n return new FirestoreStateDocumentReferenceField(field.reference_value);\n if (field.has_geo_point_value)\n return new FirestoreStateDocumentGeoPointField({\n latitude: field.geo_point_value.latitude,\n longitude: field.geo_point_value.longitude,\n });\n if (field.has_array_value)\n return new FirestoreStateDocumentArrayField(\n field.array_value.values.map(convertV1DocumentField),\n );\n if (field.has_map_value)\n return new FirestoreStateDocumentMapField(\n Object.fromEntries(\n Array.from(field.map_value.fields.entries()).map(([k, v]) => [\n k,\n convertV1DocumentField(v),\n ]),\n ),\n );\n throw new Error(`unknown field type. field=${JSON.stringify(field)}`);\n};\n\nexport const convertV1Value = (\n value: v1Value,\n): ReturnType<typeof v1Value.prototype.toObject> => {\n return convertV1DocumentField(value).toV1ValueObject();\n};\n"],"mappings":";AAAA,SAAS,eAAe;;;ACCxB,SAAS,iBAAiB;AAkhBnB,IAAM,iCAAN,MAAM,gCAEb;AAAA,EAEE,YAAqB,OAAqD;AAArD;AAAA,EAAsD;AAAA,EAD3E,OAAO;AAAA,EAGP,SAA2C;AACzC,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,OAAO,OAAO;AAAA,QACZ,OAAO,QAAQ,KAAK,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,kBAAmC;AACjC,WAAO;AAAA,MACL,WAAW;AAAA,QACT,QAAQ,OAAO;AAAA,UACb,OAAO,QAAQ,KAAK,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;AAAA,QACrE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,GAAG,OAA8C;AAC/C,WACE,iBAAiB,mCACjB,OAAO,KAAK,KAAK,KAAK,EAAE,WAAW,OAAO,KAAK,MAAM,KAAK,EAAE,UAC5D,OAAO,QAAQ,KAAK,KAAK,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM;AAC3C,YAAM,OAAO,MAAM,MAAM,CAAC;AAC1B,UAAI,CAAC,KAAM,QAAO;AAClB,aAAO,KAAK,GAAG,CAAC;AAAA,IAClB,CAAC;AAAA,EAEL;AAAA,EAEA,GAAG,QAA+C;AAChD,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,QAA+C;AACjD,WAAO;AAAA,EACT;AAAA,EAEA,GAAG,QAA+C;AAChD,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,QAA+C;AACjD,WAAO;AAAA,EACT;AACF;;;AD5jBA,SAAS,UAAU,KAAqB;AACtC,SAAO,IAAI,SAAS,GAAG,IAAI,KAAK,GAAG,OAAO;AAC5C;AAEA,SAAS,oBAAoB,YAAsB,KAAuB;AACxE,QAAM,aAAa,UAAU,GAAG;AAChC,SAAO,WACJ,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,CAAC,EAC5C,IAAI,CAAC,MAAM,EAAE,UAAU,WAAW,SAAS,CAAC,CAAC;AAClD;AAEA,SAAS,YAAY,YAAsB,KAAsB;AAC/D,SAAO,WAAW,WAAW,KAAK,WAAW,SAAS,UAAU,GAAG,CAAC;AACtE;AAEO,SAAS,aACd,SACA,QACA,YAC8C;AAC9C,SAAO,QAAQ,SAAS,CAAC,UAAU;AACjC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,YAAM,YAAY,YAAY,YAAY,GAAG;AAC7C,YAAM,mBAAmB,oBAAoB,YAAY,GAAG;AAC5D,UAAI,WAAW;AACb,cAAM,GAAG,IAAI;AAAA,MACf,WACE,MAAM,SAAS,gBACd,MAAM,GAAG,KAAK,QAAQ,MAAM,GAAG,EAAE,SAAS,cAC3C;AACA,cAAM,GAAG,IAAI,IAAI;AAAA,UACf,aAAa,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,MAAM,OAAO,gBAAgB;AAAA,QACrE;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":[]}