diff --git a/etc/firebase-admin.remote-config.api.md b/etc/firebase-admin.remote-config.api.md
index fb07bfad76..aeadcbf779 100644
--- a/etc/firebase-admin.remote-config.api.md
+++ b/etc/firebase-admin.remote-config.api.md
@@ -8,6 +8,21 @@
 
 import { Agent } from 'http';
 
+// @public
+export interface AndCondition {
+    conditions?: Array<OneOfCondition>;
+}
+
+// @public
+export type DefaultConfig = {
+    [key: string]: string | number | boolean;
+};
+
+// @public
+export type EvaluationContext = {
+    randomizationId?: string;
+};
+
 // @public
 export interface ExplicitParameterValue {
     value: string;
@@ -18,11 +33,21 @@ export interface ExplicitParameterValue {
 // @public
 export function getRemoteConfig(app?: App): RemoteConfig;
 
+// @public
+export interface GetServerTemplateOptions {
+    defaultConfig?: DefaultConfig;
+}
+
 // @public
 export interface InAppDefaultValue {
     useInAppDefault: boolean;
 }
 
+// @public
+export interface InitServerTemplateOptions extends GetServerTemplateOptions {
+    template?: ServerTemplateDataType;
+}
+
 // @public
 export interface ListVersionsOptions {
     endTime?: Date | string;
@@ -38,16 +63,60 @@ export interface ListVersionsResult {
     versions: Version[];
 }
 
+// @public
+export interface MicroPercentRange {
+    microPercentLowerBound?: number;
+    microPercentUpperBound?: number;
+}
+
+// @public
+export interface NamedCondition {
+    condition: OneOfCondition;
+    name: string;
+}
+
+// @public
+export interface OneOfCondition {
+    andCondition?: AndCondition;
+    false?: Record<string, never>;
+    orCondition?: OrCondition;
+    percent?: PercentCondition;
+    true?: Record<string, never>;
+}
+
+// @public
+export interface OrCondition {
+    conditions?: Array<OneOfCondition>;
+}
+
 // @public
 export type ParameterValueType = 'STRING' | 'BOOLEAN' | 'NUMBER' | 'JSON';
 
+// @public
+export interface PercentCondition {
+    microPercent?: number;
+    microPercentRange?: MicroPercentRange;
+    percentOperator?: PercentConditionOperator;
+    seed?: string;
+}
+
+// @public
+export enum PercentConditionOperator {
+    BETWEEN = "BETWEEN",
+    GREATER_THAN = "GREATER_THAN",
+    LESS_OR_EQUAL = "LESS_OR_EQUAL",
+    UNKNOWN = "UNKNOWN"
+}
+
 // @public
 export class RemoteConfig {
     // (undocumented)
     readonly app: App;
     createTemplateFromJSON(json: string): RemoteConfigTemplate;
+    getServerTemplate(options?: GetServerTemplateOptions): Promise<ServerTemplate>;
     getTemplate(): Promise<RemoteConfigTemplate>;
     getTemplateAtVersion(versionNumber: number | string): Promise<RemoteConfigTemplate>;
+    initServerTemplate(options?: InitServerTemplateOptions): ServerTemplate;
     listVersions(options?: ListVersionsOptions): Promise<ListVersionsResult>;
     publishTemplate(template: RemoteConfigTemplate, options?: {
         force: boolean;
@@ -104,9 +173,49 @@ export interface RemoteConfigUser {
     name?: string;
 }
 
+// @public
+export interface ServerConfig {
+    getBoolean(key: string): boolean;
+    getNumber(key: string): number;
+    getString(key: string): string;
+    getValue(key: string): Value;
+}
+
+// @public
+export interface ServerTemplate {
+    evaluate(context?: EvaluationContext): ServerConfig;
+    load(): Promise<void>;
+    set(template: ServerTemplateDataType): void;
+    toJSON(): ServerTemplateData;
+}
+
+// @public
+export interface ServerTemplateData {
+    conditions: NamedCondition[];
+    readonly etag: string;
+    parameters: {
+        [key: string]: RemoteConfigParameter;
+    };
+    version?: Version;
+}
+
+// @public
+export type ServerTemplateDataType = ServerTemplateData | string;
+
 // @public
 export type TagColor = 'BLUE' | 'BROWN' | 'CYAN' | 'DEEP_ORANGE' | 'GREEN' | 'INDIGO' | 'LIME' | 'ORANGE' | 'PINK' | 'PURPLE' | 'TEAL';
 
+// @public
+export interface Value {
+    asBoolean(): boolean;
+    asNumber(): number;
+    asString(): string;
+    getSource(): ValueSource;
+}
+
+// @public
+export type ValueSource = 'static' | 'default' | 'remote';
+
 // @public
 export interface Version {
     description?: string;
diff --git a/package-lock.json b/package-lock.json
index d2ab1fd15a..c76f671c31 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,105 +11,47 @@
       "dev": true
     },
     "@ampproject/remapping": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
-      "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
       "dev": true,
       "requires": {
-        "@jridgewell/gen-mapping": "^0.3.0",
-        "@jridgewell/trace-mapping": "^0.3.9"
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
       }
     },
     "@babel/code-frame": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
-      "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
+      "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
       "dev": true,
       "requires": {
-        "@babel/highlight": "^7.23.4",
-        "chalk": "^2.4.2"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "color-convert": {
-          "version": "1.9.3",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-          "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-          "dev": true,
-          "requires": {
-            "color-name": "1.1.3"
-          }
-        },
-        "color-name": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-          "dev": true
-        },
-        "escape-string-regexp": {
-          "version": "1.0.5",
-          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-          "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
+        "@babel/highlight": "^7.24.2",
+        "picocolors": "^1.0.0"
       }
     },
     "@babel/compat-data": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz",
-      "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz",
+      "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==",
       "dev": true
     },
     "@babel/core": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz",
-      "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz",
+      "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==",
       "dev": true,
       "requires": {
         "@ampproject/remapping": "^2.2.0",
-        "@babel/code-frame": "^7.23.5",
-        "@babel/generator": "^7.23.5",
-        "@babel/helper-compilation-targets": "^7.22.15",
+        "@babel/code-frame": "^7.24.2",
+        "@babel/generator": "^7.24.4",
+        "@babel/helper-compilation-targets": "^7.23.6",
         "@babel/helper-module-transforms": "^7.23.3",
-        "@babel/helpers": "^7.23.5",
-        "@babel/parser": "^7.23.5",
-        "@babel/template": "^7.22.15",
-        "@babel/traverse": "^7.23.5",
-        "@babel/types": "^7.23.5",
+        "@babel/helpers": "^7.24.4",
+        "@babel/parser": "^7.24.4",
+        "@babel/template": "^7.24.0",
+        "@babel/traverse": "^7.24.1",
+        "@babel/types": "^7.24.0",
         "convert-source-map": "^2.0.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
@@ -132,26 +74,26 @@
       }
     },
     "@babel/generator": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz",
-      "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz",
+      "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.23.5",
-        "@jridgewell/gen-mapping": "^0.3.2",
-        "@jridgewell/trace-mapping": "^0.3.17",
+        "@babel/types": "^7.24.0",
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25",
         "jsesc": "^2.5.1"
       }
     },
     "@babel/helper-compilation-targets": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz",
-      "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==",
+      "version": "7.23.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
+      "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
       "dev": true,
       "requires": {
-        "@babel/compat-data": "^7.22.9",
-        "@babel/helper-validator-option": "^7.22.15",
-        "browserslist": "^4.21.9",
+        "@babel/compat-data": "^7.23.5",
+        "@babel/helper-validator-option": "^7.23.5",
+        "browserslist": "^4.22.2",
         "lru-cache": "^5.1.1",
         "semver": "^6.3.1"
       },
@@ -205,12 +147,12 @@
       }
     },
     "@babel/helper-module-imports": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
-      "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
+      "version": "7.24.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz",
+      "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.22.15"
+        "@babel/types": "^7.24.0"
       }
     },
     "@babel/helper-module-transforms": {
@@ -245,9 +187,9 @@
       }
     },
     "@babel/helper-string-parser": {
-      "version": "7.23.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
-      "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz",
+      "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==",
       "dev": true
     },
     "@babel/helper-validator-identifier": {
@@ -263,25 +205,26 @@
       "dev": true
     },
     "@babel/helpers": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz",
-      "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz",
+      "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==",
       "dev": true,
       "requires": {
-        "@babel/template": "^7.22.15",
-        "@babel/traverse": "^7.23.5",
-        "@babel/types": "^7.23.5"
+        "@babel/template": "^7.24.0",
+        "@babel/traverse": "^7.24.1",
+        "@babel/types": "^7.24.0"
       }
     },
     "@babel/highlight": {
-      "version": "7.23.4",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
-      "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
+      "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
       "dev": true,
       "requires": {
         "@babel/helper-validator-identifier": "^7.22.20",
         "chalk": "^2.4.2",
-        "js-tokens": "^4.0.0"
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.0.0"
       },
       "dependencies": {
         "ansi-styles": {
@@ -343,37 +286,37 @@
       }
     },
     "@babel/parser": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz",
-      "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==",
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz",
+      "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==",
       "dev": true
     },
     "@babel/template": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
-      "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
+      "version": "7.24.0",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
+      "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.22.13",
-        "@babel/parser": "^7.22.15",
-        "@babel/types": "^7.22.15"
+        "@babel/code-frame": "^7.23.5",
+        "@babel/parser": "^7.24.0",
+        "@babel/types": "^7.24.0"
       }
     },
     "@babel/traverse": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz",
-      "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==",
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz",
+      "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.23.5",
-        "@babel/generator": "^7.23.5",
+        "@babel/code-frame": "^7.24.1",
+        "@babel/generator": "^7.24.1",
         "@babel/helper-environment-visitor": "^7.22.20",
         "@babel/helper-function-name": "^7.23.0",
         "@babel/helper-hoist-variables": "^7.22.5",
         "@babel/helper-split-export-declaration": "^7.22.6",
-        "@babel/parser": "^7.23.5",
-        "@babel/types": "^7.23.5",
-        "debug": "^4.1.0",
+        "@babel/parser": "^7.24.1",
+        "@babel/types": "^7.24.0",
+        "debug": "^4.3.1",
         "globals": "^11.1.0"
       },
       "dependencies": {
@@ -386,9 +329,9 @@
       }
     },
     "@babel/types": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz",
-      "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==",
+      "version": "7.24.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
+      "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
       "dev": true,
       "requires": {
         "@babel/helper-string-parser": "^7.23.4",
@@ -447,6 +390,23 @@
         "js-yaml": "^4.1.0",
         "minimatch": "^3.1.2",
         "strip-json-comments": "^3.1.1"
+      },
+      "dependencies": {
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        },
+        "strip-json-comments": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+          "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+          "dev": true
+        }
       }
     },
     "@eslint/js": {
@@ -477,82 +437,40 @@
       }
     },
     "@firebase/app": {
-      "version": "0.9.29",
-      "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.29.tgz",
-      "integrity": "sha512-HbKTjfmILklasIu/ij6zKnFf3SgLYXkBDVN7leJfVGmohl+zA7Ig+eXM1ZkT1pyBJ8FTYR+mlOJer/lNEnUCtw==",
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.0.tgz",
+      "integrity": "sha512-bemcsqQD4teEnCM/+FiK8LFjlfoIFewMY3LOIgxa59ISlkk4zlw4ezz1iLY45yQ6ip6WDwky7cx9UruFBAn6iw==",
       "dev": true,
       "requires": {
-        "@firebase/component": "0.6.5",
-        "@firebase/logger": "0.4.0",
-        "@firebase/util": "1.9.4",
+        "@firebase/component": "0.6.6",
+        "@firebase/logger": "0.4.1",
+        "@firebase/util": "1.9.5",
         "idb": "7.1.1",
         "tslib": "^2.1.0"
-      },
-      "dependencies": {
-        "@firebase/component": {
-          "version": "0.6.5",
-          "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.5.tgz",
-          "integrity": "sha512-2tVDk1ixi12sbDmmfITK8lxSjmcb73BMF6Qwc3U44hN/J1Fi1QY/Hnnb6klFlbB9/G16a3J3d4nXykye2EADTw==",
-          "dev": true,
-          "requires": {
-            "@firebase/util": "1.9.4",
-            "tslib": "^2.1.0"
-          }
-        },
-        "@firebase/util": {
-          "version": "1.9.4",
-          "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.4.tgz",
-          "integrity": "sha512-WLonYmS1FGHT97TsUmRN3qnTh5TeeoJp1Gg5fithzuAgdZOUtsYECfy7/noQ3llaguios8r5BuXSEiK82+UrxQ==",
-          "dev": true,
-          "requires": {
-            "tslib": "^2.1.0"
-          }
-        }
       }
     },
     "@firebase/app-check-interop-types": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz",
-      "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg=="
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.1.tgz",
+      "integrity": "sha512-NILZbe6RH3X1pZmJnfOfY2gLIrlKmrkUMMrrK6VSXHcSE0eQv28xFEcw16D198i9JYZpy5Kwq394My62qCMaIw=="
     },
     "@firebase/app-compat": {
-      "version": "0.2.29",
-      "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.29.tgz",
-      "integrity": "sha512-NqUdegXJfwphx9i/2bOE2CTZ55TC9bbDg+iwkxVShsPBJhD3CzQJkFhoDz4ccfbJaKZGsqjY3fisgX5kbDROnA==",
+      "version": "0.2.30",
+      "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.30.tgz",
+      "integrity": "sha512-S3FI3yx36xq5NYWXv/rqZiEnkQ89QwfGdl26iWZ9skuOGM96DYQUxs/zs7NkfAQcfpXC8f5DuUrE0Rz/0XdTEg==",
       "dev": true,
       "requires": {
-        "@firebase/app": "0.9.29",
-        "@firebase/component": "0.6.5",
-        "@firebase/logger": "0.4.0",
-        "@firebase/util": "1.9.4",
+        "@firebase/app": "0.10.0",
+        "@firebase/component": "0.6.6",
+        "@firebase/logger": "0.4.1",
+        "@firebase/util": "1.9.5",
         "tslib": "^2.1.0"
-      },
-      "dependencies": {
-        "@firebase/component": {
-          "version": "0.6.5",
-          "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.5.tgz",
-          "integrity": "sha512-2tVDk1ixi12sbDmmfITK8lxSjmcb73BMF6Qwc3U44hN/J1Fi1QY/Hnnb6klFlbB9/G16a3J3d4nXykye2EADTw==",
-          "dev": true,
-          "requires": {
-            "@firebase/util": "1.9.4",
-            "tslib": "^2.1.0"
-          }
-        },
-        "@firebase/util": {
-          "version": "1.9.4",
-          "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.4.tgz",
-          "integrity": "sha512-WLonYmS1FGHT97TsUmRN3qnTh5TeeoJp1Gg5fithzuAgdZOUtsYECfy7/noQ3llaguios8r5BuXSEiK82+UrxQ==",
-          "dev": true,
-          "requires": {
-            "tslib": "^2.1.0"
-          }
-        }
       }
     },
     "@firebase/app-types": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz",
-      "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q=="
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.1.tgz",
+      "integrity": "sha512-nFGqTYsnDFn1oXf1tCwPAc+hQPxyvBT/QB7qDjwK+IDYThOn63nGhzdUTXxVD9Ca8gUY/e5PQMngeo0ZW/E3uQ=="
     },
     "@firebase/auth": {
       "version": "1.4.0",
@@ -567,6 +485,34 @@
         "tslib": "^2.1.0"
       },
       "dependencies": {
+        "@firebase/component": {
+          "version": "0.6.4",
+          "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz",
+          "integrity": "sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==",
+          "dev": true,
+          "requires": {
+            "@firebase/util": "1.9.3",
+            "tslib": "^2.1.0"
+          }
+        },
+        "@firebase/logger": {
+          "version": "0.4.0",
+          "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz",
+          "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==",
+          "dev": true,
+          "requires": {
+            "tslib": "^2.1.0"
+          }
+        },
+        "@firebase/util": {
+          "version": "1.9.3",
+          "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz",
+          "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==",
+          "dev": true,
+          "requires": {
+            "tslib": "^2.1.0"
+          }
+        },
         "node-fetch": {
           "version": "2.6.7",
           "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@@ -598,6 +544,25 @@
           "integrity": "sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==",
           "dev": true
         },
+        "@firebase/component": {
+          "version": "0.6.4",
+          "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz",
+          "integrity": "sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==",
+          "dev": true,
+          "requires": {
+            "@firebase/util": "1.9.3",
+            "tslib": "^2.1.0"
+          }
+        },
+        "@firebase/util": {
+          "version": "1.9.3",
+          "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz",
+          "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==",
+          "dev": true,
+          "requires": {
+            "tslib": "^2.1.0"
+          }
+        },
         "node-fetch": {
           "version": "2.6.7",
           "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@@ -610,9 +575,9 @@
       }
     },
     "@firebase/auth-interop-types": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz",
-      "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg=="
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.2.tgz",
+      "integrity": "sha512-k3NA28Jfoo0+o391bFjoV9X5QLnUL1WbLhZZRbTQhZdmdGYJfX8ixtNNlHsYQ94bwG0QRbsmvkzDnzuhHrV11w=="
     },
     "@firebase/auth-types": {
       "version": "0.12.1",
@@ -621,76 +586,76 @@
       "dev": true
     },
     "@firebase/component": {
-      "version": "0.6.4",
-      "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz",
-      "integrity": "sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==",
+      "version": "0.6.6",
+      "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.6.tgz",
+      "integrity": "sha512-pp7sWqHmAAlA3os6ERgoM3k5Cxff510M9RLXZ9Mc8KFKMBc2ct3RkZTWUF7ixJNvMiK/iNgRLPDrLR2gtRJ9iQ==",
       "requires": {
-        "@firebase/util": "1.9.3",
+        "@firebase/util": "1.9.5",
         "tslib": "^2.1.0"
       }
     },
     "@firebase/database": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.2.tgz",
-      "integrity": "sha512-8X6NBJgUQzDz0xQVaCISoOLINKat594N2eBbMR3Mu/MH/ei4WM+aAMlsNzngF22eljXu1SILP5G3evkyvsG3Ng==",
-      "requires": {
-        "@firebase/app-check-interop-types": "0.3.0",
-        "@firebase/auth-interop-types": "0.2.1",
-        "@firebase/component": "0.6.4",
-        "@firebase/logger": "0.4.0",
-        "@firebase/util": "1.9.3",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.4.tgz",
+      "integrity": "sha512-k84cXh+dtpzvY6yOhfyr1B+I1vjvSMtmlqotE0lTNVylc8m5nmOohjzpTLEQDrBWvwACX/VP5fEyajAdmnOKqA==",
+      "requires": {
+        "@firebase/app-check-interop-types": "0.3.1",
+        "@firebase/auth-interop-types": "0.2.2",
+        "@firebase/component": "0.6.6",
+        "@firebase/logger": "0.4.1",
+        "@firebase/util": "1.9.5",
         "faye-websocket": "0.11.4",
         "tslib": "^2.1.0"
       }
     },
     "@firebase/database-compat": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.2.tgz",
-      "integrity": "sha512-09ryJnXDvuycsxn8aXBzLhBTuCos3HEnCOBWY6hosxfYlNCGnLvG8YMlbSAt5eNhf7/00B095AEfDsdrrLjxqA==",
-      "requires": {
-        "@firebase/component": "0.6.4",
-        "@firebase/database": "1.0.2",
-        "@firebase/database-types": "1.0.0",
-        "@firebase/logger": "0.4.0",
-        "@firebase/util": "1.9.3",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.4.tgz",
+      "integrity": "sha512-GEEDAvsSMAkqy0BIFSVtFzoOIIcKHFfDM4aXHtWL/JCaNn4OOjH7td73jDfN3ALvpIN4hQki0FcxQ89XjqaTjQ==",
+      "requires": {
+        "@firebase/component": "0.6.6",
+        "@firebase/database": "1.0.4",
+        "@firebase/database-types": "1.0.2",
+        "@firebase/logger": "0.4.1",
+        "@firebase/util": "1.9.5",
         "tslib": "^2.1.0"
       }
     },
     "@firebase/database-types": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.0.tgz",
-      "integrity": "sha512-SjnXStoE0Q56HcFgNQ+9SsmJc0c8TqGARdI/T44KXy+Ets3r6x/ivhQozT66bMnCEjJRywYoxNurRTMlZF8VNg==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.2.tgz",
+      "integrity": "sha512-JRigr5JNLEHqOkI99tAGHDZF47469/cJz1tRAgGs8Feh+3ZmQy/vVChSqwMp2DuVUGp9PlmGsNSlpINJ/hDuIA==",
       "requires": {
-        "@firebase/app-types": "0.9.0",
-        "@firebase/util": "1.9.3"
+        "@firebase/app-types": "0.9.1",
+        "@firebase/util": "1.9.5"
       }
     },
     "@firebase/logger": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz",
-      "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==",
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.1.tgz",
+      "integrity": "sha512-tTIixB5UJbG9ZHSGZSZdX7THr3KWOLrejZ9B7jYsm6fpwgRNngKznQKA2wgYVyvBc1ta7dGFh9NtJ8n7qfiYIw==",
       "requires": {
         "tslib": "^2.1.0"
       }
     },
     "@firebase/util": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz",
-      "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==",
+      "version": "1.9.5",
+      "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.5.tgz",
+      "integrity": "sha512-PP4pAFISDxsf70l3pEy34Mf3GkkUcVQ3MdKp6aSVb7tcpfUQxnsdV7twDd8EkfB6zZylH6wpUAoangQDmCUMqw==",
       "requires": {
         "tslib": "^2.1.0"
       }
     },
     "@google-cloud/firestore": {
-      "version": "7.5.0",
-      "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.5.0.tgz",
-      "integrity": "sha512-bhFKaCybfK/jzqhVm1Y1o8p3wOHVEo8opj7IJGF2sdqS69xl6QD1zpnrgssi/4HUj9bxIqtcs33Ofz//deV+rg==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.6.0.tgz",
+      "integrity": "sha512-WUDbaLY8UnPxgwsyIaxj6uxCtSDAaUyvzWJykNH5rZ9i92/SZCsPNNMN0ajrVpAR81hPIL4amXTaMJ40y5L+Yg==",
       "optional": true,
       "requires": {
         "fast-deep-equal": "^3.1.1",
         "functional-red-black-tree": "^1.0.1",
-        "google-gax": "^4.0.4",
-        "protobufjs": "^7.2.5"
+        "google-gax": "^4.3.1",
+        "protobufjs": "^7.2.6"
       }
     },
     "@google-cloud/paginator": {
@@ -740,38 +705,6 @@
         "uuid": "^8.0.0"
       },
       "dependencies": {
-        "duplexify": {
-          "version": "4.1.3",
-          "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
-          "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
-          "optional": true,
-          "requires": {
-            "end-of-stream": "^1.4.1",
-            "inherits": "^2.0.3",
-            "readable-stream": "^3.1.1",
-            "stream-shift": "^1.0.2"
-          }
-        },
-        "google-auth-library": {
-          "version": "9.7.0",
-          "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.7.0.tgz",
-          "integrity": "sha512-I/AvzBiUXDzLOy4iIZ2W+Zq33W4lcukQv1nl7C8WUA6SQwyQwUwu3waNmWNAvzds//FG8SZ+DnKnW/2k6mQS8A==",
-          "optional": true,
-          "requires": {
-            "base64-js": "^1.3.0",
-            "ecdsa-sig-formatter": "^1.0.11",
-            "gaxios": "^6.1.1",
-            "gcp-metadata": "^6.1.0",
-            "gtoken": "^7.0.0",
-            "jws": "^4.0.0"
-          }
-        },
-        "stream-shift": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
-          "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
-          "optional": true
-        },
         "uuid": {
           "version": "8.3.2",
           "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@@ -781,9 +714,9 @@
       }
     },
     "@grpc/grpc-js": {
-      "version": "1.10.4",
-      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.4.tgz",
-      "integrity": "sha512-MqBisuxTkYvPFnEiu+dag3xG/NBUDzSbAFAWlzfkGnQkjVZ6by3h4atbBc+Ikqup1z5BfB4BN18gKWR1YyppNw==",
+      "version": "1.10.6",
+      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.6.tgz",
+      "integrity": "sha512-xP58G7wDQ4TCmN/cMUHh00DS7SRDv/+lC+xFLrTkMIN8h55X5NhZMLYbvy7dSELP15qlI6hPhNCRWVMtZMwqLA==",
       "optional": true,
       "requires": {
         "@grpc/proto-loader": "^0.7.10",
@@ -820,9 +753,9 @@
       "dev": true
     },
     "@humanwhocodes/object-schema": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
-      "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+      "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
       "dev": true
     },
     "@istanbuljs/load-nyc-config": {
@@ -906,26 +839,26 @@
       "dev": true
     },
     "@jridgewell/gen-mapping": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
-      "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+      "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
       "dev": true,
       "requires": {
-        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/set-array": "^1.2.1",
         "@jridgewell/sourcemap-codec": "^1.4.10",
-        "@jridgewell/trace-mapping": "^0.3.9"
+        "@jridgewell/trace-mapping": "^0.3.24"
       }
     },
     "@jridgewell/resolve-uri": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
-      "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
       "dev": true
     },
     "@jridgewell/set-array": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
-      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
       "dev": true
     },
     "@jridgewell/sourcemap-codec": {
@@ -935,9 +868,9 @@
       "dev": true
     },
     "@jridgewell/trace-mapping": {
-      "version": "0.3.20",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
-      "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
+      "version": "0.3.25",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
       "dev": true,
       "requires": {
         "@jridgewell/resolve-uri": "^3.1.0",
@@ -1041,13 +974,13 @@
             "string-argv": "~0.3.1"
           }
         },
-        "minimatch": {
-          "version": "3.0.8",
-          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
-          "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
+        "semver": {
+          "version": "7.5.4",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+          "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
           "dev": true,
           "requires": {
-            "brace-expansion": "^1.1.7"
+            "lru-cache": "^6.0.0"
           }
         },
         "typescript": {
@@ -1088,6 +1021,15 @@
             "semver": "~7.5.4",
             "z-schema": "~5.0.2"
           }
+        },
+        "semver": {
+          "version": "7.5.4",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+          "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
         }
       }
     },
@@ -1237,6 +1179,15 @@
           "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz",
           "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==",
           "dev": true
+        },
+        "semver": {
+          "version": "7.5.4",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+          "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
         }
       }
     },
@@ -1248,6 +1199,14 @@
       "requires": {
         "resolve": "~1.22.1",
         "strip-json-comments": "~3.1.1"
+      },
+      "dependencies": {
+        "strip-json-comments": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+          "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+          "dev": true
+        }
       }
     },
     "@rushstack/terminal": {
@@ -1274,13 +1233,13 @@
             "z-schema": "~5.0.2"
           }
         },
-        "supports-color": {
-          "version": "8.1.1",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
-          "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+        "semver": {
+          "version": "7.5.4",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+          "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
           "dev": true,
           "requires": {
-            "has-flag": "^4.0.0"
+            "lru-cache": "^6.0.0"
           }
         }
       }
@@ -1306,9 +1265,9 @@
       }
     },
     "@sinonjs/commons": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
-      "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+      "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
       "dev": true,
       "requires": {
         "type-detect": "4.0.8"
@@ -1358,9 +1317,9 @@
       "optional": true
     },
     "@tsconfig/node10": {
-      "version": "1.0.9",
-      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
-      "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
+      "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
       "dev": true
     },
     "@tsconfig/node12": {
@@ -1417,9 +1376,9 @@
       "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg=="
     },
     "@types/chai": {
-      "version": "4.3.11",
-      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz",
-      "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==",
+      "version": "4.3.14",
+      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.14.tgz",
+      "integrity": "sha512-Wj71sXE4Q4AkGdG9Tvq1u/fquNz9EdG4LIJMwVVII7ashjD/8cf8fyIfJAjRr6YcsXnSE8cOGQPq1gqeR8z+3w==",
       "dev": true
     },
     "@types/chai-as-promised": {
@@ -1451,9 +1410,9 @@
       }
     },
     "@types/express-serve-static-core": {
-      "version": "4.17.41",
-      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz",
-      "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==",
+      "version": "4.19.0",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz",
+      "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==",
       "requires": {
         "@types/node": "*",
         "@types/qs": "*",
@@ -1488,9 +1447,9 @@
       }
     },
     "@types/lodash": {
-      "version": "4.14.202",
-      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
-      "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
+      "version": "4.17.0",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz",
+      "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==",
       "dev": true
     },
     "@types/long": {
@@ -1532,17 +1491,17 @@
       }
     },
     "@types/node": {
-      "version": "20.12.2",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.2.tgz",
-      "integrity": "sha512-zQ0NYO87hyN6Xrclcqp7f8ZbXNbRfoGWNcMvHTPQp9UUrwI0mI7XBz+cu7/W6/VClYo2g63B0cjull/srU7LgQ==",
+      "version": "20.12.6",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.6.tgz",
+      "integrity": "sha512-3KurE8taB8GCvZBPngVbp0lk5CKi8M9f9k1rsADh0Evdz5SzJ+Q+Hx9uHoFGsLnLnd1xmkDQr2hVhlA0Mn0lKQ==",
       "requires": {
         "undici-types": "~5.26.4"
       }
     },
     "@types/qs": {
-      "version": "6.9.10",
-      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz",
-      "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw=="
+      "version": "6.9.14",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.14.tgz",
+      "integrity": "sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA=="
     },
     "@types/range-parser": {
       "version": "1.2.7",
@@ -1571,9 +1530,9 @@
       }
     },
     "@types/semver": {
-      "version": "7.5.6",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz",
-      "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
+      "version": "7.5.8",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+      "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
       "dev": true
     },
     "@types/send": {
@@ -1586,13 +1545,13 @@
       }
     },
     "@types/serve-static": {
-      "version": "1.15.5",
-      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz",
-      "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==",
+      "version": "1.15.7",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
+      "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
       "requires": {
         "@types/http-errors": "*",
-        "@types/mime": "*",
-        "@types/node": "*"
+        "@types/node": "*",
+        "@types/send": "*"
       }
     },
     "@types/sinon": {
@@ -1752,9 +1711,9 @@
       }
     },
     "acorn": {
-      "version": "8.11.2",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
-      "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
+      "version": "8.11.3",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+      "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
       "dev": true
     },
     "acorn-jsx": {
@@ -1770,9 +1729,9 @@
       "dev": true
     },
     "agent-base": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
-      "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
+      "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
       "optional": true,
       "requires": {
         "debug": "^4.3.4"
@@ -2161,13 +2120,13 @@
       "dev": true
     },
     "array-buffer-byte-length": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
-      "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
+      "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "is-array-buffer": "^3.0.1"
+        "call-bind": "^1.0.5",
+        "is-array-buffer": "^3.0.4"
       }
     },
     "array-differ": {
@@ -2255,17 +2214,18 @@
       "dev": true
     },
     "arraybuffer.prototype.slice": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz",
-      "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
+      "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
       "dev": true,
       "requires": {
-        "array-buffer-byte-length": "^1.0.0",
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1",
-        "get-intrinsic": "^1.2.1",
-        "is-array-buffer": "^3.0.2",
+        "array-buffer-byte-length": "^1.0.1",
+        "call-bind": "^1.0.5",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.22.3",
+        "es-errors": "^1.2.1",
+        "get-intrinsic": "^1.2.3",
+        "is-array-buffer": "^3.0.4",
         "is-shared-array-buffer": "^1.0.2"
       }
     },
@@ -2349,10 +2309,13 @@
       "dev": true
     },
     "available-typed-arrays": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
-      "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
-      "dev": true
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+      "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+      "dev": true,
+      "requires": {
+        "possible-typed-array-names": "^1.0.0"
+      }
     },
     "aws-sign2": {
       "version": "0.7.0",
@@ -2471,6 +2434,16 @@
         "file-uri-to-path": "1.0.0"
       }
     },
+    "bl": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+      "requires": {
+        "buffer": "^5.5.0",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.4.0"
+      }
+    },
     "bluebird": {
       "version": "3.7.2",
       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@@ -2503,26 +2476,24 @@
       "dev": true
     },
     "browserslist": {
-      "version": "4.22.2",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz",
-      "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
+      "version": "4.23.0",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+      "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
       "dev": true,
       "requires": {
-        "caniuse-lite": "^1.0.30001565",
-        "electron-to-chromium": "^1.4.601",
+        "caniuse-lite": "^1.0.30001587",
+        "electron-to-chromium": "^1.4.668",
         "node-releases": "^2.0.14",
         "update-browserslist-db": "^1.0.13"
       }
     },
     "buffer": {
-      "version": "4.9.2",
-      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
-      "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
-      "dev": true,
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
       "requires": {
-        "base64-js": "^1.0.2",
-        "ieee754": "^1.1.4",
-        "isarray": "^1.0.0"
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
       }
     },
     "buffer-equal": {
@@ -2572,14 +2543,16 @@
       }
     },
     "call-bind": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
-      "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+      "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
       "dev": true,
       "requires": {
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
         "function-bind": "^1.1.2",
-        "get-intrinsic": "^1.2.1",
-        "set-function-length": "^1.1.1"
+        "get-intrinsic": "^1.2.4",
+        "set-function-length": "^1.2.1"
       }
     },
     "callsites": {
@@ -2595,9 +2568,9 @@
       "dev": true
     },
     "caniuse-lite": {
-      "version": "1.0.30001566",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz",
-      "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==",
+      "version": "1.0.30001607",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001607.tgz",
+      "integrity": "sha512-WcvhVRjXLKFB/kmOFVwELtMxyhq3iM/MvmXcyCe2PNf166c39mptscOc/45TTS96n2gpNV2z7+NakArTWZCQ3w==",
       "dev": true
     },
     "caseless": {
@@ -2647,6 +2620,17 @@
       "requires": {
         "ansi-styles": "^4.1.0",
         "supports-color": "^7.1.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
       }
     },
     "check-error": {
@@ -2780,10 +2764,9 @@
       }
     },
     "chownr": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
-      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
-      "dev": true
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
     },
     "class-utils": {
       "version": "0.3.6",
@@ -3109,13 +3092,13 @@
       }
     },
     "d": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz",
+      "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==",
       "dev": true,
       "requires": {
-        "es5-ext": "^0.10.50",
-        "type": "^1.0.1"
+        "es5-ext": "^0.10.64",
+        "type": "^2.7.2"
       }
     },
     "dashdash": {
@@ -3127,6 +3110,39 @@
         "assert-plus": "^1.0.0"
       }
     },
+    "data-view-buffer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
+      "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.6",
+        "es-errors": "^1.3.0",
+        "is-data-view": "^1.0.1"
+      }
+    },
+    "data-view-byte-length": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz",
+      "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.7",
+        "es-errors": "^1.3.0",
+        "is-data-view": "^1.0.1"
+      }
+    },
+    "data-view-byte-offset": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz",
+      "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.6",
+        "es-errors": "^1.3.0",
+        "is-data-view": "^1.0.1"
+      }
+    },
     "debug": {
       "version": "4.3.4",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -3147,6 +3163,14 @@
       "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
       "dev": true
     },
+    "decompress-response": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+      "requires": {
+        "mimic-response": "^3.1.0"
+      }
+    },
     "deep-eql": {
       "version": "4.1.3",
       "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
@@ -3156,6 +3180,11 @@
         "type-detect": "^4.0.0"
       }
     },
+    "deep-extend": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+      "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+    },
     "deep-is": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -3203,14 +3232,14 @@
       "dev": true
     },
     "define-data-property": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
-      "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
       "dev": true,
       "requires": {
-        "get-intrinsic": "^1.2.1",
-        "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.0"
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.0.1"
       }
     },
     "define-properties": {
@@ -3280,10 +3309,9 @@
       "dev": true
     },
     "detect-libc": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
-      "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
-      "dev": true
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
+      "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="
     },
     "diff": {
       "version": "5.0.0",
@@ -3319,14 +3347,6 @@
         "inherits": "^2.0.3",
         "readable-stream": "^3.1.1",
         "stream-shift": "^1.0.2"
-      },
-      "dependencies": {
-        "stream-shift": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
-          "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
-          "optional": true
-        }
       }
     },
     "each-props": {
@@ -3358,9 +3378,9 @@
       }
     },
     "electron-to-chromium": {
-      "version": "1.4.605",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.605.tgz",
-      "integrity": "sha512-V52j+P5z6cdRqTjPR/bYNxx7ETCHIkm5VIGuyCy3CMrfSnbEpIlLnk5oHmZo7gYvDfh2TfHeanB6rawyQ23ktg==",
+      "version": "1.4.730",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.730.tgz",
+      "integrity": "sha512-oJRPo82XEqtQAobHpJIR3zW5YO3sSRRkPz2an4yxi1UvqhsGm54vR/wzTFV74a3soDOJ8CKW7ajOOX5ESzddwg==",
       "dev": true
     },
     "emoji-regex": {
@@ -3392,61 +3412,92 @@
       }
     },
     "es-abstract": {
-      "version": "1.22.3",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
-      "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==",
-      "dev": true,
-      "requires": {
-        "array-buffer-byte-length": "^1.0.0",
-        "arraybuffer.prototype.slice": "^1.0.2",
-        "available-typed-arrays": "^1.0.5",
-        "call-bind": "^1.0.5",
-        "es-set-tostringtag": "^2.0.1",
+      "version": "1.23.3",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
+      "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
+      "dev": true,
+      "requires": {
+        "array-buffer-byte-length": "^1.0.1",
+        "arraybuffer.prototype.slice": "^1.0.3",
+        "available-typed-arrays": "^1.0.7",
+        "call-bind": "^1.0.7",
+        "data-view-buffer": "^1.0.1",
+        "data-view-byte-length": "^1.0.1",
+        "data-view-byte-offset": "^1.0.0",
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.0.0",
+        "es-set-tostringtag": "^2.0.3",
         "es-to-primitive": "^1.2.1",
         "function.prototype.name": "^1.1.6",
-        "get-intrinsic": "^1.2.2",
-        "get-symbol-description": "^1.0.0",
+        "get-intrinsic": "^1.2.4",
+        "get-symbol-description": "^1.0.2",
         "globalthis": "^1.0.3",
         "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.0",
-        "has-proto": "^1.0.1",
+        "has-property-descriptors": "^1.0.2",
+        "has-proto": "^1.0.3",
         "has-symbols": "^1.0.3",
-        "hasown": "^2.0.0",
-        "internal-slot": "^1.0.5",
-        "is-array-buffer": "^3.0.2",
+        "hasown": "^2.0.2",
+        "internal-slot": "^1.0.7",
+        "is-array-buffer": "^3.0.4",
         "is-callable": "^1.2.7",
-        "is-negative-zero": "^2.0.2",
+        "is-data-view": "^1.0.1",
+        "is-negative-zero": "^2.0.3",
         "is-regex": "^1.1.4",
-        "is-shared-array-buffer": "^1.0.2",
+        "is-shared-array-buffer": "^1.0.3",
         "is-string": "^1.0.7",
-        "is-typed-array": "^1.1.12",
+        "is-typed-array": "^1.1.13",
         "is-weakref": "^1.0.2",
         "object-inspect": "^1.13.1",
         "object-keys": "^1.1.1",
-        "object.assign": "^4.1.4",
-        "regexp.prototype.flags": "^1.5.1",
-        "safe-array-concat": "^1.0.1",
-        "safe-regex-test": "^1.0.0",
-        "string.prototype.trim": "^1.2.8",
-        "string.prototype.trimend": "^1.0.7",
-        "string.prototype.trimstart": "^1.0.7",
-        "typed-array-buffer": "^1.0.0",
-        "typed-array-byte-length": "^1.0.0",
-        "typed-array-byte-offset": "^1.0.0",
-        "typed-array-length": "^1.0.4",
+        "object.assign": "^4.1.5",
+        "regexp.prototype.flags": "^1.5.2",
+        "safe-array-concat": "^1.1.2",
+        "safe-regex-test": "^1.0.3",
+        "string.prototype.trim": "^1.2.9",
+        "string.prototype.trimend": "^1.0.8",
+        "string.prototype.trimstart": "^1.0.8",
+        "typed-array-buffer": "^1.0.2",
+        "typed-array-byte-length": "^1.0.1",
+        "typed-array-byte-offset": "^1.0.2",
+        "typed-array-length": "^1.0.6",
         "unbox-primitive": "^1.0.2",
-        "which-typed-array": "^1.1.13"
+        "which-typed-array": "^1.1.15"
+      }
+    },
+    "es-define-property": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+      "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+      "dev": true,
+      "requires": {
+        "get-intrinsic": "^1.2.4"
+      }
+    },
+    "es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "dev": true
+    },
+    "es-object-atoms": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
+      "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
+      "dev": true,
+      "requires": {
+        "es-errors": "^1.3.0"
       }
     },
     "es-set-tostringtag": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz",
-      "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
+      "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
       "dev": true,
       "requires": {
-        "get-intrinsic": "^1.2.2",
-        "has-tostringtag": "^1.0.0",
-        "hasown": "^2.0.0"
+        "get-intrinsic": "^1.2.4",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.1"
       }
     },
     "es-to-primitive": {
@@ -3461,13 +3512,14 @@
       }
     },
     "es5-ext": {
-      "version": "0.10.62",
-      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
-      "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+      "version": "0.10.64",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
+      "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
       "dev": true,
       "requires": {
         "es6-iterator": "^2.0.3",
         "es6-symbol": "^3.1.3",
+        "esniff": "^2.0.1",
         "next-tick": "^1.1.0"
       }
     },
@@ -3489,13 +3541,13 @@
       }
     },
     "es6-symbol": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
-      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz",
+      "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==",
       "dev": true,
       "requires": {
-        "d": "^1.0.1",
-        "ext": "^1.1.2"
+        "d": "^1.0.2",
+        "ext": "^1.7.0"
       }
     },
     "es6-weak-map": {
@@ -3511,9 +3563,9 @@
       }
     },
     "escalade": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
-      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+      "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA=="
     },
     "escape-string-regexp": {
       "version": "4.0.0",
@@ -3603,6 +3655,15 @@
             "is-glob": "^4.0.3"
           }
         },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        },
         "which": {
           "version": "2.0.2",
           "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -3624,12 +3685,24 @@
         "estraverse": "^4.1.1"
       }
     },
-    "eslint-visitor-keys": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
-      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
-      "dev": true
-    },
+    "eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true
+    },
+    "esniff": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
+      "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
+      "dev": true,
+      "requires": {
+        "d": "^1.0.1",
+        "es5-ext": "^0.10.62",
+        "event-emitter": "^0.3.5",
+        "type": "^2.7.2"
+      }
+    },
     "espree": {
       "version": "9.6.1",
       "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
@@ -3693,6 +3766,16 @@
       "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
       "dev": true
     },
+    "event-emitter": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
+      "dev": true,
+      "requires": {
+        "d": "1",
+        "es5-ext": "~0.10.14"
+      }
+    },
     "event-target-shim": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
@@ -3749,6 +3832,11 @@
         }
       }
     },
+    "expand-template": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+      "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
+    },
     "expand-tilde": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
@@ -3765,14 +3853,6 @@
       "dev": true,
       "requires": {
         "type": "^2.7.2"
-      },
-      "dependencies": {
-        "type": {
-          "version": "2.7.2",
-          "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
-          "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==",
-          "dev": true
-        }
       }
     },
     "extend": {
@@ -3865,6 +3945,15 @@
         "time-stamp": "^1.0.0"
       }
     },
+    "farmhash": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/farmhash/-/farmhash-3.3.0.tgz",
+      "integrity": "sha512-IZJWJXvX+TZJ4qZrcRZkDqI66s4VxrRD+NsduTSe0PZ9BGEDB53S0cd+e4rTXIWbL5k213W8cN6pMZuPVA+z0Q==",
+      "requires": {
+        "node-addon-api": "^5.1.0",
+        "prebuild-install": "^7.1.1"
+      }
+    },
     "fast-deep-equal": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -3905,9 +3994,9 @@
       }
     },
     "fastq": {
-      "version": "1.15.0",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
-      "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+      "version": "1.17.1",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+      "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
       "dev": true,
       "requires": {
         "reusify": "^1.0.4"
@@ -4267,6 +4356,11 @@
       "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
       "dev": true
     },
+    "fs-constants": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+      "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+    },
     "fs-extra": {
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
@@ -4373,15 +4467,16 @@
       }
     },
     "gaxios": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz",
-      "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==",
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.4.0.tgz",
+      "integrity": "sha512-apAloYrY4dlBGlhauDAYSZveafb5U6+L9titing1wox6BvWM0TSXBp603zTrLpyLMGkrcFgohnUN150dFN/zOA==",
       "optional": true,
       "requires": {
         "extend": "^3.0.2",
         "https-proxy-agent": "^7.0.1",
         "is-stream": "^2.0.0",
-        "node-fetch": "^2.6.9"
+        "node-fetch": "^2.6.9",
+        "uuid": "^9.0.1"
       }
     },
     "gcp-metadata": {
@@ -4412,11 +4507,12 @@
       "dev": true
     },
     "get-intrinsic": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
-      "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+      "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
       "dev": true,
       "requires": {
+        "es-errors": "^1.3.0",
         "function-bind": "^1.1.2",
         "has-proto": "^1.0.1",
         "has-symbols": "^1.0.3",
@@ -4436,13 +4532,14 @@
       "dev": true
     },
     "get-symbol-description": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
-      "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
+      "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.1.1"
+        "call-bind": "^1.0.5",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.4"
       }
     },
     "get-value": {
@@ -4460,6 +4557,11 @@
         "assert-plus": "^1.0.0"
       }
     },
+    "github-from-package": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+      "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
+    },
     "glob": {
       "version": "7.2.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -4472,6 +4574,17 @@
         "minimatch": "^3.1.1",
         "once": "^1.3.0",
         "path-is-absolute": "^1.0.0"
+      },
+      "dependencies": {
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        }
       }
     },
     "glob-parent": {
@@ -4688,9 +4801,9 @@
       "dev": true
     },
     "gtoken": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz",
-      "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
+      "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
       "optional": true,
       "requires": {
         "gaxios": "^6.0.0",
@@ -4953,18 +5066,18 @@
       "dev": true
     },
     "has-property-descriptors": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
-      "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
       "dev": true,
       "requires": {
-        "get-intrinsic": "^1.2.2"
+        "es-define-property": "^1.0.0"
       }
     },
     "has-proto": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
-      "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+      "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
       "dev": true
     },
     "has-symbols": {
@@ -4974,12 +5087,12 @@
       "dev": true
     },
     "has-tostringtag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
-      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
       "dev": true,
       "requires": {
-        "has-symbols": "^1.0.2"
+        "has-symbols": "^1.0.3"
       }
     },
     "has-unicode": {
@@ -5059,9 +5172,9 @@
       }
     },
     "hasown": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
-      "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
       "dev": true,
       "requires": {
         "function-bind": "^1.1.2"
@@ -5105,6 +5218,19 @@
         "get-prop": "0.0.10",
         "minimist": "^1.2.0",
         "stream-buffers": "^3.0.0"
+      },
+      "dependencies": {
+        "buffer": {
+          "version": "4.9.2",
+          "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+          "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+          "dev": true,
+          "requires": {
+            "base64-js": "^1.0.2",
+            "ieee754": "^1.1.4",
+            "isarray": "^1.0.0"
+          }
+        }
       }
     },
     "http-parser-js": {
@@ -5146,9 +5272,9 @@
       }
     },
     "https-proxy-agent": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz",
-      "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==",
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz",
+      "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==",
       "optional": true,
       "requires": {
         "agent-base": "^7.0.2",
@@ -5164,13 +5290,12 @@
     "ieee754": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
-      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
-      "dev": true
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
     },
     "ignore": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
-      "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+      "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
       "dev": true
     },
     "import-fresh": {
@@ -5219,16 +5344,15 @@
     "ini": {
       "version": "1.3.8",
       "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
-      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
-      "dev": true
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
     },
     "internal-slot": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz",
-      "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==",
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
+      "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
       "dev": true,
       "requires": {
-        "get-intrinsic": "^1.2.2",
+        "es-errors": "^1.3.0",
         "hasown": "^2.0.0",
         "side-channel": "^1.0.4"
       }
@@ -5265,14 +5389,13 @@
       }
     },
     "is-array-buffer": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
-      "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
+      "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
       "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.2.0",
-        "is-typed-array": "^1.1.10"
+        "get-intrinsic": "^1.2.1"
       }
     },
     "is-arrayish": {
@@ -5339,6 +5462,15 @@
         "hasown": "^2.0.0"
       }
     },
+    "is-data-view": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz",
+      "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==",
+      "dev": true,
+      "requires": {
+        "is-typed-array": "^1.1.13"
+      }
+    },
     "is-date-object": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
@@ -5391,9 +5523,9 @@
       "dev": true
     },
     "is-negative-zero": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
-      "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+      "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
       "dev": true
     },
     "is-number": {
@@ -5458,12 +5590,12 @@
       }
     },
     "is-shared-array-buffer": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
-      "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz",
+      "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2"
+        "call-bind": "^1.0.7"
       }
     },
     "is-stream": {
@@ -5490,12 +5622,12 @@
       }
     },
     "is-typed-array": {
-      "version": "1.1.12",
-      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz",
-      "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
+      "version": "1.1.13",
+      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
+      "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
       "dev": true,
       "requires": {
-        "which-typed-array": "^1.1.11"
+        "which-typed-array": "^1.1.14"
       }
     },
     "is-typedarray": {
@@ -5675,6 +5807,15 @@
           "requires": {
             "semver": "^7.5.3"
           }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
         }
       }
     },
@@ -5690,9 +5831,9 @@
       }
     },
     "istanbul-reports": {
-      "version": "3.1.6",
-      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz",
-      "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",
+      "version": "3.1.7",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
+      "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
       "dev": true,
       "requires": {
         "html-escaper": "^2.0.0",
@@ -5862,9 +6003,9 @@
       "dev": true
     },
     "just-extend": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz",
-      "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==",
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz",
+      "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==",
       "dev": true
     },
     "jwa": {
@@ -5892,9 +6033,9 @@
       },
       "dependencies": {
         "@types/jsonwebtoken": {
-          "version": "9.0.5",
-          "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz",
-          "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==",
+          "version": "9.0.6",
+          "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz",
+          "integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==",
           "requires": {
             "@types/node": "*"
           }
@@ -6162,8 +6303,7 @@
     "long": {
       "version": "5.2.3",
       "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
-      "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==",
-      "optional": true
+      "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
     },
     "loupe": {
       "version": "2.3.7",
@@ -6439,10 +6579,15 @@
         "mime-db": "1.52.0"
       }
     },
+    "mimic-response": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
+    },
     "minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
+      "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
       "dev": true,
       "requires": {
         "brace-expansion": "^1.1.7"
@@ -6451,8 +6596,7 @@
     "minimist": {
       "version": "1.2.8",
       "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
-      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
-      "dev": true
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
     },
     "minipass": {
       "version": "5.0.0",
@@ -6508,6 +6652,11 @@
       "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
       "dev": true
     },
+    "mkdirp-classic": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+      "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
+    },
     "mocha": {
       "version": "10.4.0",
       "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz",
@@ -6647,14 +6796,11 @@
             "picomatch": "^2.2.1"
           }
         },
-        "supports-color": {
-          "version": "8.1.1",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
-          "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
+        "strip-json-comments": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+          "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+          "dev": true
         },
         "yargs": {
           "version": "16.2.0",
@@ -6715,9 +6861,9 @@
       }
     },
     "nan": {
-      "version": "2.18.0",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
-      "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==",
+      "version": "2.19.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz",
+      "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==",
       "dev": true,
       "optional": true
     },
@@ -6740,6 +6886,11 @@
         "to-regex": "^3.0.1"
       }
     },
+    "napi-build-utils": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
+      "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
+    },
     "natural-compare": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -6765,47 +6916,16 @@
       "dev": true
     },
     "nise": {
-      "version": "5.1.5",
-      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz",
-      "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==",
+      "version": "5.1.9",
+      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz",
+      "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==",
       "dev": true,
       "requires": {
-        "@sinonjs/commons": "^2.0.0",
-        "@sinonjs/fake-timers": "^10.0.2",
-        "@sinonjs/text-encoding": "^0.7.1",
-        "just-extend": "^4.0.2",
-        "path-to-regexp": "^1.7.0"
-      },
-      "dependencies": {
-        "@sinonjs/commons": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
-          "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
-          "dev": true,
-          "requires": {
-            "type-detect": "4.0.8"
-          }
-        },
-        "@sinonjs/fake-timers": {
-          "version": "10.3.0",
-          "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
-          "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
-          "dev": true,
-          "requires": {
-            "@sinonjs/commons": "^3.0.0"
-          },
-          "dependencies": {
-            "@sinonjs/commons": {
-              "version": "3.0.0",
-              "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
-              "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
-              "dev": true,
-              "requires": {
-                "type-detect": "4.0.8"
-              }
-            }
-          }
-        }
+        "@sinonjs/commons": "^3.0.0",
+        "@sinonjs/fake-timers": "^11.2.2",
+        "@sinonjs/text-encoding": "^0.7.2",
+        "just-extend": "^6.2.0",
+        "path-to-regexp": "^6.2.1"
       }
     },
     "nock": {
@@ -6819,11 +6939,18 @@
         "propagate": "^2.0.0"
       }
     },
+    "node-abi": {
+      "version": "3.57.0",
+      "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.57.0.tgz",
+      "integrity": "sha512-Dp+A9JWxRaKuHP35H77I4kCKesDy5HUDEmScia2FyncMTOXASMyg251F5PhFoDA5uqBrDDffiLpbqnrZmNXW+g==",
+      "requires": {
+        "semver": "^7.3.5"
+      }
+    },
     "node-addon-api": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
-      "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
-      "dev": true
+      "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
     },
     "node-fetch": {
       "version": "2.7.0",
@@ -7586,21 +7713,10 @@
       "dev": true
     },
     "path-to-regexp": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
-      "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
-      "dev": true,
-      "requires": {
-        "isarray": "0.0.1"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "0.0.1",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-          "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
-          "dev": true
-        }
-      }
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz",
+      "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==",
+      "dev": true
     },
     "path-type": {
       "version": "4.0.0",
@@ -7725,6 +7841,31 @@
       "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
       "dev": true
     },
+    "possible-typed-array-names": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
+      "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+      "dev": true
+    },
+    "prebuild-install": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz",
+      "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==",
+      "requires": {
+        "detect-libc": "^2.0.0",
+        "expand-template": "^2.0.3",
+        "github-from-package": "0.0.0",
+        "minimist": "^1.2.3",
+        "mkdirp-classic": "^0.5.3",
+        "napi-build-utils": "^1.0.1",
+        "node-abi": "^3.3.0",
+        "pump": "^3.0.0",
+        "rc": "^1.2.7",
+        "simple-get": "^4.0.0",
+        "tar-fs": "^2.0.0",
+        "tunnel-agent": "^0.6.0"
+      }
+    },
     "prelude-ls": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -7805,10 +7946,9 @@
       "dev": true
     },
     "pump": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
-      "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
-      "dev": true,
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
       "requires": {
         "end-of-stream": "^1.1.0",
         "once": "^1.3.1"
@@ -7837,6 +7977,16 @@
             "stream-shift": "^1.0.0"
           }
         },
+        "pump": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+          "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+          "dev": true,
+          "requires": {
+            "end-of-stream": "^1.1.0",
+            "once": "^1.3.1"
+          }
+        },
         "readable-stream": {
           "version": "2.3.8",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
@@ -7896,6 +8046,17 @@
         "safe-buffer": "^5.1.0"
       }
     },
+    "rc": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+      "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+      "requires": {
+        "deep-extend": "^0.6.0",
+        "ini": "~1.3.0",
+        "minimist": "^1.2.0",
+        "strip-json-comments": "~2.0.1"
+      }
+    },
     "read-pkg": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
@@ -8127,14 +8288,15 @@
       }
     },
     "regexp.prototype.flags": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz",
-      "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
+      "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "set-function-name": "^2.0.0"
+        "call-bind": "^1.0.6",
+        "define-properties": "^1.2.1",
+        "es-errors": "^1.3.0",
+        "set-function-name": "^2.0.1"
       }
     },
     "release-zalgo": {
@@ -8336,13 +8498,12 @@
       "optional": true
     },
     "retry-request": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.1.tgz",
-      "integrity": "sha512-ZI6vJp9rfB71mrZpw+n9p/B6HCsd7QJlSEQftZ+xfJzr3cQ9EPGKw1FF0BnViJ0fYREX6FhymBD2CARpmsFciQ==",
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz",
+      "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==",
       "optional": true,
       "requires": {
         "@types/request": "^2.48.8",
-        "debug": "^4.1.1",
         "extend": "^3.0.2",
         "teeny-request": "^9.0.0"
       }
@@ -8481,13 +8642,13 @@
       }
     },
     "safe-array-concat": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz",
-      "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
+      "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.2.1",
+        "call-bind": "^1.0.7",
+        "get-intrinsic": "^1.2.4",
         "has-symbols": "^1.0.3",
         "isarray": "^2.0.5"
       },
@@ -8515,13 +8676,13 @@
       }
     },
     "safe-regex-test": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
-      "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
+      "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.1.3",
+        "call-bind": "^1.0.6",
+        "es-errors": "^1.3.0",
         "is-regex": "^1.1.4"
       }
     },
@@ -8532,9 +8693,9 @@
       "dev": true
     },
     "semver": {
-      "version": "7.5.4",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
-      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
       "requires": {
         "lru-cache": "^6.0.0"
       }
@@ -8564,26 +8725,29 @@
       "dev": true
     },
     "set-function-length": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
-      "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+      "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
       "dev": true,
       "requires": {
-        "define-data-property": "^1.1.1",
-        "get-intrinsic": "^1.2.1",
+        "define-data-property": "^1.1.4",
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2",
+        "get-intrinsic": "^1.2.4",
         "gopd": "^1.0.1",
-        "has-property-descriptors": "^1.0.0"
+        "has-property-descriptors": "^1.0.2"
       }
     },
     "set-function-name": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz",
-      "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+      "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
       "dev": true,
       "requires": {
-        "define-data-property": "^1.0.1",
+        "define-data-property": "^1.1.4",
+        "es-errors": "^1.3.0",
         "functions-have-names": "^1.2.3",
-        "has-property-descriptors": "^1.0.0"
+        "has-property-descriptors": "^1.0.2"
       }
     },
     "set-value": {
@@ -8631,14 +8795,15 @@
       "dev": true
     },
     "side-channel": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
-      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+      "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.0",
-        "get-intrinsic": "^1.0.2",
-        "object-inspect": "^1.9.0"
+        "call-bind": "^1.0.7",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.4",
+        "object-inspect": "^1.13.1"
       }
     },
     "signal-exit": {
@@ -8647,6 +8812,21 @@
       "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
       "dev": true
     },
+    "simple-concat": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
+      "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
+    },
+    "simple-get": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
+      "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+      "requires": {
+        "decompress-response": "^6.0.0",
+        "once": "^1.3.1",
+        "simple-concat": "^1.0.0"
+      }
+    },
     "sinon": {
       "version": "17.0.1",
       "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz",
@@ -8662,10 +8842,19 @@
       },
       "dependencies": {
         "diff": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
-          "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==",
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
+          "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
           "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
         }
       }
     },
@@ -8857,9 +9046,9 @@
       }
     },
     "spdx-exceptions": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
-      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
+      "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
       "dev": true
     },
     "spdx-expression-parse": {
@@ -8873,9 +9062,9 @@
       }
     },
     "spdx-license-ids": {
-      "version": "3.0.16",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz",
-      "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==",
+      "version": "3.0.17",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
+      "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==",
       "dev": true
     },
     "split-string": {
@@ -8965,10 +9154,9 @@
       "dev": true
     },
     "stream-shift": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
-      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
-      "dev": true
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
+      "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="
     },
     "streamfilter": {
       "version": "3.0.0",
@@ -8996,47 +9184,49 @@
       }
     },
     "string.prototype.padend": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz",
-      "integrity": "sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==",
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz",
+      "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1"
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.2",
+        "es-object-atoms": "^1.0.0"
       }
     },
     "string.prototype.trim": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz",
-      "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==",
+      "version": "1.2.9",
+      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz",
+      "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1"
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-abstract": "^1.23.0",
+        "es-object-atoms": "^1.0.0"
       }
     },
     "string.prototype.trimend": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz",
-      "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==",
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz",
+      "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1"
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.0.0"
       }
     },
     "string.prototype.trimstart": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz",
-      "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==",
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+      "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.2.0",
-        "es-abstract": "^1.22.1"
+        "call-bind": "^1.0.7",
+        "define-properties": "^1.2.1",
+        "es-object-atoms": "^1.0.0"
       }
     },
     "string_decoder": {
@@ -9065,10 +9255,9 @@
       }
     },
     "strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-      "dev": true
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
     },
     "strnum": {
       "version": "1.0.5",
@@ -9083,9 +9272,9 @@
       "optional": true
     },
     "supports-color": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
       "dev": true,
       "requires": {
         "has-flag": "^4.0.0"
@@ -9108,9 +9297,9 @@
       }
     },
     "tar": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
-      "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
+      "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
       "dev": true,
       "requires": {
         "chownr": "^2.0.0",
@@ -9119,6 +9308,37 @@
         "minizlib": "^2.1.1",
         "mkdirp": "^1.0.3",
         "yallist": "^4.0.0"
+      },
+      "dependencies": {
+        "chownr": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+          "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+          "dev": true
+        }
+      }
+    },
+    "tar-fs": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
+      "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
+      "requires": {
+        "chownr": "^1.1.1",
+        "mkdirp-classic": "^0.5.2",
+        "pump": "^3.0.0",
+        "tar-stream": "^2.1.4"
+      }
+    },
+    "tar-stream": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+      "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+      "requires": {
+        "bl": "^4.0.3",
+        "end-of-stream": "^1.4.1",
+        "fs-constants": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^3.1.1"
       }
     },
     "teeny-request": {
@@ -9390,7 +9610,6 @@
       "version": "0.6.0",
       "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
       "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
-      "dev": true,
       "requires": {
         "safe-buffer": "^5.0.1"
       }
@@ -9402,9 +9621,9 @@
       "dev": true
     },
     "type": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-      "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+      "version": "2.7.2",
+      "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
+      "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==",
       "dev": true
     },
     "type-check": {
@@ -9429,50 +9648,55 @@
       "dev": true
     },
     "typed-array-buffer": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz",
-      "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz",
+      "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.2.1",
-        "is-typed-array": "^1.1.10"
+        "call-bind": "^1.0.7",
+        "es-errors": "^1.3.0",
+        "is-typed-array": "^1.1.13"
       }
     },
     "typed-array-byte-length": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz",
-      "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz",
+      "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
+        "call-bind": "^1.0.7",
         "for-each": "^0.3.3",
-        "has-proto": "^1.0.1",
-        "is-typed-array": "^1.1.10"
+        "gopd": "^1.0.1",
+        "has-proto": "^1.0.3",
+        "is-typed-array": "^1.1.13"
       }
     },
     "typed-array-byte-offset": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz",
-      "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz",
+      "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==",
       "dev": true,
       "requires": {
-        "available-typed-arrays": "^1.0.5",
-        "call-bind": "^1.0.2",
+        "available-typed-arrays": "^1.0.7",
+        "call-bind": "^1.0.7",
         "for-each": "^0.3.3",
-        "has-proto": "^1.0.1",
-        "is-typed-array": "^1.1.10"
+        "gopd": "^1.0.1",
+        "has-proto": "^1.0.3",
+        "is-typed-array": "^1.1.13"
       }
     },
     "typed-array-length": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
-      "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz",
+      "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==",
       "dev": true,
       "requires": {
-        "call-bind": "^1.0.2",
+        "call-bind": "^1.0.7",
         "for-each": "^0.3.3",
-        "is-typed-array": "^1.1.9"
+        "gopd": "^1.0.1",
+        "has-proto": "^1.0.3",
+        "is-typed-array": "^1.1.13",
+        "possible-typed-array-names": "^1.0.0"
       }
     },
     "typedarray": {
@@ -9877,16 +10101,16 @@
       "dev": true
     },
     "which-typed-array": {
-      "version": "1.1.13",
-      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz",
-      "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==",
+      "version": "1.1.15",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
+      "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
       "dev": true,
       "requires": {
-        "available-typed-arrays": "^1.0.5",
-        "call-bind": "^1.0.4",
+        "available-typed-arrays": "^1.0.7",
+        "call-bind": "^1.0.7",
         "for-each": "^0.3.3",
         "gopd": "^1.0.1",
-        "has-tostringtag": "^1.0.0"
+        "has-tostringtag": "^1.0.2"
       }
     },
     "wide-align": {
diff --git a/package.json b/package.json
index 5391d11c81..95e6a03557 100644
--- a/package.json
+++ b/package.json
@@ -200,8 +200,10 @@
     "@firebase/database-compat": "^1.0.2",
     "@firebase/database-types": "^1.0.0",
     "@types/node": "^20.10.3",
+    "farmhash": "^3.3.0",
     "jsonwebtoken": "^9.0.0",
     "jwks-rsa": "^3.0.1",
+    "long": "^5.2.3",
     "node-forge": "^1.3.1",
     "uuid": "^9.0.0"
   },
diff --git a/src/remote-config/condition-evaluator-internal.ts b/src/remote-config/condition-evaluator-internal.ts
new file mode 100644
index 0000000000..b23958cd77
--- /dev/null
+++ b/src/remote-config/condition-evaluator-internal.ts
@@ -0,0 +1,178 @@
+/*!
+ * Copyright 2024 Google Inc.
+ *
+ * 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.
+ */
+
+'use strict';
+
+import {
+  AndCondition,
+  OneOfCondition,
+  EvaluationContext,
+  NamedCondition,
+  OrCondition,
+  PercentCondition,
+  PercentConditionOperator
+} from './remote-config-api';
+import * as farmhash from 'farmhash';
+import long = require('long');
+
+/**
+ * Encapsulates condition evaluation logic to simplify organization and
+ * facilitate testing.
+ *
+ * @internal
+ */
+export class ConditionEvaluator {
+  private static MAX_CONDITION_RECURSION_DEPTH = 10;
+
+  public evaluateConditions(
+    namedConditions: NamedCondition[],
+    context: EvaluationContext): Map<string, boolean> {
+    // The order of the conditions is significant.
+    // A JS Map preserves the order of insertion ("Iteration happens in insertion order"
+    // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#description).
+    const evaluatedConditions = new Map();
+
+    for (const namedCondition of namedConditions) {
+      evaluatedConditions.set(
+        namedCondition.name,
+        this.evaluateCondition(namedCondition.condition, context));
+    }
+
+    return evaluatedConditions;
+  }
+
+  private evaluateCondition(
+    condition: OneOfCondition,
+    context: EvaluationContext,
+    nestingLevel = 0): boolean {
+    if (nestingLevel >= ConditionEvaluator.MAX_CONDITION_RECURSION_DEPTH) {
+      // TODO: add logging once we have a wrapped logger.
+      return false;
+    }
+    if (condition.orCondition) {
+      return this.evaluateOrCondition(condition.orCondition, context, nestingLevel + 1)
+    }
+    if (condition.andCondition) {
+      return this.evaluateAndCondition(condition.andCondition, context, nestingLevel + 1)
+    }
+    if (condition.true) {
+      return true;
+    }
+    if (condition.false) {
+      return false;
+    }
+    if (condition.percent) {
+      return this.evaluatePercentCondition(condition.percent, context);
+    }
+    // TODO: add logging once we have a wrapped logger.
+    return false;
+  }
+
+  private evaluateOrCondition(
+    orCondition: OrCondition,
+    context: EvaluationContext,
+    nestingLevel: number): boolean {
+
+    const subConditions = orCondition.conditions || [];
+
+    for (const subCondition of subConditions) {
+      // Recursive call.
+      const result = this.evaluateCondition(
+        subCondition, context, nestingLevel + 1);
+
+      // Short-circuit the evaluation result for true.
+      if (result) {
+        return result;
+      }
+    }
+    return false;
+  }
+
+  private evaluateAndCondition(
+    andCondition: AndCondition,
+    context: EvaluationContext,
+    nestingLevel: number): boolean {
+
+    const subConditions = andCondition.conditions || [];
+
+    for (const subCondition of subConditions) {
+      // Recursive call.
+      const result = this.evaluateCondition(
+        subCondition, context, nestingLevel + 1);
+
+      // Short-circuit the evaluation result for false.
+      if (!result) {
+        return result;
+      }
+    }
+    return true;
+  }
+
+  private evaluatePercentCondition(
+    percentCondition: PercentCondition,
+    context: EvaluationContext
+  ): boolean {
+    if (!context.randomizationId) {
+      // TODO: add logging once we have a wrapped logger.
+      return false;
+    }
+
+    // This is the entry point for processing percent condition data from the response.
+    // We're not using a proto library, so we can't assume undefined fields have
+    // default values.
+    const { seed, percentOperator, microPercent, microPercentRange } = percentCondition;
+
+    if (!percentOperator) {
+      // TODO: add logging once we have a wrapped logger.
+      return false;
+    }
+
+    const normalizedMicroPercent = microPercent || 0;
+    const normalizedMicroPercentUpperBound = microPercentRange?.microPercentUpperBound || 0;
+    const normalizedMicroPercentLowerBound = microPercentRange?.microPercentLowerBound || 0;
+
+    const seedPrefix = seed && seed.length > 0 ? `${seed}.` : '';
+    const stringToHash = `${seedPrefix}${context.randomizationId}`;
+
+
+    // Using a 64-bit long for consistency with the Remote Config fetch endpoint.
+    let hash64 = long.fromString(farmhash.fingerprint64(stringToHash));
+
+    // Negate the hash if its value is less than 0. We handle this manually because the
+    // Long library doesn't provided an absolute value method.
+    if (hash64.lt(0)) {
+      hash64 = hash64.negate();
+    }
+
+    const instanceMicroPercentile = hash64.mod(100 * 1_000_000);
+    
+    switch (percentOperator) {
+    case PercentConditionOperator.LESS_OR_EQUAL:
+      return instanceMicroPercentile.lte(normalizedMicroPercent);
+    case PercentConditionOperator.GREATER_THAN:
+      return instanceMicroPercentile.gt(normalizedMicroPercent);
+    case PercentConditionOperator.BETWEEN:
+      return instanceMicroPercentile.gt(normalizedMicroPercentLowerBound)
+        && instanceMicroPercentile.lte(normalizedMicroPercentUpperBound);
+    case PercentConditionOperator.UNKNOWN:
+    default:
+      break;
+    }
+
+    // TODO: add logging once we have a wrapped logger.
+    return false;
+  }
+}
diff --git a/src/remote-config/index.ts b/src/remote-config/index.ts
index e4719b2e43..9198284b0e 100644
--- a/src/remote-config/index.ts
+++ b/src/remote-config/index.ts
@@ -25,18 +25,35 @@ import { FirebaseApp } from '../app/firebase-app';
 import { RemoteConfig } from './remote-config';
 
 export {
+  AndCondition,
+  DefaultConfig,
+  EvaluationContext,
   ExplicitParameterValue,
+  GetServerTemplateOptions,
   InAppDefaultValue,
+  InitServerTemplateOptions,
   ListVersionsOptions,
   ListVersionsResult,
+  MicroPercentRange,
+  NamedCondition,
+  OneOfCondition,
+  OrCondition,
   ParameterValueType,
+  PercentConditionOperator,
+  PercentCondition,
   RemoteConfigCondition,
   RemoteConfigParameter,
   RemoteConfigParameterGroup,
   RemoteConfigParameterValue,
   RemoteConfigTemplate,
   RemoteConfigUser,
+  ServerConfig,
+  ServerTemplate,
+  ServerTemplateData,
+  ServerTemplateDataType,
   TagColor,
+  Value,
+  ValueSource,
   Version,
 } from './remote-config-api';
 export { RemoteConfig } from './remote-config';
diff --git a/src/remote-config/internal/value-impl.ts b/src/remote-config/internal/value-impl.ts
new file mode 100644
index 0000000000..6d71476538
--- /dev/null
+++ b/src/remote-config/internal/value-impl.ts
@@ -0,0 +1,61 @@
+/*!
+ * Copyright 2024 Google Inc.
+ *
+ * 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.
+ */
+
+'use strict';
+
+import {
+  Value,
+  ValueSource,
+} from '../remote-config-api';
+
+/**
+ * Implements type-safe getters for parameter values.
+ * 
+ * Visible for testing.
+ * 
+ * @internal
+ */
+export class ValueImpl implements Value {
+  public static readonly DEFAULT_VALUE_FOR_BOOLEAN = false;
+  public static readonly DEFAULT_VALUE_FOR_STRING = '';
+  public static readonly DEFAULT_VALUE_FOR_NUMBER = 0;
+  public static readonly BOOLEAN_TRUTHY_VALUES = ['1', 'true', 't', 'yes', 'y', 'on'];
+  constructor(
+    private readonly source: ValueSource,
+    private readonly value = ValueImpl.DEFAULT_VALUE_FOR_STRING) { }
+  asString(): string {
+    return this.value;
+  }
+  asBoolean(): boolean {
+    if (this.source === 'static') {
+      return ValueImpl.DEFAULT_VALUE_FOR_BOOLEAN;
+    }
+    return ValueImpl.BOOLEAN_TRUTHY_VALUES.indexOf(this.value.toLowerCase()) >= 0;
+  }
+  asNumber(): number {
+    if (this.source === 'static') {
+      return ValueImpl.DEFAULT_VALUE_FOR_NUMBER;
+    }
+    const num = Number(this.value);
+    if (isNaN(num)) {
+      return ValueImpl.DEFAULT_VALUE_FOR_NUMBER;
+    }
+    return num;
+  }
+  getSource(): ValueSource {
+    return this.source;
+  }
+}
diff --git a/src/remote-config/remote-config-api-client-internal.ts b/src/remote-config/remote-config-api-client-internal.ts
index b8cfe22fc4..f1a0ad1c10 100644
--- a/src/remote-config/remote-config-api-client-internal.ts
+++ b/src/remote-config/remote-config-api-client-internal.ts
@@ -21,10 +21,19 @@ import { PrefixedFirebaseError } from '../utils/error';
 import * as utils from '../utils/index';
 import * as validator from '../utils/validator';
 import { deepCopy } from '../utils/deep-copy';
-import { ListVersionsOptions, ListVersionsResult, RemoteConfigTemplate } from './remote-config-api';
+import {
+  ListVersionsOptions,
+  ListVersionsResult,
+  RemoteConfigTemplate,
+  ServerTemplateData
+} from './remote-config-api';
 
 // Remote Config backend constants
-const FIREBASE_REMOTE_CONFIG_V1_API = 'https://firebaseremoteconfig.googleapis.com/v1';
+/**
+  * Allows the `FIREBASE_REMOTE_CONFIG_URL_BASE` environment
+  * variable to override the default API endpoint URL.
+  */
+const FIREBASE_REMOTE_CONFIG_URL_BASE = process.env.FIREBASE_REMOTE_CONFIG_URL_BASE || 'https://firebaseremoteconfig.googleapis.com';
 const FIREBASE_REMOTE_CONFIG_HEADERS = {
   'X-Firebase-Client': `fire-admin-node/${utils.getSdkVersion()}`,
   // There is a known issue in which the ETag is not properly returned in cases where the request
@@ -166,6 +175,24 @@ export class RemoteConfigApiClient {
       });
   }
 
+  public getServerTemplate(): Promise<ServerTemplateData> {
+    return this.getUrl()
+      .then((url) => {
+        const request: HttpRequestConfig = {
+          method: 'GET',
+          url: `${url}/namespaces/firebase-server/serverRemoteConfig`,
+          headers: FIREBASE_REMOTE_CONFIG_HEADERS
+        };
+        return this.httpClient.send(request);
+      })
+      .then((resp) => {
+        return this.toRemoteConfigServerTemplate(resp);
+      })
+      .catch((err) => {
+        throw this.toFirebaseError(err);
+      });
+  }
+
   private sendPutRequest(template: RemoteConfigTemplate, etag: string, validateOnly?: boolean): Promise<HttpResponse> {
     let path = 'remoteConfig';
     if (validateOnly) {
@@ -191,7 +218,7 @@ export class RemoteConfigApiClient {
   private getUrl(): Promise<string> {
     return this.getProjectIdPrefix()
       .then((projectIdPrefix) => {
-        return `${FIREBASE_REMOTE_CONFIG_V1_API}/${projectIdPrefix}`;
+        return `${FIREBASE_REMOTE_CONFIG_URL_BASE}/v1/${projectIdPrefix}`;
       });
   }
 
@@ -255,6 +282,24 @@ export class RemoteConfigApiClient {
     };
   }
 
+  /**
+   * Creates a RemoteConfigServerTemplate from the API response.
+   * If provided, customEtag is used instead of the etag returned in the API response.
+   *
+   * @param {HttpResponse} resp API response object.
+   * @param {string} customEtag A custom etag to replace the etag fom the API response (Optional).
+   */
+  private toRemoteConfigServerTemplate(resp: HttpResponse, customEtag?: string): ServerTemplateData {
+    const etag = (typeof customEtag === 'undefined') ? resp.headers['etag'] : customEtag;
+    this.validateEtag(etag);
+    return {
+      conditions: resp.data.conditions,
+      parameters: resp.data.parameters,
+      etag,
+      version: resp.data.version,
+    };
+  }
+
   /**
    * Checks if the given RemoteConfigTemplate object is valid.
    * The object must have valid parameters, parameter groups, conditions, and an etag.
diff --git a/src/remote-config/remote-config-api.ts b/src/remote-config/remote-config-api.ts
index 90f3bd4970..3ededc58c9 100644
--- a/src/remote-config/remote-config-api.ts
+++ b/src/remote-config/remote-config-api.ts
@@ -54,6 +54,165 @@ export interface RemoteConfigCondition {
   tagColor?: TagColor;
 }
 
+/**
+ * Represents a Remote Config condition in the dataplane.
+ * A condition targets a specific group of users. A list of these conditions
+ * comprise part of a Remote Config template.
+ */
+export interface NamedCondition {
+
+  /**
+   * A non-empty and unique name of this condition.
+   */
+  name: string;
+
+  /**
+   * The logic of this condition.
+   * See the documentation on
+   * {@link https://firebase.google.com/docs/remote-config/condition-reference | condition expressions}
+   * for the expected syntax of this field.
+   */
+  condition: OneOfCondition;
+}
+
+/**
+ * Represents a condition that may be one of several types.
+ * Only the first defined field will be processed.
+ */
+export interface OneOfCondition {
+
+  /**
+   * Makes this condition an OR condition.
+   */
+  orCondition?: OrCondition;
+
+  /**
+   * Makes this condition an AND condition.
+   */
+  andCondition?: AndCondition;
+
+  /**
+   * Makes this condition a constant true.
+   */
+  true?: Record<string, never>;
+
+  /**
+   * Makes this condition a constant false.
+   */
+  false?: Record<string, never>;
+
+  /**
+   * Makes this condition a percent condition.
+   */
+  percent?: PercentCondition;
+}
+
+/**
+ * Represents a collection of conditions that evaluate to true if all are true.
+ */
+export interface AndCondition {
+
+  /**
+   * The collection of conditions.
+   */
+  conditions?: Array<OneOfCondition>;
+}
+
+/**
+ * Represents a collection of conditions that evaluate to true if any are true.
+ */
+export interface OrCondition {
+
+  /**
+   * The collection of conditions.
+   */
+  conditions?: Array<OneOfCondition>;
+}
+
+/**
+ * Defines supported operators for percent conditions.
+ */
+export enum PercentConditionOperator {
+
+  /**
+   * A catchall error case.
+   */
+  UNKNOWN = 'UNKNOWN',
+
+  /**
+   * Target percentiles less than or equal to the target percent.
+   * A condition using this operator must specify microPercent.
+   */
+  LESS_OR_EQUAL = 'LESS_OR_EQUAL',
+
+  /**
+   * Target percentiles greater than the target percent.
+   * A condition using this operator must specify microPercent.
+   */
+  GREATER_THAN = 'GREATER_THAN',
+
+  /**
+   * Target percentiles within an interval defined by a lower bound and an
+   * upper bound. The lower bound is an exclusive (open) bound and the
+   * micro_percent_range_upper_bound is an inclusive (closed) bound.
+   * A condition using this operator must specify microPercentRange.
+   */
+  BETWEEN = 'BETWEEN'
+}
+
+/**
+ * Represents the limit of percentiles to target in micro-percents.
+ * The value must be in the range [0 and 100000000]
+ */
+export interface MicroPercentRange {
+
+  /**
+   * The lower limit of percentiles to target in micro-percents.
+   * The value must be in the range [0 and 100000000].
+   */
+  microPercentLowerBound?: number;
+
+  /**
+   * The upper limit of percentiles to target in micro-percents.
+   * The value must be in the range [0 and 100000000].
+   */
+  microPercentUpperBound?: number;
+}
+
+/**
+ * Represents a condition that compares the instance pseudo-random
+ * percentile to a given limit.
+ */
+export interface PercentCondition {
+
+  /**
+   * The choice of percent operator to determine how to compare targets
+   * to percent(s).
+   */
+  percentOperator?: PercentConditionOperator;
+
+  /**
+   * The limit of percentiles to target in micro-percents when
+   * using the LESS_OR_EQUAL and GREATER_THAN operators. The value must
+   * be in the range [0 and 100000000].
+   */
+  microPercent?: number;
+
+  /**
+   * The seed used when evaluating the hash function to map an instance to
+   * a value in the hash space. This is a string which can have 0 - 32
+   * characters and can contain ASCII characters [-_.0-9a-zA-Z].The string
+   * is case-sensitive.
+   */
+  seed?: string;
+
+  /**
+   * The micro-percent interval to be used with the
+   * BETWEEN operator.
+   */
+  microPercentRange?: MicroPercentRange;
+}
+
 /**
  * Interface representing an explicit parameter value.
  */
@@ -135,7 +294,7 @@ export interface RemoteConfigParameterGroup {
 }
 
 /**
- * Interface representing a Remote Config template.
+ * Represents a Remote Config client template.
  */
 export interface RemoteConfigTemplate {
   /**
@@ -167,6 +326,105 @@ export interface RemoteConfigTemplate {
   version?: Version;
 }
 
+/**
+ * Represents the data in a Remote Config server template.
+ */
+export interface ServerTemplateData {
+  /**
+   * A list of conditions in descending order by priority.
+   */
+  conditions: NamedCondition[];
+
+  /**
+   * Map of parameter keys to their optional default values and optional conditional values.
+   */
+  parameters: { [key: string]: RemoteConfigParameter };
+
+  /**
+   * Current Remote Config template ETag (read-only).
+   */
+  readonly etag: string;
+
+  /**
+   * Version information for the current Remote Config template.
+   */
+  version?: Version;
+}
+
+/**
+ * Represents optional arguments that can be used when instantiating {@link ServerTemplate}.
+ */
+export interface GetServerTemplateOptions {
+
+  /**
+   * Defines in-app default parameter values, so that your app behaves as
+   * intended before it connects to the Remote Config backend, and so that
+   * default values are available if none are set on the backend.
+   */
+  defaultConfig?: DefaultConfig;
+}
+
+/**
+ * Represents the type of a Remote Config server template that can be set on
+ * {@link ServerTemplate}. This can either be a {@link ServerTemplateData} object
+ * or a template JSON string.
+ */
+export type ServerTemplateDataType = ServerTemplateData | string;
+
+/**
+ * Represents optional arguments that can be used when instantiating
+ * {@link ServerTemplate} synchonously.
+ */
+export interface InitServerTemplateOptions extends GetServerTemplateOptions {
+
+  /**
+   * Enables integrations to use template data loaded independently. For
+   * example, customers can reduce initialization latency by pre-fetching and
+   * caching template data and then using this option to initialize the SDK with
+   * that data.
+   */
+  template?: ServerTemplateDataType,
+}
+
+/**
+ * Represents a stateful abstraction for a Remote Config server template.
+ */
+export interface ServerTemplate {
+  /**
+   * Evaluates the current template to produce a {@link ServerConfig}.
+   */
+  evaluate(context?: EvaluationContext): ServerConfig;
+
+  /**
+   * Fetches and caches the current active version of the
+   * project's {@link ServerTemplate}.
+   */
+  load(): Promise<void>;
+
+  /**
+   * Sets and caches a {@link ServerTemplateData} or a JSON string representing
+   * the server template
+   */
+  set(template: ServerTemplateDataType): void;
+
+  /**
+   * Returns a JSON representation of {@link ServerTemplateData}
+   */
+  toJSON(): ServerTemplateData;
+}
+
+/**
+ * Represents template evaluation input signals.
+ */
+export type EvaluationContext = {
+
+  /**
+   * Defines the identifier to use when splitting a group. For example,
+   * this is used by the percent condition.
+   */
+  randomizationId?: string
+};
+
 /**
  * Interface representing a Remote Config user.
  */
@@ -289,3 +547,102 @@ export interface ListVersionsOptions {
    */
   endTime?: Date | string;
 }
+
+/**
+ * Represents the configuration produced by evaluating a server template.
+ */
+export interface ServerConfig {
+
+  /**
+   * Gets the value for the given key as a boolean.
+   *
+   * Convenience method for calling <code>serverConfig.getValue(key).asBoolean()</code>.
+   *
+   * @param key - The name of the parameter.
+   *
+   * @returns The value for the given key as a boolean.
+   */
+  getBoolean(key: string): boolean;
+
+  /**
+   * Gets the value for the given key as a number.
+   *
+   * Convenience method for calling <code>serverConfig.getValue(key).asNumber()</code>.
+   *
+   * @param key - The name of the parameter.
+   *
+   * @returns The value for the given key as a number.
+   */
+  getNumber(key: string): number;
+
+  /**
+   * Gets the value for the given key as a string.
+   * Convenience method for calling <code>serverConfig.getValue(key).asString()</code>.
+   *
+   * @param key - The name of the parameter.
+   *
+   * @returns The value for the given key as a string.
+   */
+  getString(key: string): string;
+
+  /**
+   * Gets the {@link Value} for the given key.
+   *
+   * Ensures application logic will always have a type-safe reference,
+   * even if the parameter is removed remotely.
+   *
+   * @param key - The name of the parameter.
+   *
+   * @returns The value for the given key.
+   */
+  getValue(key: string): Value;
+}
+
+/**
+ * Wraps a parameter value with metadata and type-safe getters.
+ *
+ * Type-safe getters insulate application logic from remote
+ * changes to parameter names and types.
+ */
+export interface Value {
+
+  /**
+   * Gets the value as a boolean.
+   *
+   * The following values (case insensitive) are interpreted as true:
+   * "1", "true", "t", "yes", "y", "on". Other values are interpreted as false.
+   */
+  asBoolean(): boolean;
+
+  /**
+   * Gets the value as a number. Comparable to calling <code>Number(value) || 0</code>.
+   */
+  asNumber(): number;
+
+  /**
+   * Gets the value as a string.
+   */
+  asString(): string;
+
+  /**
+   * Gets the {@link ValueSource} for the given key.
+   */
+  getSource(): ValueSource;
+}
+
+/**
+ * Indicates the source of a value.
+ *
+ * <ul>
+ *   <li>"static" indicates the value was defined by a static constant.</li>
+ *   <li>"default" indicates the value was defined by default config.</li>
+ *   <li>"remote" indicates the value was defined by config produced by
+ *   evaluating a template.</li>
+ * </ul>
+ */
+export type ValueSource = 'static' | 'default' | 'remote';
+
+/**
+ * Defines the format for in-app default parameter values.
+ */
+export type DefaultConfig = { [key: string]: string | number | boolean };
diff --git a/src/remote-config/remote-config.ts b/src/remote-config/remote-config.ts
index 27cbd05793..c529501315 100644
--- a/src/remote-config/remote-config.ts
+++ b/src/remote-config/remote-config.ts
@@ -17,15 +17,30 @@
 import { App } from '../app';
 import * as validator from '../utils/validator';
 import { FirebaseRemoteConfigError, RemoteConfigApiClient } from './remote-config-api-client-internal';
+import { ConditionEvaluator } from './condition-evaluator-internal';
+import { ValueImpl } from './internal/value-impl';
 import {
   ListVersionsOptions,
   ListVersionsResult,
   RemoteConfigCondition,
   RemoteConfigParameter,
   RemoteConfigParameterGroup,
+  ServerTemplate,
   RemoteConfigTemplate,
   RemoteConfigUser,
   Version,
+  ExplicitParameterValue,
+  InAppDefaultValue,
+  ServerConfig,
+  RemoteConfigParameterValue,
+  EvaluationContext,
+  ServerTemplateData,
+  NamedCondition,
+  Value,
+  DefaultConfig,
+  GetServerTemplateOptions,
+  InitServerTemplateOptions,
+  ServerTemplateDataType,
 } from './remote-config-api';
 
 /**
@@ -168,6 +183,30 @@ export class RemoteConfig {
 
     return new RemoteConfigTemplateImpl(template);
   }
+
+  /**
+   * Instantiates {@link ServerTemplate} and then fetches and caches the latest
+   * template version of the project.
+   */
+  public async getServerTemplate(options?: GetServerTemplateOptions): Promise<ServerTemplate> {
+    const template = this.initServerTemplate(options);
+    await template.load();
+    return template;
+  }
+
+  /**
+   * Synchronously instantiates {@link ServerTemplate}.
+   */
+  public initServerTemplate(options?: InitServerTemplateOptions): ServerTemplate {
+    const template = new ServerTemplateImpl(
+      this.client, new ConditionEvaluator(), options?.defaultConfig);
+
+    if (options?.template) {
+      template.set(options?.template);
+    }
+
+    return template;
+  }
 }
 
 /**
@@ -254,6 +293,200 @@ class RemoteConfigTemplateImpl implements RemoteConfigTemplate {
   }
 }
 
+/**
+ * Remote Config dataplane template data implementation.
+ */
+class ServerTemplateImpl implements ServerTemplate {
+  private cache: ServerTemplateData;
+  private stringifiedDefaultConfig: {[key: string]: string} = {};
+
+  constructor(
+    private readonly apiClient: RemoteConfigApiClient,
+    private readonly conditionEvaluator: ConditionEvaluator,
+    public readonly defaultConfig: DefaultConfig = {}
+  ) {
+    // RC stores all remote values as string, but it's more intuitive
+    // to declare default values with specific types, so this converts
+    // the external declaration to an internal string representation.
+    for (const key in defaultConfig) {
+      this.stringifiedDefaultConfig[key] = String(defaultConfig[key]);
+    }
+  }
+
+  /**
+   * Fetches and caches the current active version of the project's {@link ServerTemplate}.
+   */
+  public load(): Promise<void> {
+    return this.apiClient.getServerTemplate()
+      .then((template) => {
+        this.cache = new ServerTemplateDataImpl(template);
+      });
+  }
+
+  /**
+   * Parses a {@link ServerTemplateDataType} and caches it.
+   */
+  public set(template: ServerTemplateDataType): void {
+    let parsed;
+    if (validator.isString(template)) {
+      try {
+        parsed = JSON.parse(template);
+      } catch (e) {
+        // Transforms JSON parse errors to Firebase error.
+        throw new FirebaseRemoteConfigError(
+          'invalid-argument',
+          `Failed to parse the JSON string: ${template}. ` + e);
+      }
+    } else {
+      parsed = template;
+    }
+    // Throws template parse errors.
+    this.cache = new ServerTemplateDataImpl(parsed);
+  }
+
+  /**
+   * Evaluates the current template in cache to produce a {@link ServerConfig}.
+   */
+  public evaluate(context: EvaluationContext = {}): ServerConfig {
+    if (!this.cache) {
+
+      // This is the only place we should throw during evaluation, since it's under the
+      // control of application logic. To preserve forward-compatibility, we should only
+      // return false in cases where the SDK is unsure how to evaluate the fetched template.
+      throw new FirebaseRemoteConfigError(
+        'failed-precondition',
+        'No Remote Config Server template in cache. Call load() before calling evaluate().');
+    }
+
+    const evaluatedConditions = this.conditionEvaluator.evaluateConditions(
+      this.cache.conditions, context);
+
+    const configValues: { [key: string]: Value } = {};
+
+    // Initializes config Value objects with default values.
+    for (const key in this.stringifiedDefaultConfig) {
+      configValues[key] = new ValueImpl('default', this.stringifiedDefaultConfig[key]);
+    }
+
+    // Overlays config Value objects derived by evaluating the template.
+    for (const [key, parameter] of Object.entries(this.cache.parameters)) {
+      const { conditionalValues, defaultValue } = parameter;
+
+      // Supports parameters with no conditional values.
+      const normalizedConditionalValues = conditionalValues || {};
+
+      let parameterValueWrapper: RemoteConfigParameterValue | undefined = undefined;
+
+      // Iterates in order over condition list. If there is a value associated
+      // with a condition, this checks if the condition is true.
+      for (const [conditionName, conditionEvaluation] of evaluatedConditions) {
+        if (normalizedConditionalValues[conditionName] && conditionEvaluation) {
+          parameterValueWrapper = normalizedConditionalValues[conditionName];
+          break;
+        }
+      }
+
+      if (parameterValueWrapper && (parameterValueWrapper as InAppDefaultValue).useInAppDefault) {
+        // TODO: add logging once we have a wrapped logger.
+        continue;
+      }
+
+      if (parameterValueWrapper) {
+        const parameterValue = (parameterValueWrapper as ExplicitParameterValue).value;
+        configValues[key] = new ValueImpl('remote', parameterValue);
+        continue;
+      }
+
+      if (!defaultValue) {
+        // TODO: add logging once we have a wrapped logger.
+        continue;
+      }
+
+      if ((defaultValue as InAppDefaultValue).useInAppDefault) {
+        // TODO: add logging once we have a wrapped logger.
+        continue;
+      }
+
+      const parameterDefaultValue = (defaultValue as ExplicitParameterValue).value;
+      configValues[key] = new ValueImpl('remote', parameterDefaultValue);
+    }
+
+    return new ServerConfigImpl(configValues);
+  }
+
+  /**
+   * @returns JSON representation of the server template
+   */
+  public toJSON(): ServerTemplateData {
+    return this.cache;
+  }
+}
+
+class ServerConfigImpl implements ServerConfig {
+  constructor(
+    private readonly configValues: { [key: string]: Value },
+  ){}
+  getBoolean(key: string): boolean {
+    return this.getValue(key).asBoolean();
+  }
+  getNumber(key: string): number {
+    return this.getValue(key).asNumber();
+  }
+  getString(key: string): string {
+    return this.getValue(key).asString();
+  }
+  getValue(key: string): Value {
+    return this.configValues[key] || new ValueImpl('static');
+  }
+}
+
+/**
+ * Remote Config dataplane template data implementation.
+ */
+class ServerTemplateDataImpl implements ServerTemplateData {
+  public parameters: { [key: string]: RemoteConfigParameter };
+  public parameterGroups: { [key: string]: RemoteConfigParameterGroup };
+  public conditions: NamedCondition[];
+  public readonly etag: string;
+  public version?: Version;
+
+  constructor(template: ServerTemplateData) {
+    if (!validator.isNonNullObject(template) ||
+      !validator.isNonEmptyString(template.etag)) {
+      throw new FirebaseRemoteConfigError(
+        'invalid-argument',
+        `Invalid Remote Config template: ${JSON.stringify(template)}`);
+    }
+
+    this.etag = template.etag;
+    if (typeof template.parameters !== 'undefined') {
+      if (!validator.isNonNullObject(template.parameters)) {
+        throw new FirebaseRemoteConfigError(
+          'invalid-argument',
+          'Remote Config parameters must be a non-null object');
+      }
+      this.parameters = template.parameters;
+    } else {
+      this.parameters = {};
+    }
+
+    if (typeof template.conditions !== 'undefined') {
+      if (!validator.isArray(template.conditions)) {
+        throw new FirebaseRemoteConfigError(
+          'invalid-argument',
+          'Remote Config conditions must be an array');
+      }
+      this.conditions = template.conditions;
+    } else {
+      this.conditions = [];
+    }
+
+    if (typeof template.version !== 'undefined') {
+      this.version = new VersionImpl(template.version);
+    }
+  }
+}
+
 /**
 * Remote Config Version internal implementation.
 */
diff --git a/test/unit/index.spec.ts b/test/unit/index.spec.ts
index c2ce02ff3f..31efeaf979 100644
--- a/test/unit/index.spec.ts
+++ b/test/unit/index.spec.ts
@@ -97,6 +97,8 @@ import './security-rules/security-rules-api-client.spec';
 import './remote-config/index.spec';
 import './remote-config/remote-config.spec';
 import './remote-config/remote-config-api-client.spec';
+import './remote-config/condition-evaluator.spec';
+import './remote-config/internal/value-impl.spec';
 
 // AppCheck
 import './app-check/app-check.spec';
diff --git a/test/unit/remote-config/condition-evaluator.spec.ts b/test/unit/remote-config/condition-evaluator.spec.ts
new file mode 100644
index 0000000000..fbf3ca4979
--- /dev/null
+++ b/test/unit/remote-config/condition-evaluator.spec.ts
@@ -0,0 +1,825 @@
+/*!
+ * Copyright 2024 Google Inc.
+ *
+ * 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.
+ */
+
+'use strict';
+
+import * as chai from 'chai';
+import * as sinon from 'sinon';
+import { ConditionEvaluator } from '../../../src/remote-config/condition-evaluator-internal';
+import {
+  PercentConditionOperator,
+  PercentCondition
+} from '../../../src/remote-config/remote-config-api';
+import { v4 as uuidv4 } from 'uuid';
+import { clone } from 'lodash';
+import * as farmhash from 'farmhash';
+
+const expect = chai.expect;
+
+
+
+describe('ConditionEvaluator', () => {
+  let stubs: sinon.SinonStub[] = [];
+
+  afterEach(() => {
+    for (const stub of stubs) {
+      stub.restore();
+    }
+    stubs = [];
+  });
+
+  describe('evaluateConditions', () => {
+    it('should evaluate empty OR condition to false', () => {
+      const condition = {
+        name: 'is_enabled',
+        condition: {
+          orCondition: {
+          }
+        }
+      };
+      const context = {}
+      const evaluator = new ConditionEvaluator();
+      expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+        new Map([['is_enabled', false]]));
+    });
+
+    it('should evaluate empty OR.AND condition to true', () => {
+      const condition = {
+        name: 'is_enabled',
+        condition: {
+          orCondition: {
+            conditions: [
+              {
+                andCondition: {
+                }
+              }
+            ]
+          }
+        }
+      };
+      const context = {}
+      const evaluator = new ConditionEvaluator();
+      expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+        new Map([['is_enabled', true]]));
+    });
+
+    it('should evaluate OR.AND.TRUE condition to true', () => {
+      const condition = {
+        name: 'is_enabled',
+        condition: {
+          orCondition: {
+            conditions: [
+              {
+                andCondition: {
+                  conditions: [
+                    {
+                      true: {
+                      }
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        }
+      };
+      const context = {}
+      const evaluator = new ConditionEvaluator();
+      expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+        new Map([['is_enabled', true]]));
+    });
+
+    it('should evaluate OR.AND.FALSE condition to false', () => {
+      const condition = {
+        name: 'is_enabled',
+        condition: {
+          orCondition: {
+            conditions: [
+              {
+                andCondition: {
+                  conditions: [
+                    {
+                      false: {
+                      }
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        }
+      };
+      const context = {}
+      const evaluator = new ConditionEvaluator();
+      expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+        new Map([['is_enabled', false]]));
+    });
+
+    it('should evaluate non-OR top-level condition', () => {
+      // The server wraps conditions in OR.AND, but the evaluation logic
+      // is more general.
+      const condition = {
+        name: 'is_enabled',
+        condition: {
+          true: {
+          }
+        }
+      };
+      const context = {}
+      const evaluator = new ConditionEvaluator();
+      expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+        new Map([['is_enabled', true]]));
+    });
+
+    describe('percentCondition', () => {
+      it('should evaluate an unknown operator to false', () => {
+        // Verifies future operators won't trigger errors.
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.UNKNOWN
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+          new Map([['is_enabled', false]]));
+      });
+
+      it('should evaluate less or equal to max to true', () => {
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.LESS_OR_EQUAL,
+                      seed: 'abcdef',
+                      microPercent: 100_000_000
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+          new Map([['is_enabled', true]]));
+      });
+
+      it('should evaluate less or equal to min to false', () => {
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.LESS_OR_EQUAL,
+                      seed: 'abcdef',
+                      microPercent: 0
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+          new Map([['is_enabled', false]]));
+      });
+
+      it('should use zero for undefined microPercent', () => {
+        // Stubs ID hasher to return a number larger than zero.
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('1');
+        stubs.push(stub);
+
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.LESS_OR_EQUAL,
+                      // Leaves microPercent undefined
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+
+        // Evaluates false because 1 is not <= 0
+        expect(actual).to.be.false;
+      });
+
+      it('should use zeros for undefined microPercentRange', () => {
+        // Stubs ID hasher to return a number in range.
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('1');
+        stubs.push(stub);
+
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.BETWEEN,
+                      // Leaves microPercentRange undefined
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+
+        // Evaluates false because 1 is not in (0,0]
+        expect(actual).to.be.false;
+      });
+
+      it('should use zero for undefined microPercentUpperBound', () => {
+        // Stubs ID hasher to return a number outside range.
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('1');
+        stubs.push(stub);
+
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.BETWEEN,
+                      microPercentRange: {
+                        microPercentLowerBound: 0
+                        // Leaves upper bound undefined
+                      }
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+
+        // Evaluates false because 1 is not in (0,0]
+        expect(actual).to.be.false;
+      });
+
+      it('should use zero for undefined microPercentLowerBound', () => {
+        // Stubs ID hasher to return a number in range.
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('1');
+        stubs.push(stub);
+
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.BETWEEN,
+                      microPercentRange: {
+                        microPercentUpperBound: 1
+                        // Leaves lower bound undefined
+                      }
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+
+        // Evaluates true because 1 is in (0,1]
+        expect(actual).to.be.true;
+      });
+
+      it('should evaluate 9 as less or equal to 10', () => {
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('9');
+
+        stubs.push(stub);
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.LESS_OR_EQUAL,
+                      seed: 'abcdef',
+                      microPercent: 10
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+        expect(actual).to.be.true;
+      });
+
+      it('should evaluate 10 as less or equal to 10', () => {
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('10');
+
+        stubs.push(stub);
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.LESS_OR_EQUAL,
+                      seed: 'abcdef',
+                      microPercent: 10
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+        expect(actual).to.be.true;
+      });
+
+      it('should evaluate 11 as not less or equal to 10', () => {
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('11');
+
+        stubs.push(stub);
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.LESS_OR_EQUAL,
+                      seed: 'abcdef',
+                      microPercent: 10
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+        expect(actual).to.be.false;
+      });
+
+      it('should negate -11 to 11 and evaluate as not less or equal to 10', () => {
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('-11');
+
+        stubs.push(stub);
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.LESS_OR_EQUAL,
+                      seed: 'abcdef',
+                      microPercent: 10
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+        expect(actual).to.be.false;
+      });
+
+      it('should evaluate greater than min to true', () => {
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.GREATER_THAN,
+                      seed: 'abcdef',
+                      microPercent: 0
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+          new Map([['is_enabled', true]]));
+      });
+
+      it('should evaluate 11M as greater than 10M', () => {
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('11');
+
+        stubs.push(stub);
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.GREATER_THAN,
+                      seed: 'abcdef',
+                      microPercent: 10
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+        expect(actual).to.be.true;
+      });
+
+      it('should evaluate 9 as not greater than 10', () => {
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('9');
+        stubs.push(stub);
+
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.GREATER_THAN,
+                      seed: 'abcdef',
+                      microPercent: 10
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+        expect(actual).to.be.false;
+      });
+
+      it('should evaluate greater than max to false', () => {
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.GREATER_THAN,
+                      seed: 'abcdef',
+                      microPercent: 100_000_000
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+          new Map([['is_enabled', false]]));
+      });
+
+      it('should evaluate between min and max to true', () => {
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.BETWEEN,
+                      seed: 'abcdef',
+                      microPercentRange: {
+                        microPercentLowerBound: 0,
+                        microPercentUpperBound: 100_000_000
+                      }
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+          new Map([['is_enabled', true]]));
+      });
+
+      it('should evaluate 10 as between 9 and 11', () => {
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('10');
+        stubs.push(stub);
+
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.BETWEEN,
+                      seed: 'abcdef',
+                      microPercentRange: {
+                        microPercentLowerBound: 9,
+                        microPercentUpperBound: 11
+                      }
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+        expect(actual).to.be.true;
+      });
+
+      it('should evaluate between equal bounds to false', () => {
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.BETWEEN,
+                      seed: 'abcdef',
+                      microPercentRange: {
+                        microPercentLowerBound: 50000000,
+                        microPercentUpperBound: 50000000
+                      }
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        expect(evaluator.evaluateConditions([condition], context)).deep.equals(
+          new Map([['is_enabled', false]]));
+      });
+
+      it('should evaluate 12 as not between 9 and 11', () => {
+        const stub = sinon
+          .stub(farmhash, 'fingerprint64')
+          .returns('12');
+        stubs.push(stub);
+
+        const condition = {
+          name: 'is_enabled',
+          condition: {
+            orCondition: {
+              conditions: [{
+                andCondition: {
+                  conditions: [{
+                    percent: {
+                      percentOperator: PercentConditionOperator.BETWEEN,
+                      seed: 'abcdef',
+                      microPercentRange: {
+                        microPercentLowerBound: 9,
+                        microPercentUpperBound: 11
+                      }
+                    }
+                  }],
+                }
+              }]
+            }
+          }
+        };
+        const context = { randomizationId: '123' }
+        const evaluator = new ConditionEvaluator();
+        const actual = evaluator.evaluateConditions([condition], context)
+          .get('is_enabled');
+        expect(actual).to.be.false;
+      });
+
+      // The following tests are probablistic. They use tolerances based on
+      // standard deviations to balance accuracy and flakiness. Random IDs will
+      // hash to the target range + 3 standard deviations 99.7% of the time,
+      // which minimizes flakiness.
+      // Use python to calculate standard deviation. For example, for 100k
+      // trials with 50% probability:
+      //   from scipy.stats import binom
+      //   print(binom.std(100_000, 0.5) * 3)
+      it('should evaluate less or equal to 10% to approx 10%', () => {
+        const percentCondition = {
+          percentOperator: PercentConditionOperator.LESS_OR_EQUAL,
+          microPercent: 10_000_000 // 10%
+        };
+        const evaluator = new ConditionEvaluator();
+        const truthyAssignments = evaluateRandomAssignments(percentCondition, 100_000, evaluator);
+        // 284 is 3 standard deviations for 100k trials with 10% probability.
+        const tolerance = 284;
+        expect(truthyAssignments).to.be.greaterThanOrEqual(10000 - tolerance);
+        expect(truthyAssignments).to.be.lessThanOrEqual(10000 + tolerance);
+      });
+
+      it('should evaluate between 0 to 10% to approx 10%', () => {
+        const percentCondition = {
+          percentOperator: PercentConditionOperator.BETWEEN,
+          microPercentRange: {
+            microPercentLowerBound: 0,
+            microPercentUpperBound: 10_000_000
+          }
+        };
+        const evaluator = new ConditionEvaluator();
+        const truthyAssignments = evaluateRandomAssignments(percentCondition, 100_000, evaluator);
+        // 284 is 3 standard deviations for 100k trials with 10% probability.
+        const tolerance = 284;
+        expect(truthyAssignments).to.be.greaterThanOrEqual(10000 - tolerance);
+        expect(truthyAssignments).to.be.lessThanOrEqual(10000 + tolerance);
+      });
+
+      it('should evaluate greater than 10% to approx 90%', () => {
+        const percentCondition = {
+          percentOperator: PercentConditionOperator.GREATER_THAN,
+          microPercent: 10_000_000
+        };
+        const evaluator = new ConditionEvaluator();
+        const truthyAssignments = evaluateRandomAssignments(percentCondition, 100_000, evaluator);
+        // 284 is 3 standard deviations for 100k trials with 90% probability.
+        const tolerance = 284;
+        expect(truthyAssignments).to.be.greaterThanOrEqual(90000 - tolerance);
+        expect(truthyAssignments).to.be.lessThanOrEqual(90000 + tolerance);
+      });
+
+      it('should evaluate between 40% to 60% to approx 20%', () => {
+        const percentCondition = {
+          percentOperator: PercentConditionOperator.BETWEEN,
+          microPercentRange: {
+            microPercentLowerBound: 40_000_000,
+            microPercentUpperBound: 60_000_000
+          }
+        };
+        const evaluator = new ConditionEvaluator();
+        const truthyAssignments = evaluateRandomAssignments(percentCondition, 100_000, evaluator);
+        // 379 is 3 standard deviations for 100k trials with 20% probability.
+        const tolerance = 379;
+        expect(truthyAssignments).to.be.greaterThanOrEqual(20000 - tolerance);
+        expect(truthyAssignments).to.be.lessThanOrEqual(20000 + tolerance);
+      });
+
+      it('should evaluate between interquartile range to approx 50%', () => {
+        const percentCondition = {
+          percentOperator: PercentConditionOperator.BETWEEN,
+          microPercentRange: {
+            microPercentLowerBound: 25_000_000,
+            microPercentUpperBound: 75_000_000
+          }
+        };
+        const evaluator = new ConditionEvaluator();
+        const truthyAssignments = evaluateRandomAssignments(percentCondition, 100_000, evaluator);
+        // 474 is 3 standard deviations for 100k trials with 50% probability.
+        const tolerance = 474;
+        expect(truthyAssignments).to.be.greaterThanOrEqual(50000 - tolerance);
+        expect(truthyAssignments).to.be.lessThanOrEqual(50000 + tolerance);
+      });
+
+      // Returns the number of assignments which evaluate to true for the specified percent condition.
+      // This method randomly generates the ids for each assignment for this purpose.
+      function evaluateRandomAssignments(
+        condition: PercentCondition,
+        numOfAssignments: number,
+        conditionEvaluator: ConditionEvaluator): number {
+
+        let evalTrueCount = 0;
+        for (let i = 0; i < numOfAssignments; i++) {
+          const clonedCondition = {
+            ...clone(condition),
+            seed: 'seed'
+          };
+          const context = { randomizationId: uuidv4() }
+          if (conditionEvaluator.evaluateConditions([{
+            name: 'is_enabled',
+            condition: { percent: clonedCondition }
+          }], context).get('is_enabled') == true) { evalTrueCount++ }
+        }
+        return evalTrueCount;
+      }
+    });
+  });
+});
diff --git a/test/unit/remote-config/internal/value-impl.spec.ts b/test/unit/remote-config/internal/value-impl.spec.ts
new file mode 100644
index 0000000000..b344d0c9d1
--- /dev/null
+++ b/test/unit/remote-config/internal/value-impl.spec.ts
@@ -0,0 +1,75 @@
+/*!
+ * Copyright 2024 Google Inc.
+ *
+ * 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.
+ */
+
+'use strict';
+
+import * as chai from 'chai';
+import { ValueImpl } from '../../../../src/remote-config/internal/value-impl';
+
+const expect = chai.expect;
+
+describe('ValueImpl', () => {
+  describe('getSource', () => {
+    it('returns the source string', () => {
+      const value = new ValueImpl('static');
+      expect(value.getSource()).to.equal('static');
+    });
+  });
+
+  describe('asString', () => {
+    it('returns string value as a string', () => {
+      const value = new ValueImpl('default', 'shiba');
+      expect(value.asString()).to.equal('shiba');
+    });
+
+    it('defaults to empty string', () => {
+      const value = new ValueImpl('static');
+      expect(value.asString()).to.equal(ValueImpl.DEFAULT_VALUE_FOR_STRING);
+    });
+  });
+
+  describe('asNumber', () => {
+    it('returns numeric value as a number', () => {
+      const value = new ValueImpl('default', '123');
+      expect(value.asNumber()).to.equal(123);
+    });
+
+    it('defaults to zero for non-numeric value', () => {
+      const value = new ValueImpl('default', 'Hi, NaN!');
+      expect(value.asNumber()).to.equal(ValueImpl.DEFAULT_VALUE_FOR_NUMBER);
+    });
+  });
+
+  describe('asBoolean', () => {
+    it("returns true for any value in RC's list of truthy values", () => {
+      for (const truthyValue of ValueImpl.BOOLEAN_TRUTHY_VALUES) {
+        const value = new ValueImpl('default', truthyValue);
+        expect(value.asBoolean()).to.be.true;
+      }
+    });
+
+    it('is case-insensitive', () => {
+      const value = new ValueImpl('default', 'TRUE');
+      expect(value.asBoolean()).to.be.true;
+    });
+
+    it("returns false for any value not in RC's list of truthy values", () => {
+      const value = new ValueImpl('default', "I'm falsy");
+      expect(value.asBoolean()).to.be.false;
+    });
+  });
+});
+
diff --git a/test/unit/remote-config/remote-config-api-client.spec.ts b/test/unit/remote-config/remote-config-api-client.spec.ts
index 9c66f78a41..52abb968c1 100644
--- a/test/unit/remote-config/remote-config-api-client.spec.ts
+++ b/test/unit/remote-config/remote-config-api-client.spec.ts
@@ -33,6 +33,7 @@ import { getSdkVersion } from '../../../src/utils/index';
 import {
   RemoteConfigTemplate, Version, ListVersionsResult,
 } from '../../../src/remote-config/index';
+import { ServerTemplateData } from '../../../src/remote-config/remote-config-api';
 
 const expect = chai.expect;
 
@@ -661,6 +662,36 @@ describe('RemoteConfigApiClient', () => {
     });
   });
 
+  describe('getServerTemplate', () => {
+    it('should reject when project id is not available', () => {
+      return clientWithoutProjectId.getServerTemplate()
+        .should.eventually.be.rejectedWith(noProjectId);
+    });
+
+    // tests for api response validations
+    runEtagHeaderTests(() => apiClient.getServerTemplate());
+    runErrorResponseTests(() => apiClient.getServerTemplate());
+
+    it('should resolve with the latest template on success', () => {
+      const stub = sinon
+        .stub(HttpClient.prototype, 'send')
+        .resolves(utils.responseFrom(TEST_RESPONSE, 200, { etag: 'etag-123456789012-1' }));
+      stubs.push(stub);
+      return apiClient.getServerTemplate()
+        .then((resp) => {
+          expect(resp.conditions).to.deep.equal(TEST_RESPONSE.conditions);
+          expect(resp.parameters).to.deep.equal(TEST_RESPONSE.parameters);
+          expect(resp.etag).to.equal('etag-123456789012-1');
+          expect(resp.version).to.deep.equal(TEST_RESPONSE.version);
+          expect(stub).to.have.been.calledOnce.and.calledWith({
+            method: 'GET',
+            url: 'https://firebaseremoteconfig.googleapis.com/v1/projects/test-project/namespaces/firebase-server/serverRemoteConfig',
+            headers: EXPECTED_HEADERS,
+          });
+        });
+    });
+  });
+
   function runTemplateVersionNumberTests(rcOperation: (v: string | number) => any): void {
     ['', null, NaN, true, [], {}].forEach((invalidVersion) => {
       it(`should reject if the versionNumber is: ${invalidVersion}`, () => {
@@ -677,7 +708,7 @@ describe('RemoteConfigApiClient', () => {
     });
   }
 
-  function runEtagHeaderTests(rcOperation: () => Promise<RemoteConfigTemplate>): void {
+  function runEtagHeaderTests(rcOperation: () => Promise<RemoteConfigTemplate | ServerTemplateData>): void {
     it('should reject when the etag is not present in the response', () => {
       const stub = sinon
         .stub(HttpClient.prototype, 'send')
@@ -690,7 +721,8 @@ describe('RemoteConfigApiClient', () => {
     });
   }
 
-  function runErrorResponseTests(rcOperation: () => Promise<RemoteConfigTemplate | ListVersionsResult>): void {
+  function runErrorResponseTests(
+    rcOperation: () => Promise<RemoteConfigTemplate | ServerTemplateData | ListVersionsResult>): void {
     it('should reject when a full platform error response is received', () => {
       const stub = sinon
         .stub(HttpClient.prototype, 'send')
diff --git a/test/unit/remote-config/remote-config.spec.ts b/test/unit/remote-config/remote-config.spec.ts
index 5459ecd90c..526dc0699e 100644
--- a/test/unit/remote-config/remote-config.spec.ts
+++ b/test/unit/remote-config/remote-config.spec.ts
@@ -34,6 +34,9 @@ import {
   RemoteConfigApiClient
 } from '../../../src/remote-config/remote-config-api-client-internal';
 import { deepCopy } from '../../../src/utils/deep-copy';
+import {
+  NamedCondition, ServerTemplate, ServerTemplateData, Version
+} from '../../../src/remote-config/remote-config-api';
 
 const expect = chai.expect;
 
@@ -98,6 +101,44 @@ describe('RemoteConfig', () => {
     version: VERSION_INFO,
   };
 
+  const SERVER_REMOTE_CONFIG_RESPONSE: {
+    // This type is effectively a RemoteConfigServerTemplate, but with mutable fields
+    // to allow easier use from within the tests. An improvement would be to
+    // alter this into a helper that creates customized RemoteConfigTemplateContent based
+    // on the needs of the test, as that would ensure type-safety.
+    conditions?: Array<NamedCondition>;
+    parameters?: object | null;
+    etag: string;
+    version?: object;
+  } = {
+    conditions: [
+      {
+        name: 'ios',
+        condition: {
+          orCondition: {
+            conditions: [
+              {
+                andCondition: {
+                  conditions: [
+                    { true: {} }
+                  ]
+                }
+              }
+            ]
+          }
+        }
+      },
+    ],
+    parameters: {
+      holiday_promo_enabled: {
+        defaultValue: { value: 'true' },
+        conditionalValues: { ios: { useInAppDefault: true } }
+      },
+    },
+    etag: 'etag-123456789012-5',
+    version: VERSION_INFO,
+  };
+
   const REMOTE_CONFIG_TEMPLATE: RemoteConfigTemplate = {
     conditions: [{
       name: 'ios',
@@ -511,6 +552,845 @@ describe('RemoteConfig', () => {
     });
   });
 
+  describe('getServerTemplate', () => {
+    const operationName = 'getServerTemplate';
+
+    it('should propagate API errors', () => {
+      const stub = sinon
+        .stub(RemoteConfigApiClient.prototype, operationName)
+        .rejects(INTERNAL_ERROR);
+      stubs.push(stub);
+
+      return remoteConfig.getServerTemplate().should.eventually.be.rejected.and.deep.equal(INTERNAL_ERROR);
+    });
+
+    it('should resolve a server template on success', () => {
+      const stub = sinon
+        .stub(RemoteConfigApiClient.prototype, operationName)
+        .resolves(SERVER_REMOTE_CONFIG_RESPONSE as ServerTemplateData);
+      stubs.push(stub);
+
+      return remoteConfig.getServerTemplate()
+        .then((template) => {
+          expect(template.toJSON().conditions.length).to.equal(1);
+          expect(template.toJSON().conditions[0].name).to.equal('ios');
+          expect(template.toJSON().etag).to.equal('etag-123456789012-5');
+
+          const version = template.toJSON().version!;
+          expect(version.versionNumber).to.equal('86');
+          expect(version.updateOrigin).to.equal('ADMIN_SDK_NODE');
+          expect(version.updateType).to.equal('INCREMENTAL_UPDATE');
+          expect(version.updateUser).to.deep.equal({
+            email: 'firebase-adminsdk@gserviceaccount.com'
+          });
+          expect(version.description).to.equal('production version');
+          expect(version.updateTime).to.equal('Mon, 15 Jun 2020 16:45:03 GMT');
+
+          const key = 'holiday_promo_enabled';
+          const p1 = template.toJSON().parameters[key];
+          expect(p1.defaultValue).deep.equals({ value: 'true' });
+          expect(p1.conditionalValues).deep.equals({ ios: { useInAppDefault: true } });
+
+          const c = template.toJSON().conditions.find((c) => c.name === 'ios');
+          expect(c).to.be.not.undefined;
+          const cond = c as NamedCondition;
+          expect(cond.name).to.equal('ios');
+
+          const parsed = template.toJSON();
+          const expectedTemplate = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+          const expectedVersion = deepCopy(VERSION_INFO);
+          expectedVersion.updateTime = new Date(expectedVersion.updateTime).toUTCString();
+          expectedTemplate.version = expectedVersion;
+          expect(parsed).deep.equals(expectedTemplate);
+        });
+    });
+
+    it('should set defaultConfig when passed', () => {
+      // Defines template with no parameters to demonstrate
+      // default config will be used instead,
+      const template = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+      template.parameters = {};
+
+      const stub = sinon
+        .stub(RemoteConfigApiClient.prototype, operationName)
+        .resolves(template);
+      stubs.push(stub);
+
+      const defaultConfig = {
+        holiday_promo_enabled: false,
+        holiday_promo_discount: 20,
+      };
+
+      return remoteConfig.getServerTemplate({ defaultConfig })
+        .then((template) => {
+          const config = template.evaluate();
+          expect(config.getBoolean('holiday_promo_enabled')).to.equal(
+            defaultConfig.holiday_promo_enabled);
+          expect(config.getNumber('holiday_promo_discount')).to.equal(
+            defaultConfig.holiday_promo_discount);
+        });
+    });
+  });
+
+  describe('initServerTemplate', () => {
+    it('should set and instantiates template when passed', () => {
+      const template = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+      template.parameters = {
+        dog_type: {
+          defaultValue: {
+            value: 'shiba'
+          }
+        }
+      };
+      const initializedTemplate = remoteConfig.initServerTemplate({ template });
+      const parsed = initializedTemplate.toJSON();
+      const expectedVersion = deepCopy(VERSION_INFO);
+      expectedVersion.updateTime = new Date(expectedVersion.updateTime).toUTCString();
+      template.version = expectedVersion as Version;
+      expect(parsed).deep.equals(deepCopy(template));
+    });
+
+    it('should set and instantiates template when json string is passed', () => {
+      const template = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+      template.parameters = {
+        dog_type: {
+          defaultValue: {
+            value: 'shiba'
+          },
+          description: 'Type of dog breed',
+          valueType: 'STRING'
+        }
+      };
+      const templateJson = JSON.stringify(template);
+      const initializedTemplate = remoteConfig.initServerTemplate({ template: templateJson });
+      const parsed = initializedTemplate.toJSON();
+      const expectedVersion = deepCopy(VERSION_INFO);
+      expectedVersion.updateTime = new Date(expectedVersion.updateTime).toUTCString();
+      template.version = expectedVersion as Version;
+      expect(parsed).deep.equals(deepCopy(template));
+    });
+
+    describe('should throw error if invalid template JSON is passed', () => {
+      const INVALID_PARAMETERS: any[] = [null, '', 'abc', 1, true, []];
+      const INVALID_CONDITIONS: any[] = [null, '', 'abc', 1, true, {}];
+
+      let sourceTemplate = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+      const jsonString = '{invalidJson: null}';
+      it('should throw if template is an invalid JSON', () => {
+        expect(() => remoteConfig.initServerTemplate({ template: jsonString }))
+          .to.throw(/Failed to parse the JSON string: ([\D\w]*)\./);
+      });
+
+      INVALID_PARAMETERS.forEach((invalidParameter) => {
+        sourceTemplate.parameters = invalidParameter;
+        const jsonString = JSON.stringify(sourceTemplate);
+        it(`should throw if the parameters is ${JSON.stringify(invalidParameter)}`, () => {
+          expect(() => remoteConfig.initServerTemplate({ template: jsonString }))
+            .to.throw('Remote Config parameters must be a non-null object');
+        });
+      });
+
+      sourceTemplate = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+      INVALID_CONDITIONS.forEach((invalidConditions) => {
+        sourceTemplate.conditions = invalidConditions;
+        const jsonString = JSON.stringify(sourceTemplate);
+        it(`should throw if the conditions is ${JSON.stringify(invalidConditions)}`, () => {
+          expect(() => remoteConfig.initServerTemplate({ template: jsonString }))
+            .to.throw('Remote Config conditions must be an array');
+        });
+      });
+    });
+  });
+
+  describe('RemoteConfigServerTemplate', () => {
+    const SERVER_REMOTE_CONFIG_RESPONSE_2 = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+    SERVER_REMOTE_CONFIG_RESPONSE_2.parameters = {
+      dog_type: {
+        defaultValue: {
+          value: 'corgi'
+        }
+      },
+      dog_type_enabled: {
+        defaultValue: {
+          value: 'true'
+        }
+      },
+      dog_age: {
+        defaultValue: {
+          value: '22'
+        }
+      },
+      dog_jsonified: {
+        defaultValue: {
+          value: '{"name":"Taro","breed":"Corgi","age":1,"fluffiness":100}'
+        }
+      },
+      dog_use_inapp_default: {
+        defaultValue: {
+          useInAppDefault: true
+        }
+      },
+      dog_no_remote_default_value: {
+      }
+    };
+
+    describe('load', () => {
+      const operationName = 'getServerTemplate';
+
+      it('should propagate API errors', () => {
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, operationName)
+          .rejects(INTERNAL_ERROR);
+        stubs.push(stub);
+
+        return remoteConfig.getServerTemplate().should.eventually.be.rejected.and.deep.equal(INTERNAL_ERROR);
+      });
+
+      it('should reject when API response is invalid', () => {
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, operationName)
+          .resolves(undefined);
+        stubs.push(stub);
+        return remoteConfig.getServerTemplate().should.eventually.be.rejected.and.have.property(
+          'message', 'Invalid Remote Config template: undefined');
+      });
+
+      it('should reject when API response does not contain an ETag', () => {
+        const response = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+        response.etag = '';
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, operationName)
+          .resolves(response as ServerTemplateData);
+        stubs.push(stub);
+        return remoteConfig.getServerTemplate()
+          .should.eventually.be.rejected.and.have.property(
+            'message', `Invalid Remote Config template: ${JSON.stringify(response)}`);
+      });
+
+      it('should reject when API response does not contain valid parameters', () => {
+        const response = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+        response.parameters = null;
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, operationName)
+          .resolves(response as ServerTemplateData);
+        stubs.push(stub);
+        return remoteConfig.getServerTemplate()
+          .should.eventually.be.rejected.and.have.property(
+            'message', 'Remote Config parameters must be a non-null object');
+      });
+
+      it('should reject when API response does not contain valid conditions', () => {
+        const response = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+        response.conditions = Object();
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, operationName)
+          .resolves(response as ServerTemplateData);
+        stubs.push(stub);
+        return remoteConfig.getServerTemplate()
+          .should.eventually.be.rejected.and.have.property(
+            'message', 'Remote Config conditions must be an array');
+      });
+
+      it('should resolve with parameters:{} when no parameters present in the response', () => {
+        const response = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+        response.parameters = undefined;
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, operationName)
+          .resolves(response as ServerTemplateData);
+        stubs.push(stub);
+        return remoteConfig.getServerTemplate()
+          .then((template) => {
+            // If parameters are not present in the response, we set it to an empty object.
+            expect(template.toJSON().parameters).deep.equals({});
+          });
+      });
+
+      it('should resolve with conditions:[] when no conditions present in the response', () => {
+        const response = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+        response.conditions = undefined;
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, operationName)
+          .resolves(response as ServerTemplateData);
+        stubs.push(stub);
+        return remoteConfig.getServerTemplate()
+          .then((template) => {
+            // If conditions are not present in the response, we set it to an empty array.
+            expect(template.toJSON().conditions).deep.equals([]);
+          });
+      });
+
+      it('should resolve a server template on success', () => {
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, operationName)
+          .resolves(SERVER_REMOTE_CONFIG_RESPONSE as ServerTemplateData);
+        stubs.push(stub);
+
+        return remoteConfig.getServerTemplate()
+          .then((template) => {
+            expect(template.toJSON().conditions.length).to.equal(1);
+            expect(template.toJSON().conditions[0].name).to.equal('ios');
+            expect(template.toJSON().etag).to.equal('etag-123456789012-5');
+
+            const version = template.toJSON().version!;
+            expect(version.versionNumber).to.equal('86');
+            expect(version.updateOrigin).to.equal('ADMIN_SDK_NODE');
+            expect(version.updateType).to.equal('INCREMENTAL_UPDATE');
+            expect(version.updateUser).to.deep.equal({
+              email: 'firebase-adminsdk@gserviceaccount.com'
+            });
+            expect(version.description).to.equal('production version');
+            expect(version.updateTime).to.equal('Mon, 15 Jun 2020 16:45:03 GMT');
+
+            const key = 'holiday_promo_enabled';
+            const p1 = template.toJSON().parameters[key];
+            expect(p1.defaultValue).deep.equals({ value: 'true' });
+            expect(p1.conditionalValues).deep.equals({ ios: { useInAppDefault: true } });
+
+            const c = template.toJSON().conditions.find((c) => c.name === 'ios');
+            expect(c).to.be.not.undefined;
+            const cond = c as NamedCondition;
+            expect(cond.name).to.equal('ios');
+            expect(cond.condition).deep.equals({
+              'orCondition': {
+                'conditions': [
+                  {
+                    'andCondition': {
+                      'conditions': [
+                        {
+                          'true': {}
+                        }
+                      ]
+                    }
+                  }
+                ]
+              }
+            });
+
+            const parsed = template.toJSON();
+            const expectedTemplate = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+            const expectedVersion = deepCopy(VERSION_INFO);
+            expectedVersion.updateTime = new Date(expectedVersion.updateTime).toUTCString();
+            expectedTemplate.version = expectedVersion;
+            expect(parsed).deep.equals(expectedTemplate);
+          });
+      });
+
+      it('should resolve with template when Version updateTime contains 3 digits in fractional seconds', () => {
+        const response = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+        const versionInfo = deepCopy(VERSION_INFO);
+        versionInfo.updateTime = '2020-10-03T17:14:10.203Z';
+        response.version = versionInfo;
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, operationName)
+          .resolves(response as ServerTemplateData);
+        stubs.push(stub);
+
+        return remoteConfig.getServerTemplate()
+          .then((template) => {
+            expect(template.toJSON().etag).to.equal('etag-123456789012-5');
+
+            const version = template.toJSON().version!;
+            expect(version.versionNumber).to.equal('86');
+            expect(version.updateOrigin).to.equal('ADMIN_SDK_NODE');
+            expect(version.updateType).to.equal('INCREMENTAL_UPDATE');
+            expect(version.updateUser).to.deep.equal({
+              email: 'firebase-adminsdk@gserviceaccount.com'
+            });
+            expect(version.description).to.equal('production version');
+            expect(version.updateTime).to.equal('Sat, 03 Oct 2020 17:14:10 GMT');
+          });
+      });
+
+      it('should resolve with template when Version updateTime contains 6 digits in fractional seconds', () => {
+        const response = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+        const versionInfo = deepCopy(VERSION_INFO);
+        versionInfo.updateTime = '2020-08-14T17:01:36.541527Z';
+        response.version = versionInfo;
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, operationName)
+          .resolves(response as ServerTemplateData);
+        stubs.push(stub);
+
+        return remoteConfig.getServerTemplate()
+          .then((template) => {
+            expect(template.toJSON().etag).to.equal('etag-123456789012-5');
+
+            const version = template.toJSON().version!;
+            expect(version.versionNumber).to.equal('86');
+            expect(version.updateOrigin).to.equal('ADMIN_SDK_NODE');
+            expect(version.updateType).to.equal('INCREMENTAL_UPDATE');
+            expect(version.updateUser).to.deep.equal({
+              email: 'firebase-adminsdk@gserviceaccount.com'
+            });
+            expect(version.description).to.equal('production version');
+            expect(version.updateTime).to.equal('Fri, 14 Aug 2020 17:01:36 GMT');
+          });
+      });
+
+      it('should resolve with template when Version updateTime contains 9 digits in fractional seconds', () => {
+        const response = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+        const versionInfo = deepCopy(VERSION_INFO);
+        versionInfo.updateTime = '2020-11-15T06:57:26.342763941Z';
+        response.version = versionInfo;
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, operationName)
+          .resolves(response as ServerTemplateData);
+        stubs.push(stub);
+
+        return remoteConfig.getServerTemplate()
+          .then((template) => {
+            expect(template.toJSON().etag).to.equal('etag-123456789012-5');
+
+            const version = template.toJSON().version!;
+            expect(version.versionNumber).to.equal('86');
+            expect(version.updateOrigin).to.equal('ADMIN_SDK_NODE');
+            expect(version.updateType).to.equal('INCREMENTAL_UPDATE');
+            expect(version.updateUser).to.deep.equal({
+              email: 'firebase-adminsdk@gserviceaccount.com'
+            });
+            expect(version.description).to.equal('production version');
+            expect(version.updateTime).to.equal('Sun, 15 Nov 2020 06:57:26 GMT');
+          });
+      });
+    });
+
+    describe('set', () => {
+      it('should set template when passed', () => {
+        const template = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+        template.parameters = {
+          dog_type: {
+            defaultValue: {
+              value: 'shiba'
+            },
+            description: 'Type of dog breed',
+            valueType: 'STRING'
+          }
+        };
+        template.version = {
+          ...deepCopy(VERSION_INFO),
+          updateTime: new Date(VERSION_INFO.updateTime).toUTCString()
+        } as Version;
+        const initializedTemplate = remoteConfig.initServerTemplate();
+        initializedTemplate.set(template);
+        const parsed = initializedTemplate.toJSON();
+        expect(parsed).deep.equals(template);
+      });
+
+      it('should set and instantiates template when json string is passed', () => {
+        const template = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+        template.parameters = {
+          dog_type: {
+            defaultValue: {
+              value: 'shiba'
+            },
+            description: 'Type of dog breed',
+            valueType: 'STRING'
+          }
+        };
+        template.version = {
+          ...deepCopy(VERSION_INFO),
+          updateTime: new Date(VERSION_INFO.updateTime).toUTCString()
+        } as Version;
+        const templateJson = JSON.stringify(template);
+        const initializedTemplate = remoteConfig.initServerTemplate();
+        initializedTemplate.set(templateJson);
+        const parsed = initializedTemplate.toJSON();
+        expect(parsed).deep.equals(template);
+      });
+
+      describe('should throw error if there are any JSON or tempalte parsing errors', () => {
+        const INVALID_PARAMETERS: any[] = [null, '', 'abc', 1, true, []];
+        const INVALID_CONDITIONS: any[] = [null, '', 'abc', 1, true, {}];
+  
+        let sourceTemplate = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+        const jsonString = '{invalidJson: null}';
+        it('should throw if template is an invalid JSON', () => {
+          expect(() => remoteConfig.initServerTemplate({ template: jsonString }))
+            .to.throw(/Failed to parse the JSON string: ([\D\w]*)\./);
+        });
+  
+        INVALID_PARAMETERS.forEach((invalidParameter) => {
+          sourceTemplate.parameters = invalidParameter;
+          const jsonString = JSON.stringify(sourceTemplate);
+          it(`should throw if the template is invalid - parameters is ${JSON.stringify(invalidParameter)}`, () => {
+            expect(() => remoteConfig.initServerTemplate({ template: jsonString }))
+              .to.throw('Remote Config parameters must be a non-null object');
+          });
+        });
+  
+        sourceTemplate = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+        INVALID_CONDITIONS.forEach((invalidConditions) => {
+          sourceTemplate.conditions = invalidConditions;
+          const jsonString = JSON.stringify(sourceTemplate);
+          it(`should throw if the template is invalid - conditions is ${JSON.stringify(invalidConditions)}`, () => {
+            expect(() => remoteConfig.initServerTemplate({ template: jsonString }))
+              .to.throw('Remote Config conditions must be an array');
+          });
+        });
+      });
+
+      it('should throw if template is an invalid JSON', () => {
+        const jsonString = '{invalidJson: null}';
+        const initializedTemplate = remoteConfig.initServerTemplate();
+        expect(() => initializedTemplate.set(jsonString))
+          .to.throw(/Failed to parse the JSON string: ([\D\w]*)\./);
+      });
+    });
+
+    describe('evaluate', () => {
+      it('returns a config when template is present in cache', () => {
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, 'getServerTemplate')
+          .resolves(SERVER_REMOTE_CONFIG_RESPONSE_2 as ServerTemplateData);
+        stubs.push(stub);
+        return remoteConfig.getServerTemplate()
+          .then((template: ServerTemplate) => {
+            const config = template.evaluate!();
+            expect(config.getString('dog_type')).to.equal('corgi');
+            expect(config.getBoolean('dog_type_enabled')).to.equal(true);
+            expect(config.getNumber('dog_age')).to.equal(22);
+          });
+      });
+
+      it('returns conditional value', () => {
+        const condition = {
+          name: 'is_true',
+          condition: {
+            orCondition: {
+              conditions: [
+                {
+                  andCondition: {
+                    conditions: [
+                      {
+                        name: '',
+                        true: {
+                        }
+                      }
+                    ]
+                  }
+                }
+              ]
+            }
+          }
+        };
+        const template = remoteConfig.initServerTemplate({
+          template: {
+            conditions: [condition],
+            parameters: {
+              is_enabled: {
+                defaultValue: { value: 'false' },
+                conditionalValues: { is_true: { value: 'true' } }
+              },
+            },
+            etag: '123'
+          }
+        });
+        const config = template.evaluate();
+        expect(config.getBoolean('is_enabled')).to.be.true;
+      });
+
+      it('honors condition order', () => {
+        const template = remoteConfig.initServerTemplate({
+          template: {
+            conditions: [
+              {
+                name: 'is_true',
+                condition: {
+                  orCondition: {
+                    conditions: [
+                      {
+                        andCondition: {
+                          conditions: [
+                            {
+                              true: {
+                              }
+                            }
+                          ]
+                        }
+                      }
+                    ]
+                  }
+                }
+              },
+              {
+                name: 'is_true_too',
+                condition: {
+                  orCondition: {
+                    conditions: [
+                      {
+                        andCondition: {
+                          conditions: [
+                            {
+                              true: {
+                              }
+                            }
+                          ]
+                        }
+                      }
+                    ]
+                  }
+                }
+              }],
+            parameters: {
+              dog_type: {
+                defaultValue: { value: 'chihuahua' },
+                conditionalValues: {
+                  // The is_true and is_true_too conditions both return true,
+                  // but is_true is first in the list, so the corresponding
+                  // value is selected.
+                  is_true_too: { value: 'dachshund' },
+                  is_true: { value: 'corgi' }
+                }
+              },
+            },
+            etag: '123'
+          }
+        });
+        const config = template.evaluate();
+        expect(config.getString('dog_type')).to.eq('corgi');
+      });
+
+      it('uses local default if parameter not in template', () => {
+        const template = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+        template.parameters = {};
+
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, 'getServerTemplate')
+          .resolves(template);
+        stubs.push(stub);
+
+        const defaultConfig = {
+          dog_coat: 'blue merle',
+        };
+
+        return remoteConfig.getServerTemplate({ defaultConfig })
+          .then((template: ServerTemplate) => {
+            const config = template.evaluate();
+            expect(config.getString('dog_coat')).to.equal(defaultConfig.dog_coat);
+          });
+      });
+
+      it('uses local default when parameter is in template but default value is undefined', () => {
+        const template = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+        template.parameters = {
+          dog_no_remote_default_value: {}
+        };
+
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, 'getServerTemplate')
+          .resolves(template);
+        stubs.push(stub);
+
+        const defaultConfig = {
+          dog_no_remote_default_value: 'local default'
+        };
+
+        return remoteConfig.getServerTemplate({ defaultConfig })
+          .then((template: ServerTemplate) => {
+            const config = template.evaluate!();
+            expect(config.getString('dog_no_remote_default_value')).to.equal(
+              defaultConfig.dog_no_remote_default_value);
+          });
+      });
+
+      it('uses local default when in-app default value specified', () => {
+        const template = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+        template.parameters = {
+          dog_no_remote_default_value: {}
+        };
+
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, 'getServerTemplate')
+          .resolves(template);
+        stubs.push(stub);
+
+        const defaultConfig = {
+          dog_use_inapp_default: '🐕'
+        };
+
+        return remoteConfig.getServerTemplate({ defaultConfig })
+          .then((template: ServerTemplate) => {
+            const config = template.evaluate!();
+            expect(config.getString('dog_use_inapp_default')).to.equal(
+              defaultConfig.dog_use_inapp_default);
+          });
+      });
+
+      it('uses local default when in-app default value specified after loading remote values', async () => {
+        // We had a bug caused by forgetting the first argument to
+        // Object.assign. This resulted in defaultConfig being overwritten
+        // by the remote values. So this test asserts we can use in-app
+        // default after loading remote values.
+        const template = remoteConfig.initServerTemplate({
+          defaultConfig: {
+            dog_type: 'corgi'
+          }
+        });
+
+        const response = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+
+        response.parameters = {
+          dog_type: {
+            defaultValue: {
+              value: 'pug'
+            }
+          },
+        }
+
+        template.set(response as ServerTemplateData);
+
+        let config = template.evaluate();
+
+        expect(config.getString('dog_type')).to.equal('pug');
+
+        response.parameters = {
+          dog_type: {
+            defaultValue: {
+              useInAppDefault: true
+            }
+          },
+        }
+
+        template.set(response as ServerTemplateData);
+
+        config = template.evaluate();
+
+        expect(config.getString('dog_type')).to.equal('corgi');
+      });
+
+      it('overrides local default when remote value exists', () => {
+        const response = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
+        response.parameters = {
+          dog_type_enabled: {
+            defaultValue: {
+              // Defines remote value
+              value: 'true'
+            }
+          },
+        }
+
+        const stub = sinon
+          .stub(RemoteConfigApiClient.prototype, 'getServerTemplate')
+          .resolves(response as ServerTemplateData);
+        stubs.push(stub);
+
+        return remoteConfig.getServerTemplate({
+          defaultConfig: {
+            // Defines local default
+            dog_type_enabled: false
+          }
+        })
+          .then((template: ServerTemplate) => {
+            const config = template.evaluate();
+            // Asserts remote value overrides local default.
+            expect(config.getBoolean('dog_type_enabled')).to.be.true;
+          });
+      });
+    });
+  });
+
+  // Note the static source is set in the getValue() method, but the other sources
+  // are set in the evaluate() method, so these tests span a couple layers.
+  describe('ServerConfig', () => {
+    describe('getValue', () => {
+      it('should return static when default and remote are not defined', () => {
+        const templateData = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+        // Omits remote parameter values.
+        templateData.parameters = {
+        };
+        // Omits in-app default values.
+        const template = remoteConfig.initServerTemplate({ template: templateData });
+        const config = template.evaluate();
+        const value = config.getValue('dog_type');
+        expect(value.asString()).to.equal('');
+        expect(value.getSource()).to.equal('static');
+      });
+  
+      it('should return default value when it is defined', () => {
+        const templateData = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+        // Omits remote parameter values.
+        templateData.parameters = {
+        };
+        const template = remoteConfig.initServerTemplate({
+          template: templateData,
+          // Defines in-app default values.
+          defaultConfig: {
+            dog_type: 'shiba'
+          }
+        });
+        const config = template.evaluate();
+        const value = config.getValue('dog_type');
+        expect(value.asString()).to.equal('shiba');
+        expect(value.getSource()).to.equal('default');
+      });
+  
+      it('should return remote value when it is defined', () => {
+        const templateData = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+        // Defines remote parameter values.
+        templateData.parameters = {
+          dog_type: {
+            defaultValue: {
+              value: 'pug'
+            }
+          }
+        };
+        const template = remoteConfig.initServerTemplate({
+          template: templateData,
+          // Defines in-app default values.
+          defaultConfig: {
+            dog_type: 'shiba'
+          }
+        });
+        const config = template.evaluate();
+        const value = config.getValue('dog_type');
+        expect(value.asString()).to.equal('pug');
+        expect(value.getSource()).to.equal('remote');
+      });
+    });
+
+    describe('getString', () => {
+      it('returns a string value', () => {
+        const templateData = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+        const template = remoteConfig.initServerTemplate({
+          template: templateData,
+          defaultConfig: {
+            dog_type: 'shiba'
+          }
+        });
+        const config = template.evaluate();
+        expect(config.getString('dog_type')).to.equal('shiba');
+      });
+    });
+
+    describe('getNumber', () => {
+      it('returns a numeric value', () => {
+        const templateData = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+        const template = remoteConfig.initServerTemplate({
+          template: templateData,
+          defaultConfig: {
+            dog_age: 12
+          }
+        });
+        const config = template.evaluate();
+        expect(config.getNumber('dog_age')).to.equal(12);
+      });
+    });
+
+    describe('getBoolean', () => {
+      it('returns a boolean value', () => {
+        const templateData = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
+        const template = remoteConfig.initServerTemplate({
+          template: templateData,
+          defaultConfig: {
+            dog_is_cute: true
+          }
+        });
+        const config = template.evaluate();
+        expect(config.getBoolean('dog_is_cute')).to.be.true;
+      });
+    });
+  });
+
   function runInvalidResponseTests(rcOperation: () => Promise<RemoteConfigTemplate>,
     operationName: any): void {
     it('should propagate API errors', () => {