hanzio 1.2.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 (145) hide show
  1. package/README.md +786 -239
  2. package/dist/api-wrapper/builders.d.ts +4 -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 +39 -2
  7. package/dist/api-wrapper/index.d.ts +5 -0
  8. package/dist/api-wrapper/otel.d.ts +44 -0
  9. package/dist/api-wrapper/paginate.d.ts +12 -0
  10. package/dist/api-wrapper/redaction.d.ts +2 -0
  11. package/dist/api-wrapper/shared.d.ts +16 -2
  12. package/dist/api-wrapper/testing.d.ts +23 -0
  13. package/dist/api-wrapper/transport.d.ts +4 -5
  14. package/dist/api-wrapper/types.d.ts +103 -18
  15. package/dist/array/index.d.ts +50 -4
  16. package/dist/cache/createCache.d.ts +31 -0
  17. package/dist/cache/index.d.ts +19 -2
  18. package/dist/color/contrast.d.ts +40 -0
  19. package/dist/color/index.d.ts +2 -0
  20. package/dist/{math → color}/metricsSeriesColors.d.ts +14 -7
  21. package/dist/cool-console-log/index.d.ts +34 -5
  22. package/dist/crypto/encoding.d.ts +18 -0
  23. package/dist/crypto/hash.d.ts +33 -0
  24. package/dist/crypto/index.d.ts +4 -0
  25. package/dist/crypto/sealer.d.ts +25 -0
  26. package/dist/crypto/webhook.d.ts +20 -0
  27. package/dist/date/index.d.ts +46 -2
  28. package/dist/emitter/index.d.ts +21 -0
  29. package/dist/error/index.d.ts +12 -0
  30. package/dist/format/index.d.ts +102 -0
  31. package/dist/function/index.d.ts +60 -0
  32. package/dist/guard/index.d.ts +20 -0
  33. package/dist/i18n/index.d.ts +37 -0
  34. package/dist/index-0zvtcbzy.js +224 -0
  35. package/dist/index-181e6vfw.js +29 -0
  36. package/dist/index-8r2vdzjg.js +87 -0
  37. package/dist/index-hy7kbb99.js +157 -0
  38. package/dist/{src/secrets/infisical.js → index-j1ybktbr.js} +500 -538
  39. package/dist/index-k9yx6bne.js +89 -0
  40. package/dist/index-kj1phgrx.js +169 -0
  41. package/dist/index-mnkcfyx8.js +78 -0
  42. package/dist/{p-queue/index.js → index-pwt0dz63.js} +7 -11
  43. package/dist/index-rp5pqghz.js +157 -0
  44. package/dist/index-srt06xd2.js +15 -0
  45. package/dist/index-wjyb9bsd.js +263 -0
  46. package/dist/index.d.ts +7 -7
  47. package/dist/jwt/index.d.ts +50 -2
  48. package/dist/math/index.d.ts +2 -2
  49. package/dist/math/linearRegressionTrend.d.ts +14 -1
  50. package/dist/math/scale.d.ts +8 -0
  51. package/dist/object/index.d.ts +19 -0
  52. package/dist/p-queue/index.d.ts +1 -1
  53. package/dist/promise/index.d.ts +97 -2
  54. package/dist/sitemap/index.d.ts +48 -6
  55. package/dist/src/api-wrapper/index.js +60 -1130
  56. package/dist/src/api-wrapper/testing.js +20 -0
  57. package/dist/{cool-console-log → src/cool-console-log}/index.js +80 -33
  58. package/dist/src/crypto/index.js +195 -0
  59. package/dist/src/format/index.js +211 -0
  60. package/dist/src/i18n/index.js +36 -0
  61. package/dist/src/index.js +739 -3145
  62. package/dist/src/jwt/index.js +138 -92
  63. package/dist/src/p-queue/index.js +9 -437
  64. package/dist/src/secrets/index.js +44 -1428
  65. package/dist/src/secrets/vite.js +6 -39
  66. package/dist/src/secrets/worker.js +10 -273
  67. package/dist/src/sitemap/index.js +183 -511
  68. package/dist/src/storage/index.js +119 -0
  69. package/dist/src/zod/index.js +340 -167
  70. package/dist/state/index.d.ts +21 -0
  71. package/dist/storage/index.d.ts +58 -0
  72. package/dist/string/index.d.ts +75 -2
  73. package/dist/typedswitch/index.d.ts +31 -24
  74. package/dist/types/index.d.ts +11 -0
  75. package/dist/url/index.d.ts +50 -1
  76. package/dist/zod/index.d.ts +32 -3
  77. package/dist/zod/introspect.d.ts +19 -0
  78. package/dist/zod/jsonToZodSchemaMapper.d.ts +3 -1
  79. package/dist/zod/zodMinMaxFilter.d.ts +22 -3
  80. package/dist/zod/zodToTypeString.d.ts +8 -3
  81. package/package.json +78 -7
  82. package/src/api-wrapper/actions-impl.ts +11 -72
  83. package/src/api-wrapper/builders.ts +32 -25
  84. package/src/api-wrapper/create-client.ts +171 -41
  85. package/src/api-wrapper/describe.ts +157 -0
  86. package/src/api-wrapper/endpoint.ts +26 -0
  87. package/src/api-wrapper/error-reporting.ts +7 -1
  88. package/src/api-wrapper/errors.ts +107 -2
  89. package/src/api-wrapper/headers.ts +11 -6
  90. package/src/api-wrapper/index.ts +5 -0
  91. package/src/api-wrapper/otel.ts +139 -0
  92. package/src/api-wrapper/paginate.ts +28 -0
  93. package/src/api-wrapper/redaction.ts +42 -0
  94. package/src/api-wrapper/shared.ts +21 -2
  95. package/src/api-wrapper/testing.ts +48 -0
  96. package/src/api-wrapper/transport.ts +41 -43
  97. package/src/api-wrapper/types.ts +185 -18
  98. package/src/array/index.ts +195 -42
  99. package/src/cache/createCache.ts +157 -0
  100. package/src/cache/index.ts +132 -147
  101. package/src/color/contrast.ts +163 -0
  102. package/src/color/index.ts +22 -0
  103. package/src/{math → color}/metricsSeriesColors.ts +47 -49
  104. package/src/cool-console-log/index.ts +128 -41
  105. package/src/crypto/encoding.ts +83 -0
  106. package/src/crypto/hash.ts +170 -0
  107. package/src/crypto/index.ts +26 -0
  108. package/src/crypto/sealer.ts +168 -0
  109. package/src/crypto/webhook.ts +86 -0
  110. package/src/date/index.ts +109 -34
  111. package/src/emitter/index.ts +82 -0
  112. package/src/error/index.ts +63 -0
  113. package/src/format/index.ts +389 -0
  114. package/src/function/index.ts +201 -0
  115. package/src/guard/index.ts +80 -0
  116. package/src/i18n/index.ts +125 -0
  117. package/src/index.ts +11 -7
  118. package/src/jwt/index.ts +199 -125
  119. package/src/math/index.ts +5 -10
  120. package/src/math/linearRegressionTrend.ts +33 -8
  121. package/src/math/scale.ts +17 -0
  122. package/src/object/index.ts +99 -0
  123. package/src/p-queue/index.ts +2 -0
  124. package/src/promise/index.ts +304 -9
  125. package/src/sitemap/index.ts +293 -130
  126. package/src/state/index.ts +133 -81
  127. package/src/storage/index.ts +187 -0
  128. package/src/string/index.ts +261 -23
  129. package/src/typedswitch/index.ts +58 -38
  130. package/src/types/index.ts +14 -0
  131. package/src/url/index.ts +146 -1
  132. package/src/zod/index.ts +105 -9
  133. package/src/zod/introspect.ts +103 -0
  134. package/src/zod/jsonToZodSchemaMapper.ts +44 -21
  135. package/src/zod/zodMinMaxFilter.ts +37 -13
  136. package/src/zod/zodToTypeString.ts +184 -181
  137. package/dist/api-wrapper/index.js +0 -250
  138. package/dist/gzip/index.d.ts +0 -2
  139. package/dist/gzip/index.js +0 -16
  140. package/dist/index.js +0 -1823
  141. package/dist/jwt/index.js +0 -19
  142. package/dist/sitemap/index.js +0 -552
  143. package/dist/src/gzip/index.js +0 -16
  144. package/dist/src/secrets/loaders.js +0 -1529
  145. package/dist/zod/index.js +0 -210
@@ -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,16 @@
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;
5
14
  };
6
15
  export type CacheStoreEntry = {
7
16
  value: unknown;
@@ -40,6 +49,14 @@ export type CacheFunctionOptions<T extends AnyFunction> = {
40
49
  onStoreError?: (error: unknown, context: {
41
50
  name: string;
42
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;
43
57
  };
58
+ export declare function cacheFunction<T extends AnyFunction>(options: CacheFunctionOptions<T> & {
59
+ store: CacheStore;
60
+ }): CachedFunction<T, Promise<Awaited<ReturnType<T>> | undefined>>;
44
61
  export declare function cacheFunction<T extends AnyFunction>(options: CacheFunctionOptions<T>): CachedFunction<T>;
45
- 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 {};
@@ -0,0 +1,60 @@
1
+ export type Debounced<A extends unknown[]> = ((...args: A) => void) & {
2
+ /** Drops the pending call. */
3
+ cancel(): void;
4
+ /** Runs the pending call now, if any. */
5
+ flush(): void;
6
+ /** True while a trailing call is waiting. */
7
+ pending(): boolean;
8
+ };
9
+ /**
10
+ * Calls `fn` with the latest args once calls stop for `waitMs` (trailing).
11
+ * `leading` also calls on the first call of a burst; `maxWaitMs` forces a
12
+ * call at least that often during a continuous burst.
13
+ */
14
+ export declare function debounce<A extends unknown[]>(fn: (...args: A) => void, waitMs: number, options?: {
15
+ leading?: boolean;
16
+ maxWaitMs?: number;
17
+ }): Debounced<A>;
18
+ /**
19
+ * One independent trailing debounce per key. `cancel`/`flush` without a key
20
+ * apply to every pending key.
21
+ */
22
+ export declare function keyedDebounce<K, A extends unknown[]>(fn: (key: K, ...args: A) => void, waitMs: number): {
23
+ call(key: K, ...args: A): void;
24
+ cancel(key?: K): void;
25
+ flush(key?: K): void;
26
+ };
27
+ /**
28
+ * Calls `fn` immediately, then at most once per `intervalMs`. With `trailing`
29
+ * (default), the last call made during an interval runs when it ends.
30
+ */
31
+ export declare function throttle<A extends unknown[]>(fn: (...args: A) => void, intervalMs: number, options?: {
32
+ trailing?: boolean;
33
+ }): ((...args: A) => void) & {
34
+ cancel(): void;
35
+ };
36
+ /** Calls `fn` on the first call only; later calls return the first result. */
37
+ export declare function once<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R;
38
+ export type HeartbeatOptions = {
39
+ /** Called on the first activity, then at most once per `intervalMs`. */
40
+ send: () => void;
41
+ /** Called once when an active heartbeat stops (idle or `stop()`). */
42
+ onStop?: () => void;
43
+ intervalMs: number;
44
+ /** Stop after this long without `activity()`. */
45
+ idleMs: number;
46
+ now?: () => number;
47
+ setTimeout?: (callback: () => void, ms: number) => unknown;
48
+ clearTimeout?: (timer: unknown) => void;
49
+ };
50
+ /**
51
+ * Turns bursts of activity into heartbeats (e.g. "is typing" signals): sends
52
+ * immediately on the first activity, then at most once per `intervalMs` while
53
+ * activity continues, and stops after `idleMs` without activity.
54
+ */
55
+ export declare function createHeartbeat(options: HeartbeatOptions): {
56
+ /** Report activity. */
57
+ activity(): void;
58
+ /** Stop now; calls `onStop` if active. Safe to call repeatedly. */
59
+ stop(): void;
60
+ };
@@ -0,0 +1,20 @@
1
+ import type { NonEmptyArray } from '../types';
2
+ /** Non-null object that is not an array. */
3
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
4
+ /** Object literal or `Object.create(null)`; excludes arrays, class instances, Dates, Maps. */
5
+ export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
6
+ export declare function isDefined<T>(value: T): value is NonNullable<T>;
7
+ export declare function isTruthy<T>(value: T): value is Exclude<T, false | 0 | 0n | '' | null | undefined>;
8
+ export declare function isString(value: unknown): value is string;
9
+ /** `typeof number`, excluding NaN. Infinity passes. */
10
+ export declare function isNumber(value: unknown): value is number;
11
+ export declare function isBoolean(value: unknown): value is boolean;
12
+ export declare function isFunction(value: unknown): value is (...args: any[]) => any;
13
+ /** Whether `value` is in `list`. Omit `value` to get a reusable guard, e.g. for `.filter`. */
14
+ export declare function isOneOf<const T extends readonly unknown[]>(list: T): (value: unknown) => value is T[number];
15
+ export declare function isOneOf<const T extends readonly unknown[]>(list: T, value: unknown): value is T[number];
16
+ /** Own-property check that narrows `key` to `keyof O`. */
17
+ export declare function hasKey<O extends object>(object: O, key: PropertyKey): key is keyof O;
18
+ export declare function isNonEmpty<T>(array: readonly T[]): array is readonly [T, ...T[]];
19
+ /** Copy of `array` typed as non-empty (e.g. for `z.enum`); throws when empty. */
20
+ export declare function nonEmpty<T>(array: readonly T[], message?: string): NonEmptyArray<T>;