hanzio 1.1.0 → 2.0.0

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.
Files changed (172) hide show
  1. package/README.md +832 -219
  2. package/dist/api-wrapper/builders.d.ts +15 -3
  3. package/dist/api-wrapper/create-client.d.ts +1 -1
  4. package/dist/api-wrapper/describe.d.ts +16 -0
  5. package/dist/api-wrapper/endpoint.d.ts +18 -0
  6. package/dist/api-wrapper/errors.d.ts +40 -2
  7. package/dist/api-wrapper/headers.d.ts +2 -1
  8. package/dist/api-wrapper/index.d.ts +5 -0
  9. package/dist/api-wrapper/otel.d.ts +44 -0
  10. package/dist/api-wrapper/paginate.d.ts +12 -0
  11. package/dist/api-wrapper/redaction.d.ts +2 -0
  12. package/dist/api-wrapper/shared.d.ts +16 -2
  13. package/dist/api-wrapper/testing.d.ts +23 -0
  14. package/dist/api-wrapper/transport.d.ts +12 -10
  15. package/dist/api-wrapper/types.d.ts +104 -18
  16. package/dist/array/index.d.ts +50 -4
  17. package/dist/cache/createCache.d.ts +31 -0
  18. package/dist/cache/index.d.ts +45 -2
  19. package/dist/color/contrast.d.ts +40 -0
  20. package/dist/color/index.d.ts +2 -0
  21. package/dist/{math → color}/metricsSeriesColors.d.ts +14 -7
  22. package/dist/cool-console-log/index.d.ts +34 -5
  23. package/dist/crypto/encoding.d.ts +18 -0
  24. package/dist/crypto/hash.d.ts +33 -0
  25. package/dist/crypto/index.d.ts +4 -0
  26. package/dist/crypto/sealer.d.ts +25 -0
  27. package/dist/crypto/webhook.d.ts +20 -0
  28. package/dist/date/index.d.ts +46 -2
  29. package/dist/emitter/index.d.ts +21 -0
  30. package/dist/error/index.d.ts +12 -0
  31. package/dist/format/index.d.ts +102 -0
  32. package/dist/function/index.d.ts +60 -0
  33. package/dist/guard/index.d.ts +20 -0
  34. package/dist/i18n/index.d.ts +37 -0
  35. package/dist/index-0zvtcbzy.js +224 -0
  36. package/dist/index-181e6vfw.js +29 -0
  37. package/dist/index-8r2vdzjg.js +87 -0
  38. package/dist/index-hy7kbb99.js +157 -0
  39. package/dist/{src/secrets/loaders.js → index-j1ybktbr.js} +608 -405
  40. package/dist/index-k9yx6bne.js +89 -0
  41. package/dist/index-kj1phgrx.js +169 -0
  42. package/dist/index-mnkcfyx8.js +78 -0
  43. package/dist/{p-queue/index.js → index-pwt0dz63.js} +7 -11
  44. package/dist/index-rp5pqghz.js +157 -0
  45. package/dist/index-srt06xd2.js +15 -0
  46. package/dist/index-wjyb9bsd.js +263 -0
  47. package/dist/index.d.ts +7 -7
  48. package/dist/jwt/index.d.ts +50 -2
  49. package/dist/math/index.d.ts +2 -2
  50. package/dist/math/linearRegressionTrend.d.ts +14 -1
  51. package/dist/math/scale.d.ts +8 -0
  52. package/dist/object/index.d.ts +19 -0
  53. package/dist/p-queue/index.d.ts +1 -1
  54. package/dist/promise/index.d.ts +97 -2
  55. package/dist/secrets/core.d.ts +47 -0
  56. package/dist/secrets/deadline.d.ts +6 -0
  57. package/dist/secrets/environment.d.ts +9 -0
  58. package/dist/secrets/errors.d.ts +11 -0
  59. package/dist/secrets/index.d.ts +13 -25
  60. package/dist/secrets/infisical-cli.d.ts +6 -0
  61. package/dist/secrets/infisical-config.d.ts +17 -0
  62. package/dist/secrets/infisical-session.d.ts +4 -0
  63. package/dist/secrets/infisical.d.ts +8 -13
  64. package/dist/secrets/loaders.d.ts +5 -12
  65. package/dist/secrets/vite.d.ts +7 -3
  66. package/dist/secrets/worker-loader.d.ts +9 -0
  67. package/dist/secrets/worker.d.ts +3 -0
  68. package/dist/sitemap/index.d.ts +48 -6
  69. package/dist/src/api-wrapper/index.js +60 -1100
  70. package/dist/src/api-wrapper/testing.js +20 -0
  71. package/dist/{cool-console-log → src/cool-console-log}/index.js +80 -33
  72. package/dist/src/crypto/index.js +195 -0
  73. package/dist/src/format/index.js +211 -0
  74. package/dist/src/i18n/index.js +36 -0
  75. package/dist/src/index.js +863 -2809
  76. package/dist/src/jwt/index.js +138 -92
  77. package/dist/src/p-queue/index.js +9 -437
  78. package/dist/src/secrets/index.js +261 -1377
  79. package/dist/src/secrets/vite.js +6 -17
  80. package/dist/src/secrets/worker.js +12 -0
  81. package/dist/src/sitemap/index.js +183 -511
  82. package/dist/src/storage/index.js +119 -0
  83. package/dist/src/zod/index.js +347 -174
  84. package/dist/state/index.d.ts +21 -0
  85. package/dist/storage/index.d.ts +58 -0
  86. package/dist/string/index.d.ts +75 -2
  87. package/dist/typedswitch/index.d.ts +37 -26
  88. package/dist/types/index.d.ts +11 -0
  89. package/dist/url/index.d.ts +50 -1
  90. package/dist/zod/index.d.ts +32 -3
  91. package/dist/zod/introspect.d.ts +19 -0
  92. package/dist/zod/jsonToZodSchemaMapper.d.ts +3 -1
  93. package/dist/zod/zodMinMaxFilter.d.ts +22 -3
  94. package/dist/zod/zodToTypeString.d.ts +8 -3
  95. package/package.json +87 -7
  96. package/src/api-wrapper/actions-impl.ts +11 -72
  97. package/src/api-wrapper/builders.ts +84 -52
  98. package/src/api-wrapper/create-client.ts +246 -96
  99. package/src/api-wrapper/describe.ts +157 -0
  100. package/src/api-wrapper/endpoint.ts +26 -0
  101. package/src/api-wrapper/error-reporting.ts +7 -1
  102. package/src/api-wrapper/errors.ts +117 -2
  103. package/src/api-wrapper/headers.ts +19 -1
  104. package/src/api-wrapper/index.ts +5 -0
  105. package/src/api-wrapper/otel.ts +139 -0
  106. package/src/api-wrapper/paginate.ts +28 -0
  107. package/src/api-wrapper/redaction.ts +43 -0
  108. package/src/api-wrapper/shared.ts +21 -2
  109. package/src/api-wrapper/testing.ts +48 -0
  110. package/src/api-wrapper/transport.ts +68 -74
  111. package/src/api-wrapper/types.ts +186 -18
  112. package/src/array/index.ts +195 -42
  113. package/src/cache/createCache.ts +157 -0
  114. package/src/cache/index.ts +235 -83
  115. package/src/color/contrast.ts +163 -0
  116. package/src/color/index.ts +22 -0
  117. package/src/{math → color}/metricsSeriesColors.ts +47 -49
  118. package/src/cool-console-log/index.ts +128 -41
  119. package/src/crypto/encoding.ts +83 -0
  120. package/src/crypto/hash.ts +170 -0
  121. package/src/crypto/index.ts +26 -0
  122. package/src/crypto/sealer.ts +168 -0
  123. package/src/crypto/webhook.ts +86 -0
  124. package/src/date/index.ts +109 -34
  125. package/src/emitter/index.ts +82 -0
  126. package/src/error/index.ts +63 -0
  127. package/src/format/index.ts +389 -0
  128. package/src/function/index.ts +201 -0
  129. package/src/guard/index.ts +80 -0
  130. package/src/i18n/index.ts +125 -0
  131. package/src/index.ts +11 -7
  132. package/src/jwt/index.ts +199 -125
  133. package/src/math/index.ts +5 -10
  134. package/src/math/linearRegressionTrend.ts +33 -8
  135. package/src/math/scale.ts +17 -0
  136. package/src/object/index.ts +99 -0
  137. package/src/p-queue/index.ts +2 -0
  138. package/src/promise/index.ts +304 -9
  139. package/src/secrets/core.ts +348 -0
  140. package/src/secrets/deadline.ts +69 -0
  141. package/src/secrets/environment.ts +69 -0
  142. package/src/secrets/errors.ts +29 -0
  143. package/src/secrets/index.ts +79 -210
  144. package/src/secrets/infisical-cli.ts +99 -0
  145. package/src/secrets/infisical-config.ts +70 -0
  146. package/src/secrets/infisical-session.ts +42 -0
  147. package/src/secrets/infisical.ts +214 -66
  148. package/src/secrets/loaders.ts +24 -48
  149. package/src/secrets/vite.ts +44 -15
  150. package/src/secrets/worker-loader.ts +56 -0
  151. package/src/secrets/worker.ts +4 -0
  152. package/src/sitemap/index.ts +293 -130
  153. package/src/state/index.ts +133 -81
  154. package/src/storage/index.ts +187 -0
  155. package/src/string/index.ts +261 -23
  156. package/src/typedswitch/index.ts +134 -103
  157. package/src/types/index.ts +14 -0
  158. package/src/url/index.ts +146 -1
  159. package/src/zod/index.ts +105 -9
  160. package/src/zod/introspect.ts +103 -0
  161. package/src/zod/jsonToZodSchemaMapper.ts +44 -21
  162. package/src/zod/zodMinMaxFilter.ts +37 -13
  163. package/src/zod/zodToTypeString.ts +184 -181
  164. package/dist/api-wrapper/index.js +0 -250
  165. package/dist/gzip/index.d.ts +0 -2
  166. package/dist/gzip/index.js +0 -16
  167. package/dist/index.js +0 -1823
  168. package/dist/jwt/index.js +0 -19
  169. package/dist/sitemap/index.js +0 -552
  170. package/dist/src/gzip/index.js +0 -16
  171. package/dist/src/secrets/infisical.js +0 -1194
  172. package/dist/zod/index.js +0 -210
@@ -1,22 +1,68 @@
1
1
  export interface ArrayDifference<TCurrent, TNext> {
2
2
  new: TNext[];
3
+ /** Items present in both arrays (`update` + `unchanged`). */
3
4
  upsert: TNext[];
4
5
  delete: TCurrent[];
5
6
  }
7
+ export interface ArrayDifferenceWithChanges<TCurrent, TNext> extends ArrayDifference<TCurrent, TNext> {
8
+ /** Matched items where `isEqual` returned false. */
9
+ update: TNext[];
10
+ /** Matched items where `isEqual` returned true. */
11
+ unchanged: TNext[];
12
+ }
6
13
  type PropertyKeyValue = string | number | symbol;
7
14
  type NonNullableValue<T> = T extends null | undefined ? never : T;
8
15
  type ItemsMatchingKeyValue<T, TKey extends keyof T, TValue> = [
9
16
  Extract<T, Record<TKey, TValue>>
10
17
  ] extends [never] ? T : Extract<T, Record<TKey, TValue>>;
11
18
  export declare function chunkArray<T>(array: readonly T[], chunkSize: number): T[][];
12
- export declare function findArrayDifferenceByKey<TCurrent, TNext, TKey extends keyof TCurrent & keyof TNext>(current: readonly TCurrent[], next: readonly TNext[], key: TKey): ArrayDifference<TCurrent, TNext>;
19
+ type DifferenceKey<TCurrent, TNext> = (keyof TCurrent & keyof TNext) | ((item: TCurrent | TNext) => unknown);
20
+ /**
21
+ * Splits `next` into items that are new or also in `current` (`upsert`), and
22
+ * lists the `current` items missing from `next` (`delete`). Items match by a
23
+ * shared key name or a key function; key values are compared as strings.
24
+ * Pass `isEqual` to also split `upsert` into `update` and `unchanged`.
25
+ */
26
+ export declare function findArrayDifferenceByKey<TCurrent, TNext>(current: readonly TCurrent[], next: readonly TNext[], key: DifferenceKey<TCurrent, TNext>, options: {
27
+ isEqual: (current: TCurrent, next: TNext) => boolean;
28
+ }): ArrayDifferenceWithChanges<TCurrent, TNext>;
29
+ export declare function findArrayDifferenceByKey<TCurrent, TNext>(current: readonly TCurrent[], next: readonly TNext[], key: DifferenceKey<TCurrent, TNext>, options?: {
30
+ isEqual?: (current: TCurrent, next: TNext) => boolean;
31
+ }): ArrayDifference<TCurrent, TNext>;
13
32
  export declare function getUniqueValues<T>(array: readonly T[]): T[];
14
33
  export declare function compact<T>(array: readonly T[]): NonNullableValue<T>[];
15
- export declare function getUniqueValuesByKey<T, TKey extends keyof T>(array: readonly T[], key: TKey): T[];
16
- export declare function groupBy<T, TKey extends keyof T, TGroupKey extends Extract<T[TKey], PropertyKeyValue>>(array: readonly T[], key: TKey): Record<TGroupKey, T[]>;
17
- export declare function groupBy<T, TGroupKey extends PropertyKeyValue>(array: readonly T[], keyFn: (item: T) => TGroupKey): Record<TGroupKey, T[]>;
34
+ export declare function getUniqueValuesByKey<T>(array: readonly T[], keyOrFn: keyof T | ((item: T) => unknown)): T[];
35
+ export declare function groupBy<T, TKey extends keyof T, TGroupKey extends Extract<T[TKey], PropertyKeyValue>>(array: readonly T[], key: TKey): Partial<Record<TGroupKey, T[]>>;
36
+ export declare function groupBy<T, TGroupKey extends PropertyKeyValue>(array: readonly T[], keyFn: (item: T) => TGroupKey): Partial<Record<TGroupKey, T[]>>;
37
+ export declare function countBy<T, TKey extends keyof T, TGroupKey extends Extract<T[TKey], PropertyKeyValue>>(array: readonly T[], key: TKey): Partial<Record<TGroupKey, number>>;
38
+ export declare function countBy<T, TGroupKey extends PropertyKeyValue>(array: readonly T[], keyFn: (item: T) => TGroupKey): Partial<Record<TGroupKey, number>>;
18
39
  export declare function keyBy<T, TKey extends keyof T, TRecordKey extends Extract<T[TKey], PropertyKeyValue>>(array: readonly T[], key: TKey): Record<TRecordKey, T>;
19
40
  export declare function keyBy<T, TRecordKey extends PropertyKeyValue>(array: readonly T[], keyFn: (item: T) => TRecordKey): Record<TRecordKey, T>;
20
41
  export declare function partition<T>(array: readonly T[], predicate: (item: T, index: number) => boolean): [matched: T[], unmatched: T[]];
21
42
  export declare function pickItemsInArray<T, TKey extends keyof T, const TValue extends readonly T[TKey][]>(array: readonly T[], key: TKey, values: TValue): ItemsMatchingKeyValue<T, TKey, TValue[number]>[];
43
+ /** Wraps a single value in an array; `null`/`undefined` become `[]`. Arrays are copied. */
44
+ export declare function toArray<T>(value: T | readonly T[] | null | undefined): T[];
45
+ type NumericKey<T> = {
46
+ [K in keyof T]: T[K] extends number | null | undefined ? K : never;
47
+ }[keyof T];
48
+ /** Sums a numeric property or callback result; nullish values count as 0. */
49
+ export declare function sumBy<T>(array: readonly T[], fn: (item: T) => number | null | undefined): number;
50
+ export declare function sumBy<T>(array: readonly T[], key: NumericKey<T>): number;
51
+ type SortSelector<T> = keyof T | ((item: T) => unknown);
52
+ export type SortCriterion<T> = SortSelector<T> | {
53
+ by: SortSelector<T>;
54
+ order: 'asc' | 'desc';
55
+ };
56
+ /**
57
+ * Returns a sorted copy. Each criterion is a key, a callback, or
58
+ * `{ by, order }`; later criteria break ties. The sort is stable, and
59
+ * `null`/`undefined` values always go last. Without criteria the items
60
+ * themselves are compared.
61
+ */
62
+ export declare function sortBy<T>(array: readonly T[], ...criteria: SortCriterion<T>[]): T[];
63
+ /**
64
+ * Numbers from `start` up to (not including) `end`. With one argument it
65
+ * counts from 0. `step` defaults to 1, or -1 when `end < start`.
66
+ */
67
+ export declare function range(start: number, end?: number, step?: number): number[];
22
68
  export {};
@@ -0,0 +1,31 @@
1
+ export type CacheSetOptions<T = unknown> = {
2
+ /** Lifetime of the entry; a function derives it from the value (e.g. a token's `expires_in`). */
3
+ ttlMs?: number | ((value: T) => number);
4
+ /** getOrSet recomputes this long before expiry; get still returns the entry until it expires. */
5
+ staleMarginMs?: number;
6
+ };
7
+ export type CreateCacheOptions = {
8
+ /** TTL used when a call doesn't pass `ttlMs` (default: no expiry). */
9
+ defaultTtlMs?: number;
10
+ /** `staleMarginMs` used when a call doesn't pass one (default: 0). */
11
+ staleMarginMs?: number;
12
+ /** Evicts the least recently used entry beyond this size (default: unbounded). */
13
+ maxEntries?: number;
14
+ /** Clock, for tests (default: `Date.now`). */
15
+ now?: () => number;
16
+ };
17
+ export type KeyValueCache = {
18
+ /**
19
+ * Get-or-compute. Concurrent callers share one in-flight call; rejections
20
+ * aren't cached (a stale value stays readable until it expires).
21
+ */
22
+ getOrSet<T>(key: string, fn: () => Promise<T> | T, options?: CacheSetOptions<T>): Promise<T>;
23
+ get<T>(key: string): T | undefined;
24
+ has(key: string): boolean;
25
+ set<T>(key: string, value: T, options?: CacheSetOptions<T>): void;
26
+ delete(key: string): void;
27
+ clear(): void;
28
+ readonly size: number;
29
+ };
30
+ /** A small in-memory key/value cache with TTL, LRU bound and in-flight dedupe. */
31
+ export declare function createCache(options?: CreateCacheOptions): KeyValueCache;
@@ -1,7 +1,33 @@
1
1
  type AnyFunction = (...args: never[]) => unknown;
2
- type CachedFunction<T extends AnyFunction> = {
2
+ type CachedFunction<T extends AnyFunction, TPeek = Awaited<ReturnType<T>> | undefined> = {
3
3
  (...args: Parameters<T>): ReturnType<T>;
4
4
  clearCache: () => void;
5
+ /** Drops the entry (and any in-flight call) for these arguments. */
6
+ invalidate: (...args: Parameters<T>) => void;
7
+ /**
8
+ * What a call would return from the cache right now (including a stale
9
+ * value with refreshInBackground), without calling fn, starting a refresh
10
+ * or counting as a use for maxEntries. `undefined` on a miss. With a
11
+ * `store` this reads the store and returns a Promise.
12
+ */
13
+ peek: (...args: Parameters<T>) => TPeek;
14
+ };
15
+ export type CacheStoreEntry = {
16
+ value: unknown;
17
+ timestamp: number;
18
+ };
19
+ /**
20
+ * Contract for external cache stores (e.g. a Redis adapter).
21
+ * Serialization is the adapter's responsibility; this library never imports
22
+ * the underlying client.
23
+ */
24
+ export type CacheStore = {
25
+ get: (key: string) => Promise<CacheStoreEntry | undefined>;
26
+ /** `ttlMs` maps to the store's native expiry (e.g. Redis `PX`). */
27
+ set: (key: string, entry: CacheStoreEntry, ttlMs: number) => Promise<void>;
28
+ delete: (key: string) => Promise<void>;
29
+ /** Optional; called by clearCache when defined. */
30
+ clear?: () => Promise<void>;
5
31
  };
6
32
  export type CacheFunctionOptions<T extends AnyFunction> = {
7
33
  name: string;
@@ -14,6 +40,23 @@ export type CacheFunctionOptions<T extends AnyFunction> = {
14
40
  onBackgroundRefreshError?: (error: unknown, context: {
15
41
  name: string;
16
42
  }) => void;
43
+ /**
44
+ * External cache store (e.g. a Redis adapter). Only supported for async
45
+ * functions; when omitted the built-in in-memory cache is used.
46
+ */
47
+ store?: ReturnType<T> extends Promise<unknown> ? CacheStore : never;
48
+ /** Called when a store get/set/delete/clear fails (default: silent). */
49
+ onStoreError?: (error: unknown, context: {
50
+ name: string;
51
+ }) => void;
52
+ /**
53
+ * Evicts the least recently used entry beyond this many entries (default:
54
+ * unbounded). Applies to the in-memory cache only.
55
+ */
56
+ maxEntries?: number;
17
57
  };
58
+ export declare function cacheFunction<T extends AnyFunction>(options: CacheFunctionOptions<T> & {
59
+ store: CacheStore;
60
+ }): CachedFunction<T, Promise<Awaited<ReturnType<T>> | undefined>>;
18
61
  export declare function cacheFunction<T extends AnyFunction>(options: CacheFunctionOptions<T>): CachedFunction<T>;
19
- export {};
62
+ export * from './createCache';
@@ -0,0 +1,40 @@
1
+ /** An sRGB color: `r`/`g`/`b` in 0..255, `a` in 0..1. */
2
+ export type Rgba = {
3
+ r: number;
4
+ g: number;
5
+ b: number;
6
+ a: number;
7
+ };
8
+ /**
9
+ * Splits `rgb(…)`/`rgba(…)`/`hsl(…)`/`hsla(…)` (comma, space or slash syntax)
10
+ * into its name and 3-4 raw arguments. Internal; shared with `withAlpha`.
11
+ */
12
+ export declare function colorFunctionArgs(color: string): {
13
+ name: 'rgb' | 'hsl';
14
+ args: string[];
15
+ } | undefined;
16
+ /**
17
+ * Parse `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()`/`rgba()` and
18
+ * `hsl()`/`hsla()` (comma or space/slash syntax, percentages allowed).
19
+ * Returns `undefined` for anything else (named colors, CSS variables, …).
20
+ */
21
+ export declare function parseColor(color: string): Rgba | undefined;
22
+ /** `#rrggbb` (or `#rrggbbaa` with `alpha: true`). Throws on unparseable strings. */
23
+ export declare function toHex(color: Rgba | string, options?: {
24
+ alpha?: boolean;
25
+ }): string;
26
+ /** `rgb(r, g, b)`, or `rgba(r, g, b, a)` when translucent. Throws on unparseable strings. */
27
+ export declare function toRgbString(color: Rgba | string): string;
28
+ /** WCAG 2.x relative luminance (0 = black, 1 = white); alpha is ignored. */
29
+ export declare function relativeLuminance(color: Rgba | string): number;
30
+ /** WCAG contrast ratio between two colors, from 1 to 21. */
31
+ export declare function contrastRatio(a: Rgba | string, b: Rgba | string): number;
32
+ /** Whichever of `light`/`dark` contrasts more with `background` (ties go to `light`). */
33
+ export declare function contrastText(background: Rgba | string, options?: {
34
+ light?: string;
35
+ dark?: string;
36
+ }): string;
37
+ /** Blend `b` into `a` by `weight` (0 = `a`, 1 = `b`) as hex; alpha kept only when translucent. */
38
+ export declare function mixColors(a: Rgba | string, b: Rgba | string, weight: number): string;
39
+ /** Darken toward black (`amount` < 0) or lighten toward white (> 0); `amount` in -1..1. Returns hex. */
40
+ export declare function shade(color: Rgba | string, amount: number): string;
@@ -0,0 +1,2 @@
1
+ export { contrastRatio, contrastText, mixColors, parseColor, type Rgba, relativeLuminance, shade, toHex, toRgbString } from './contrast';
2
+ export { createResolveColorsByKeys, getHeatmapCellStyle, hashStringToColorIndex, METRICS_SERIES_PALETTE_SIZE, pickFromPalette, type ResolveColorsByKeysResult, resolveSeriesColorMap, seriesColorKey, withAlpha } from './metricsSeriesColors';
@@ -1,13 +1,15 @@
1
1
  /**
2
- * Shared metrics chart series color assignment for web (CSS tokens) and Expo (hex palette).
3
- * Same hash + collision strategy so labels map to the same slot index everywhere.
2
+ * Stable series color assignment: the same hash + collision strategy maps a
3
+ * label to the same palette slot index on every platform.
4
4
  */
5
- /** Matches PrimeVue semantic palette slot count (cyan…blue) and Expo SERIES_COLORS length */
5
+ /** Default palette slot count. */
6
6
  export declare const METRICS_SERIES_PALETTE_SIZE = 10;
7
7
  /**
8
8
  * FNV-1a 32-bit hash — stable palette index for a string key.
9
9
  */
10
- export declare function hashStringToColorIndex(str: string): number;
10
+ export declare function hashStringToColorIndex(str: string, paletteSize?: number): number;
11
+ /** Stable pick from `palette` for `seed` (same seed → same item). Throws on an empty palette. */
12
+ export declare function pickFromPalette<T>(seed: string, palette: readonly T[]): T;
11
13
  /**
12
14
  * Prefer human-visible label so charts and tables agree on the same color key.
13
15
  */
@@ -15,7 +17,7 @@ export declare function seriesColorKey(label: string | undefined, key: string):
15
17
  /**
16
18
  * Map each distinct key to a palette index (hash preference, then max-min circular spacing).
17
19
  */
18
- export declare function resolveSeriesColorMap(keys: string[]): Map<string, number>;
20
+ export declare function resolveSeriesColorMap(keys: string[], paletteSize?: number): Map<string, number>;
19
21
  export type ResolveColorsByKeysResult = {
20
22
  getColor: (key: string) => string;
21
23
  getHoverColor: (key: string) => string;
@@ -23,9 +25,14 @@ export type ResolveColorsByKeysResult = {
23
25
  /**
24
26
  * Resolve stable colors per key using palette index → RGB/CSS from the caller.
25
27
  */
26
- export declare function createResolveColorsByKeys(keys: string[], getSeriesColor: (index: number) => string, getSeriesHoverColor: (index: number) => string): ResolveColorsByKeysResult;
28
+ export declare function createResolveColorsByKeys(keys: string[], getSeriesColor: (index: number) => string, getSeriesHoverColor: (index: number) => string, paletteSize?: number): ResolveColorsByKeysResult;
27
29
  /**
28
- * Add alpha to hex, rgb(), or rgba() strings.
30
+ * Apply `alpha` (0..1) to a color, replacing any existing alpha.
31
+ *
32
+ * - `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa` → `rgba(r, g, b, a)` (works in React Native)
33
+ * - `rgb()`/`rgba()`/`hsl()`/`hsla()`, comma or space syntax → `rgba(…)` / `hsla(…)`
34
+ * - anything else (CSS variables, named colors, `oklch()`, …) →
35
+ * `color-mix(in srgb, <color> <alpha*100>%, transparent)` (web only)
29
36
  */
30
37
  export declare function withAlpha(color: string, alpha: number): string;
31
38
  /**
@@ -31,10 +31,16 @@ export declare const terminalColors: {
31
31
  readonly bgWhite: "\u001B[47m";
32
32
  };
33
33
  export type TerminalColor = keyof typeof terminalColors;
34
+ /**
35
+ * Whether stdout should get ANSI colors: `NO_COLOR` disables, `FORCE_COLOR`
36
+ * (other than `'0'`) enables, otherwise only when stdout is a TTY.
37
+ * Always false where `process` doesn't exist (browsers, Workers).
38
+ */
39
+ export declare function supportsColor(): boolean;
34
40
  /** Log levels supported by the logger */
35
41
  export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
36
- /** Attributes that can be passed to log methods */
37
- export type LogAttributes = Record<string, string | number | boolean>;
42
+ /** Attributes that can be passed to log methods. Objects are rendered as compact JSON. */
43
+ export type LogAttributes = Record<string, unknown>;
38
44
  /** Log method signature */
39
45
  export type LogMethod = (message: string, attributes?: LogAttributes) => void;
40
46
  /** Logger interface with colored output */
@@ -43,14 +49,18 @@ export type CoolLogger = {
43
49
  info: LogMethod;
44
50
  warn: LogMethod;
45
51
  error: LogMethod;
52
+ /** Returns a logger that adds `attributes` to every entry (call-site attributes win). */
53
+ child: (attributes: LogAttributes) => CoolLogger;
46
54
  };
47
55
  /**
48
56
  * Options for creating a cool logger
49
57
  */
50
58
  export type CoolLoggerOptions = {
51
- /** Only use colors in development (default: true) */
59
+ /** Only use colors in development (default: true). Colors also require `supportsColor()`. */
52
60
  colorsInDevOnly?: boolean;
53
- /** Callback for each log entry (e.g., to add to OpenTelemetry span) */
61
+ /** Lowest level that is printed (default: 'debug' in dev, 'info' in production) */
62
+ minLevel?: LogLevel;
63
+ /** Callback for each log entry, before level filtering (e.g., to add to OpenTelemetry span) */
54
64
  onLog?: (level: LogLevel, message: string, attributes?: LogAttributes) => void;
55
65
  };
56
66
  /**
@@ -62,6 +72,7 @@ export type CoolLoggerOptions = {
62
72
  * logger.info('User logged in', { userId: '123' })
63
73
  * logger.warn('Rate limit approaching', { remaining: 10 })
64
74
  * logger.error('Payment failed', { reason: 'insufficient_funds' })
75
+ * const requestLog = logger.child({ requestId: 'abc' })
65
76
  * ```
66
77
  */
67
78
  export declare function createCoolLogger(options?: CoolLoggerOptions): CoolLogger;
@@ -85,7 +96,25 @@ export declare function logOperationSummary(operationName: string, durationMs: n
85
96
  error?: Error | unknown;
86
97
  }): void;
87
98
  /**
88
- * Helper to colorize text for console output
99
+ * Starts timing an operation. Call the returned `done` to log it via `logOperationSummary`.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const done = startOperation('user.create')
104
+ * try {
105
+ * await doSomething()
106
+ * done()
107
+ * } catch (error) {
108
+ * done(false, error)
109
+ * }
110
+ * ```
111
+ */
112
+ export declare function startOperation(operationName: string, options?: {
113
+ devOnly?: boolean;
114
+ }): (success?: boolean, error?: unknown) => void;
115
+ /**
116
+ * Helper to colorize text for console output. Returns `text` unchanged in
117
+ * production or when colors aren't supported (see `supportsColor`).
89
118
  *
90
119
  * @example
91
120
  * ```ts
@@ -0,0 +1,18 @@
1
+ export type BytesLike = Uint8Array | ArrayBuffer;
2
+ export declare function utf8Encode(value: string): Uint8Array<ArrayBuffer>;
3
+ /** Invalid UTF-8 sequences become U+FFFD (same as `TextDecoder` / `Buffer#toString`). */
4
+ export declare function utf8Decode(bytes: BytesLike): string;
5
+ /** Standard base64 with `=` padding. */
6
+ export declare function encodeBase64(bytes: BytesLike): string;
7
+ /** Decodes standard base64 (padding optional). Throws `TypeError` on invalid input. */
8
+ export declare function decodeBase64(value: string): Uint8Array<ArrayBuffer>;
9
+ /** URL-safe base64 without padding (RFC 4648 §5). */
10
+ export declare function encodeBase64Url(bytes: BytesLike): string;
11
+ /** Decodes URL-safe base64 (padding optional). Throws `TypeError` on invalid input. */
12
+ export declare function decodeBase64Url(value: string): Uint8Array<ArrayBuffer>;
13
+ /** Lowercase hex. */
14
+ export declare function bytesToHex(bytes: BytesLike): string;
15
+ /** Decodes hex (either case). Throws `TypeError` on odd length or non-hex characters. */
16
+ export declare function hexToBytes(value: string): Uint8Array<ArrayBuffer>;
17
+ /** UTF-8 for strings; a copy for bytes, so Web Crypto always gets an `ArrayBuffer`-backed view. */
18
+ export declare function toBytes(data: string | BytesLike): Uint8Array<ArrayBuffer>;
@@ -0,0 +1,33 @@
1
+ import { type BytesLike } from './encoding';
2
+ export type HmacAlgorithm = 'SHA-256' | 'SHA-384' | 'SHA-512';
3
+ export type SignatureEncoding = 'hex' | 'base64' | 'base64url';
4
+ export type HmacSignOptions = {
5
+ /** Default: `'SHA-256'` */
6
+ algorithm?: HmacAlgorithm;
7
+ /** Default: `'hex'` */
8
+ encoding?: SignatureEncoding;
9
+ };
10
+ export type HmacVerifyOptions = HmacSignOptions & {
11
+ /** Required prefix stripped before decoding, e.g. `'sha256='`. Missing prefix → `false`. */
12
+ prefix?: string;
13
+ };
14
+ /**
15
+ * Constant-time equality for strings (compared as UTF-8) or bytes. Runs over the
16
+ * longer input, so timing reveals at most the lengths, never where they differ.
17
+ */
18
+ export declare function safeEqual(a: string | Uint8Array, b: string | Uint8Array): boolean;
19
+ /** Cryptographically secure random bytes. */
20
+ export declare function randomBytes(byteLength: number): Uint8Array<ArrayBuffer>;
21
+ /** Random base64url token. 32 bytes → 43 characters, 256 bits of entropy. */
22
+ export declare function randomToken(byteLength?: number): string;
23
+ export declare function sha256(data: string | BytesLike): Promise<Uint8Array>;
24
+ export declare function sha256Hex(data: string | BytesLike): Promise<string>;
25
+ export declare function hmacSign(secret: string | BytesLike, data: string | BytesLike, options?: HmacSignOptions): Promise<string>;
26
+ /**
27
+ * Checks `signature` against the HMAC of `data` using Web Crypto's `verify`
28
+ * (constant-time). Malformed or wrongly-prefixed signatures return `false`;
29
+ * configuration problems (empty secret, unknown algorithm) throw.
30
+ */
31
+ export declare function verifyHmacSignature(secret: string | BytesLike, data: string | BytesLike, signature: string, options?: HmacVerifyOptions): Promise<boolean>;
32
+ /** @internal */
33
+ export declare function getSubtleCrypto(): SubtleCrypto;
@@ -0,0 +1,4 @@
1
+ export { type BytesLike, bytesToHex, decodeBase64, decodeBase64Url, encodeBase64, encodeBase64Url, hexToBytes, utf8Decode, utf8Encode } from './encoding';
2
+ export { type HmacAlgorithm, type HmacSignOptions, type HmacVerifyOptions, hmacSign, randomBytes, randomToken, type SignatureEncoding, safeEqual, sha256, sha256Hex, verifyHmacSignature } from './hash';
3
+ export * from './sealer';
4
+ export * from './webhook';
@@ -0,0 +1,25 @@
1
+ import { type BytesLike } from './encoding';
2
+ export type SealerOptions = {
3
+ /** High-entropy secret (e.g. `APP_SECRET`). The AES key is derived from it with HKDF-SHA-256. */
4
+ secret: string | BytesLike;
5
+ /** HKDF salt. Default: empty */
6
+ salt?: string;
7
+ /** HKDF info; use a distinct value per purpose. Default: `'hanzio.sealer.aes-256-gcm.v1'` */
8
+ info?: string;
9
+ };
10
+ export type Sealer = {
11
+ seal(plaintext: string): Promise<string>;
12
+ open(sealed: string): Promise<string>;
13
+ sealJson(value: unknown): Promise<string>;
14
+ openJson<T = unknown>(sealed: string): Promise<T>;
15
+ };
16
+ export type SealerErrorCode = 'malformed' | 'unsupported_version' | 'decrypt_failed';
17
+ export declare class SealerError extends Error {
18
+ readonly code: SealerErrorCode;
19
+ constructor(code: SealerErrorCode);
20
+ }
21
+ /**
22
+ * AES-256-GCM sealing with a random 96-bit IV per value. Envelope:
23
+ * `enc:v1:<b64url(iv)>.<b64url(tag)>.<b64url(ciphertext)>`.
24
+ */
25
+ export declare function createSealer(options: SealerOptions): Sealer;
@@ -0,0 +1,20 @@
1
+ export type VerifySvixSignatureOptions = {
2
+ /** Signing secret, `whsec_<base64>` (the prefix is optional). */
3
+ secret: string;
4
+ /** The raw request body, exactly as received. */
5
+ payload: string;
6
+ headers: Headers | Record<string, string | undefined>;
7
+ /** Max age/skew of the timestamp header, in seconds. Default: 300 */
8
+ toleranceSec?: number;
9
+ /** Current time in milliseconds. Default: `Date.now()` */
10
+ now?: number;
11
+ };
12
+ /**
13
+ * Verifies a Svix / Standard Webhooks signature (Resend, Clerk, …): base64
14
+ * HMAC-SHA256 over `${id}.${timestamp}.${payload}`, keyed by the base64 body
15
+ * of the `whsec_` secret. Reads `svix-*` headers, falling back to `webhook-*`.
16
+ * Every `v1,` entry in the space-separated signature header is checked, so
17
+ * secret rotation works. Returns `false` for missing/stale headers or a bad
18
+ * signature; throws on an invalid secret.
19
+ */
20
+ export declare function verifySvixSignature(options: VerifySvixSignatureOptions): Promise<boolean>;
@@ -1,11 +1,55 @@
1
+ export type PeriodType = 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'QUARTERLY' | 'YEARLY';
2
+ /**
3
+ * Add `n` periods to `date`, keeping its time of day (wall-clock time in local
4
+ * mode, so DST shifts don't move it). Month-based adds clamp the day of month:
5
+ * Jan 31 + 1 month → Feb 28 (or 29).
6
+ */
7
+ export declare function addPeriod(date: Date, type: PeriodType, n: number, options?: {
8
+ utc?: boolean;
9
+ }): Date;
10
+ /**
11
+ * Calendar period containing `referenceDate`, shifted by `offset` periods
12
+ * (`-1` = previous, `1` = next).
13
+ *
14
+ * `start` is midnight on the first day and `end` is midnight at the **start**
15
+ * of the last day, so the range is inclusive for date-only comparisons. For
16
+ * timestamps use `timestamp < addPeriod(end, 'DAILY', 1)` as the exclusive bound.
17
+ *
18
+ * Uses the local timezone unless `utc: true`. Weeks start on Monday (ISO)
19
+ * unless `weekStartsOn: 0` (Sunday).
20
+ */
21
+ export declare function getPeriod(type: PeriodType, referenceDate?: Date, offset?: number, options?: {
22
+ utc?: boolean;
23
+ weekStartsOn?: 0 | 1;
24
+ }): {
25
+ start: Date;
26
+ end: Date;
27
+ };
1
28
  /**
2
29
  * Calculate the previous period date range for a given period type.
30
+ * Same as `getPeriod(periodType, referenceDate, -1)`.
3
31
  */
4
- export declare function getPreviousReportPeriod(periodType: 'MONTHLY' | 'QUARTERLY' | 'YEARLY', referenceDate?: Date): {
32
+ export declare function getPreviousReportPeriod(periodType: PeriodType, referenceDate?: Date): {
5
33
  start: Date;
6
34
  end: Date;
7
35
  };
36
+ export type FormatPeriodLabelOptions = {
37
+ includeDay?: boolean;
38
+ /** Default `'en-GB'`. */
39
+ locale?: string;
40
+ /** Joins the two formatted dates. Default `' — '`. Ignored when `collapse` is set. */
41
+ separator?: string;
42
+ /**
43
+ * Use `Intl.DateTimeFormat#formatRange`, which drops repeated parts and uses
44
+ * the locale's range dash: "January – March 2025" instead of
45
+ * "January 2025 — March 2025".
46
+ */
47
+ collapse?: boolean;
48
+ /** IANA time zone, e.g. `'UTC'` for periods from `getPeriod(..., { utc: true })`. */
49
+ timeZone?: string;
50
+ };
8
51
  /**
9
52
  * Format a date range as a human-readable period label.
53
+ * The third argument is either `includeDay` or an options object.
10
54
  */
11
- export declare function formatPeriodLabel(start: Date | string, end: Date | string, includeDay?: boolean): string;
55
+ export declare function formatPeriodLabel(start: Date | string, end: Date | string, options?: boolean | FormatPeriodLabelOptions): string;
@@ -0,0 +1,21 @@
1
+ export type Listener<P> = (payload: P) => void;
2
+ /** Payload is optional for events typed `void` or `undefined`. */
3
+ export type EmitArgs<P> = undefined extends P ? [payload?: P] : [payload: P];
4
+ export type Emitter<Events extends Record<string, unknown>> = {
5
+ /** Adds a listener; returns an unsubscribe function. */
6
+ on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): () => void;
7
+ /** Like `on`, but removed before its first call. */
8
+ once<K extends keyof Events>(event: K, listener: Listener<Events[K]>): () => void;
9
+ off<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void;
10
+ /**
11
+ * Calls every listener, even if some throw, then rethrows the error (or an
12
+ * `AggregateError` if several threw). Listeners added during an emit wait
13
+ * for the next one; listeners removed during an emit are skipped.
14
+ */
15
+ emit<K extends keyof Events>(event: K, ...args: EmitArgs<Events[K]>): void;
16
+ /** Removes all listeners for `event`, or for every event. */
17
+ clear(event?: keyof Events): void;
18
+ listenerCount(event: keyof Events): number;
19
+ };
20
+ /** Typed event emitter. Adding the same listener twice keeps one entry. */
21
+ export declare function createEmitter<Events extends Record<string, unknown>>(): Emitter<Events>;
@@ -0,0 +1,12 @@
1
+ /** Thrown by `must` and `invariant` when no custom error is given. */
2
+ export declare class InvariantError extends Error {
3
+ name: string;
4
+ }
5
+ /** An error's message: Error → `message`, string → itself, `{ message: string }` → that, else `String(error)`. */
6
+ export declare function getErrorMessage(error: unknown, fallback?: string): string;
7
+ /** Returns Errors unchanged, wraps anything else; a `prefix` always wraps as `${prefix}: ${message}` with `cause`. */
8
+ export declare function toError(error: unknown, prefix?: string): Error;
9
+ /** Returns `value`, throwing when it is null or undefined. Pass a function to throw your own error. */
10
+ export declare function must<T>(value: T, message?: string | (() => Error)): NonNullable<T>;
11
+ /** Throws when `condition` is falsy. Pass a function to throw your own error. */
12
+ export declare function invariant(condition: unknown, message?: string | (() => Error)): asserts condition;
@@ -0,0 +1,102 @@
1
+ type DateInput = Date | string | number;
2
+ /** Cached `Intl.NumberFormat`, one instance per (locale, options). */
3
+ export declare const getNumberFormat: (locale: string, options?: Intl.NumberFormatOptions | undefined) => Intl.NumberFormat;
4
+ /** Cached `Intl.DateTimeFormat`, one instance per (locale, options). */
5
+ export declare const getDateTimeFormat: (locale: string, options?: Intl.DateTimeFormatOptions | undefined) => Intl.DateTimeFormat;
6
+ /** Cached `Intl.RelativeTimeFormat`, one instance per (locale, options). */
7
+ export declare const getRelativeTimeFormat: (locale: string, options?: Intl.RelativeTimeFormatOptions | undefined) => Intl.RelativeTimeFormat;
8
+ /** Cached `Intl.ListFormat`, one instance per (locale, options). */
9
+ export declare const getListFormat: (locale: string, options?: Intl.ListFormatOptions | undefined) => Intl.ListFormat;
10
+ /** Cached `Intl.PluralRules`, one instance per (locale, options). */
11
+ export declare const getPluralRules: (locale: string, options?: Intl.PluralRulesOptions | undefined) => Intl.PluralRules;
12
+ export declare function formatNumber(value: number, locale: string, options?: Intl.NumberFormatOptions): string;
13
+ /** A fraction (`0.25`) as a percentage (`"25%"`). */
14
+ export declare function formatPercent(value: number, locale: string, options?: Intl.NumberFormatOptions): string;
15
+ /**
16
+ * An amount in whole currency units (`12.5`, not cents). Pass
17
+ * `{ maximumFractionDigits: 0 }` for whole-unit prices.
18
+ */
19
+ export declare function formatCurrency(amount: number, currency: string, locale: string, options?: Intl.NumberFormatOptions): string;
20
+ /** Short compact notation: `1234` -> `"1.2K"` (en-US), `"1,2 t"` (da-DK). */
21
+ export declare function formatCompactNumber(value: number, locale: string): string;
22
+ export type FormatBytesOptions = {
23
+ /** Default `'en-US'`. */
24
+ locale?: string;
25
+ /** Fraction digits (trailing zeros dropped). Default 0 for KB, 1 above. */
26
+ decimals?: number;
27
+ /** 1 KB = 1024 B when true (default), 1000 B when false. */
28
+ binary?: boolean;
29
+ };
30
+ /**
31
+ * Human-readable file size: `"512 B"`, `"12 KB"`, `"3.4 MB"`. Unit labels are
32
+ * not localized; the number is.
33
+ */
34
+ export declare function formatBytes(bytes: number, options?: FormatBytesOptions): string;
35
+ export type DurationParts = {
36
+ days: number;
37
+ hours: number;
38
+ minutes: number;
39
+ seconds: number;
40
+ milliseconds: number;
41
+ };
42
+ /** Break milliseconds into whole units. Negative or non-finite input is 0. */
43
+ export declare function splitDuration(ms: number): DurationParts;
44
+ /** Stopwatch style: `"1:05:09"` from an hour up, `"5:09"` below. */
45
+ export declare function formatClockDuration(ms: number): string;
46
+ export type FormatDurationOptions = {
47
+ /** `'long'` "1 hour, 5 minutes", `'short'` "1 hr, 5 min" (default), `'narrow'` "1h 5m". */
48
+ style?: 'long' | 'short' | 'narrow';
49
+ /** Adjacent units shown, starting at the largest non-zero one. Default 2. */
50
+ maxUnits?: number;
51
+ };
52
+ /**
53
+ * Localized duration, truncated (never rounded up). Units are adjacent: with
54
+ * the default `maxUnits: 2`, 1 day 0 h 5 min reads "1 day", not "1 day, 5 min".
55
+ */
56
+ export declare function formatDuration(ms: number, locale: string, options?: FormatDurationOptions): string;
57
+ export type FormatRelativeTimeOptions = {
58
+ /** Reference time. Default `Date.now()`. */
59
+ now?: DateInput;
60
+ /** `'auto'` (default) says "yesterday"; `'always'` says "1 day ago". */
61
+ numeric?: Intl.RelativeTimeFormatNumeric;
62
+ style?: Intl.RelativeTimeFormatStyle;
63
+ /**
64
+ * `false` drops "ago"/"in" and prints the bare amount ("5 minutes"), for
65
+ * use next to a label that already says when. Default `true`.
66
+ */
67
+ withSuffix?: boolean;
68
+ };
69
+ /**
70
+ * Time from `now` in the best-fitting unit: "5 minutes ago", "in 2 days",
71
+ * "yesterday". Unparseable input returns `''`.
72
+ */
73
+ export declare function formatRelativeTime(date: DateInput, locale: string, options?: FormatRelativeTimeOptions): string;
74
+ /**
75
+ * How long something has waited, compact for a pill or list row: "14 min",
76
+ * "2 hr", "3 days". Floors (never claims more waiting than happened), never
77
+ * shows less than "1 min", and treats a future timestamp as no waiting.
78
+ *
79
+ * A `number` is a duration in milliseconds; a `Date`/string is a start
80
+ * timestamp measured against `now`. Unparseable input returns `''`.
81
+ */
82
+ export declare function formatWait(value: DateInput, locale: string, options?: {
83
+ now?: DateInput;
84
+ }): string;
85
+ /**
86
+ * One span with the shared parts collapsed: "24–30 Aug 2026". Without any
87
+ * date/time fields in `options` it uses day + short month + year; `timeZone`
88
+ * and friends combine with that default.
89
+ */
90
+ export declare function formatDateRange(start: DateInput, end: DateInput, locale: string, options?: Intl.DateTimeFormatOptions): string;
91
+ /** "a, b, and c". Falls back to a comma join where `Intl.ListFormat` is missing. */
92
+ export declare function formatList(items: readonly string[], locale: string, options?: Intl.ListFormatOptions): string;
93
+ /** Badge count: `150` -> `"99+"`. Negative or NaN is `"0"`. */
94
+ export declare function formatCappedCount(count: number, max?: number): string;
95
+ /**
96
+ * Pick the plural form for `count` and fill in `{count}` (locale-formatted).
97
+ * `pluralize(3, { one: '{count} item', other: '{count} items' }, 'en-US')`.
98
+ */
99
+ export declare function pluralize(count: number, forms: Partial<Record<Intl.LDMLPluralRule, string>> & {
100
+ other: string;
101
+ }, locale: string): string;
102
+ export {};