/** * Dependency-free object + iterable utilities and the canonical repository * owner for stable identity, loose scalar coercion, record guards, sequences, * and common object transforms. Reuse these helpers across packages instead of * adding local equivalents with different edge-case behavior. * * Value guards / coercions / shape types: {@link isRecord} narrows parsed JSON * to a record, {@link toNumber} coerces a hand-typed numeral, {@link toBoolean} * coerces loose truthy/falsy values, {@link toDuration} parses `1h30m` / * `2 milliseconds` to millis, {@link toDate} coerces a date/ISO string/epoch * number/relative duration (`toDate` and `toDuration` fall back to each other, so * either accepts both readings), {@link optional} * spreads a field only when it is present, {@link deepEqual} compares * structurally, {@link isSerializableValue} rejects anything a JSON round trip * would lose or coerce, {@link toStableKey} canonicalizes a value so an identity * can be derived from it, and {@link NameLike}/{@link NonFunctionKeys} describe * object shapes. * * Iterable helpers: {@link sequence} wraps source(s) in a lazy, `Array`-compatible * {@link Sequence}. Every * transform/terminal is a standalone function operating on plain {@link * Container}s (see {@link map}, {@link filter}, {@link group}, ...); the {@link * Sequence} methods are thin forwarders over them so the same logic backs both * the free-function and the fluent/chained styles. * * @module */ /** Lazy sequence over iterable source(s). See {@link sequence}. */ export type Sequence = SequenceImpl; type SequenceSource = Iterable | ReadonlyMap | OneOrMany | Extract | null | undefined; /** * A non-scalar {@link Iterable} - one to treat as a collection of elements * rather than a scalar. {@link isContainer} narrows to this, excluding strings, * `String`/`RegExp` objects, and functions. {@link Collection} is the eagerly- * sized subset. The element defaults to `unknown` so any `Collection` is * assignable to a bare `Container`. */ export type Container = Iterable; /** * A built-in, eagerly-sized {@link Container}: an {@link Array}, {@link Set}, or * {@link Map} (whose *values* are `T` - a Map iterates `[key, value]` entries, * so its element type differs, but its value type is `T`). All share a cheap * emptiness check ({@link isEmpty}). */ export type Collection = ReadonlyArray | ReadonlySet | ReadonlyMap; /** Non-empty tuple used where an API requires at least one value. */ export type OneOrMany = [T, ...T[]]; /** Narrow a readonly array to a non-empty {@link OneOrMany} tuple. */ export declare function isOneOrMany(value: readonly T[]): value is OneOrMany; /** A source accepted by a variadic op: a {@link Container} of `T`, or nothing. */ type Source = Container | null | undefined; /** * Element type of a {@link group} bucket array: when the predicate `P` is a type * guard (`value is S`), the bucket is narrowed to `S & T`; otherwise it stays `T`. */ type GroupValue = P extends (value: any, ...rest: any[]) => value is infer S ? S & T : T; /** A map of group name -> predicate, as accepted by {@link group}. */ type GroupPredicates = Record boolean>; /** * Type guard for a {@link Collection}: an {@link Array}, {@link Set}, or * {@link Map}. Narrows `value` so its element/value type is treated as `T`. * * @typeParam T - Element (or Map value) type asserted for the collection. * @param value - Value to test. * @returns `true` (narrowing `value` to {@link Collection}<`T`>) for a * built-in array/set/map. */ export declare function isCollection(value: unknown): value is Collection; /** * `true` when a {@link Collection} has no elements. Uses `length` for arrays * and `size` for {@link Set}/{@link Map}. * * @param collection - The array, set, or map to test. */ export declare function isEmpty(collection: Collection | Record, options?: { recursive?: boolean; }): boolean; /** * Normalizes a source to an iterable of its `T` values, so Maps are treated * uniformly with arrays/sets: a {@link Map} yields its *values* (matching * {@link Collection}'s value-typed `T`), any other iterable yields itself. This * is what {@link sequence} consumes, so a `Map` contributes values everywhere * rather than `[key, value]` entries. */ export declare function values(source: Iterable | ReadonlyMap): Iterable; /** * Type guard for a {@link Container} - an iterable to be treated as a collection * rather than a scalar. * * Deliberately excludes values that are technically iterable but should be * treated as scalars here - strings, `String`/`RegExp` objects, and functions - * so a lone string is never spread character-by-character. * * @typeParam T - Element type asserted for the iterable. * @param value - Value to test. * @returns `true` (narrowing `value` to {@link Container}<`T`>) for a non-string * iterable. */ export declare function isContainer(value: unknown): value is Container; /** * Same semantics as `Array.prototype.map`, over a single {@link Container}. * * @param source - The container to map (nullish yields an empty sequence). * @param callback - Called per element with its index; its result is emitted. */ export declare function map(source: Source, callback: (value: T, index: number) => U): Sequence; /** * Same semantics as `Array.prototype.filter`, over a single {@link Container}. * A type-guard predicate narrows the resulting element type. * * @param source - The container to filter (nullish yields an empty sequence). * @param predicate - Keeps elements for which it returns `true`. */ export declare function filter(source: Source, predicate: (value: T, index: number) => value is S): Sequence; export declare function filter(source: Source, predicate: (value: T, index: number) => boolean): Sequence; /** * Concatenates the sources and yields only elements that are not `null` or * `undefined`, narrowing the element type to {@link NonNullable}<`T`>. * * @param sources - Containers to concatenate (nullish sources are skipped). */ export declare function nonNull(...sources: readonly Source[]): Sequence>; /** * Same semantics as `Array.prototype.flatMap`, over a single {@link Container}. * * @param source - The container to map (nullish yields an empty sequence). * @param callback - Returns a value or array of values, flattened one level. */ export declare function flatMap(source: Source, callback: (value: T, index: number) => U | ReadonlyArray): Sequence; /** * Same semantics as `Array.prototype.flat` (arrays only). `depth` leads so the * sources can stay variadic; use `depth < 1` for a no-op passthrough. * * @param depth - How many array levels to flatten. * @param sources - Containers to concatenate, then flatten (nullish skipped). */ export declare function flat(depth: number, ...sources: readonly Source[]): Sequence; /** * Concatenates the sources, then lazily yields values in encounter order, * skipping a value only when an equal one was already yielded (`Set` / * SameValueZero). Uniqueness is checked per element as it is consumed. * * @param sources - Containers to concatenate (nullish sources are skipped). */ export declare function distinct(...sources: readonly Source[]): Sequence; /** * Yields `source`, then each appended `item` in order (arrays spread one level), * mirroring `Array.prototype.concat`. `items` are scalar values/arrays, not * containers, so `source` stays a single leading argument. * * @param source - The leading container (nullish yields just the items). * @param items - Values (or arrays of values) appended after the source. */ export declare function concat(source: Source, ...items: readonly (T | ReadonlyArray)[]): Sequence; /** * Yields at most `count` elements from the front of the concatenated sources. * `count` leads so the sources can stay variadic. * * @param count - Maximum number of elements to yield (`<= 0` yields none). * @param sources - Containers to concatenate (nullish sources are skipped). */ export declare function take(count: number, ...sources: readonly Source[]): Sequence; /** * Splits the concatenated sources into one array per named predicate. Consumes * the input once, routing each element to the FIRST predicate it satisfies (so * groups are disjoint); elements matching no predicate are dropped. Type-guard * predicates narrow their group's element type (see {@link GroupValue}). * `predicates` leads so the sources can stay variadic. * * @typeParam G - The map of group name -> predicate. * @param predicates - Named predicates; evaluated in declaration order. * @param sources - Containers to concatenate (nullish sources are skipped). * @returns An object with the same keys, each an array of its group's elements. * * @example * const { strings, fns } = group({ strings: isString, fns: isFunction }, xs); */ export declare function group>(predicates: G, ...sources: readonly Source[]): { [K in keyof G]: GroupValue[]; }; /** * Same semantics as `Array.prototype.find`, over a single {@link Container}. * Consumes elements until a match. A type guard narrows the return type. */ export declare function find(source: Source, predicate: (value: T, index: number) => value is S): S | undefined; export declare function find(source: Source, predicate: (value: T, index: number) => boolean): T | undefined; /** * Same semantics as `Array.prototype.findLast`, over a single {@link Container}. * Consumes the full source. A type guard narrows the return type. */ export declare function findLast(source: Source, predicate: (value: T, index: number) => value is S): S | undefined; export declare function findLast(source: Source, predicate: (value: T, index: number) => boolean): T | undefined; /** * Same semantics as `Array.prototype.findIndex`, over a single {@link Container}. * Consumes elements until a match. */ export declare function findIndex(source: Source, predicate: (value: T, index: number) => boolean): number; /** * Same semantics as `Array.prototype.findLastIndex`, over a single * {@link Container}. Consumes the full source. */ export declare function findLastIndex(source: Source, predicate: (value: T, index: number) => boolean): number; /** * Same semantics as `Array.prototype.some`, over a single {@link Container}. * Short-circuits on the first match. */ export declare function some(source: Source, predicate: (value: T, index: number) => boolean): boolean; /** * Same semantics as `Array.prototype.every`, over a single {@link Container}. * Short-circuits on the first failure. */ export declare function every(source: Source, predicate: (value: T, index: number) => value is S): boolean; export declare function every(source: Source, predicate: (value: T, index: number) => boolean): boolean; /** * Same semantics as `Array.prototype.forEach`, over a single {@link Container}. * Consumes the source. */ export declare function forEach(source: Source, callback: (value: T, index: number) => void): void; /** * Same semantics as `Array.prototype.at` over the concatenated sources. * Non-negative indices scan lazily; negative indices materialize first. `index` * leads so the sources can stay variadic. * * @param index - Zero-based position; negative counts from the end. * @param sources - Containers to concatenate (nullish sources are skipped). */ export declare function at(index: number, ...sources: readonly Source[]): T | undefined; /** Normalize one value or an existing value list into a value list. */ export declare function toOneOrMany(input: T | OneOrMany): OneOrMany; /** * Materializes the concatenated sources into a new array. Consumes single-pass * sources. * * @param sources - Containers to concatenate (nullish sources are skipped). */ export declare function toArray(...sources: readonly Source[]): readonly T[]; /** * Lazy iterable sequence with `Array`-compatible transforms and terminal * methods. Single-pass by default; call {@link SequenceImpl.cache} to retain * pulled values for re-iteration. Built for generators and other sources where * a second pass is not guaranteed. * * The methods forward to the standalone functions of the same name - see each * method's `@see` - so the free-function and chained styles share one impl. */ declare class SequenceImpl { private readonly source; private readonly buffer; private iterator?; private exhausted; constructor(source: Iterable, buffer: T[] | undefined, state?: { readonly exhausted?: boolean; }); private get caching(); /** Advance the underlying source once, creating the iterator on first use. */ private pull; [Symbol.iterator](): Iterator; /** @see {@link map} */ map(callback: (value: T, index: number) => U): Sequence; /** @see {@link filter} */ filter(predicate: (value: T, index: number) => value is S): Sequence; filter(predicate: (value: T, index: number) => boolean): Sequence; /** @see {@link nonNull} */ nonNull(): Sequence>; /** @see {@link flatMap} */ flatMap(callback: (value: T, index: number) => U | ReadonlyArray): Sequence; /** @see {@link flat} */ flat(depth?: number): Sequence; /** @see {@link distinct} */ distinct(): Sequence; /** @see {@link concat} */ concat(...items: readonly (T | ReadonlyArray)[]): Sequence; /** * Lazily yields this sequence followed by each iterable `source` in order. * Like {@link concat}, but for iterable sources (generators, other sequences, * `Set`, `Map`, etc.) rather than scalar values or arrays. A {@link Map} * source contributes its values. * * @see {@link sequence} */ join(...sources: readonly SequenceSource[]): Sequence; /** @see {@link take} */ take(count: number): Sequence; /** @see {@link group} */ group>(predicates: G): { [K in keyof G]: GroupValue[]; }; /** * Returns a cached, re-iterable view of this sequence. An already-caching * sequence returns itself; otherwise this one-pass sequence is wrapped in a * new instance that retains pulled values. Iterate the returned instance, not * the original, to avoid competing for the same single-pass source. */ cache(): Sequence; /** @see {@link find} */ find(predicate: (value: T, index: number) => value is S): S | undefined; find(predicate: (value: T, index: number) => boolean): T | undefined; /** @see {@link findLast} */ findLast(predicate: (value: T, index: number) => value is S): S | undefined; findLast(predicate: (value: T, index: number) => boolean): T | undefined; /** @see {@link findIndex} */ findIndex(predicate: (value: T, index: number) => boolean): number; /** @see {@link findLastIndex} */ findLastIndex(predicate: (value: T, index: number) => boolean): number; /** @see {@link some} */ some(predicate: (value: T, index: number) => boolean): boolean; /** @see {@link every} (the `S extends T` overload narrows at compile time only). */ every(predicate: (value: T, index: number) => value is S): this is Sequence; every(predicate: (value: T, index: number) => boolean): boolean; /** @see {@link forEach} */ forEach(callback: (value: T, index: number) => void): void; /** @see {@link at} */ at(index: number): T | undefined; /** * Materialize the sequence into a new array. Consumes a single-pass source; a * cached, exhausted sequence copies its buffer directly. * * @see {@link toArray} */ toArray(): readonly T[]; } /** * Wrap one or more iterable `sources` in a single lazy {@link Sequence}, * iterated in order. `null`/`undefined` sources are skipped; when nothing * remains (every source omitted, `null`, `undefined`, or an empty array / * `Set` / `Map`), {@link emptySequence} is returned. The result is single-pass * - call `.cache()` to make it re-iterable. * * A {@link Map} source contributes its values (see {@link values}), consistent * with {@link Collection}'s value-typed `T`. * * @typeParam T - Element type of the sequence. * @param sources - Iterables to concatenate, in order (`Map` sources use values). */ export declare function sequence(...sources: readonly SequenceSource[]): Sequence; /** Minimal shape for objects that expose an optional `name` (e.g. AppKit plugins). */ export interface NameLike { name?: string; } /** Property keys of `T` whose values are not callable. */ export type NonFunctionKeys = { [K in keyof T]: T[K] extends (...args: any[]) => any ? never : K; }[keyof T]; /** * Narrow `value` to a plain (non-array) object. Use as a type guard * before indexing into / mutating parsed JSON so the access is * type-safe. * * @example * if (isRecord(parsed)) parsed.foo = 1; */ export declare function isRecord(value: unknown): value is Record; /** A JSON scalar. `undefined` is deliberately absent - `JSON.stringify` drops it. */ export type SerializablePrimitive = string | number | boolean | null; /** * Any value that survives a `JSON.stringify`/`JSON.parse` round trip unchanged. * * Use it instead of `unknown` on a boundary that will serialize its input - a * message payload, a cache entry, a config blob written to disk - so a `Date`, * a `Map`, or a class instance is a compile error at the call site rather than a * receiver quietly getting a string or `{}`. {@link isSerializableValue} is the * runtime half, for input the compiler cannot vouch for. */ export type SerializableValue = SerializablePrimitive | SerializableValue[] | { [key: string]: SerializableValue; }; /** * True when `value` survives a JSON round trip with no loss and no coercion. * * Stricter than "`JSON.stringify` did not throw", because that succeeds while * silently CHANGING the value: a `Date` becomes a string, `NaN` and `Infinity` * become `null`, a `Map` becomes `{}`, and `undefined` disappears from an object * or turns into `null` inside an array. Each of those reaches the far side as * something other than what was sent, so all of them are rejected here. * * Rejected: non-finite numbers, `undefined`, functions, symbols, bigints, class * instances and anything else with a prototype other than `Object.prototype` or * `null` (`Date`, `Map`, `Set`, `RegExp`, `Buffer`), and any object graph * containing a cycle. Accepted: strings, booleans, `null`, finite numbers, plain * objects, arrays, and nestings of those. * * Narrows to {@link SerializableValue} rather than asserting, so it also serves * as the validator for untrusted input - a request body, a decoded notification * payload - where the answer should be a 400 and not a throw. Never throws. * * Distinct from {@link deepEqual}'s notion of comparable: that one HANDLES * `Date`/`Map`/`Set` structurally, while this one rejects them precisely because * JSON cannot carry them. * * @param ancestors Cycle-detection set for the recursive walk. Internal; callers * pass one value. * * @example * isSerializableValue({ a: [1, "x", null] }); // true * isSerializableValue({ at: new Date() }); // false - would become a string * isSerializableValue(Number.NaN); // false - would become null */ export declare function isSerializableValue(value: unknown, ancestors?: Set): value is SerializableValue; /** * Canonical string for a structured value, for deriving a STABLE IDENTITY from * it - an advisory-lock id, a channel name, a cache key. * * The guarantee is two-way, which is what makes it safe to hash: values that * should share an identity produce the same string (object key order does not * matter), and values that should not are never conflated. Every token carries * its type, so `1` and `"1"` differ; a string carries its length, so * `["a", "bc"]` and `["ab", "c"]` differ; arrays keep order while object keys * are sorted. * * `JSON.stringify` cannot do this job - key order leaks in, `undefined` vanishes, * `1` and `"1"` collide after quoting is stripped, and a cycle throws a * `TypeError` naming neither the value nor the caller's intent. * * Deliberately strict where a silent answer would be a WRONG identity rather * than a missing one, since two callers disagreeing about a lock or a channel is * invisible until it corrupts something. Throws `TypeError` on a cycle, on a * non-finite number (`NaN` is not equal to itself, so it cannot have a stable * identity), and on a `function` or `symbol` (no meaningful value identity). * `undefined` and `null` are accepted as distinct tokens. * * `Date` is canonicalized by instant, unlike the hash canonicalizer in * `./hash.ts`, which folds every `Date` onto one token. Prefer this function when * distinctness is a correctness requirement; prefer `hash.fnvHash` when a short, * collision-tolerant digest is enough. * * @param value - The value to canonicalize. * @param seen - Cycle-detection set for the recursive walk. Internal; callers * pass one value. * * @example * toStableKey({ a: 1, b: 2 }) === toStableKey({ b: 2, a: 1 }); // true * toStableKey(1) !== toStableKey("1"); // true */ export declare function toStableKey(value: unknown, seen?: Set): string; /** * `{ [key]: value }` when `value` is present, otherwise `undefined` - so an * absent optional field stays ABSENT when spread, rather than becoming an * explicit `undefined` that `exactOptionalPropertyTypes` rejects. * * Spelling that inline costs a repeated, double-evaluated ternary per field * (`...(v ? { key: v } : {})`), which is how config resolvers end up computing * the same value twice. * * @example * return { * ...optional("appId", configuredAppId), * ...optional("endpoint", config.endpoint), * }; */ export declare function optional(key: K, value: V | null | undefined): Record | undefined; /** * Options for {@link toNumber}. * * Both switches turn OFF a leniency that is helpful for a hand-typed setting but * wrong when the string's other characters carry meaning. {@link toDate} and * {@link toDuration} disable both, since a space inside `2026 08 02` is a field * separator and a percent has no epoch or millisecond reading. */ export interface ToNumberOptions { /** * Whether internal digit-group separators are stripped, so `"1,000"` and * `"1 000"` read as `1000`. Defaults to `true`. * * Placement is not validated when enabled, so `"1,00,0"` also reads as `1000`. * Disable it when whitespace or a comma delimits FIELDS rather than grouping * digits, because stripping them silently fuses those fields into one number. */ separators?: boolean; /** * Whether a trailing percent sign divides the result by `100`, so `"25%"` reads * as `0.25`. Defaults to `true`. * * Disable it where a percentage has no meaning, so `"25%"` is a miss rather * than a number two orders of magnitude away from what the text says. */ percent?: boolean; } /** * Coerce a loose numeric value to a real, FINITE `number`, or `undefined` when it * carries no numeric meaning. * * The one place a hand-typed number is interpreted, alongside {@link toBoolean}, * {@link toDate}, and {@link toDuration}. Reach for it instead of `Number(x)` or a * hand-rolled numeric regex: bare `Number` maps `""`, `null`, `[]`, and * whitespace to `0` and anything else to `NaN`, so a caller has to re-check the * result every time, and ad hoc regexes tend to drift in what they accept. * * Accepts: a finite `number`; a `bigint`; or a decimal string with an optional * leading sign, leading/trailing whitespace, whitespace after the sign, * digit-group separators (`"1,000"`, `"1 000"`), a bare fraction (`".5"`), a * trailing point (`"1."`), scientific notation (`"1e3"`), and an optional * trailing percent sign (`"25%"`, `"1.5 %"`). A trailing `%` divides the parsed * value by `100`, so `"25%"` becomes `0.25`. * * Separators are stripped without validating their placement, so `"1,00,0"` reads * as `1000`; this is a coercion for hand-typed configuration, not a locale-aware * validator. Anything else, including `NaN`, `Infinity`, an empty or * whitespace-only string, `null`, `undefined`, a boolean, multiple signs, * malformed exponents, misplaced percent signs, or `"12px"`, returns `undefined`. * * Returns `undefined` rather than throwing, matching the other coercions, so it * composes naturally with `??` fallbacks. * * @example * toNumber("1,000"); // 1000 * toNumber(" -2.5 "); // -2.5 * toNumber("1e3"); // 1000 * toNumber("12.5 %"); // 0.125 * toNumber(""); // undefined (Number("") would be 0) * toNumber("12px"); // undefined * toNumber("1 000", { separators: false }); // undefined */ export declare function toNumber(value: unknown, options?: ToNumberOptions): number | undefined; /** * Coerce a loose boolean-ish value to a real `boolean`, or `undefined` * when it can't be interpreted. Recognizes `true`/`t`/`on`/`1`/`yes`/`y` * and their negatives (case- and whitespace-insensitive for strings), as * well as the numbers `1` and `0`. */ export declare function toBoolean(value: unknown): boolean | undefined; /** * Options for {@link toDate}. * * The mirror of {@link ToDurationOptions}: each function can fall back to the * other, so each has one switch turning that fallback off. */ export interface ToDateOptions { /** * Whether a {@link toDuration} expression is read as an instant relative to * now, so `-7d` and `7 days ago` become `now - 7 days`. Defaults to `true`. * * Set `false` when the value must be a real date and a relative expression * should be a miss - a stored timestamp, a user-supplied `Date` header, an * `expires_at` field - since a duration silently resolving against the current * clock makes the same input mean something different on every call. Also what * {@link toDuration} passes when it recurses, so the two cannot bounce a value * between them forever. */ parseDuration?: boolean; } /** * Options for {@link toDuration}. * * The mirror of {@link ToDateOptions}: each function can fall back to the other, * so each has one switch turning that fallback off. */ export interface ToDurationOptions { /** * Whether a {@link toDate} value is read as the signed offset from now, so * `2026-08-02` becomes however long until (or since) that instant. Defaults to * `true`. * * Set `false` when only a length of time is meaningful - a timeout, a poll * interval, a cache TTL - because a date would otherwise yield a plausible but * wrong number that also drifts with the clock. Also what {@link toDate} passes * when it recurses. */ parseDate?: boolean; } /** * Coerce a loose duration to MILLISECONDS, or `undefined` when it can't be * interpreted. * * Deliberately lenient, because these values are typed by hand into env vars, * CLI flags, and config files: whitespace between amount and unit is optional and * unlimited, units are case-insensitive, plurals and the common abbreviations are * equivalent (`2ms` === `2 milliseconds`, `1h` === `1 hr` === `1 Hour`), `and` and * thousands separators are ignored (`1,500 ms`, `1 hour and 30 minutes`), and * terms compose (`1h30m`). A plain `number` passes through as milliseconds. * * Signs are supported so a duration can express an OFFSET rather than only a * length: a leading `-` (or a trailing `ago`) makes the result negative, and an * unsigned term inherits the previous term's sign, so `-1h30m` is -90 minutes * rather than -60 + 30. {@link toDate} uses that to turn `-7d` / `7 days ago` * into an instant relative to now. * * An UNKNOWN unit fails the whole parse rather than being skipped - `1 fortnight` * returning `1` silently would be worse than returning nothing - which is also * what keeps {@link toDate} from mistaking `1 Jan 2026` for a duration. * * A value that is not a duration at all is offered to {@link toDate} and, when it * IS a date, read as the signed offset from now (`date - now`), which makes the * two functions inverses: a past instant is negative, a future one positive. * `options.parseDate: false` turns that off when only a length of time makes * sense - see {@link ToDurationOptions}. * * @example * toDuration("30s"); // 30_000 * toDuration("1 hour 30 minutes"); // 5_400_000 * toDuration("-7 days"); // -604_800_000 * toDuration("2 weeks ago"); // -1_209_600_000 * toDuration("2026-08-02"); // ms from now to that instant * toDuration("2026-08-02", { parseDate: false }); // undefined * toDuration("soon"); // undefined */ export declare function toDuration(value: unknown, options?: ToDurationOptions): number | undefined; /** * Coerce a loose date-ish value to a real `Date`, or `undefined` when it can't be * interpreted. Accepts, in this order: * * - a `Date` (passed through; an invalid one is a miss); * - an epoch NUMBER or numeric string, in seconds or milliseconds (see * {@link SECONDS_CEILING}); * - `now` (and its `today` spelling), for the current instant; * - a {@link toDuration} expression, resolved RELATIVE TO NOW, so `-7d`, * `7 days ago`, and `in 30 minutes` are all valid instants; * - anything `Date.parse` understands (`2026-08-02`, an ISO/UTC instant). * * The epoch unit inference is the reason this exists rather than * `new Date(value)`: a bare epoch arrives as a STRING from an env var or a CLI * flag as often as it arrives as a number (`date +%s`, a JSON field, a copied log * line), and `Date.parse("1785697899")` reads that as a YEAR, silently landing * 1.7 billion years out. Numeric strings are therefore routed to the epoch path, * never to `Date.parse`. * * The duration fallback runs LAST, after `Date.parse`, so a real date is never * mistaken for an offset. `options.parseDuration: false` removes it entirely when * the value must be an absolute instant - see {@link ToDateOptions}. * * Like {@link toBoolean} this NEVER throws and returns `undefined` for anything * uninterpretable, so a caller decides whether a bad value is fatal, a warning, * or a fallback. * * @example * toDate("now"); // this instant * toDate("2026-08-02"); // 2026-08-02T00:00:00.000Z * toDate("1785697899"); // seconds -> 2026-08-02T... * toDate(1785697899000); // millis -> the same instant * toDate("30 days ago"); // now - 30d * toDate("30 days ago", { parseDuration: false }); // undefined * toDate("nope"); // undefined */ export declare function toDate(value: unknown, options?: ToDateOptions): Date | undefined; /** * Structural deep-equality with an optional custom comparator. * * {@link deepEqual} mirrors the semantics of the `fast-deep-equal` * package (handled: nested plain objects/arrays, `Map`, `Set`, `Date`, * `RegExp`, typed arrays, `NaN`, and `+0`/`-0` treated as equal) but is * dependency-free so `@dbx-tools/shared-core` keeps no runtime deps. * * The optional `comparator` short-circuits the structural walk at any * node: return `true`/`false` to force the result for that pair, or * `undefined` to defer to the built-in comparison. It is invoked for the * root pair and recursively for each nested pair, so a caller can, e.g., * compare two domain objects by id while letting everything else fall * back to structural equality. * * @example * deepEqual({ a: 1 }, { a: 1 }); // true * deepEqual([1, 2], [1, 2]); // true * deepEqual(a, b, (x, y) => * isEntity(x) && isEntity(y) ? x.id === y.id : undefined); */ export type DeepEqualComparator = (a: unknown, b: unknown) => boolean | undefined; /** Compare nested values structurally with an optional recursive override. */ export declare function deepEqual(a: unknown, b: unknown, comparator?: DeepEqualComparator): boolean; export {};