From d458d7a8844e73afe0610099ca7ae7afc928ced6 Mon Sep 17 00:00:00 2001 From: Etienne Latendresse Date: Tue, 21 Jul 2026 15:10:40 -0400 Subject: [PATCH 1/2] Rename geotargeting addon to georouting and simplify to per-region edge hosts --- README.md | 20 +++---- lib/addons/georouting.test.js | 50 ++++++++++++++++++ lib/addons/georouting.ts | 39 ++++++++++++++ lib/addons/geotargeting.test.js | 94 --------------------------------- lib/addons/geotargeting.ts | 72 ------------------------- 5 files changed, 99 insertions(+), 176 deletions(-) create mode 100644 lib/addons/georouting.test.js create mode 100644 lib/addons/georouting.ts delete mode 100644 lib/addons/geotargeting.test.js delete mode 100644 lib/addons/geotargeting.ts diff --git a/README.md b/README.md index afbb994..4bc5dec 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ JavaScript SDK for integrating with an [Optable Data Connectivity Node (DCN)](ht - [Rules](#rules) - [Return Value](#return-value) - [Input Type](#input-type) -- [Geotargeting](#geotargeting) +- [Georouting](#georouting) - [Demo Pages](#demo-pages) ## Installing @@ -1175,27 +1175,27 @@ type NodeTargetingRule = { }; ``` -## Geotargeting +## Georouting -The geotargeting addon maps a visitor's geo (country code) to the Optable host and node that should serve them, so that a single SDK bundle can route traffic to region-specific DCNs. +The georouting addon maps a visitor's geo (country code) to the Optable edge host that should serve them, so that a single SDK bundle can route traffic to the right regional edge. ```typescript -import { getGeoConfig } from "@optable/web-sdk/lib/dist/addons/geotargeting"; +import { getGeoRouting } from "@optable/web-sdk/lib/dist/addons/georouting"; -const geoConfig = getGeoConfig("acme", visitorGeo); // e.g. { host: "na.edge.optable.co", node: "acme" } for "US" +const host = getGeoRouting(visitorGeo); // e.g. "na.edge.optable.co" for "US" -if (geoConfig) { +if (host) { const sdk = new OptableSDK({ - host: geoConfig.host, - node: geoConfig.node, + host, + node: "my-node", site: "my-site", }); } ``` -The first argument is the node name: the tenant name, optionally with an `-auth` suffix to select the auth variant of the node (e.g. `"acme-auth"`). `getGeoConfig` returns `null` when the geo is not supported, in which case region-specific initialization should be skipped. For geos served by a dedicated cloud host rather than a regional edge host, `node` is `undefined` and the host's default node is used. +`getGeoRouting` returns `null` when the geo is not supported, in which case region-specific initialization should be skipped. The default `GeoMap` supports the `US`, `CA`, `GB`, `UK` and `AU` geos, each mapped to its regional edge host; pass a custom `GeoMap` as the second argument for other regions. -The default `GeoMap` supports the `US`, `CA`, `GB`, `UK` and `AU` geos and reflects a specific provisioning shape: regional edge nodes in US/CA and dedicated per-tenant cloud hosts (`.cloud..optable.co`) in AU and the EU. The dedicated hosts only exist for tenants provisioned that way — pass a custom `GeoMap` as the third argument when your topology differs; see `lib/addons/geotargeting.ts` for the entry format. +The default map assumes one edge host per region. If a customer runs distinct DCNs that share an edge host (for example separate US and CA DCNs both on `na.edge`), resolve the host with this addon and select the DCN via `node`/`site` in the SDK config, or pass a custom `GeoMap`. ## Demo Pages diff --git a/lib/addons/georouting.test.js b/lib/addons/georouting.test.js new file mode 100644 index 0000000..4fa6072 --- /dev/null +++ b/lib/addons/georouting.test.js @@ -0,0 +1,50 @@ +import { getGeoRouting, DEFAULT_GEO_MAP } from "./georouting.ts"; + +describe("getGeoRouting", () => { + test("resolves the edge host for each supported region", () => { + expect(getGeoRouting("US")).toBe("na.edge.optable.co"); + expect(getGeoRouting("CA")).toBe("ca.edge.optable.co"); + expect(getGeoRouting("AU")).toBe("au.edge.optable.co"); + }); + + test("resolves GB and UK to the same EU edge host", () => { + expect(getGeoRouting("GB")).toBe("eu.edge.optable.co"); + expect(getGeoRouting("UK")).toBe("eu.edge.optable.co"); + }); + + test("returns null for an unsupported geo", () => { + expect(getGeoRouting("FR")).toBeNull(); + }); + + test("returns null for a missing geo", () => { + expect(getGeoRouting("")).toBeNull(); + expect(getGeoRouting(undefined)).toBeNull(); + }); + + test("returns null for geos inherited from Object.prototype", () => { + expect(getGeoRouting("constructor")).toBeNull(); + expect(getGeoRouting("__proto__")).toBeNull(); + expect(getGeoRouting("toString")).toBeNull(); + }); + + test("is case-sensitive on the geo key", () => { + expect(getGeoRouting("us")).toBeNull(); + }); + + test("accepts a custom geo map", () => { + const geoMap = { + BR: "sa.edge.optable.co", + MX: "na.edge.optable.co", + }; + expect(getGeoRouting("BR", geoMap)).toBe("sa.edge.optable.co"); + expect(getGeoRouting("MX", geoMap)).toBe("na.edge.optable.co"); + expect(getGeoRouting("US", geoMap)).toBeNull(); + }); + + test("exposes the default geo map", () => { + expect(DEFAULT_GEO_MAP.US).toBe("na.edge.optable.co"); + expect(DEFAULT_GEO_MAP.CA).toBe("ca.edge.optable.co"); + expect(DEFAULT_GEO_MAP.GB).toBe("eu.edge.optable.co"); + expect(DEFAULT_GEO_MAP.AU).toBe("au.edge.optable.co"); + }); +}); diff --git a/lib/addons/georouting.ts b/lib/addons/georouting.ts new file mode 100644 index 0000000..a7e374b --- /dev/null +++ b/lib/addons/georouting.ts @@ -0,0 +1,39 @@ +/* + * The georouting addon maps a visitor's geo (country code) to the Optable edge + * host that should serve them, so a single SDK bundle can route traffic to the + * regional edge closest to (and provisioned for) the visitor. + * + * Every supported region has its own edge host, so the map is a plain + * geo → host lookup. The caller supplies the SDK `node`/`site` — this addon + * only resolves the host. + * + * Note on same-region DCNs: the default map assumes one edge host per region. + * If a customer runs distinct DCNs that share an edge host (e.g. separate US + * and CA DCNs both on `na.edge`), resolve the host with this addon and select + * the DCN via `node`/`site` in the SDK config, or pass a custom GeoMap. + */ + +export type GeoMap = Record; + +export const DEFAULT_GEO_MAP: GeoMap = { + AU: "au.edge.optable.co", + CA: "ca.edge.optable.co", + GB: "eu.edge.optable.co", + UK: "eu.edge.optable.co", + US: "na.edge.optable.co", +}; + +/* + * getGeoRouting() resolves the Optable edge host for a given geo. + * + * Returns null when the geo is missing or not present in the map, in which + * case the caller should skip region-specific initialization. + */ +export function getGeoRouting(geo: string | undefined, geoMap: GeoMap = DEFAULT_GEO_MAP): string | null { + // hasOwnProperty guards against inherited Object.prototype members being + // picked up when an unexpected geo like "constructor" is looked up. + if (geo === undefined || !Object.prototype.hasOwnProperty.call(geoMap, geo)) { + return null; + } + return geoMap[geo]; +} diff --git a/lib/addons/geotargeting.test.js b/lib/addons/geotargeting.test.js deleted file mode 100644 index 6eaaf95..0000000 --- a/lib/addons/geotargeting.test.js +++ /dev/null @@ -1,94 +0,0 @@ -import { getGeoConfig, DEFAULT_GEO_MAP } from "./geotargeting.ts"; - -describe("getGeoConfig", () => { - test("resolves the regional edge host and node for US", () => { - expect(getGeoConfig("acme", "US")).toEqual({ - host: "na.edge.optable.co", - node: "acme", - }); - }); - - test("resolves the auth node for US", () => { - expect(getGeoConfig("acme-auth", "US")).toEqual({ - host: "na.edge.optable.co", - node: "acme-auth", - }); - }); - - test("resolves the regional edge host and node for CA", () => { - expect(getGeoConfig("acme", "CA")).toEqual({ - host: "ca.edge.optable.co", - node: "acme-ca", - }); - }); - - test("resolves the auth node for CA", () => { - expect(getGeoConfig("acme-auth", "CA")).toEqual({ - host: "ca.edge.optable.co", - node: "acme-ca-auth", - }); - }); - - test("resolves a dedicated cloud host with undefined node for AU", () => { - expect(getGeoConfig("acme", "AU")).toStrictEqual({ - host: "acme.cloud.au.optable.co", - node: undefined, - }); - }); - - test("resolves a dedicated auth cloud host with undefined node for AU", () => { - expect(getGeoConfig("acme-auth", "AU")).toStrictEqual({ - host: "acme-auth.cloud.au.optable.co", - node: undefined, - }); - }); - - test("resolves GB and UK to the same EU cloud host", () => { - const expected = { host: "acme.cloud.eu.optable.co", node: undefined }; - expect(getGeoConfig("acme", "GB")).toStrictEqual(expected); - expect(getGeoConfig("acme", "UK")).toStrictEqual(expected); - }); - - test("returns null for an unsupported geo", () => { - expect(getGeoConfig("acme", "FR")).toBeNull(); - }); - - test("returns null for a missing geo", () => { - expect(getGeoConfig("acme", "")).toBeNull(); - expect(getGeoConfig("acme", undefined)).toBeNull(); - }); - - test("returns null for geos inherited from Object.prototype", () => { - expect(getGeoConfig("acme", "constructor")).toBeNull(); - expect(getGeoConfig("acme", "__proto__")).toBeNull(); - expect(getGeoConfig("acme", "toString")).toBeNull(); - }); - - test("only treats a trailing -auth as the auth variant", () => { - expect(getGeoConfig("author-press", "US")).toEqual({ - host: "na.edge.optable.co", - node: "author-press", - }); - }); - - test("accepts a custom geo map", () => { - const geoMap = { - BR: ["-br.cloud", "-br-auth.cloud", "sa.edge.optable.co"], - MX: [".cloud.mx", "-auth.cloud.mx"], - }; - expect(getGeoConfig("acme", "BR", geoMap)).toEqual({ - host: "sa.edge.optable.co", - node: "acme-br", - }); - expect(getGeoConfig("acme-auth", "MX", geoMap)).toStrictEqual({ - host: "acme-auth.cloud.mx.optable.co", - node: undefined, - }); - expect(getGeoConfig("acme", "US", geoMap)).toBeNull(); - }); - - test("exposes the default geo map", () => { - expect(DEFAULT_GEO_MAP.US).toEqual([".cloud", "-auth.cloud", "na.edge.optable.co"]); - expect(DEFAULT_GEO_MAP.CA).toEqual(["-ca.cloud", "-ca-auth.cloud", "ca.edge.optable.co"]); - }); -}); diff --git a/lib/addons/geotargeting.ts b/lib/addons/geotargeting.ts deleted file mode 100644 index 636b430..0000000 --- a/lib/addons/geotargeting.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * The geotargeting addon maps a visitor's geo (country code) to the Optable - * host and node that should serve them, so that a single SDK bundle can route - * traffic to region-specific DCNs. - * - * A GeoMap entry is a tuple of host fragments keyed by country code: - * [0] — host suffix for the standard (non-auth) node - * [1] — host suffix for the auth node - * [2] — optional regional edge host shared by multiple nodes - * - * When a regional edge host ([2]) is present, it is used as the host and the - * node name is derived from the tenant name plus the suffix with ".cloud" - * removed (e.g. "acme" + "-ca-auth" → "acme-ca-auth"). Otherwise the tenant - * runs on a dedicated cloud host built as `${name}${suffix}.optable.co` and - * the node is undefined (the host's default node is used). - * - * DEFAULT_GEO_MAP reflects one specific provisioning shape: regional edge - * nodes in US/CA and dedicated per-tenant cloud hosts in AU and the EU. The - * dedicated hosts only exist for tenants provisioned that way — tenants with - * a different topology must pass their own GeoMap. - */ - -export type GeoMapEntry = [string, string] | [string, string, string]; -export type GeoMap = Record; - -export interface GeoConfig { - host: string; - node: string | undefined; -} - -const EU_ENTRY: GeoMapEntry = [".cloud.eu", "-auth.cloud.eu"]; - -export const DEFAULT_GEO_MAP: GeoMap = { - AU: [".cloud.au", "-auth.cloud.au"], - CA: ["-ca.cloud", "-ca-auth.cloud", "ca.edge.optable.co"], - GB: EU_ENTRY, - UK: EU_ENTRY, - US: [".cloud", "-auth.cloud", "na.edge.optable.co"], -}; - -/* - * getGeoConfig() resolves the host and node for a node name in a given geo. - * - * nodeName is the tenant name, optionally with an "-auth" suffix selecting the - * auth variant of the node (e.g. "acme" or "acme-auth"). - * - * Returns null when the geo is missing or not present in the map, in which - * case the caller should skip region-specific initialization. - */ -export function getGeoConfig( - nodeName: string, - geo: string | undefined, - geoMap: GeoMap = DEFAULT_GEO_MAP -): GeoConfig | null { - const entry = geo === undefined ? undefined : geoMap[geo]; - // Array.isArray also rejects inherited Object.prototype members picked up - // when an unexpected geo like "constructor" is looked up in the map - if (!Array.isArray(entry)) { - return null; - } - - const auth = /-auth$/i.test(nodeName); - const name = auth ? nodeName.replace(/-auth$/i, "") : nodeName; - const suffix = auth ? entry[1] : entry[0]; - const edgeHost = entry[2]; - - if (edgeHost != null) { - return { host: edgeHost, node: name + suffix.replace(/\.cloud/, "") }; - } - - return { host: `${name}${suffix}.optable.co`, node: undefined }; -} From 059345386b6c3909c9091d98449eed34a185a4f4 Mon Sep 17 00:00:00 2001 From: Etienne Latendresse Date: Tue, 21 Jul 2026 15:52:02 -0400 Subject: [PATCH 2/2] Rename addon to geo-routing and key the default map by region code (NA/CA/EU/AU) --- README.md | 14 +++++----- lib/addons/geo-routing.test.js | 42 ++++++++++++++++++++++++++++ lib/addons/geo-routing.ts | 35 ++++++++++++++++++++++++ lib/addons/georouting.test.js | 50 ---------------------------------- lib/addons/georouting.ts | 39 -------------------------- 5 files changed, 84 insertions(+), 96 deletions(-) create mode 100644 lib/addons/geo-routing.test.js create mode 100644 lib/addons/geo-routing.ts delete mode 100644 lib/addons/georouting.test.js delete mode 100644 lib/addons/georouting.ts diff --git a/README.md b/README.md index 4bc5dec..f3d9256 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ JavaScript SDK for integrating with an [Optable Data Connectivity Node (DCN)](ht - [Rules](#rules) - [Return Value](#return-value) - [Input Type](#input-type) -- [Georouting](#georouting) +- [Geo-routing](#geo-routing) - [Demo Pages](#demo-pages) ## Installing @@ -1175,14 +1175,14 @@ type NodeTargetingRule = { }; ``` -## Georouting +## Geo-routing -The georouting addon maps a visitor's geo (country code) to the Optable edge host that should serve them, so that a single SDK bundle can route traffic to the right regional edge. +The geo-routing addon maps a visitor's region code to the Optable edge host that should serve them, so that a single SDK bundle can route traffic to the right regional edge. ```typescript -import { getGeoRouting } from "@optable/web-sdk/lib/dist/addons/georouting"; +import { getGeoRouting } from "@optable/web-sdk/lib/dist/addons/geo-routing"; -const host = getGeoRouting(visitorGeo); // e.g. "na.edge.optable.co" for "US" +const host = getGeoRouting(visitorRegion); // e.g. "na.edge.optable.co" for "NA" if (host) { const sdk = new OptableSDK({ @@ -1193,9 +1193,9 @@ if (host) { } ``` -`getGeoRouting` returns `null` when the geo is not supported, in which case region-specific initialization should be skipped. The default `GeoMap` supports the `US`, `CA`, `GB`, `UK` and `AU` geos, each mapped to its regional edge host; pass a custom `GeoMap` as the second argument for other regions. +`getGeoRouting` returns `null` when the region is not supported, in which case region-specific initialization should be skipped. The default `GeoMap` supports the `NA`, `CA`, `EU` and `AU` region codes, each mapped to its regional edge host; pass a custom `GeoMap` as the second argument for other regions. -The default map assumes one edge host per region. If a customer runs distinct DCNs that share an edge host (for example separate US and CA DCNs both on `na.edge`), resolve the host with this addon and select the DCN via `node`/`site` in the SDK config, or pass a custom `GeoMap`. +Keys are region codes, not country codes. Translating a visitor's country code to a region code (for example `GB`/`UK` → `EU`) is the caller's responsibility — the addon deliberately knows only regions. The caller also supplies the SDK `node`/`site`; this addon only resolves the host. ## Demo Pages diff --git a/lib/addons/geo-routing.test.js b/lib/addons/geo-routing.test.js new file mode 100644 index 0000000..492e29f --- /dev/null +++ b/lib/addons/geo-routing.test.js @@ -0,0 +1,42 @@ +import { getGeoRouting, DEFAULT_GEO_MAP } from "./geo-routing.ts"; + +describe("getGeoRouting", () => { + test("resolves the edge host for each supported region", () => { + expect(getGeoRouting("NA")).toBe("na.edge.optable.co"); + expect(getGeoRouting("CA")).toBe("ca.edge.optable.co"); + expect(getGeoRouting("EU")).toBe("eu.edge.optable.co"); + expect(getGeoRouting("AU")).toBe("au.edge.optable.co"); + }); + + test("returns null for an unsupported region", () => { + expect(getGeoRouting("SA")).toBeNull(); + }); + + test("returns null for a missing region", () => { + expect(getGeoRouting("")).toBeNull(); + expect(getGeoRouting(undefined)).toBeNull(); + }); + + test("returns null for regions inherited from Object.prototype", () => { + expect(getGeoRouting("constructor")).toBeNull(); + expect(getGeoRouting("__proto__")).toBeNull(); + expect(getGeoRouting("toString")).toBeNull(); + }); + + test("is case-sensitive on the region key", () => { + expect(getGeoRouting("na")).toBeNull(); + }); + + test("accepts a custom geo map", () => { + const geoMap = { SA: "sa.edge.optable.co" }; + expect(getGeoRouting("SA", geoMap)).toBe("sa.edge.optable.co"); + expect(getGeoRouting("NA", geoMap)).toBeNull(); + }); + + test("exposes the default geo map", () => { + expect(DEFAULT_GEO_MAP.NA).toBe("na.edge.optable.co"); + expect(DEFAULT_GEO_MAP.CA).toBe("ca.edge.optable.co"); + expect(DEFAULT_GEO_MAP.EU).toBe("eu.edge.optable.co"); + expect(DEFAULT_GEO_MAP.AU).toBe("au.edge.optable.co"); + }); +}); diff --git a/lib/addons/geo-routing.ts b/lib/addons/geo-routing.ts new file mode 100644 index 0000000..443dab1 --- /dev/null +++ b/lib/addons/geo-routing.ts @@ -0,0 +1,35 @@ +/* + * The geo-routing addon maps a visitor's region code to the Optable edge host + * that should serve them, so a single SDK bundle can route traffic to the + * regional edge closest to (and provisioned for) the visitor. + * + * Keys are region codes (not country codes) that map 1:1 to Optable edge hosts. + * Translating a visitor's country code to a region code is the caller's + * responsibility — the addon deliberately knows only regions, not the full + * country-to-region table. The caller also supplies the SDK `node`/`site`; + * this addon only resolves the host. + */ + +export type GeoMap = Record; + +export const DEFAULT_GEO_MAP: GeoMap = { + AU: "au.edge.optable.co", + CA: "ca.edge.optable.co", + EU: "eu.edge.optable.co", + NA: "na.edge.optable.co", +}; + +/* + * getGeoRouting() resolves the Optable edge host for a given region code. + * + * Returns null when the region is missing or not present in the map, in which + * case the caller should skip region-specific initialization. + */ +export function getGeoRouting(region: string | undefined, geoMap: GeoMap = DEFAULT_GEO_MAP): string | null { + // hasOwnProperty guards against inherited Object.prototype members being + // picked up when an unexpected region like "constructor" is looked up. + if (region === undefined || !Object.prototype.hasOwnProperty.call(geoMap, region)) { + return null; + } + return geoMap[region]; +} diff --git a/lib/addons/georouting.test.js b/lib/addons/georouting.test.js deleted file mode 100644 index 4fa6072..0000000 --- a/lib/addons/georouting.test.js +++ /dev/null @@ -1,50 +0,0 @@ -import { getGeoRouting, DEFAULT_GEO_MAP } from "./georouting.ts"; - -describe("getGeoRouting", () => { - test("resolves the edge host for each supported region", () => { - expect(getGeoRouting("US")).toBe("na.edge.optable.co"); - expect(getGeoRouting("CA")).toBe("ca.edge.optable.co"); - expect(getGeoRouting("AU")).toBe("au.edge.optable.co"); - }); - - test("resolves GB and UK to the same EU edge host", () => { - expect(getGeoRouting("GB")).toBe("eu.edge.optable.co"); - expect(getGeoRouting("UK")).toBe("eu.edge.optable.co"); - }); - - test("returns null for an unsupported geo", () => { - expect(getGeoRouting("FR")).toBeNull(); - }); - - test("returns null for a missing geo", () => { - expect(getGeoRouting("")).toBeNull(); - expect(getGeoRouting(undefined)).toBeNull(); - }); - - test("returns null for geos inherited from Object.prototype", () => { - expect(getGeoRouting("constructor")).toBeNull(); - expect(getGeoRouting("__proto__")).toBeNull(); - expect(getGeoRouting("toString")).toBeNull(); - }); - - test("is case-sensitive on the geo key", () => { - expect(getGeoRouting("us")).toBeNull(); - }); - - test("accepts a custom geo map", () => { - const geoMap = { - BR: "sa.edge.optable.co", - MX: "na.edge.optable.co", - }; - expect(getGeoRouting("BR", geoMap)).toBe("sa.edge.optable.co"); - expect(getGeoRouting("MX", geoMap)).toBe("na.edge.optable.co"); - expect(getGeoRouting("US", geoMap)).toBeNull(); - }); - - test("exposes the default geo map", () => { - expect(DEFAULT_GEO_MAP.US).toBe("na.edge.optable.co"); - expect(DEFAULT_GEO_MAP.CA).toBe("ca.edge.optable.co"); - expect(DEFAULT_GEO_MAP.GB).toBe("eu.edge.optable.co"); - expect(DEFAULT_GEO_MAP.AU).toBe("au.edge.optable.co"); - }); -}); diff --git a/lib/addons/georouting.ts b/lib/addons/georouting.ts deleted file mode 100644 index a7e374b..0000000 --- a/lib/addons/georouting.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * The georouting addon maps a visitor's geo (country code) to the Optable edge - * host that should serve them, so a single SDK bundle can route traffic to the - * regional edge closest to (and provisioned for) the visitor. - * - * Every supported region has its own edge host, so the map is a plain - * geo → host lookup. The caller supplies the SDK `node`/`site` — this addon - * only resolves the host. - * - * Note on same-region DCNs: the default map assumes one edge host per region. - * If a customer runs distinct DCNs that share an edge host (e.g. separate US - * and CA DCNs both on `na.edge`), resolve the host with this addon and select - * the DCN via `node`/`site` in the SDK config, or pass a custom GeoMap. - */ - -export type GeoMap = Record; - -export const DEFAULT_GEO_MAP: GeoMap = { - AU: "au.edge.optable.co", - CA: "ca.edge.optable.co", - GB: "eu.edge.optable.co", - UK: "eu.edge.optable.co", - US: "na.edge.optable.co", -}; - -/* - * getGeoRouting() resolves the Optable edge host for a given geo. - * - * Returns null when the geo is missing or not present in the map, in which - * case the caller should skip region-specific initialization. - */ -export function getGeoRouting(geo: string | undefined, geoMap: GeoMap = DEFAULT_GEO_MAP): string | null { - // hasOwnProperty guards against inherited Object.prototype members being - // picked up when an unexpected geo like "constructor" is looked up. - if (geo === undefined || !Object.prototype.hasOwnProperty.call(geoMap, geo)) { - return null; - } - return geoMap[geo]; -}