workerd: `IncomingRequestCfProperties` is missing geo data

Hello,

This issue pertains to @cloudflare/worker-types. I would open a pull request as I have the issue resolved in my local package, but I’m not sure how the type generation works in this repo.

The issue is that IncomingRequestCfProperties is not including the IncomingRequestCfPropertiesGeographicInformation.

IncomingRequestCfProperties are currently generated as:

declare type IncomingRequestCfProperties<HostMetadata = unknown> =
  IncomingRequestCfPropertiesBase &
    IncomingRequestCfPropertiesBotManagementEnterprise &
    IncomingRequestCfPropertiesCloudflareForSaaSEnterprise<HostMetadata> &
    IncomingRequestCfPropertiesGeographicInformation &
    IncomingRequestCfPropertiesCloudflareAccessOrApiShield;

Which is fine, but the IncomingRequestCfPropertiesGeographicInformation is omitted from the export as it is a union type and not an interface.

Currently the following is generated for IncomingRequestCfPropertiesGeographicInformation:

/**
 * Geographic data about the request's origin.
 */
declare type IncomingRequestCfPropertiesGeographicInformation =
  | {}
  | {
      /** The country code `"T1"` is used for requests originating on TOR  */
      country: "T1";
    }
  | {
      /**
       * The [ISO 3166-1 Alpha 2](https://www.iso.org/iso-3166-country-codes.html) country code the request originated from.
       *
       * If your worker is [configured to accept TOR connections](https://support.cloudflare.com/hc/en-us/articles/203306930-Understanding-Cloudflare-Tor-support-and-Onion-Routing), this may also be `"T1"`, indicating a request that originated over TOR.
       *
       * If Cloudflare is unable to determine where the request originated this property is omitted.
       *
       * @example "GB"
       */
      country: Iso3166Alpha2Code;
      /**
       * If present, this property indicates that the request originated in the EU
       *
       * @example "1"
       */
      isEUCountry?: "1";
      /**
       * A two-letter code indicating the continent the request originated from.
       *
       * @example "AN"
       */
      continent: ContinentCode;
      /**
       * The city the request originated from
       *
       * @example "Austin"
       */
      city?: string;
      /**
       * Postal code of the incoming request
       *
       * @example "78701"
       */
      postalCode?: string;
      /**
       * Latitude of the incoming request
       *
       * @example "30.27130"
       */
      latitude?: string;
      /**
       * Longitude of the incoming request
       *
       * @example "-97.74260"
       */
      longitude?: string;
      /**
       * Timezone of the incoming request
       *
       * @example "America/Chicago"
       */
      timezone?: string;
      /**
       * If known, the ISO 3166-2 name for the first level region associated with
       * the IP address of the incoming request
       *
       * @example "Texas"
       */
      region?: string;
      /**
       * If known, the ISO 3166-2 code for the first-level region associated with
       * the IP address of the incoming request
       *
       * @example "TX"
       */
      regionCode?: string;
      /**
       * Metro code (DMA) of the incoming request
       *
       * @example "635"
       */
      metroCode?: string;
    };

This should be converted to an interface so the intersection IncomingRequestCfProperties type is valid. It should be changed to something like:

/**
 * Geographic data about the request's origin.
 */
declare interface IncomingRequestCfPropertiesGeographicInformation {
  /**
   * The [ISO 3166-1 Alpha 2](https://www.iso.org/iso-3166-country-codes.html) country code the request originated from.
   *
   * If your worker is [configured to accept TOR connections](https://support.cloudflare.com/hc/en-us/articles/203306930-Understanding-Cloudflare-Tor-support-and-Onion-Routing), this may also be `"T1"`, indicating a request that originated over TOR.
   *
   * If Cloudflare is unable to determine where the request originated this property is omitted.
   *
   * @example "GB"
   */
  country?: Iso3166Alpha2Code | 'T1';
  /**
   * If present, this property indicates that the request originated in the EU
   *
   * @example "1"
   */
  isEUCountry?: "1";
  /**
   * A two-letter code indicating the continent the request originated from.
   *
   * @example "AN"
   */
  continent?: ContinentCode;
  /**
   * The city the request originated from
   *
   * @example "Austin"
   */
  city?: string;
  /**
   * Postal code of the incoming request
   *
   * @example "78701"
   */
  postalCode?: string;
  /**
   * Latitude of the incoming request
   *
   * @example "30.27130"
   */
  latitude?: string;
  /**
   * Longitude of the incoming request
   *
   * @example "-97.74260"
   */
  longitude?: string;
  /**
   * Timezone of the incoming request
   *
   * @example "America/Chicago"
   */
  timezone?: string;
  /**
   * If known, the ISO 3166-2 name for the first level region associated with
   * the IP address of the incoming request
   *
   * @example "Texas"
   */
  region?: string;
  /**
   * If known, the ISO 3166-2 code for the first-level region associated with
   * the IP address of the incoming request
   *
   * @example "TX"
   */
  regionCode?: string;
  /**
   * Metro code (DMA) of the incoming request
   *
   * @example "635"
   */
  metroCode?: string;
};

Making all the properties optional handles the case where there is no data {} and making the country field a union of Iso3166Alpha2Code | 'T1' handles the TOR case.

Looking forward to seeing this patched. In the mean time I just have to modify my local node_modules/@cloudflare/workers-types on every install. Its pretty annoying. I can’t create my own fork since https://github.com/cloudflare/workers-types is no longer maintained. If you have a better suggestion for a stop gap that doesn’t involve // @ts-ignore I’m all ears.

Thanks!

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 5
  • Comments: 18 (7 by maintainers)

Commits related to this issue

Most upvoted comments

Sounds good! I’ve got it working locally and I think this is definitely an improvement. 👍 Any thoughts on when it might land?

Thanks for elaborating on your use case @SupremeTechnopriest. Speaking with @penalosa and @petebacondarwin, we think the best way forward would be to make IncomingRequestCfProperties and RequestInitCfProperties extend Record<string, unknown>. This allows us to “correctly” type fetch, and the incoming request, giving code completion and type checking for known properties, whilst matching the runtime behaviour of ignoring unknown cf properties. This also allows you to pass an incoming request directly where an outgoing request was expected, add outgoing cf properties on an incoming request (as you do in your example), and add custom cf properties when performing fetches across service bindings. See this playground.

Hey! 👋 Going to try work on this today, so should be in next week’s release. 👍

I also want to know the ETA of this.

@mrbbot Sounds good on the IncomingRequestCfPropertiesGeographicInformation interface!

In regards to the second issue, my worker constructs sub requests using a pipeline of pure functions. The problem I’m seeing is that when you construct a request (new Request()) the cf object changes from IncomingRequestCfProperties to RequestInitCfProperties. I understand that the RequestInit type has the correct cf interface, but I don’t set all the cf properties at construct time. Here is a simplified example.

https://codesandbox.io/p/sandbox/practical-flower-q8twiw?selection=[{"endColumn"%3A1%2C"endLineNumber"%3A14%2C"startColumn"%3A1%2C"startLineNumber"%3A14}]&file=%2Fsrc%2Findex.ts

Do you have any ideas to support this use case? I think we would either need to break the type out into IncomingRequest and Request or add another generic.

If we broke it out into two types you could do:

async handleFetch(request: IncomingRequst) {
  return fetch(request as Request)
}

Or we can modify fetch to accept IncomingRequest | Request. I understand why you would want to keep one type for requests, but as it stands the Request type is inaccurate. We should at least add a way to force it to be accurate via another generic type:

declare class Request<CfHostMetadata = unknown, CfType = IncomingRequestCfProperties<CfHostMetadata>> extends Body {
  constructor(input: RequestInfo, init?: RequestInit);
  clone(): Request<CfHostMetadata>;
  /** Returns request's HTTP method, which is "GET" by default. */
  readonly method: string;
  /** Returns the URL of request as a string. */
  readonly url: string;
  /** Returns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the "Host" header. */
  readonly headers: Headers;
  /** Returns the redirect mode associated with request, which is a string indicating how redirects for the request will be handled during fetching. A request will follow redirects by default. */
  readonly redirect: string;
  readonly fetcher: Fetcher | null;
  /** Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort event handler. */
  readonly signal: AbortSignal;
  readonly cf?: CfType;
}

I am still of the belief that these are really two different classes. CfHostMetadata is not present on a constructed request and there is ambiguity between the cf object… but I would be happy with what ever solution you decide on as long as I can pass a constructed request around and have it typed correctly.

Hey, sorry I’ve been busy this week. We’ve discussed this with MrBBot, and I’ve been trying to reproduce your issue.

For reference, see this TS playground, and you have a point regarding the union type if it contains {} - removing it fixed the issue, replacing it with Record<string, never> re-introduced the desired optional nature.

@mrbbot, any thoughts about changing it? 🤔

@mrbbot The issue is probably a child of this https://github.com/cloudflare/workers-types/pull/310. Which direction is the team likely to end up heading?