Circle V2 API Docs
    Preparing search index...

    Module @repo/utils

    @repo/utils

    @repo/utils provides shared non-React utility functions. Code is organized into subpath exports: @repo/utils/time (timestamp formatting and date helpers using date-fns and @internationalized/date), @repo/utils/constants, @repo/utils/labels (human-readable labels), @repo/utils/objects, and @repo/utils/async.

    %%{init:{"theme":"dark"}}%% graph TD utils["@repo/utils"] --> db["@repo/db"] utils --> errors["@repo/errors"] utils -.-> typescript_config["@repo/typescript-config"] utils -.-> vitest_config["@repo/vitest-config"]
    %%{init:{"theme":"default"}}%% graph TD utils["@repo/utils"] --> db["@repo/db"] utils --> errors["@repo/errors"] utils -.-> typescript_config["@repo/typescript-config"] utils -.-> vitest_config["@repo/vitest-config"]
    graph TD
      utils["@repo/utils"] --> db["@repo/db"]
      utils --> errors["@repo/errors"]
      utils -.-> typescript_config["@repo/typescript-config"]
      utils -.-> vitest_config["@repo/vitest-config"]

    Formats a date/timestamp into a string using named format keys. Returns a placeholder ("—" by default) when the value is falsy or invalid.

    import { formatTimestamp } from "@repo/utils/time";

    formatTimestamp(new Date());
    // => "2026-03-05 12:00:00" (default: SQL_DATETIME)

    formatTimestamp(new Date(), { format: "DISPLAY_DATE_LONG" });
    // => "March 5, 2026"

    formatTimestamp(new Date(), { format: "DISPLAY_DATETIME_SHORT" });
    // => "Mar 5, 2026, 12:00 PM"

    formatTimestamp(new Date(), { format: "FEED_DATETIME" });
    // => "Mar 5 at 12:00 PM"

    Handling missing values:

    formatTimestamp(undefined);
    // => "—" (EMPTY_VALUE_LABEL)

    formatTimestamp(null, { placeholder: "No date" });
    // => "No date"

    formatTimestamp(null, { placeholder: null });
    // => null (useful for conditional rendering)

    Available format keys:

    Key Example output Use case
    SQL_DATETIME (default) 2026-03-05 12:00:00 Storage, APIs
    ISO_DATETIME 2026-03-05T12:00:00 ISO 8601
    ISO_DATE 2026-03-05 Date-only storage
    ISO_TIME 12:00:00 Time-only storage
    SORTABLE_DATETIME 20260305_120000 Filenames, sorting
    DISPLAY_DATE_LONG March 5, 2026 Full date in UI
    DISPLAY_DATE_SHORT Mar 5, 2026 Compact date in UI
    DISPLAY_DATE_NUMERIC 03/05/2026 Numeric date in UI
    DISPLAY_TIME 12:00 PM Time in UI
    DISPLAY_TIME_WITH_SECONDS 12:00:00 PM Precise time in UI
    DISPLAY_DATETIME_LONG March 5, 2026 at 12:00 PM Full datetime in UI
    DISPLAY_DATETIME_SHORT Mar 5, 2026, 12:00 PM Compact datetime in UI
    DISPLAY_DATETIME_COMPACT 03/05/26, 12:00 PM Minimal datetime
    LOG_TIMESTAMP 2026-03-05 12:00:00.000 Log files
    AUDIT_DATETIME 2026-03-05T12:00:00.000 Audit trails
    FEED_DATETIME Mar 5 at 12:00 PM Activity feeds (current year)
    FEED_DATETIME_WITH_YEAR Mar 5, 2026 at 12:00 PM Activity feeds (cross-year)
    MONTH_YEAR March 2026 Month headers
    MONTH_YEAR_SHORT Mar '26 Compact month headers
    DAY_MONTH Thursday, March 5 Day-of-week display

    Convert between ISO date strings and @internationalized/date CalendarDate objects:

    import { dateStringToCalendarDate, dateValueToString } from "@repo/utils/time";

    const calendarDate = dateStringToCalendarDate("2026-03-05");
    // => CalendarDate { year: 2026, month: 3, day: 5 }

    const isoString = dateValueToString({ year: 2026, month: 3, day: 5 });
    // => "2026-03-05"

    Builds a display name from person data. Accepts camelCase objects, snake_case objects, or positional arguments:

    import { getPersonLabel } from "@repo/utils/labels";

    getPersonLabel({ firstName: "Jane", lastName: "Doe" });
    // => "Jane Doe"

    getPersonLabel({ first_name: "Jane", last_name: "Doe" });
    // => "Jane Doe" (snake_case works too)

    getPersonLabel("Jane", "Doe", "MD");
    // => "Jane Doe, MD"

    getPersonLabel({ firstName: "Jane", lastName: undefined });
    // => "Jane"

    getPersonLabel(undefined, undefined);
    // => null
    import { capitalize } from "@repo/utils/labels";

    capitalize("hello");
    // => "Hello"

    Removes falsy values from an object (useful for building query params or optional payloads):

    import { compactObject } from "@repo/utils/objects/compactObject";

    compactObject({ name: "Jane", email: "", phone: undefined, active: true });
    // => { name: "Jane", active: true }

    Promise-based delay:

    import { wait } from "@repo/utils/async/wait";

    await wait(1000); // pause for 1 second

    Runs a worker over a list with at most limit in flight, resolving to the results in input order:

    import { mapWithConcurrency } from "@repo/utils/async/mapWithConcurrency";

    const pages = await mapWithConcurrency(urls, 8, (url) => fetchPage(url));

    Retries on the returned value rather than a throw, so it pairs with safe():

    import { exponentialBackoffMs, retryWhile } from "@repo/utils/async/retry";
    import { MS_PER_SECOND } from "@repo/utils/time";

    const { result, attempts } = await retryWhile(() => safe(() => fetch(url)), {
    maxAttempts: 3,
    shouldRetry: (sent) => sent.error !== null || isTransientHttpStatus(sent.data.status),
    delayMs: (attemptsSoFar) => exponentialBackoffMs(attemptsSoFar, MS_PER_SECOND, 250),
    });

    isTransientHttpStatus (429 or 5xx), isFetchTimeout (the rejection AbortSignal.timeout() gives a fetch) and retryAfterMs, which reads a Retry-After header in seconds or as a date. It returns the full wait, so a caller with a limit decides whether to wait or give up:

    import { isFetchTimeout, isTransientHttpStatus, retryAfterMs } from "@repo/utils/http";

    isTransientHttpStatus(503); // true
    isFetchTimeout(error); // true when AbortSignal.timeout() cut the request off
    retryAfterMs(response.headers); // 3000 for "Retry-After: 3", null when absent

    describeIssues turns zod issues into path: code (expected type) lines with no values, safe to log for PHI payloads:

    import { describeIssues } from "@repo/utils/validation";

    throw new VendorResponseError("invalid-response").with({ issues: describeIssues(parsed.error.issues) });
    // issues: ["result.vitals.height: invalid_type (expected string)"]

    Placeholder strings for consistent UI rendering when values are missing:

    import { EMPTY_VALUE_LABEL, UNKNOWN_VALUE_LABEL, N_A_VALUE_LABEL } from "@repo/utils/constants";

    EMPTY_VALUE_LABEL; // "—"
    UNKNOWN_VALUE_LABEL; // "Unknown"
    N_A_VALUE_LABEL; // "N/A"

    EMPTY_VALUE_LABEL is the default placeholder used by formatTimestamp when a value is falsy.

    The nearest ancestor whose overflow-y scrolls and whose content overflows it, or null when only the page scrolls. Measure against it to know whether an element fits in the pane that will scroll it.

    const paneHeight = findVerticalScrollParent(row)?.clientHeight ?? document.documentElement.clientHeight;
    

    Whether a key event belongs to an IME composition: the event's isComposing flag, or the key code Safari sends for the Enter that ends one. Check it before treating Enter or Escape in a text field as an action.

    if (event.key === "Enter" && !isComposingKeyEvent(event)) {
    save();
    }

    For a control's onMouseDown: prevents the default while an input or textarea is focused, so pressing a toolbar button does not blur an editor shut.

    <HStack onMouseDown={keepTextEntryFocused}>…</HStack>
    
    Script Description
    test Runs Vitest with coverage.
    test:watch Runs Vitest in watch mode.
    check-types Runs tsc --noEmit to typecheck the package.

    Modules

    async/mapWithConcurrency
    async/mapWithConcurrency.test
    async/retry
    async/retry.test
    async/wait
    collections/arrayOrEmpty
    collections/arrayOrEmpty.test
    collections/findMatchingByName
    collections/findMatchingByName.test
    collections/getByNullableKey
    collections/getByNullableKey.test
    collections/matchAllowed
    collections/matchAllowed.test
    collections/toArray
    collections/toArray.test
    collections/TrimmedKeyMap
    collections/TrimmedKeyMap.test
    constants
    crypto/normalizeSha256
    crypto/normalizeSha256.test
    crypto/sha256
    crypto/sha256.test
    crypto/timingSafeEquals
    crypto/timingSafeEquals.test
    dom
    env/getAllowedEnv
    env/getAllowedEnv.test
    env/requireEnv
    env/requireEnv.test
    files/isPdf
    files/isPdf.test
    formatting
    http
    keys
    labels
    math
    mime
    objects/compactObject
    objects/isRecord
    objects/isRecord.test
    string
    time
    url
    validation