@dbx-tools/shared-core 0.3.44 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Fluent, callable predicate combinators that preserve TypeScript type guards.
3
+ *
4
+ * A Predicate<T, U> is:
5
+ * - Callable as `(value: T) => value is U`
6
+ * - Composable through `.and()`, `.or()`, and `.negate()`
7
+ *
8
+ * Ordinary boolean predicates are treated as narrowing to `T`, meaning they
9
+ * do not narrow the input type by themselves. Composed predicates passed to
10
+ * `.and()` / `.or()` may return any value; they are tested for truthiness.
11
+ *
12
+ * @module
13
+ */
14
+ /** An ordinary boolean predicate. */
15
+ export type PredicateFunction<T> = (value: T) => boolean;
16
+ /** A predicate that narrows T to U. */
17
+ export type TypePredicateFunction<T, U extends T> = (value: T) => value is U;
18
+ /** A predicate tested for truthiness when composed with `.and()` / `.or()`. */
19
+ export type PredicateInput<T> = (value: T) => unknown;
20
+ /**
21
+ * Extracts the narrowed type from a type predicate.
22
+ *
23
+ * Ordinary boolean predicates do not narrow, so they produce T. A guard whose
24
+ * narrowed type is disjoint from T (`Extract<U, T>` is `never`) is treated as a
25
+ * non-narrowing filter and keeps T, rather than collapsing the chain to `never` -
26
+ * this is what a negated narrowing guard looks like (e.g. `hasName(...).negate()`,
27
+ * which widens back to a supertype of T).
28
+ */
29
+ type NarrowedBy<T, P> = P extends (value: any) => value is infer U ? [Extract<U, T>] extends [never] ? T : Extract<U, T> : T;
30
+ /** Intersects the narrowed types produced by a tuple of predicates. */
31
+ type AndNarrowed<T, P extends readonly PredicateInput<T>[], Result = T> = P extends readonly [
32
+ infer First,
33
+ ...infer Rest extends readonly PredicateInput<T>[]
34
+ ] ? AndNarrowed<T, Rest, Result & NarrowedBy<T, First>> : Result;
35
+ /** Unions the narrowed types produced by a tuple of predicates. */
36
+ type OrNarrowed<T, P extends readonly PredicateInput<T>[], Result = never> = P extends readonly [
37
+ infer First,
38
+ ...infer Rest extends readonly PredicateInput<T>[]
39
+ ] ? OrNarrowed<T, Rest, Result | NarrowedBy<T, First>> : Result;
40
+ /**
41
+ * A callable predicate with fluent composition methods.
42
+ *
43
+ * T is the accepted input type.
44
+ * U is the type established when the predicate returns true.
45
+ */
46
+ export interface Predicate<T, U extends T = T> {
47
+ (value: T): value is U;
48
+ /**
49
+ * Returns a predicate requiring this predicate and every supplied predicate
50
+ * to match.
51
+ *
52
+ * Type guards are intersected. Additional predicates are checked against the
53
+ * type already established by this predicate.
54
+ */
55
+ and<const P extends readonly PredicateInput<U>[]>(...predicates: P): Predicate<T, Extract<U & AndNarrowed<U, P>, T>>;
56
+ /**
57
+ * Returns a predicate requiring this predicate or any supplied predicate
58
+ * to match.
59
+ *
60
+ * Type guards are unioned. Because an ordinary boolean predicate could
61
+ * accept any value of `U`, including one causes the resulting predicate to
62
+ * narrow only to `U`.
63
+ */
64
+ or<const P extends readonly PredicateInput<U>[]>(...predicates: P): Predicate<T, Extract<U | OrNarrowed<U, P>, T>>;
65
+ /**
66
+ * Returns the logical inverse of this predicate.
67
+ *
68
+ * For a type predicate narrowing T to U, the result narrows to
69
+ * Exclude<T, U>.
70
+ */
71
+ negate(): Predicate<T, Exclude<T, U>>;
72
+ }
73
+ /**
74
+ * Wraps a type predicate while preserving its narrowed type.
75
+ */
76
+ export declare function create<T, U extends T>(predicate: TypePredicateFunction<T, U>): Predicate<T, U>;
77
+ /**
78
+ * Wraps an ordinary boolean or truthy predicate.
79
+ */
80
+ export declare function create<T>(predicate: PredicateInput<T>): Predicate<T, T>;
81
+ export {};
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Fluent, callable predicate combinators that preserve TypeScript type guards.
3
+ *
4
+ * A Predicate<T, U> is:
5
+ * - Callable as `(value: T) => value is U`
6
+ * - Composable through `.and()`, `.or()`, and `.negate()`
7
+ *
8
+ * Ordinary boolean predicates are treated as narrowing to `T`, meaning they
9
+ * do not narrow the input type by themselves. Composed predicates passed to
10
+ * `.and()` / `.or()` may return any value; they are tested for truthiness.
11
+ *
12
+ * @module
13
+ */
14
+ /** Coerce a predicate result to boolean the way `if (...)` does. */
15
+ function isTruthy(test) {
16
+ return (value) => Boolean(test(value));
17
+ }
18
+ /**
19
+ * Creates the callable predicate object.
20
+ *
21
+ * The public generic behavior is provided by Predicate<T, U>. Runtime
22
+ * composition coerces predicate results to boolean; type predicates are
23
+ * ordinary boolean functions at runtime.
24
+ */
25
+ function buildPredicate(test) {
26
+ const check = isTruthy(test);
27
+ const callable = ((value) => check(value));
28
+ return Object.assign(callable, {
29
+ and(...predicates) {
30
+ return buildPredicate((value) => check(value) && predicates.every((predicate) => isTruthy(predicate)(value)));
31
+ },
32
+ or(...predicates) {
33
+ return buildPredicate((value) => check(value) || predicates.some((predicate) => isTruthy(predicate)(value)));
34
+ },
35
+ negate() {
36
+ return buildPredicate((value) => !check(value));
37
+ },
38
+ });
39
+ }
40
+ export function create(predicate) {
41
+ return buildPredicate(predicate);
42
+ }
43
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlZGljYXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3ByZWRpY2F0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7O0dBWUc7QUFtRkgsb0VBQW9FO0FBQ3BFLFNBQVMsUUFBUSxDQUFJLElBQTJCO0lBQzlDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUN6QyxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsU0FBUyxjQUFjLENBQWlCLElBQTJCO0lBQ2pFLE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM3QixNQUFNLFFBQVEsR0FBRyxDQUFDLENBQUMsS0FBUSxFQUFjLEVBQUUsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQTZCLENBQUM7SUFFdEYsT0FBTyxNQUFNLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRTtRQUM3QixHQUFHLENBQ0QsR0FBRyxVQUFhO1lBR2hCLE9BQU8sY0FBYyxDQUNuQixDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxJQUFJLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQyxLQUFVLENBQUMsQ0FBQyxDQUM1RixDQUFDO1FBQ0osQ0FBQztRQUVELEVBQUUsQ0FDQSxHQUFHLFVBQWE7WUFHaEIsT0FBTyxjQUFjLENBQ25CLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLElBQUksVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDLFNBQVMsRUFBRSxFQUFFLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLEtBQVUsQ0FBQyxDQUFDLENBQzNGLENBQUM7UUFDSixDQUFDO1FBRUQsTUFBTTtZQUNKLE9BQU8sY0FBYyxDQUFtQixDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNwRSxDQUFDO0tBQ0YsQ0FBb0IsQ0FBQztBQUN4QixDQUFDO0FBWUQsTUFBTSxVQUFVLE1BQU0sQ0FDcEIsU0FBMEQ7SUFFMUQsT0FBTyxjQUFjLENBQU8sU0FBUyxDQUFDLENBQUM7QUFDekMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRmx1ZW50LCBjYWxsYWJsZSBwcmVkaWNhdGUgY29tYmluYXRvcnMgdGhhdCBwcmVzZXJ2ZSBUeXBlU2NyaXB0IHR5cGUgZ3VhcmRzLlxuICpcbiAqIEEgUHJlZGljYXRlPFQsIFU+IGlzOlxuICogLSBDYWxsYWJsZSBhcyBgKHZhbHVlOiBUKSA9PiB2YWx1ZSBpcyBVYFxuICogLSBDb21wb3NhYmxlIHRocm91Z2ggYC5hbmQoKWAsIGAub3IoKWAsIGFuZCBgLm5lZ2F0ZSgpYFxuICpcbiAqIE9yZGluYXJ5IGJvb2xlYW4gcHJlZGljYXRlcyBhcmUgdHJlYXRlZCBhcyBuYXJyb3dpbmcgdG8gYFRgLCBtZWFuaW5nIHRoZXlcbiAqIGRvIG5vdCBuYXJyb3cgdGhlIGlucHV0IHR5cGUgYnkgdGhlbXNlbHZlcy4gQ29tcG9zZWQgcHJlZGljYXRlcyBwYXNzZWQgdG9cbiAqIGAuYW5kKClgIC8gYC5vcigpYCBtYXkgcmV0dXJuIGFueSB2YWx1ZTsgdGhleSBhcmUgdGVzdGVkIGZvciB0cnV0aGluZXNzLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG4vKiogQW4gb3JkaW5hcnkgYm9vbGVhbiBwcmVkaWNhdGUuICovXG5leHBvcnQgdHlwZSBQcmVkaWNhdGVGdW5jdGlvbjxUPiA9ICh2YWx1ZTogVCkgPT4gYm9vbGVhbjtcblxuLyoqIEEgcHJlZGljYXRlIHRoYXQgbmFycm93cyBUIHRvIFUuICovXG5leHBvcnQgdHlwZSBUeXBlUHJlZGljYXRlRnVuY3Rpb248VCwgVSBleHRlbmRzIFQ+ID0gKHZhbHVlOiBUKSA9PiB2YWx1ZSBpcyBVO1xuXG4vKiogQSBwcmVkaWNhdGUgdGVzdGVkIGZvciB0cnV0aGluZXNzIHdoZW4gY29tcG9zZWQgd2l0aCBgLmFuZCgpYCAvIGAub3IoKWAuICovXG5leHBvcnQgdHlwZSBQcmVkaWNhdGVJbnB1dDxUPiA9ICh2YWx1ZTogVCkgPT4gdW5rbm93bjtcblxuLyoqXG4gKiBFeHRyYWN0cyB0aGUgbmFycm93ZWQgdHlwZSBmcm9tIGEgdHlwZSBwcmVkaWNhdGUuXG4gKlxuICogT3JkaW5hcnkgYm9vbGVhbiBwcmVkaWNhdGVzIGRvIG5vdCBuYXJyb3csIHNvIHRoZXkgcHJvZHVjZSBULiBBIGd1YXJkIHdob3NlXG4gKiBuYXJyb3dlZCB0eXBlIGlzIGRpc2pvaW50IGZyb20gVCAoYEV4dHJhY3Q8VSwgVD5gIGlzIGBuZXZlcmApIGlzIHRyZWF0ZWQgYXMgYVxuICogbm9uLW5hcnJvd2luZyBmaWx0ZXIgYW5kIGtlZXBzIFQsIHJhdGhlciB0aGFuIGNvbGxhcHNpbmcgdGhlIGNoYWluIHRvIGBuZXZlcmAgLVxuICogdGhpcyBpcyB3aGF0IGEgbmVnYXRlZCBuYXJyb3dpbmcgZ3VhcmQgbG9va3MgbGlrZSAoZS5nLiBgaGFzTmFtZSguLi4pLm5lZ2F0ZSgpYCxcbiAqIHdoaWNoIHdpZGVucyBiYWNrIHRvIGEgc3VwZXJ0eXBlIG9mIFQpLlxuICovXG50eXBlIE5hcnJvd2VkQnk8VCwgUD4gPSBQIGV4dGVuZHMgKHZhbHVlOiBhbnkpID0+IHZhbHVlIGlzIGluZmVyIFVcbiAgPyBbRXh0cmFjdDxVLCBUPl0gZXh0ZW5kcyBbbmV2ZXJdXG4gICAgPyBUXG4gICAgOiBFeHRyYWN0PFUsIFQ+XG4gIDogVDtcblxuLyoqIEludGVyc2VjdHMgdGhlIG5hcnJvd2VkIHR5cGVzIHByb2R1Y2VkIGJ5IGEgdHVwbGUgb2YgcHJlZGljYXRlcy4gKi9cbnR5cGUgQW5kTmFycm93ZWQ8VCwgUCBleHRlbmRzIHJlYWRvbmx5IFByZWRpY2F0ZUlucHV0PFQ+W10sIFJlc3VsdCA9IFQ+ID0gUCBleHRlbmRzIHJlYWRvbmx5IFtcbiAgaW5mZXIgRmlyc3QsXG4gIC4uLmluZmVyIFJlc3QgZXh0ZW5kcyByZWFkb25seSBQcmVkaWNhdGVJbnB1dDxUPltdLFxuXVxuICA/IEFuZE5hcnJvd2VkPFQsIFJlc3QsIFJlc3VsdCAmIE5hcnJvd2VkQnk8VCwgRmlyc3Q+PlxuICA6IFJlc3VsdDtcblxuLyoqIFVuaW9ucyB0aGUgbmFycm93ZWQgdHlwZXMgcHJvZHVjZWQgYnkgYSB0dXBsZSBvZiBwcmVkaWNhdGVzLiAqL1xudHlwZSBPck5hcnJvd2VkPFQsIFAgZXh0ZW5kcyByZWFkb25seSBQcmVkaWNhdGVJbnB1dDxUPltdLCBSZXN1bHQgPSBuZXZlcj4gPSBQIGV4dGVuZHMgcmVhZG9ubHkgW1xuICBpbmZlciBGaXJzdCxcbiAgLi4uaW5mZXIgUmVzdCBleHRlbmRzIHJlYWRvbmx5IFByZWRpY2F0ZUlucHV0PFQ+W10sXG5dXG4gID8gT3JOYXJyb3dlZDxULCBSZXN0LCBSZXN1bHQgfCBOYXJyb3dlZEJ5PFQsIEZpcnN0Pj5cbiAgOiBSZXN1bHQ7XG5cbi8qKlxuICogQSBjYWxsYWJsZSBwcmVkaWNhdGUgd2l0aCBmbHVlbnQgY29tcG9zaXRpb24gbWV0aG9kcy5cbiAqXG4gKiBUIGlzIHRoZSBhY2NlcHRlZCBpbnB1dCB0eXBlLlxuICogVSBpcyB0aGUgdHlwZSBlc3RhYmxpc2hlZCB3aGVuIHRoZSBwcmVkaWNhdGUgcmV0dXJucyB0cnVlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFByZWRpY2F0ZTxULCBVIGV4dGVuZHMgVCA9IFQ+IHtcbiAgKHZhbHVlOiBUKTogdmFsdWUgaXMgVTtcblxuICAvKipcbiAgICogUmV0dXJucyBhIHByZWRpY2F0ZSByZXF1aXJpbmcgdGhpcyBwcmVkaWNhdGUgYW5kIGV2ZXJ5IHN1cHBsaWVkIHByZWRpY2F0ZVxuICAgKiB0byBtYXRjaC5cbiAgICpcbiAgICogVHlwZSBndWFyZHMgYXJlIGludGVyc2VjdGVkLiBBZGRpdGlvbmFsIHByZWRpY2F0ZXMgYXJlIGNoZWNrZWQgYWdhaW5zdCB0aGVcbiAgICogdHlwZSBhbHJlYWR5IGVzdGFibGlzaGVkIGJ5IHRoaXMgcHJlZGljYXRlLlxuICAgKi9cbiAgYW5kPGNvbnN0IFAgZXh0ZW5kcyByZWFkb25seSBQcmVkaWNhdGVJbnB1dDxVPltdPihcbiAgICAuLi5wcmVkaWNhdGVzOiBQXG4gICk6IFByZWRpY2F0ZTxULCBFeHRyYWN0PFUgJiBBbmROYXJyb3dlZDxVLCBQPiwgVD4+O1xuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGEgcHJlZGljYXRlIHJlcXVpcmluZyB0aGlzIHByZWRpY2F0ZSBvciBhbnkgc3VwcGxpZWQgcHJlZGljYXRlXG4gICAqIHRvIG1hdGNoLlxuICAgKlxuICAgKiBUeXBlIGd1YXJkcyBhcmUgdW5pb25lZC4gQmVjYXVzZSBhbiBvcmRpbmFyeSBib29sZWFuIHByZWRpY2F0ZSBjb3VsZFxuICAgKiBhY2NlcHQgYW55IHZhbHVlIG9mIGBVYCwgaW5jbHVkaW5nIG9uZSBjYXVzZXMgdGhlIHJlc3VsdGluZyBwcmVkaWNhdGUgdG9cbiAgICogbmFycm93IG9ubHkgdG8gYFVgLlxuICAgKi9cbiAgb3I8Y29uc3QgUCBleHRlbmRzIHJlYWRvbmx5IFByZWRpY2F0ZUlucHV0PFU+W10+KFxuICAgIC4uLnByZWRpY2F0ZXM6IFBcbiAgKTogUHJlZGljYXRlPFQsIEV4dHJhY3Q8VSB8IE9yTmFycm93ZWQ8VSwgUD4sIFQ+PjtcblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgbG9naWNhbCBpbnZlcnNlIG9mIHRoaXMgcHJlZGljYXRlLlxuICAgKlxuICAgKiBGb3IgYSB0eXBlIHByZWRpY2F0ZSBuYXJyb3dpbmcgVCB0byBVLCB0aGUgcmVzdWx0IG5hcnJvd3MgdG9cbiAgICogRXhjbHVkZTxULCBVPi5cbiAgICovXG4gIG5lZ2F0ZSgpOiBQcmVkaWNhdGU8VCwgRXhjbHVkZTxULCBVPj47XG59XG5cbi8qKiBDb2VyY2UgYSBwcmVkaWNhdGUgcmVzdWx0IHRvIGJvb2xlYW4gdGhlIHdheSBgaWYgKC4uLilgIGRvZXMuICovXG5mdW5jdGlvbiBpc1RydXRoeTxUPih0ZXN0OiAodmFsdWU6IFQpID0+IHVua25vd24pOiBQcmVkaWNhdGVGdW5jdGlvbjxUPiB7XG4gIHJldHVybiAodmFsdWUpID0+IEJvb2xlYW4odGVzdCh2YWx1ZSkpO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIGNhbGxhYmxlIHByZWRpY2F0ZSBvYmplY3QuXG4gKlxuICogVGhlIHB1YmxpYyBnZW5lcmljIGJlaGF2aW9yIGlzIHByb3ZpZGVkIGJ5IFByZWRpY2F0ZTxULCBVPi4gUnVudGltZVxuICogY29tcG9zaXRpb24gY29lcmNlcyBwcmVkaWNhdGUgcmVzdWx0cyB0byBib29sZWFuOyB0eXBlIHByZWRpY2F0ZXMgYXJlXG4gKiBvcmRpbmFyeSBib29sZWFuIGZ1bmN0aW9ucyBhdCBydW50aW1lLlxuICovXG5mdW5jdGlvbiBidWlsZFByZWRpY2F0ZTxULCBVIGV4dGVuZHMgVD4odGVzdDogKHZhbHVlOiBUKSA9PiB1bmtub3duKTogUHJlZGljYXRlPFQsIFU+IHtcbiAgY29uc3QgY2hlY2sgPSBpc1RydXRoeSh0ZXN0KTtcbiAgY29uc3QgY2FsbGFibGUgPSAoKHZhbHVlOiBUKTogdmFsdWUgaXMgVSA9PiBjaGVjayh2YWx1ZSkpIGFzICh2YWx1ZTogVCkgPT4gdmFsdWUgaXMgVTtcblxuICByZXR1cm4gT2JqZWN0LmFzc2lnbihjYWxsYWJsZSwge1xuICAgIGFuZDxjb25zdCBQIGV4dGVuZHMgcmVhZG9ubHkgUHJlZGljYXRlSW5wdXQ8VT5bXT4oXG4gICAgICAuLi5wcmVkaWNhdGVzOiBQXG4gICAgKTogUHJlZGljYXRlPFQsIEV4dHJhY3Q8VSAmIEFuZE5hcnJvd2VkPFUsIFA+LCBUPj4ge1xuICAgICAgdHlwZSBSZXN1bHQgPSBFeHRyYWN0PFUgJiBBbmROYXJyb3dlZDxVLCBQPiwgVD47XG4gICAgICByZXR1cm4gYnVpbGRQcmVkaWNhdGU8VCwgUmVzdWx0PihcbiAgICAgICAgKHZhbHVlKSA9PiBjaGVjayh2YWx1ZSkgJiYgcHJlZGljYXRlcy5ldmVyeSgocHJlZGljYXRlKSA9PiBpc1RydXRoeShwcmVkaWNhdGUpKHZhbHVlIGFzIFUpKSxcbiAgICAgICk7XG4gICAgfSxcblxuICAgIG9yPGNvbnN0IFAgZXh0ZW5kcyByZWFkb25seSBQcmVkaWNhdGVJbnB1dDxVPltdPihcbiAgICAgIC4uLnByZWRpY2F0ZXM6IFBcbiAgICApOiBQcmVkaWNhdGU8VCwgRXh0cmFjdDxVIHwgT3JOYXJyb3dlZDxVLCBQPiwgVD4+IHtcbiAgICAgIHR5cGUgUmVzdWx0ID0gRXh0cmFjdDxVIHwgT3JOYXJyb3dlZDxVLCBQPiwgVD47XG4gICAgICByZXR1cm4gYnVpbGRQcmVkaWNhdGU8VCwgUmVzdWx0PihcbiAgICAgICAgKHZhbHVlKSA9PiBjaGVjayh2YWx1ZSkgfHwgcHJlZGljYXRlcy5zb21lKChwcmVkaWNhdGUpID0+IGlzVHJ1dGh5KHByZWRpY2F0ZSkodmFsdWUgYXMgVSkpLFxuICAgICAgKTtcbiAgICB9LFxuXG4gICAgbmVnYXRlKCk6IFByZWRpY2F0ZTxULCBFeGNsdWRlPFQsIFU+PiB7XG4gICAgICByZXR1cm4gYnVpbGRQcmVkaWNhdGU8VCwgRXhjbHVkZTxULCBVPj4oKHZhbHVlKSA9PiAhY2hlY2sodmFsdWUpKTtcbiAgICB9LFxuICB9KSBhcyBQcmVkaWNhdGU8VCwgVT47XG59XG5cbi8qKlxuICogV3JhcHMgYSB0eXBlIHByZWRpY2F0ZSB3aGlsZSBwcmVzZXJ2aW5nIGl0cyBuYXJyb3dlZCB0eXBlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlPFQsIFUgZXh0ZW5kcyBUPihwcmVkaWNhdGU6IFR5cGVQcmVkaWNhdGVGdW5jdGlvbjxULCBVPik6IFByZWRpY2F0ZTxULCBVPjtcblxuLyoqXG4gKiBXcmFwcyBhbiBvcmRpbmFyeSBib29sZWFuIG9yIHRydXRoeSBwcmVkaWNhdGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGU8VD4ocHJlZGljYXRlOiBQcmVkaWNhdGVJbnB1dDxUPik6IFByZWRpY2F0ZTxULCBUPjtcblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZTxULCBVIGV4dGVuZHMgVCA9IFQ+KFxuICBwcmVkaWNhdGU6IFR5cGVQcmVkaWNhdGVGdW5jdGlvbjxULCBVPiB8IFByZWRpY2F0ZUlucHV0PFQ+LFxuKTogUHJlZGljYXRlPFQsIFU+IHtcbiAgcmV0dXJuIGJ1aWxkUHJlZGljYXRlPFQsIFU+KHByZWRpY2F0ZSk7XG59XG4iXX0=
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Options controlling {@link tokenizeWithOptions}. All default off except
3
+ * `camelCase`.
4
+ *
5
+ * - `distinct` - drop duplicate tokens (first occurrence wins).
6
+ * - `lowerCase` - lowercase every token.
7
+ * - `capitalize` - upper-case each token's first letter (then
8
+ * {@link TOKENIZE_OVERRIDES} fix up `ai` -> `AI` and `v2` -> `V2`).
9
+ * - `omitUriScheme` - strip a leading `scheme://` before tokenizing.
10
+ * - `omitEmailDomain` - keep only the local part of an email.
11
+ * - `camelCase` - split on camelCase boundaries / digit runs / acronyms
12
+ * (default `true`); when `false`, split only on non-alphanumerics.
13
+ */
14
+ export type TokenizeOptions = {
15
+ distinct?: boolean;
16
+ lowerCase?: boolean;
17
+ capitalize?: boolean;
18
+ omitUriScheme?: boolean;
19
+ omitEmailDomain?: boolean;
20
+ camelCase?: boolean;
21
+ };
22
+ export type KeyOptions = Omit<TokenizeOptions, "lowerCase" | "capitalize"> & {
23
+ maxLength?: number;
24
+ truncateStrategy?: "hash" | "trim" | "empty";
25
+ truncateHashLength?: number;
26
+ };
27
+ export type IdentifierOptions = KeyOptions & {
28
+ delimiter?: string;
29
+ };
30
+ export declare function tokenizeWithOptions(options: TokenizeOptions, ...values: unknown[]): Generator<string>;
31
+ export declare function tokenize(...values: unknown[]): Generator<string>;
32
+ /**
33
+ * Join tokenized values with `delimiter`. When the next token would push the
34
+ * result over `maxLength`: `trim` stops adding; `empty` returns `""`; `hash`
35
+ * appends a digest of accepted tokens plus the overflow token if the result
36
+ * still fits, otherwise `""`.
37
+ */
38
+ export declare function toIdentifierWithOptions(options: IdentifierOptions, ...values: unknown[]): string;
39
+ export declare function toIdentifier(...values: unknown[]): string;
40
+ /**
41
+ * Slugified identifier: same rules as {@link toIdentifierWithOptions} with the
42
+ * delimiter forced to `-`. Accepts {@link KeyOptions} so callers cannot
43
+ * override the delimiter.
44
+ */
45
+ export declare function toSlugWithOptions(options: KeyOptions, ...values: unknown[]): string;
46
+ export declare function toSlug(...values: unknown[]): string;
47
+ /**
48
+ * Trim `value` and return `null` for non-strings, `undefined`, or
49
+ * strings that are empty after trimming. Lets call sites collapse the
50
+ * common
51
+ *
52
+ * ```ts
53
+ * typeof v === "string" && v.trim() ? v.trim() : null
54
+ * ```
55
+ *
56
+ * dance into a single helper. Useful for HTTP header / query / form
57
+ * extractors where downstream code wants `string | null` to drive a
58
+ * cheap `??` / `if (x)` cascade.
59
+ */
60
+ export declare function trimToNull(value: unknown): string | null;
61
+ /**
62
+ * Normalize a config list that may arrive as an array or as a single
63
+ * comma/whitespace-separated string: split, apply `transform`, drop empties,
64
+ * and de-duplicate (first occurrence wins).
65
+ *
66
+ * This is the shape every allow-list / fallback-order setting in this repo
67
+ * takes, because the same value can come from typed config (`string[]`) or from
68
+ * an environment variable (`"a, b c"`). Pass `transform` to normalize entries
69
+ * as they are read; it defaults to trimming.
70
+ *
71
+ * @example
72
+ * parseList("docs.example.com, *.databricks.com");
73
+ * parseList(process.env.MODEL_FALLBACKS);
74
+ * parseList(raw, normalizeUrlPattern);
75
+ */
76
+ export declare function parseList(raw: string | readonly string[] | undefined | null, transform?: (entry: string) => string): string[];
77
+ /**
78
+ * {@link trimToNull} with an empty-string miss instead of `null`, for callers
79
+ * that build a string unconditionally and treat "absent" as "".
80
+ *
81
+ * Reading loosely-typed JSON is the motivating case: a field that should be a
82
+ * string may be missing or the wrong type, and the caller wants `""` rather
83
+ * than a null check at every access.
84
+ *
85
+ * @example
86
+ * const title = trimToEmpty(record.title); // always a string
87
+ */
88
+ export declare function trimToEmpty(value: unknown): string;
89
+ /**
90
+ * Trim the first usable string out of `value`. Returns `null` when
91
+ * `value` is `undefined`, `null`, an empty string, or an array whose
92
+ * first string member is empty. Mirrors how Express / Node header
93
+ * accessors expose single vs. repeated headers - the first
94
+ * non-empty entry wins, everything else is ignored.
95
+ */
96
+ export declare function firstNonEmpty(value: unknown): string | null;
97
+ /**
98
+ * Escape the five characters significant in HTML text and
99
+ * double-quoted attribute values (`&`, `<`, `>`, `"`, `'`) so an
100
+ * untrusted string can be interpolated into markup without breaking
101
+ * out of its context. `&` is replaced first so ampersands introduced
102
+ * by the later replacements aren't double-escaped.
103
+ */
104
+ export declare function escapeHtml(value: string): string;
105
+ /**
106
+ * Slugify `value` (using the standard {@link toIdentifierWithOptions}
107
+ * tokenizer + delimiter rules) and **always** suffix a short
108
+ * deterministic hash. Use when you need a stable, slugified id that
109
+ * is guaranteed to be unique across descriptions sharing the same
110
+ * leading tokens (tool ids, cache keys, etc.).
111
+ *
112
+ * Behaviour differs from `toIdentifierWithOptions({ maxLength,
113
+ * truncateStrategy: "hash" })`: that helper only appends a hash when
114
+ * the slug *overflows* `maxLength`. This helper appends a hash
115
+ * unconditionally so the result is collision-resistant even for
116
+ * short inputs. The hash is computed over the raw `value` so two
117
+ * descriptions producing the same slug still get different ids.
118
+ *
119
+ * @param value - Source string (typically a tool/agent description).
120
+ * @param options.delimiter - Token separator (default `"_"`).
121
+ * @param options.slugMaxLength - Cap on the slug portion (the part
122
+ * before the hash). Default 32.
123
+ * @param options.hashLength - Length of the suffix produced by
124
+ * {@link fnvHashWithOptions} (Crockford-style base-32 alphabet, max 7
125
+ * chars). Default 6.
126
+ * @param options.fallbackPrefix - Prefix used when the slug is empty
127
+ * (e.g. punctuation-only input). Default `"id"`.
128
+ */
129
+ export declare function toUniqueSlug(value: string, options?: {
130
+ delimiter?: string;
131
+ slugMaxLength?: number;
132
+ hashLength?: number;
133
+ fallbackPrefix?: string;
134
+ }): string;
135
+ /**
136
+ * A node in the description tree consumed by {@link toDescription}.
137
+ *
138
+ * - `string` - a text paragraph.
139
+ * - `Description[]` - a sequence of stacked blocks at the same level
140
+ * (no list markers). Plain text adjacent to a list (either direction)
141
+ * flushes together so the prose reads as a lead-in or trailing
142
+ * summary. Two text paragraphs, two adjacent lists, and anything
143
+ * touching a map get a blank-line break.
144
+ * - `{ bullets: [...] }` / `{ numbered: [...] }` - explicit list. A
145
+ * list of one bare string drops its marker (`-` / `1.`); a list of
146
+ * one item with nested children keeps its marker as the visual anchor
147
+ * for the indented children.
148
+ * - any other object - headers map: each key becomes a `Header:` line
149
+ * followed by a blank line and the rendered value.
150
+ */
151
+ export type Description = string | readonly Description[] | {
152
+ readonly [key: string]: Description;
153
+ };
154
+ /**
155
+ * Format a nested description tree as a Markdown-ish string suitable
156
+ * for an LLM system prompt, Zod `.describe()` block, Mastra tool
157
+ * description, or any other long-form text destination.
158
+ *
159
+ * Every string section is dedented (common leading whitespace stripped),
160
+ * right-trimmed line by line, and freed of leading / trailing blank
161
+ * lines, so callers can write multi-line template literals indented
162
+ * naturally in source without leaking that indentation into the
163
+ * consumer-facing output. Plain-string inputs flow through unchanged
164
+ * apart from the same normalization pass, so a single multi-line
165
+ * template literal works directly:
166
+ *
167
+ * ```ts
168
+ * toDescription(`
169
+ * Ask the Genie space "${alias}" a question.
170
+ * Pass the answer through as-is.
171
+ * `);
172
+ * // Ask the Genie space "default" a question.
173
+ * // Pass the answer through as-is.
174
+ *
175
+ * toDescription([
176
+ * `
177
+ * Ask the Genie space a question.
178
+ * Phrase it from the user's perspective.
179
+ * `,
180
+ * { bullets: [
181
+ * ["Pass the answer through as-is", { numbered: ["item", "item"] }],
182
+ * ]},
183
+ * { Instructions: "Reply with the SQL only." },
184
+ * ]);
185
+ * // Ask the Genie space a question.
186
+ * // Phrase it from the user's perspective.
187
+ * // - Pass the answer through as-is
188
+ * // 1. item
189
+ * // 2. item
190
+ * //
191
+ * // Instructions:
192
+ * //
193
+ * // Reply with the SQL only.
194
+ * ```
195
+ *
196
+ * See {@link Description} for the node grammar.
197
+ */
198
+ export declare function toDescription(node: Description): string;
199
+ /**
200
+ * Format a count with its noun, pluralizing (naive `+s`) unless the count is 1:
201
+ * `pluralize(1, "barrel")` -> `"1 barrel"`, `pluralize(3, "barrel")` ->
202
+ * `"3 barrels"`. Collapses the `${n} noun${n === 1 ? "" : "s"}` idiom.
203
+ */
204
+ export declare function pluralize(count: number, noun: string): string;
205
+ /**
206
+ * Title-case a snake / kebab / camel identifier into a human-readable label:
207
+ * tokenize (lowercased, capitalized), then join with spaces. Falls back to
208
+ * the raw `value` when it yields no tokens (e.g. punctuation-only input).
209
+ * `bge_large_en` -> `"Bge Large En"`, `promptId` -> `"Prompt Id"`.
210
+ *
211
+ * The single source of truth for the "humanize a column / field name" idiom -
212
+ * reused by the chat data-grid headers and the export renderer. Extra
213
+ * {@link TokenizeOptions} (e.g. `camelCase: false`) merge over the defaults.
214
+ */
215
+ export declare function toLabel(value: string, options?: TokenizeOptions): string;
216
+ /**
217
+ * Upper-case the first character of `value`, leaving the rest untouched.
218
+ * `"working"` -> `"Working"`. Collapses the
219
+ * `s.charAt(0).toUpperCase() + s.slice(1)` idiom.
220
+ */
221
+ export declare function capitalize(value: string): string;