Repository: League-of-Foundry-Developers/foundry-vtt-types
Pull request #2536 (2024-03-26 16:29:48)

V11/common.utils
Bring foundry/common/utils up to date for v11.


JPMeehan: Could you please write some tests for the `geometry` file?

Patch:
diff --git a/src/foundry/client/pixi/extensions/circle.d.mts b/src/foundry/client/pixi/extensions/circle.d.mts
index 8394fc403..75a9d2305 100644
--- a/src/foundry/client/pixi/extensions/circle.d.mts
+++ b/src/foundry/client/pixi/extensions/circle.d.mts
@@ -24,7 +24,7 @@ declare module "pixi.js" {
      * @param b - The second endpoint on segment A|B
      * @returns Points where the segment A|B intersects the circle
      */
-    segmentIntersections(a: Point, b: Point): Point[];
+    segmentIntersections(a: Point, b: Point): [Point?, Point?];
 
     /**
      * Calculate an x,y point on this circle's circumference given an angle
diff --git a/src/foundry/common/module.d.mts b/src/foundry/common/module.d.mts
index bfac3458b..9e62ac434 100644
--- a/src/foundry/common/module.d.mts
+++ b/src/foundry/common/module.d.mts
@@ -88,11 +88,60 @@ declare global {
   type Semaphore = _utils.Semaphore;
   var Semaphore: typeof _utils.Semaphore; // eslint-disable-line no-var
 
-  type IterableWeakMap = _utils.IterableWeakMap;
+  type IterableWeakMap<K extends WeakKey, V> = _utils.IterableWeakMap<K, V>;
   var IterableWeakMap: typeof _utils.IterableWeakMap; // eslint-disable-line no-var
 
-  type IterableWeakSet = _utils.IterableWeakSet;
+  type IterableWeakSet<T extends WeakKey> = _utils.IterableWeakSet<T>;
   var IterableWeakSet: typeof _utils.IterableWeakSet; // eslint-disable-line no-var
 
-  // TODO: All of the functions from utils/geometry, utils/helpers, utils/http, and utils/logging
+  /* eslint-disable no-var */
+  /* --- geometry --- */
+  var orient2dFast: typeof _utils.orient2dFast;
+  var lineSegmentIntersects: typeof _utils.lineSegmentIntersects;
+  var lineLineIntersection: typeof _utils.lineLineIntersection;
+  var lineSegmentIntersection: typeof _utils.lineSegmentIntersection;
+  var lineCircleIntersection: typeof _utils.lineCircleIntersection;
+  var closestPointToSegment: typeof _utils.closestPointToSegment;
+  var quadraticIntersection: typeof _utils.quadraticIntersection;
+
+  /* --- helpers --- */
+  var benchmark: typeof _utils.benchmark;
+  var threadLock: typeof _utils.threadLock;
+  var debounce: typeof _utils.debounce;
+  var debouncedReload: typeof _utils.debouncedReload;
+  var deepClone: typeof _utils.deepClone;
+  var diffObject: typeof _utils.diffObject;
+  var objectsEqual: typeof _utils.objectsEqual;
+  var duplicate: typeof _utils.duplicate;
+  var isSubclass: typeof _utils.isSubclass;
+  var getDefiningClass: typeof _utils.getDefiningClass;
+  var encodeURL: typeof _utils.encodeURL;
+  var expandObject: typeof _utils.expandObject;
+  var filterObject: typeof _utils.filterObject;
+  var flattenObject: typeof _utils.flattenObject;
+  var getParentClasses: typeof _utils.getParentClasses;
+  var getRoute: typeof _utils.getRoute;
+  var getType: typeof _utils.getType;
+  var hasProperty: typeof _utils.hasProperty;
+  var getProperty: typeof _utils.getProperty;
+  var setProperty: typeof _utils.setProperty;
+  var invertObject: typeof _utils.invertObject;
+  var isObjectEmpty: typeof _utils.isObjectEmpty;
+  var isEmpty: typeof _utils.isEmpty;
+  var mergeObject: typeof _utils.mergeObject;
+  var parseS3URL: typeof _utils.parseS3URL;
+  var randomID: typeof _utils.randomID;
+  var timeSince: typeof _utils.timeSince;
+  var formatFileSize: typeof _utils.formatFileSize;
+  var parseUuid: typeof _utils.parseUuid;
+
+  /* --- http --- */
+
+  var fetchWithTimeout: typeof _utils.fetchWithTimeout;
+  var fetchJsonWithTimeout: typeof _utils.fetchJsonWithTimeout;
+
+  /* --- logging --- */
+
+  var logCompatibilityWarning: typeof _utils.logCompatibilityWarning;
+  /* eslint-enable no-var */
 }
diff --git a/src/foundry/common/utils/collection.d.mts b/src/foundry/common/utils/collection.d.mts
index f43d35bc6..f9685ffb9 100644
--- a/src/foundry/common/utils/collection.d.mts
+++ b/src/foundry/common/utils/collection.d.mts
@@ -83,8 +83,8 @@ declare class Collection<T> extends Map<string, T> {
   /**
    * Get an element from the Collection by its key.
    * @param key     - The key of the entry to retrieve
-   * @param strict  - Throw an Error if the requested id does not exist, otherwise
-   *                  (default: `false`)
+   * @param strict  - Throw an Error if the requested key does not exist,
+   *                  otherwise return undefined. (default: `false`)
    * @returns The retrieved entry value, if the key exists, otherwise undefined
    *
    * @example Get an element from the Collection by key
@@ -102,9 +102,8 @@ declare class Collection<T> extends Map<string, T> {
    * Get an entry from the Collection by name.
    * Use of this method assumes that the objects stored in the collection have a "name" attribute.
    * @param name    - The name of the entry to retrieve
-   * @param strict  - Throw an Error if the requested id does not exist,
-   *                  otherwise return null.
-   *                  (default: `false`)
+   * @param strict  - Throw an Error if the requested name does not exist,
+   *                  otherwise return undefined. (default: `false`)
    * @returns The retrieved Entity, if one was found, otherwise undefined
    *
    * @example Get an element from the Collection by name (if applicable)
diff --git a/src/foundry/common/utils/color.d.mts b/src/foundry/common/utils/color.d.mts
index cd8377a2e..502499d14 100644
--- a/src/foundry/common/utils/color.d.mts
+++ b/src/foundry/common/utils/color.d.mts
@@ -74,7 +74,7 @@ declare class Color extends Number {
 
   /**
    * Mix this Color with some other Color using a provided interpolation weight.
-   * @param other - Some other Color to mix with
+   * @param other  - Some other Color to mix with
    * @param weight - The mixing weight placed on this color where weight is placed on the other color
    * @returns The resulting mixed Color
    */
@@ -120,6 +120,100 @@ declare class Color extends Number {
    */
   [Symbol.iterator](): Generator<number>;
 
+  /**
+   * Set an rgb array with the rgb values contained in this Color class.
+   * @param vec3 - Receive the result. Must be an array with at least a length of 3.
+   */
+  applyRGB(vec: number[]): void;
+
+  /**
+   * Apply a linear interpolation between two colors, according to the weight.
+   * @param color1 - The first color to mix.
+   * @param color2 - The second color to mix.
+   * @param weight - Weight of the linear interpolation.
+   * @returns The resulting mixed color
+   */
+  static mix(color1: number, color2: number, weight: number): number;
+
+  /**
+   * Multiply a color by a scalar
+   * @param color  - The color to multiply.
+   * @param scalar - A static scalar to multiply with.
+   * @returns The resulting color as a number.
+   */
+  static multiplyScalar(color: number, scalar: number): number;
+
+  /**
+   * Maximize two colors.
+   * @param color1 - The first color.
+   * @param color2 - The second color.
+   * @returns The resulting color as a number.
+   */
+  static maximize(color1: number, color2: number): number;
+
+  /**
+   * Maximize a color by a static scalar.
+   * @param color  - The color to maximize.
+   * @param scalar - Scalar to maximize with (normalized).
+   * @returns The resulting color as a number.
+   */
+  static maximizeScalar(color: number, scalar: number): number;
+
+  /**
+   * Add two colors.
+   * @param color1 - The first color.
+   * @param color2 - The second color.
+   * @returns The resulting color as a number.
+   */
+  static add(color1: number, color2: number): number;
+
+  /**
+   * Add a static scalar to a color.
+   * @param color  - The color.
+   * @param scalar - Scalar to add with (normalized).
+   * @returns The resulting color as a number.
+   */
+  static addScalar(color: number, scalar: number): number;
+
+  /**
+   * Subtract two colors.
+   * @param color1 - The first color.
+   * @param color2 - The second color.
+   * @returns The resulting color as a number.
+   */
+  static subtract(color1: number, color2: number): number;
+
+  /**
+   * Subtract a color by a static scalar.
+   * @param color  - The color.
+   * @param scalar - Scalar to subtract with (normalized).
+   * @returns The resulting color as a number.
+   */
+  static subtractScalar(color: number, scalar: number): number;
+
+  /**
+   * Minimize two colors.
+   * @param color1 - The first color.
+   * @param color2 - The second color.
+   * @returns The resulting color as a number.
+   */
+  static minimize(color1: number, color2: number): number;
+
+  /**
+   * Minimize a color by a static scalar.
+   * @param color  - The color.
+   * @param scalar - Scalar to minimize with (normalized).
+   * @returns The resulting color as a number.
+   */
+  static minimizeScalar(color: number, scalar: number): number;
+
+  /**
+   * Convert a color to RGB and assign values to a passed array.
+   * @param color - The color to convert to RGB values.
+   * @param vec3  - Receive the result. Must be an array with at least a length of 3.
+   */
+  static applyRGB(color: number, vec3: number[]): void;
+
   /**
    * Create a Color instance from an RGB array.
    * @param color - A color input
diff --git a/src/foundry/common/utils/geometry.d.mts b/src/foundry/common/utils/geometry.d.mts
index 7e4e68a4f..0049ac68a 100644
--- a/src/foundry/common/utils/geometry.d.mts
+++ b/src/foundry/common/utils/geometry.d.mts
@@ -13,7 +13,7 @@
  *          A negative value if the points are in clockwise order (C lies to the right of AB)
  *          Zero if the points A, B, and C are collinear.
  */
-export declare function orient2dFast(a: Point, b: Point, c: Point): number;
+export function orient2dFast(a: Point, b: Point, c: Point): number;
 
 /**
  * Quickly test whether the line segment AB intersects with the line segment CD.
@@ -26,7 +26,7 @@ export declare function orient2dFast(a: Point, b: Point, c: Point): number;
  *
  * @returns Do the line segments intersect?
  */
-export declare function lineSegmentIntersects(a: Point, b: Point, c: Point, d: Point): boolean;
+export function lineSegmentIntersects(a: Point, b: Point, c: Point, d: Point): boolean;
 
 export interface LineIntersection {
   /** The x-coordinate of intersection */
@@ -39,7 +39,7 @@ export interface LineIntersection {
   t0: number;
 
   /** The vector distance from C to D on segment CD */
-  t1?: number;
+  t1?: number | undefined;
 }
 
 interface LineIntersectionOptions {
@@ -62,12 +62,12 @@ interface LineIntersectionOptions {
  *
  * @returns An intersection point, or null if no intersection occurred
  */
-export declare function lineLineIntersection(
+export function lineLineIntersection(
   a: Point,
   b: Point,
   c: Point,
   d: Point,
-  options: LineIntersectionOptions,
+  options?: LineIntersectionOptions,
 ): LineIntersection | null;
 
 /**
@@ -83,7 +83,7 @@ export declare function lineLineIntersection(
  *
  * @returns An intersection point, or null if no intersection occurred
  */
-export declare function lineSegmentIntersection(
+export function lineSegmentIntersection(
   a: Point,
   b: Point,
   c: Point,
@@ -91,7 +91,7 @@ export declare function lineSegmentIntersection(
   epsilon?: number,
 ): LineIntersection | null;
 
-interface LineCircleIntersection {
+export interface LineCircleIntersection {
   /** Is point A inside the circle? */
   aInside: boolean;
 
@@ -108,7 +108,7 @@ interface LineCircleIntersection {
   tangent: boolean;
 
   /** Intersection points: zero, one, or two */
-  intersections: [number] | [number, number] | [number, number, number];
+  intersections: [Point?, Point?];
 }
 
 /**
@@ -123,7 +123,7 @@ interface LineCircleIntersection {
  *
  * @returns The intersection of the segment AB with the circle
  */
-export declare function lineCircleIntersection(
+export function lineCircleIntersection(
   a: Point,
   b: Point,
   center: Point,
@@ -140,7 +140,7 @@ export declare function lineCircleIntersection(
  *
  * @returns The closest point to C on segment AB
  */
-export declare function closestPointToSegment(c: Point, a: Point, b: Point): Point;
+export function closestPointToSegment(c: Point, a: Point, b: Point): Point;
 
 /**
  * Determine the points of intersection between a line segment (p0,p1) and a circle.
@@ -154,10 +154,10 @@ export declare function closestPointToSegment(c: Point, a: Point, b: Point): Poi
  * @param epsilon - A small tolerance for floating point precision
  *                  (default: `0`)
  */
-export declare function quadraticIntersection(
+export function quadraticIntersection(
   p0: Point,
   p1: Point,
   center: Point,
   radius: number,
   epsilon?: number,
-): { x: number; y: number }[];
+): [Point?, Point?];
diff --git a/src/foundry/common/utils/helpers.d.mts b/src/foundry/common/utils/helpers.d.mts
index abf128633..598189622 100644
--- a/src/foundry/common/utils/helpers.d.mts
+++ b/src/foundry/common/utils/helpers.d.mts
@@ -1,4 +1,7 @@
-import type { AnyClass, DeepPartial, InexactPartial } from "../../../types/utils.d.mts";
+import type { DocumentType } from "../../../types/helperTypes.d.mts";
+import type { AnyClass, ConstructorOf, DeepPartial, InexactPartial } from "../../../types/utils.d.mts";
+import Document from "../abstract/document.mjs";
+import type { DocumentId } from "../data/fields.d.mts";
 
 /**
  * Benchmark the performance of a function, calling it a requested number of iterations.
@@ -14,7 +17,7 @@ export function benchmark<F extends (...args: any[]) => unknown>(
 
 /**
  * A debugging function to test latency or timeouts by forcibly locking the thread for an amount of time.
- * @param ms - A number of milliseconds to lock
+ * @param ms    - A number of milliseconds to lock
  * @param debug - (default: `false`)
  * @returns
  */
@@ -114,6 +117,14 @@ export type Duplicated<T> = T extends NonStringifiable ? never : InnerDuplicated
  */
 export function isSubclass<Parent extends AnyClass>(cls: AnyClass, parent: Parent): cls is Parent;
 
+/**
+ * Search up the prototype chain and return the class that defines the given property.
+ * @param cls      - The starting class.
+ * @param property - The property name.
+ * @returns The class that defines the property.
+ */
+export function getDefiningClass(cls: ConstructorOf<any>, property: string): ConstructorOf<any>;
+
 /**
  * Encode a url-like string by replacing any characters which need encoding
  * @param path - A fully-qualified URL or url component (like a relative path)
@@ -126,11 +137,10 @@ export function encodeURL(path: string): string;
  * inner objects.
  *
  * @param obj - The object to expand
- * @param _d  - Track the recursion depth to prevent overflow
- *              (default: `0`)
  * @returns An expanded object
+ *
  */
-export function expandObject(obj: object, _d?: number): object;
+export function expandObject(obj: object): object;
 
 /**
  * Filter the contents of some source object using the structure of a template object.
@@ -168,7 +178,7 @@ export interface FilterObjectOptions {
 /**
  * Flatten a possibly multi-dimensional object to a one-dimensional one by converting all nested keys to dot notation
  * @param obj - The object to flatten
- * @param d   - Track the recursion depth to prevent overflow
+ * @param _d  - Track the recursion depth to prevent overflow. (default: `0`)
  * @returns A flattened object
  */
 export function flattenObject(obj: object, _d?: number): object;
@@ -272,7 +282,7 @@ export function isNewerVersion(v1: number | string, v0: number | string): boolea
  * A simple function to test whether an Object is empty
  * @param obj - The object to test
  * @returns Is the object empty?
- * @deprecated since v10 until v12, will be removed in v11 - Use isEmpty instead.
+ * @deprecated since v10, will be removed in v12 - Use isEmpty instead.
  */
 export function isObjectEmpty(obj: object): boolean;
 
@@ -317,13 +327,13 @@ export type MergeObject<T, U, M extends MergeObjectOptions> = UpdateInsert<
  *
  * @example Control whether merges are performed recursively
  * ```typescript
- * mergeObject({k1: {i1: "v1"}}, {k1: {i2: "v2"}}, {recursive: false}); // {k1: {i1: "v2"}}
+ * mergeObject({k1: {i1: "v1"}}, {k1: {i2: "v2"}}, {recursive: false}); // {k1: {i2: "v2"}}
  * mergeObject({k1: {i1: "v1"}}, {k1: {i2: "v2"}}, {recursive: true}); // {k1: {i1: "v1", i2: "v2"}}
  * ```
  *
  * @example Deleting an existing object key
  * ```typescript
- * mergeObject({k1: "v1", k2: "v2"}, {"-=k1": null});   // {k2: "v2"}
+ * mergeObject({k1: "v1", k2: "v2"}, {"-=k1": null}, {performDeletions: true});   // {k2: "v2"}
  * ```
  */
 export function mergeObject<
@@ -393,6 +403,33 @@ export interface MergeObjectOptions {
   performDeletions?: boolean | undefined; //TODO: implement this in the mergeObject return type
 }
 
+/**
+ * A helper function for merging objects when the target key does not exist in the original
+ * @internal
+ */
+declare function _mergeInsert(
+  original: object,
+  k: string | number | symbol,
+  v: any,
+  options?: Pick<MergeObjectOptions, "insertKeys" | "insertValues" | "performDeletions">,
+  _d?: number,
+): void;
+
+/**
+ * A helper function for merging objects when the target key exists in the original
+ * @internal
+ */
+declare function _mergeUpdate<T extends object, K extends keyof T>(
+  original: T,
+  k: K,
+  v: any,
+  options?: Pick<
+    MergeObjectOptions,
+    "insertKeys" | "insertValues" | "enforceTypes" | "overwrite" | "recursive" | "performDeletions"
+  >,
+  _d?: number,
+): void;
+
 /**
  * Parse an S3 key to learn the bucket and the key prefix used for the request.
  * @param key - A fully qualified key name or prefix path.
@@ -414,6 +451,82 @@ export function randomID(length?: number): string;
  */
 export function timeSince(timeStamp: Date | string): string;
 
+/**
+ * Format a file size to an appropriate order of magnitude.
+ * @param size    - The size in bytes.
+ * @param options - Additional options. (default: `{}`)
+ * @returns
+ */
+export function formatFileSize(size: number, options: FormatFileSizeOptions): string;
+
+interface FormatFileSizeOptions {
+  /**
+   * The number of decimal places to round to.
+   * @defaultValue `2`
+   */
+  decimalPlaces?: number | undefined;
+
+  /**
+   * The base to use. In base 10 a kilobyte is 1000 bytes. In base 2 it is 1024 bytes.
+   * @defaultValue `10`
+   */
+  base?: 2 | 10 | undefined;
+}
+
+interface ResolvedUUID {
+  /**
+   * The original UUID.
+   */
+  uuid: string;
+
+  /**
+   * The parent collection.
+   */
+  collection?: DocumentCollection<any, any> | undefined;
+
+  /**
+   * The parent document.
+   */
+  documentId?: DocumentId | undefined;
+
+  /**
+   * The parent document type.
+   */
+  documentType?: DocumentType | undefined;
+
+  /**
+   * An already-resolved document.
+   */
+  doc?: Document<any, any>;
+
+  /**
+   * Any remaining Embedded Document parts.
+   */
+  embedded: string[];
+}
+
+export function parseUuid(uuid: string, options: ParseUUIDOptions): ResolvedUUID;
+
+interface ParseUUIDOptions {
+  /**
+   * A document to resolve relative UUIDs against.
+   */
+  relative?: Document<any, any> | undefined;
+}
+
+/**
+ * Resolve a UUID relative to another document.
+ * The general-purpose algorithm for resolving relative UUIDs is as follows:
+ * 1. If the number of parts is odd, remove the first part and resolve it against the current document and update the
+ *    current document.
+ * 2. If the number of parts is even, resolve embedded documents against the current document.
+ * @param uuid     - The UUID to resolve.
+ * @param relative - The document to resolve against.
+ * @returns
+ * @internal
+ */
+declare function _resolveRelativeUuid(uuid: string, relative: Document<any, any>): ResolvedUUID;
+
 /**
  * Converts an RGB color value to HSV. Conversion formula adapted from http://en.wikipedia.org/wiki/HSV_color_space.
  * Assumes r, g, and b are contained in the set [0, 1] and returns h, s, and v in the set [0, 1].
diff --git a/src/foundry/common/utils/iterable-weak-map.d.mts b/src/foundry/common/utils/iterable-weak-map.d.mts
index 104f8292f..2f9084c0f 100644
--- a/src/foundry/common/utils/iterable-weak-map.d.mts
+++ b/src/foundry/common/utils/iterable-weak-map.d.mts
@@ -1,9 +1,85 @@
-export default IterableWeakMap;
+interface IterableWeakMapHeldValue<K extends WeakKey> {
+  /**
+   * The set to be cleaned.
+   */
+  set: Set<WeakRef<K>>;
+
+  /**
+   * The ref to remove.
+   */
+  ref: WeakRef<K>;
+}
 
 /**
  * Stores a map of objects with weak references to the keys, allowing them to be garbage collected. Both keys and values
  * can be iterated over, unlike a WeakMap.
+ * @typeParam K - the type of the keys contained in the IterableWeakMap
+ * @typeParam V - The type of the values contained in the IterableWeakMap
  */
-declare class IterableWeakMap extends WeakMap {
-  // TODO: Define methods and properties
+declare class IterableWeakMap<K extends WeakKey, V> extends WeakMap<K, V> {
+  /**
+   * A set of weak refs to the map's keys, allowing enumeration.
+   */
+  #refs: Set<WeakRef<K>>;
+
+  /**
+   * A FinalizationRegistry instance to clean up the ref set when objects are garbage collected.
+   */
+  #finalizer: FinalizationRegistry<IterableWeakMapHeldValue<K>>;
+
+  /**
+   * @param entries - The initial entries.
+   */
+  constructor(entries?: Iterable<[K, V]>);
+
+  /**
+   * Clean up the corresponding ref in the set when its value is garbage collected.
+   * @param heldValue - The value held by the finalizer.
+   */
+  static #cleanup<K extends WeakKey>(heldValue: IterableWeakMapHeldValue<K>): void;
+
+  /**
+   * Remove a key from the map.
+   * @param key - The key to remove.
+   * @returns `true` if a key was removed, `false` otherwise.
+   */
+  delete(key: K): boolean;
+
+  /**
+   * Retrieve a value from the map.
+   * @param key - The value's key.
+   * @returns
+   */
+  get(key: K): V | undefined;
+
+  /**
+   * Place a value in the map.
+   * @param key   - The key.
+   * @param value - The value.
+   * @returns
+   */
+  set(key: K, value: V): this;
+
+  /**
+   * Enumerate the entries.
+   * @returns
+   */
+  [Symbol.iterator](): Generator<[K, V], void, never>;
+
+  /**
+   * Enumerate the entries.
+   */
+  entries(): Generator<[K, V], void, never>;
+
+  /**
+   * Enumerate the keys.
+   */
+  keys(): Generator<K, void, never>;
+
+  /**
+   * Enumerate the values.
+   */
+  values(): Generator<V, void, never>;
 }
+
+export default IterableWeakMap;
diff --git a/src/foundry/common/utils/iterable-weak-set.d.mts b/src/foundry/common/utils/iterable-weak-set.d.mts
index 291204912..384f2656b 100644
--- a/src/foundry/common/utils/iterable-weak-set.d.mts
+++ b/src/foundry/common/utils/iterable-weak-set.d.mts
@@ -1,9 +1,49 @@
-export default IterableWeakSet;
+import type IterableWeakMap from "./iterable-weak-map.d.mts";
 
 /**
  * Stores a set of objects with weak references to them, allowing them to be garbage collected. Can be iterated over,
  * unlike a WeakSet.
+ * @typeParam T - The type of the objects contained in the WeakSet
  */
-declare class IterableWeakSet extends WeakSet {
-  // TODO: Define methods and properties
+declare class IterableWeakSet<T extends WeakKey> extends WeakSet<T> {
+  /**
+   * The backing iterable weak map.
+   */
+  #map: IterableWeakMap<T, T>;
+
+  /**
+   * @param entries - The initial entries.
+   */
+  constructor(entries?: Iterable<T>);
+
+  /**
+   * Enumerate the values.
+   */
+  [Symbol.iterator](): Generator<T, void, never>;
+
+  /**
+   * Add a value to the set.
+   * @param value - The value to add.
+   */
+  add(value: T): this;
+
+  /**
+   * Delete a value from the set.
+   * @param value - The value to delete.
+   */
+  delete(value: T): boolean;
+
+  /**
+   * Whether this set contains the given value.
+   * @param value - The value to test.
+   * @returns
+   */
+  has(value: T): boolean;
+
+  /**
+   * Enumerate the collection.
+   */
+  values(): Generator<T, void, never>;
 }
+
+export default IterableWeakSet;


Test patch:
diff --git a/tests/foundry/common/utils/geometry.mjs.test-d.ts b/tests/foundry/common/utils/geometry.mjs.test-d.ts
new file mode 100644
index 000000000..2c8465dd9
--- /dev/null
+++ b/tests/foundry/common/utils/geometry.mjs.test-d.ts
@@ -0,0 +1,37 @@
+import { expectTypeOf } from "vitest";
+
+// Global export test
+expectTypeOf(orient2dFast).toEqualTypeOf(foundry.utils.orient2dFast);
+expectTypeOf(lineSegmentIntersects).toEqualTypeOf(lineSegmentIntersects);
+expectTypeOf(lineLineIntersection).toEqualTypeOf(foundry.utils.lineLineIntersection);
+expectTypeOf(lineSegmentIntersects).toEqualTypeOf(foundry.utils.lineSegmentIntersects);
+// ---
+
+const a = { x: 0, y: 0 };
+const b = new PIXI.Point(0, 0);
+const c = { x: 0, y: 0 };
+const d = new PIXI.Point(0, 0);
+
+const o = new PIXI.Circle(0, 0, 0);
+const r = new Ray(a, b);
+
+expectTypeOf(foundry.utils.orient2dFast(a, b, c)).toEqualTypeOf<number>();
+
+expectTypeOf(foundry.utils.lineSegmentIntersects(a, b, c, d)).toEqualTypeOf<boolean>();
+
+expectTypeOf(foundry.utils.lineLineIntersection(a, b, c, d)).toEqualTypeOf<foundry.utils.LineIntersection | null>();
+expectTypeOf(
+  foundry.utils.lineLineIntersection(a, b, c, d, { t1: true }),
+).toEqualTypeOf<foundry.utils.LineIntersection | null>();
+expectTypeOf(foundry.utils.lineLineIntersection(a, b, c, d, { t1: true })).toEqualTypeOf(
+  r.intersectSegment([0, 0, 0, 0]),
+);
+
+expectTypeOf(foundry.utils.lineSegmentIntersects(a, b, c, d)).toEqualTypeOf<boolean>();
+
+expectTypeOf(foundry.utils.lineCircleIntersection(a, b, c, 0).intersections).toEqualTypeOf(
+  o.segmentIntersections(a, b),
+);
+expectTypeOf(foundry.utils.lineCircleIntersection(a, b, c, 0).intersections).toEqualTypeOf(
+  foundry.utils.quadraticIntersection(a, b, c, 0),
+);
diff --git a/tests/foundry/common/utils/helpers.mjs.test-d.ts b/tests/foundry/common/utils/helpers.mjs.test-d.ts
index bfe42bc2b..b8fad0856 100644
--- a/tests/foundry/common/utils/helpers.mjs.test-d.ts
+++ b/tests/foundry/common/utils/helpers.mjs.test-d.ts
@@ -1,5 +1,12 @@
 import { expectTypeOf, assertType } from "vitest";
 
+// Global export test
+expectTypeOf(benchmark).toEqualTypeOf(foundry.utils.benchmark);
+expectTypeOf(deepClone).toEqualTypeOf(foundry.utils.deepClone);
+expectTypeOf(duplicate).toEqualTypeOf(foundry.utils.duplicate);
+expectTypeOf(mergeObject).toEqualTypeOf(foundry.utils.mergeObject);
+// ---
+
 // benchmark
 
 declare function functionWithoutParameters(): void;
diff --git a/tests/foundry/common/utils/http.mjs.test-d.ts b/tests/foundry/common/utils/http.mjs.test-d.ts
index faa9b1c73..eab56bcbd 100644
--- a/tests/foundry/common/utils/http.mjs.test-d.ts
+++ b/tests/foundry/common/utils/http.mjs.test-d.ts
@@ -1,4 +1,8 @@
 import { expectTypeOf } from "vitest";
 
+// Global export test
+expectTypeOf(fetchWithTimeout).toEqualTypeOf(foundry.utils.fetchWithTimeout);
+// ---
+
 expectTypeOf(foundry.utils.fetchWithTimeout("/")).toEqualTypeOf<Promise<Response>>();
 expectTypeOf(foundry.utils.fetchJsonWithTimeout("/")).toEqualTypeOf<Promise<unknown>>();
diff --git a/tests/foundry/common/utils/iterable-weak-map.test-d.ts b/tests/foundry/common/utils/iterable-weak-map.test-d.ts
new file mode 100644
index 000000000..8d430461b
--- /dev/null
+++ b/tests/foundry/common/utils/iterable-weak-map.test-d.ts
@@ -0,0 +1,11 @@
+import { expectTypeOf } from "vitest";
+
+const m = new IterableWeakMap<{ x: number }, { y: string }>();
+
+expectTypeOf(m.get({ x: 1 })).toEqualTypeOf<{ y: string } | undefined>();
+
+expectTypeOf(m.set({ x: 1 }, { y: "2" })).toEqualTypeOf<IterableWeakMap<{ x: number }, { y: string }>>();
+
+expectTypeOf(m.entries()).toEqualTypeOf<Generator<[{ x: number }, { y: string }], void, never>>();
+expectTypeOf(m.keys()).toEqualTypeOf<Generator<{ x: number }, void, never>>();
+expectTypeOf(m.values()).toEqualTypeOf<Generator<{ y: string }, void, never>>();
diff --git a/tests/foundry/common/utils/iterable-weak-set.test-d.ts b/tests/foundry/common/utils/iterable-weak-set.test-d.ts
new file mode 100644
index 000000000..1b925c58f
--- /dev/null
+++ b/tests/foundry/common/utils/iterable-weak-set.test-d.ts
@@ -0,0 +1,7 @@
+import { expectTypeOf } from "vitest";
+
+const m = new IterableWeakSet<{ x: number }>();
+
+expectTypeOf(m.add({ x: 1 })).toEqualTypeOf<IterableWeakSet<{ x: number }>>();
+
+expectTypeOf(m.values()).toEqualTypeOf<Generator<{ x: number }, void, never>>();
diff --git a/tests/foundry/common/utils/logging.mjs.test-d.ts b/tests/foundry/common/utils/logging.mjs.test-d.ts
new file mode 100644
index 000000000..37e6e5bb8
--- /dev/null
+++ b/tests/foundry/common/utils/logging.mjs.test-d.ts
@@ -0,0 +1,3 @@
+import { expectTypeOf } from "vitest";
+
+expectTypeOf(logCompatibilityWarning).toEqualTypeOf(foundry.utils.logCompatibilityWarning);