@unireq/core 1.0.0 → 1.0.2

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.
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # @unireq/core
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@unireq/core.svg)](https://www.npmjs.com/package/@unireq/core)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ The core package provides the foundational building blocks for the Unireq ecosystem: client creation, policy composition, flow-control primitives, introspection, validation, and the DX-focused error catalog.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ pnpm add @unireq/core
12
+ ```
13
+
14
+ ## Quick Start
15
+
16
+ ```typescript
17
+ import { client, retry, backoff } from '@unireq/core';
18
+ import { http, headers, timeout, parse } from '@unireq/http';
19
+
20
+ const api = client(
21
+ http('https://api.example.com'),
22
+ headers({ 'x-api-key': 'secret' }),
23
+ timeout(10_000),
24
+ retry(httpRetryPredicate(), [backoff()], { tries: 3 }),
25
+ parse.json(),
26
+ );
27
+
28
+ const user = await api.get('/users/42');
29
+ ```
30
+
31
+ ## Features
32
+
33
+ | Category | Symbols | Purpose |
34
+ | --- | --- | --- |
35
+ | Client factory | `client`, `Policy`, `Transport` | Create clients by composing transports + policies |
36
+ | Composition | `compose`, `either`, `match`, `policy`, `slot` | Build reusable middleware stacks |
37
+ | Flow control | `retry`, `backoff`, `circuitBreaker`, `throttle` | Resilience with retries, backoff, circuit breaking |
38
+ | Introspection | `inspect`, `inspectable`, `getHandlerGraph`, `log` | Trace policy graphs and structured logging |
39
+ | Validation | `validate`, `ValidationAdapter` | Guarantee typed responses with any schema library |
40
+ | Errors | `HttpError`, `TimeoutError`, `NetworkError` | Consistent error surface |
41
+
42
+ ## Policy Composition
43
+
44
+ ```typescript
45
+ import { compose, either } from '@unireq/core';
46
+ import { parse } from '@unireq/http';
47
+ import { xml } from '@unireq/xml';
48
+
49
+ const smartParser = compose(
50
+ either(
51
+ (ctx) => ctx.headers.accept?.includes('application/json') ?? false,
52
+ parse.json(),
53
+ xml(),
54
+ ),
55
+ );
56
+ ```
57
+
58
+ ## Validation with Zod
59
+
60
+ ```typescript
61
+ import { client, validate } from '@unireq/core';
62
+ import { http, parse } from '@unireq/http';
63
+ import { z } from 'zod';
64
+
65
+ const UserSchema = z.object({
66
+ id: z.number(),
67
+ email: z.string().email(),
68
+ });
69
+
70
+ const zodAdapter = {
71
+ validate: (schema, data) => schema.parse(data),
72
+ };
73
+
74
+ const api = client(
75
+ http('https://api.example.com'),
76
+ parse.json(),
77
+ validate(UserSchema, zodAdapter),
78
+ );
79
+ ```
80
+
81
+ ## Error Handling
82
+
83
+ Every error extends `UnireqError` with a stable `code` string:
84
+
85
+ - `NetworkError` - DNS failures, connection resets, TLS issues
86
+ - `TimeoutError` - Request timeout exceeded
87
+ - `HttpError` - HTTP error responses
88
+ - `SerializationError` - Body parsing/encoding issues
89
+ - `DuplicatePolicyError` - Slot conflicts
90
+ - `MissingCapabilityError` - Transport lacks required capability
91
+
92
+ ## Header Utilities
93
+
94
+ Convert between `Record<string, string>` and native `Headers`:
95
+
96
+ ```typescript
97
+ import { toNativeHeaders, fromNativeHeaders } from '@unireq/core';
98
+
99
+ const nativeHeaders = toNativeHeaders({ 'content-type': 'application/json' });
100
+ const record = fromNativeHeaders(response.headers);
101
+ ```
102
+
103
+ ## Documentation
104
+
105
+ Full documentation available at [unireq.dev](https://oorabona.github.io/unireq/)
106
+
107
+ ## License
108
+
109
+ MIT
package/dist/index.d.ts CHANGED
@@ -1,7 +1,183 @@
1
+ /**
2
+ * Result type for functional error handling
3
+ *
4
+ * Provides a type-safe way to handle success and error cases
5
+ * without throwing exceptions. Inspired by Rust's Result type.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const result = await client.safe.get<User>('/users/1');
10
+ *
11
+ * // Pattern matching
12
+ * const name = result.match({
13
+ * ok: (response) => response.data.name,
14
+ * err: (error) => 'Unknown',
15
+ * });
16
+ *
17
+ * // Chaining operations
18
+ * const profile = result
19
+ * .map(r => r.data)
20
+ * .flatMap(user => fetchProfile(user.id));
21
+ *
22
+ * // Unwrapping with fallback
23
+ * const data = result.unwrapOr(defaultResponse);
24
+ * ```
25
+ */
26
+ /**
27
+ * Pattern matching interface for Result
28
+ */
29
+ interface ResultPatterns<T, E, U> {
30
+ readonly ok: (value: T) => U;
31
+ readonly err: (error: E) => U;
32
+ }
33
+ /**
34
+ * Ok variant - represents success
35
+ */
36
+ declare class OkImpl<T, E> {
37
+ readonly value: T;
38
+ readonly _tag: "Ok";
39
+ constructor(value: T);
40
+ /**
41
+ * Returns true if this is an Ok value
42
+ */
43
+ isOk(): this is OkImpl<T, E>;
44
+ /**
45
+ * Returns true if this is an Err value
46
+ */
47
+ isErr(): this is ErrImpl<T, E>;
48
+ /**
49
+ * Transforms the success value, leaving errors unchanged
50
+ */
51
+ map<U>(fn: (value: T) => U): Result<U, E>;
52
+ /**
53
+ * Maps a function that returns a Result, flattening the result
54
+ */
55
+ flatMap<U>(fn: (value: T) => Result<U, E>): Result<U, E>;
56
+ /**
57
+ * Returns the success value or throws if this is an Err
58
+ * @throws Error if this is an Err
59
+ */
60
+ unwrap(): T;
61
+ /**
62
+ * Returns the success value or the provided fallback
63
+ */
64
+ unwrapOr(_fallback: T): T;
65
+ /**
66
+ * Returns the error value or throws if this is an Ok
67
+ * @throws Error if this is an Ok
68
+ */
69
+ unwrapErr(): never;
70
+ /**
71
+ * Pattern matching on the result
72
+ */
73
+ match<U>(patterns: ResultPatterns<T, E, U>): U;
74
+ }
75
+ /**
76
+ * Err variant - represents failure
77
+ */
78
+ declare class ErrImpl<T, E> {
79
+ readonly error: E;
80
+ readonly _tag: "Err";
81
+ constructor(error: E);
82
+ /**
83
+ * Returns true if this is an Ok value
84
+ */
85
+ isOk(): this is OkImpl<T, E>;
86
+ /**
87
+ * Returns true if this is an Err value
88
+ */
89
+ isErr(): this is ErrImpl<T, E>;
90
+ /**
91
+ * Transforms the success value, leaving errors unchanged
92
+ * For Err, this is a no-op that returns the same Err
93
+ */
94
+ map<U>(_fn: (value: T) => U): Result<U, E>;
95
+ /**
96
+ * Maps a function that returns a Result, flattening the result
97
+ * For Err, this is a no-op that returns the same Err
98
+ */
99
+ flatMap<U>(_fn: (value: T) => Result<U, E>): Result<U, E>;
100
+ /**
101
+ * Returns the success value or throws if this is an Err
102
+ * @throws The error value
103
+ */
104
+ unwrap(): never;
105
+ /**
106
+ * Returns the success value or the provided fallback
107
+ */
108
+ unwrapOr(fallback: T): T;
109
+ /**
110
+ * Returns the error value or throws if this is an Ok
111
+ */
112
+ unwrapErr(): E;
113
+ /**
114
+ * Pattern matching on the result
115
+ */
116
+ match<U>(patterns: ResultPatterns<T, E, U>): U;
117
+ }
118
+ /**
119
+ * Result type - either Ok<T> or Err<E>
120
+ */
121
+ type Result<T, E> = OkImpl<T, E> | ErrImpl<T, E>;
122
+ /**
123
+ * Creates a successful Result containing the value
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * const result = ok({ name: 'Alice' });
128
+ * console.log(result.unwrap()); // { name: 'Alice' }
129
+ * ```
130
+ */
131
+ declare function ok<T, E = never>(value: T): Result<T, E>;
132
+ /**
133
+ * Creates a failed Result containing the error
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * const result = err(new Error('Not found'));
138
+ * console.log(result.isErr()); // true
139
+ * ```
140
+ */
141
+ declare function err<T = never, E = unknown>(error: E): Result<T, E>;
142
+ /**
143
+ * Type guard to check if a Result is Ok
144
+ */
145
+ declare function isOk<T, E>(result: Result<T, E>): result is OkImpl<T, E>;
146
+ /**
147
+ * Type guard to check if a Result is Err
148
+ */
149
+ declare function isErr<T, E>(result: Result<T, E>): result is ErrImpl<T, E>;
150
+ /**
151
+ * Wraps a Promise in a Result, catching any errors
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * const result = await fromPromise(fetch('/api/users'));
156
+ * if (result.isOk()) {
157
+ * console.log(result.value);
158
+ * }
159
+ * ```
160
+ */
161
+ declare function fromPromise<T, E = Error>(promise: Promise<T>): Promise<Result<T, E>>;
162
+ /**
163
+ * Wraps a synchronous function in a Result, catching any errors
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * const result = fromTry(() => JSON.parse(jsonString));
168
+ * const data = result.unwrapOr({ default: true });
169
+ * ```
170
+ */
171
+ declare function fromTry<T, E = Error>(fn: () => T): Result<T, E>;
172
+
1
173
  /**
2
174
  * Core types for the unireq framework
3
175
  */
4
- /** Request context passed through the policy chain */
176
+
177
+ /**
178
+ * Request context passed through the policy chain
179
+ * @frozen Adding required fields is a breaking change across 55 importer files. All new fields must be optional.
180
+ */
5
181
  interface RequestContext {
6
182
  readonly url: string;
7
183
  readonly method: string;
@@ -59,16 +235,121 @@ interface ClientOptions {
59
235
  readonly defaultScheme?: 'http' | 'https' | 'ftp' | 'imap';
60
236
  readonly policies?: ReadonlyArray<Policy>;
61
237
  }
238
+ /**
239
+ * Per-request options for client methods
240
+ * Allows passing body, policies, and signal in a structured way
241
+ *
242
+ * @example
243
+ * ```ts
244
+ * // Using options object
245
+ * await client.post('/users', {
246
+ * body: { name: 'Alice' },
247
+ * policies: [timeout(5000)],
248
+ * signal: AbortSignal.timeout(10000),
249
+ * });
250
+ *
251
+ * // Equivalent to variadic API
252
+ * await client.post('/users', { name: 'Alice' }, timeout(5000));
253
+ * ```
254
+ */
255
+ interface RequestOptions {
256
+ /** Request body (for POST, PUT, PATCH) */
257
+ readonly body?: unknown;
258
+ /** Per-request policies to compose with client-level policies */
259
+ readonly policies?: ReadonlyArray<Policy>;
260
+ /** AbortSignal for request cancellation */
261
+ readonly signal?: AbortSignal;
262
+ }
263
+ /**
264
+ * Safe client methods that return Result<Response<T>, Error> instead of throwing
265
+ *
266
+ * @example
267
+ * ```ts
268
+ * const result = await client.safe.get<User>('/users/1');
269
+ *
270
+ * // Pattern matching
271
+ * result.match({
272
+ * ok: (response) => console.log(response.data),
273
+ * err: (error) => console.error(error.message),
274
+ * });
275
+ *
276
+ * // Chaining
277
+ * const name = result
278
+ * .map(r => r.data)
279
+ * .map(user => user.name)
280
+ * .unwrapOr('Unknown');
281
+ * ```
282
+ */
283
+ interface SafeClient {
284
+ /** Request with variadic policies */
285
+ request<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;
286
+ /** Request with options object */
287
+ request<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;
288
+ /** GET with variadic policies */
289
+ get<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;
290
+ /** GET with options object */
291
+ get<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;
292
+ /** HEAD with variadic policies */
293
+ head<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;
294
+ /** HEAD with options object */
295
+ head<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;
296
+ /** POST with body and variadic policies */
297
+ post<T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;
298
+ /** POST with options object */
299
+ post<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;
300
+ /** PUT with body and variadic policies */
301
+ put<T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;
302
+ /** PUT with options object */
303
+ put<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;
304
+ /** DELETE with variadic policies */
305
+ delete<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;
306
+ /** DELETE with options object */
307
+ delete<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;
308
+ /** PATCH with body and variadic policies */
309
+ patch<T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;
310
+ /** PATCH with options object */
311
+ patch<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;
312
+ /** OPTIONS with variadic policies */
313
+ options<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;
314
+ /** OPTIONS with options object */
315
+ options<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;
316
+ }
62
317
  /** Client instance */
63
318
  interface Client {
64
- readonly request: <T = unknown>(url: string, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;
65
- readonly get: <T = unknown>(url: string, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;
66
- readonly head: <T = unknown>(url: string, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;
67
- readonly post: <T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;
68
- readonly put: <T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;
69
- readonly delete: <T = unknown>(url: string, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;
70
- readonly patch: <T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;
71
- readonly options: <T = unknown>(url: string, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;
319
+ /** Make a request with variadic policies */
320
+ request<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;
321
+ /** Make a request with options object */
322
+ request<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;
323
+ /** GET with variadic policies */
324
+ get<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;
325
+ /** GET with options object */
326
+ get<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;
327
+ /** HEAD with variadic policies */
328
+ head<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;
329
+ /** HEAD with options object */
330
+ head<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;
331
+ /** POST with body and variadic policies */
332
+ post<T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;
333
+ /** POST with options object */
334
+ post<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;
335
+ /** PUT with body and variadic policies */
336
+ put<T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;
337
+ /** PUT with options object */
338
+ put<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;
339
+ /** DELETE with variadic policies */
340
+ delete<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;
341
+ /** DELETE with options object */
342
+ delete<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;
343
+ /** PATCH with body and variadic policies */
344
+ patch<T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;
345
+ /** PATCH with options object */
346
+ patch<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;
347
+ /** OPTIONS with variadic policies */
348
+ options<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;
349
+ /** OPTIONS with options object */
350
+ options<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;
351
+ /** Safe methods that return Result instead of throwing */
352
+ readonly safe: SafeClient;
72
353
  }
73
354
  /** Predicate function for conditional branching */
74
355
  type Predicate<T = unknown> = (ctx: RequestContext) => T | Promise<T>;
@@ -412,6 +693,7 @@ declare function circuitBreaker(options?: CircuitBreakerOptions): Policy;
412
693
  */
413
694
 
414
695
  /**
696
+ * @frozen Public API contract — 51 files depend on this signature. Changes require impact review.
415
697
  * Creates a client with the given transport and policies
416
698
  * @param transport - The transport function or transport with capabilities
417
699
  * @param policies - Variadic list of policies to apply
@@ -424,6 +706,7 @@ declare function client(transport: Transport | TransportWithCapabilities, ...pol
424
706
  */
425
707
 
426
708
  /**
709
+ * @frozen Core composition primitive — 28 files depend transitively. Changes require impact review.
427
710
  * Composes multiple policies into a single policy chain (onion model)
428
711
  * @param policies - Array of policies to compose
429
712
  * @returns A single composed policy
@@ -529,6 +812,7 @@ declare function resetIdCounter(): void;
529
812
  */
530
813
  declare function redactOptions(opts: Record<string, unknown>, extraKeys?: string[]): Record<string, unknown>;
531
814
  /**
815
+ * @frozen Signature change affects 46 files across 8 packages — requires monorepo-wide impact review.
532
816
  * Creates a tagged policy with metadata for introspection
533
817
  * @param fn - Policy function
534
818
  * @param meta - Policy metadata (without id)
@@ -805,6 +1089,47 @@ declare function getHeader(headers: Record<string, string>, name: string): strin
805
1089
  * ```
806
1090
  */
807
1091
  declare function setHeader(headers: Record<string, string>, name: string, value: string): Record<string, string>;
1092
+ /**
1093
+ * Converts a Record<string, string> headers object to native Headers
1094
+ *
1095
+ * Use this helper when you need to pass headers to external libraries
1096
+ * that require the native Headers API (e.g., fetch, Web APIs).
1097
+ *
1098
+ * Note: In most cases, you should prefer Record<string, string> because:
1099
+ * - It's more efficient (no iteration overhead)
1100
+ * - It's JSON-serializable
1101
+ * - It works directly with unireq APIs
1102
+ *
1103
+ * @param headers - Headers object (Record<string, string>)
1104
+ * @returns Native Headers instance
1105
+ *
1106
+ * @example
1107
+ * ```ts
1108
+ * const headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer token' };
1109
+ * const nativeHeaders = toNativeHeaders(headers);
1110
+ *
1111
+ * // Pass to external API that requires native Headers
1112
+ * fetch(url, { headers: nativeHeaders });
1113
+ * ```
1114
+ */
1115
+ declare function toNativeHeaders(headers: Record<string, string>): Headers;
1116
+ /**
1117
+ * Converts native Headers to Record<string, string>
1118
+ *
1119
+ * Use this helper when receiving headers from external sources
1120
+ * (e.g., Response.headers from fetch) and need to use them with unireq APIs.
1121
+ *
1122
+ * @param headers - Native Headers instance
1123
+ * @returns Headers as Record<string, string>
1124
+ *
1125
+ * @example
1126
+ * ```ts
1127
+ * const response = await fetch(url);
1128
+ * const headers = fromNativeHeaders(response.headers);
1129
+ * console.log(headers['content-type']); // 'application/json'
1130
+ * ```
1131
+ */
1132
+ declare function fromNativeHeaders(headers: Headers): Record<string, string>;
808
1133
 
809
1134
  /**
810
1135
  * Validation policy using Adapter Pattern
@@ -825,4 +1150,4 @@ interface ValidationAdapter<TSchema, TOutput> {
825
1150
  */
826
1151
  declare function validate<TSchema, TOutput>(schema: TSchema, adapter: ValidationAdapter<TSchema, TOutput>): Policy;
827
1152
 
828
- export { type AuditLogEntry, type AuditLogger, type AuditOptions, type BackoffOptions, type BodyDescriptor, CircuitBreakerOpenError, type CircuitBreakerOptions, type Client, type ClientOptions, type Connector, DuplicatePolicyError, type EitherBranch, HANDLER_GRAPH, type Handler, HttpError, INSPECTABLE_META, type InspectFormat, type InspectOptions, type InspectableMeta, InvalidSlotError, type Kind, type LogOptions, type Logger, MissingCapabilityError, type MultipartPart, NetworkError, NotAcceptableError, type Policy, type Predicate, type RequestContext, type Response, type RetryDelayStrategy, type RetryOptions, type RetryPredicate, type SecurityEventType, SerializationError, type SlotMetadata, SlotType, type ThrottleOptions, TimeoutError, type Transport, type TransportCapabilities, type TransportWithCapabilities, URLNormalizationError, UnireqError, UnsupportedAuthForTransport, UnsupportedMediaTypeError, type ValidationAdapter, appendQueryParams, assertHas, attachToGraph, audit, backoff, circuitBreaker, client, compose, createConsoleAuditLogger, createLoggerAdapter, either, getHandlerGraph, getHeader, getInspectableMeta, getSlotMetadata, hasSlotType, inspect, inspectable, isBodyDescriptor, isInspectable, log, match, normalizeHeaders, normalizeURL, policy, redactOptions, registerSlot, resetIdCounter, retry, serializationPolicy, setHeader, slot, throttle, validate, validatePolicyChain };
1153
+ export { type AuditLogEntry, type AuditLogger, type AuditOptions, type BackoffOptions, type BodyDescriptor, CircuitBreakerOpenError, type CircuitBreakerOptions, type Client, type ClientOptions, type Connector, DuplicatePolicyError, type EitherBranch, HANDLER_GRAPH, type Handler, HttpError, INSPECTABLE_META, type InspectFormat, type InspectOptions, type InspectableMeta, InvalidSlotError, type Kind, type LogOptions, type Logger, MissingCapabilityError, type MultipartPart, NetworkError, NotAcceptableError, type Policy, type Predicate, type RequestContext, type RequestOptions, type Response, type Result, type ResultPatterns, type RetryDelayStrategy, type RetryOptions, type RetryPredicate, type SafeClient, type SecurityEventType, SerializationError, type SlotMetadata, SlotType, type ThrottleOptions, TimeoutError, type Transport, type TransportCapabilities, type TransportWithCapabilities, URLNormalizationError, UnireqError, UnsupportedAuthForTransport, UnsupportedMediaTypeError, type ValidationAdapter, appendQueryParams, assertHas, attachToGraph, audit, backoff, circuitBreaker, client, compose, createConsoleAuditLogger, createLoggerAdapter, either, err, fromNativeHeaders, fromPromise, fromTry, getHandlerGraph, getHeader, getInspectableMeta, getSlotMetadata, hasSlotType, inspect, inspectable, isBodyDescriptor, isErr, isInspectable, isOk, log, match, normalizeHeaders, normalizeURL, ok, policy, redactOptions, registerSlot, resetIdCounter, retry, serializationPolicy, setHeader, slot, throttle, toNativeHeaders, validate, validatePolicyChain };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import {randomUUID}from'crypto';var C=Symbol("unireq.handlerGraph"),S=Symbol("unireq.inspectable"),ce=["token","accessToken","refreshToken","clientSecret","clientId","password","privateKey","secret","apiKey","authorization"],ee=0;function le(){ee=0;}function te(e){return process.env.NODE_ENV==="test"||process.env.VITEST?`${e}#${ee++}`:`${e}#${randomUUID().slice(0,8)}`}function _(e){if(e===null||typeof e!="object")return e;if(Array.isArray(e))return e.map(_);let t={};for(let[n,r]of Object.entries(e))t[n]=_(r);return t}function $(e,t=[]){if(!e||typeof e!="object")return {};let n=new Set([...ce,...t]),r=_(e);for(let o of Object.keys(r))n.has(o)&&(r[o]="***redacted***"),(o.toLowerCase().includes("secret")||o.toLowerCase().includes("token"))&&(r[o]="***redacted***");return r}function q(e,t,n){e[t]=n;}function j(e,t){return e[t]}function p(e,t){let n={...t,id:te(t.name),options:t.options?$(t.options):void 0};return q(e,S,n),e}function de(e,t){let n=j(e,C),r=n?[...n,t]:[t];q(e,C,r);}function I(e){return j(e,C)??[]}function U(e,t){let n={...t,id:te(t.name),options:t.options?$(t.options):void 0};return q(e,S,n),e}function m(e){if(!(!e||typeof e!="function"&&typeof e!="object"))return j(e,S)}function pe(e){return m(e)!==void 0}var ye=["authorization","cookie","set-cookie","x-api-key","x-auth-token","x-csrf-token","x-session-id"];function fe(){return randomUUID()}function H(e){try{let t=new URL(e),n=["token","api_key","apikey","secret","password","auth"];for(let r of n)t.searchParams.has(r)&&t.searchParams.set(r,"[REDACTED]");return t.toString()}catch{return e}}function me(e){return e>=500?"error":e===401||e===403||e>=400?"warn":"info"}function ge(e){let{logger:t,getUserId:n,getSessionId:r,getClientIp:o,redactHeaders:i=ye,logSuccess:s=true,correlationIdGenerator:c=fe,detectSuspiciousActivity:l}=e;return p(async(a,d)=>{let y=c(),f=Date.now(),h=new Date().toISOString(),O=n?.(a),L=r?.(a),M=o?.(a),D=a.headers["user-agent"];await t.log({timestamp:h,correlationId:y,eventType:"request_started",severity:"info",method:a.method,url:H(a.url),userId:O,sessionId:L,clientIp:M,userAgent:D});try{let g=await d(a),v=Date.now()-f,R="request_completed",b=me(g.status);return g.status===401?(R="auth_failure",b="warn"):g.status===403?(R="access_denied",b="warn"):g.status===429&&(R="rate_limit_exceeded",b="warn"),l?.(a,g,void 0)&&(R="suspicious_activity",b="critical"),(s||g.status>=400)&&await t.log({timestamp:new Date().toISOString(),correlationId:y,eventType:R,severity:b,method:a.method,url:H(a.url),statusCode:g.status,durationMs:v,userId:O,sessionId:L,clientIp:M,userAgent:D}),g}catch(g){let v=Date.now()-f,R=g instanceof Error?g:new Error(String(g)),b="request_failed",X="error";throw l?.(a,void 0,R)&&(b="suspicious_activity",X="critical"),await t.log({timestamp:new Date().toISOString(),correlationId:y,eventType:b,severity:X,method:a.method,url:H(a.url),durationMs:v,userId:O,sessionId:L,clientIp:M,userAgent:D,errorMessage:R.message,errorCode:R.code}),g}},{name:"audit",kind:"other",options:{logSuccess:s,redactHeaders:i}})}function he(){return {log:e=>{console.log(JSON.stringify(e));}}}function Re(e){return {log:t=>{let n=`[${t.eventType}] ${t.method} ${t.url}`,r={...t};switch(t.severity){case "critical":case "error":e.error(n,r);break;case "warn":e.warn(n,r);break;default:e.info(n,r);}}}}function be(e,t,n,r,o){let i=t*r**e,s=Math.min(i,n);return o?Math.floor(Math.random()*s):s}function Te(e={}){let{initial:t=1e3,max:n=3e4,multiplier:r=2,jitter:o=true}=e;return U({getDelay:(s,c,l)=>be(l,t,n,r,o)},{name:"backoff",kind:"strategy",options:{initial:t,max:n,multiplier:r,jitter:o}})}var u=class extends Error{constructor(n,r,o){super(n);this.code=r;this.cause=o;this.name="UnireqError";}},B=class extends u{constructor(t,n){super(t,"NETWORK_ERROR",n),this.name="NetworkError";}},N=class extends u{constructor(n,r){super(`Request timed out after ${n}ms`,"TIMEOUT",r);this.timeoutMs=n;this.name="TimeoutError";}},z=class extends u{status;statusText;headers;data;constructor(t){super(`HTTP Error ${t.status}: ${t.statusText}`,"HTTP_ERROR"),this.name="HttpError",this.status=t.status,this.statusText=t.statusText,this.headers=t.headers,this.data=t.data;}},P=class extends u{constructor(t,n){super(t,"SERIALIZATION_ERROR",n),this.name="SerializationError";}},x=class extends u{constructor(n){super(`Duplicate policy detected: ${n}. Each policy can only be registered once in the chain.`,"DUPLICATE_POLICY");this.policyName=n;this.name="DuplicatePolicyError";}},F=class extends u{constructor(n,r){super(`Authentication type "${n}" is not supported by transport "${r}".`,"UNSUPPORTED_AUTH");this.authType=n;this.transportType=r;this.name="UnsupportedAuthForTransport";}},G=class extends u{constructor(n,r){let o=r?` (received: ${r})`:"";super(`Server cannot produce a response matching the Accept header. Accepted types: ${n.join(", ")}${o}`,"NOT_ACCEPTABLE");this.acceptedTypes=n;this.receivedType=r;this.name="NotAcceptableError";}},K=class extends u{constructor(n,r){let o=r?` (sent: ${r})`:"";super(`Server cannot process the request payload media type. Supported types: ${n.join(", ")}${o}`,"UNSUPPORTED_MEDIA_TYPE");this.supportedTypes=n;this.sentType=r;this.name="UnsupportedMediaTypeError";}},k=class extends u{constructor(n,r){super(`Transport "${r}" does not support required capability: ${n}`,"MISSING_CAPABILITY");this.capability=n;this.transportType=r;this.name="MissingCapabilityError";}},w=class extends u{constructor(n,r){super(`Invalid slot configuration for ${n}: ${r}`,"INVALID_SLOT");this.slotType=n;this.name="InvalidSlotError";}},T=class extends u{constructor(n,r){super(`Failed to normalize URL "${n}": ${r}`,"URL_NORMALIZATION_FAILED");this.url=n;this.reason=r;this.name="URLNormalizationError";}};var V=class extends u{constructor(n){super("Circuit breaker is OPEN","CIRCUIT_BREAKER_OPEN");this.resetTime=n;this.name="CircuitBreakerOpenError";}};function Je(e={}){let{failureThreshold:t=5,resetTimeout:n=3e4,shouldFail:r=()=>true}=e,o="CLOSED",i=0,s=0;return p(async(c,l)=>{let a=Date.now();if(o==="OPEN")if(a>=s)o="HALF_OPEN";else throw new V(s);try{let d=await l(c);return o==="HALF_OPEN"&&(o="CLOSED"),i=0,d}catch(d){throw r(d)&&(o==="HALF_OPEN"?(o="OPEN",s=Date.now()+n):(i++,i>=t&&(o="OPEN",s=Date.now()+n))),d}},{name:"circuitBreaker",kind:"other",options:{failureThreshold:t,resetTimeout:n,state:o}})}function E(...e){if(e.length===0)return async(r,o)=>o(r);if(e.length===1){let r=e[0];return r||(async(o,i)=>i(o))}let t=async(r,o)=>{let i=async(s,c)=>{let l=e[s];return l?l(c,a=>i(s+1,a)):o(c)};return i(0,r)},n=e.filter(r=>r!==void 0).map(r=>m(r)).filter(r=>r!==void 0);return p(t,{name:"compose",kind:"other",children:n})}function re(e){return typeof e=="object"&&e!==null&&"__brand"in e&&e.__brand==="BodyDescriptor"&&"serialize"in e&&typeof e.serialize=="function"}function W(){return async(e,t)=>{if(re(e.body)){let n=e.body,r=n.serialize(),o=n.contentType&&!e.headers["content-type"]&&!e.headers["Content-Type"]&&!(r instanceof FormData),i={...e,body:r,headers:{...e.headers,...o?{"content-type":n.contentType}:{}}};return t(i)}return t(e)}}var ne=new WeakMap;function oe(e,t){ne.set(e,t);}function Y(e){return ne.get(e)}function J(e,t){let n=new Map;for(let r of e){let o=Y(r);if(!o)continue;let i=`${o.type}:${o.name}`;if(n.get(i))throw new x(o.name);if(n.set(i,o),t&&o.requiredCapabilities){for(let c of o.requiredCapabilities)if(!t[c])throw new k(c,"current")}}we(Array.from(n.values()));}function we(e){let t=-1,n=-1,r=-1;if(e.forEach((o,i)=>{switch(o.type){case "transport":t=i;break;case "auth":n=i;break;case "parser":r=i;break}}),t!==-1&&t!==e.length-1)throw new w("transport","Transport slot must be the last policy in the chain");if(n!==-1&&r!==-1&&n>r)throw new w("auth","Auth slot must come before parser slot in the chain")}function Pe(e){return t=>(oe(t,e),t)}function xe(e,t){return Y(e)?.type===t}function Q(e,t={}){let{base:n,defaultScheme:r="https"}=t;try{if(e.includes("://"))return new URL(e).toString();if(e.startsWith("//"))return new URL(`${r}:${e}`).toString();if(n){let i=n.includes("://")?n:`${r}://${n}`;return new URL(e,i).toString()}if(!e.match(/^[a-z][a-z0-9+.-]*:/i))throw new T(e,'Relative URL requires URI in transport. Use http("https://api.com") or provide absolute URL.');return new URL(e).toString()}catch(o){if(o instanceof T)throw o;let i=o instanceof Error?o.message:String(o);throw new T(e,i)}}function ke(e,t){let n=new URL(e);for(let[r,o]of Object.entries(t))o!==void 0&&n.searchParams.append(r,String(o));return n.toString()}function Ee(e){let t={};for(let[n,r]of Object.entries(e))t[n.toLowerCase()]=r;return t}function Ae(e,t){let n=t.toLowerCase();if(n in e)return e[n];for(let[r,o]of Object.entries(e))if(r.toLowerCase()===n)return o}function Ce(e,t,n){let r=t.toLowerCase(),o={};for(let[i,s]of Object.entries(e))i.toLowerCase()!==r&&(o[i]=s);return o[r]=n,o}function Se(e,...t){let n=typeof e=="function"?e:e.transport,r=typeof e=="function"?void 0:e.capabilities;J(t,r);let o=E(W(),...t),i=async(s,c,l,a)=>{if(!s||typeof s!="string"||s.trim().length===0)throw new u("URL must be a non-empty string","INVALID_URL");let y={url:s.includes("://")?Q(s,{}):s,method:c,headers:{},body:l};if(a.length>0){let f=E(...a);return E(o,f)(y,n)}return o(y,n)};return {request:(s,...c)=>i(s,"GET",void 0,c),get:(s,...c)=>i(s,"GET",void 0,c),head:(s,...c)=>i(s,"HEAD",void 0,c),post:(s,c,...l)=>i(s,"POST",c,l),put:(s,c,...l)=>i(s,"PUT",c,l),delete:(s,...c)=>i(s,"DELETE",void 0,c),patch:(s,c,...l)=>i(s,"PATCH",c,l),options:(s,...c)=>i(s,"OPTIONS",void 0,c)}}function Ie(e,t,n){let r=async(a,d)=>await Promise.resolve(e(a))?t(a,d):n?n(a,d):d(a),o=e.name||"anonymous predicate",i=m(t),s=i?[i]:[],c=n?m(n):void 0;return p(r,{name:"either",kind:"other",branch:{predicate:o,thenBranch:s,elseBranch:c?[c]:[]}})}function Oe(e,t){let n=async(o,i)=>{for(let[s,c]of e)if(await Promise.resolve(s(o)))return c(o,i);return t?t(o,i):i(o)},r=e.map(([o,i],s)=>{let c=o.name||`branch ${s+1}`,l=m(i);return {id:`match-branch-${s}`,name:c,kind:"other",children:l?[l]:[]}});if(t){let o=m(t);r.push({id:"match-default",name:"default",kind:"other",children:o?[o]:[]});}return p(n,{name:"match",kind:"other",children:r})}function A(e,t={}){let{format:n="json"}=t,r=I(e);if(r.length===0){let o=m(e);o&&o.name!=="anonymous"&&(r=[o]);}return r.length===0?n==="tree"?"(empty policy chain)":"[]":n==="tree"?Me(r):Le(r)}A.json=e=>A(e,{format:"json"});A.tree=e=>A(e,{format:"tree"});function Le(e){return JSON.stringify(e,null,2)}function Me(e){let t=[],n=(r,o,i,s="")=>{let c="";o>0&&(c=i?"\u2514\u2500 ":"\u251C\u2500 ");let l=De(r.options);if(t.push(`${s}${c}${r.name} (${r.kind})${l}`),r.children&&r.children.length>0){let a=s+(o>0?i?" ":"\u2502 ":""),d=r.children.length;r.children.forEach((y,f)=>{n(y,o+1,f===d-1,a);});}if(r.branch){let a=s+(o>0?i?" ":"\u2502 ":"");if(t.push(`${a} ? ${r.branch.predicate}`),r.branch.thenBranch.length>0){t.push(`${a} \u251C\u2500 then:`);let d=`${a} \u2502 `,y=r.branch.thenBranch.length;r.branch.thenBranch.forEach((f,h)=>{n(f,o+2,h===y-1,d);});}if(r.branch.elseBranch.length>0){t.push(`${a} \u2514\u2500 else:`);let d=`${a} `,y=r.branch.elseBranch.length;r.branch.elseBranch.forEach((f,h)=>{n(f,o+2,h===y-1,d);});}}};return e.forEach((r,o)=>{n(r,0,o===e.length-1,"");}),t.join(`
2
- `)}function De(e){return !e||Object.keys(e).length===0?"":` [${Object.entries(e).map(([n,r])=>`${n}=${Z(r)}`).join(", ")}]`}function Z(e){if(e==null)return "null";if(typeof e=="string")return `"${e}"`;if(typeof e=="boolean"||typeof e=="number")return String(e);if(Array.isArray(e))return e.length===0?"[]":e.length<=3?`[${e.map(Z).join(", ")}]`:`[${e.slice(0,2).map(Z).join(", ")}, ... +${e.length-2}]`;if(typeof e=="object"){let t=Object.keys(e);if(t.length===0)return "{}";let n=t.slice(0,2).join(", ");return t.length>2?`{${n}, ...}`:`{${n}}`}return String(e)}function ve(e,t){let n=I(e);if(n.length===0){let o=m(e);o&&o.name!=="anonymous"&&(n=[o]);}let r=o=>{for(let i of o)if(i.kind===t||i.children&&r(i.children)||i.branch&&(r(i.branch.thenBranch)||r(i.branch.elseBranch)))return true;return false};if(!r(n))throw new Error(`Expected policy kind "${t}" not found in handler graph`)}var $e=["authorization","cookie","set-cookie","x-api-key"];function qe(e){let{logger:t,redactHeaders:n=$e,logBody:r=false}=e,o=i=>{let s={...i};for(let c of n){let l=Object.keys(s).find(a=>a.toLowerCase()===c.toLowerCase());l&&(s[l]="[REDACTED]");}return s};return p(async(i,s)=>{let c=Date.now(),l=randomUUID();t.info(`Request ${l} started`,{requestId:l,method:i.method,url:i.url,headers:o(i.headers),body:r?i.body:void 0});try{let a=await s(i),d=Date.now()-c;return t.info(`Request ${l} completed`,{requestId:l,status:a.status,duration:d,headers:o(a.headers),data:r?a.data:void 0}),a}catch(a){let d=Date.now()-c;throw t.error(`Request ${l} failed`,{requestId:l,duration:d,error:a}),a}},{name:"log",kind:"other",options:{redactHeaders:n,logBody:r}})}async function ie(e,t,n,r){for(let o of e){let i=await o.getDelay(t,n,r);if(i!==void 0)return i}return 0}function je(e,t,n){let{tries:r=3,onRetry:o}=n??{};return p(async(i,s)=>{let c,l=null;for(let a=0;a<r;a++)try{let d=await s(i);if(c=d,!await Promise.resolve(e(d,null,a,i))||a===r-1)return d;o&&await Promise.resolve(o(a+1,null,d));let f=await ie(t,d,null,a);f>0&&await new Promise(h=>setTimeout(h,f));}catch(d){if(l=d instanceof Error?d:new Error(String(d)),a===r-1||!await Promise.resolve(e(null,l,a,i)))throw l;o&&await Promise.resolve(o(a+1,l,null));let f=await ie(t,null,l,a);f>0&&await new Promise(h=>setTimeout(h,f));}return c},{name:"retry",kind:"retry",options:{tries:r},children:[...(()=>{let i=m(e);return i?[i]:[]})(),...t.map(i=>m(i)).filter(i=>i!==void 0)]})}function wt(e){let{limit:t,interval:n=1e3}=e,r=t,o=Date.now(),i=t/n,s=()=>{let c=Date.now(),a=(c-o)*i;a>0&&(r=Math.min(t,r+a),o=c);};return p(async(c,l)=>{if(s(),r<1){let a=1-r,d=Math.ceil(a/i);await new Promise(y=>setTimeout(y,d)),s();}return r-=1,l(c)},{name:"throttle",kind:"other",options:{limit:t,interval:n}})}var se=(r=>(r.Transport="transport",r.Auth="auth",r.Parser="parser",r))(se||{});function Ue(e,t){return p(async(n,r)=>{let o=await r(n);try{let i=await t.validate(e,o.data);return {...o,data:i}}catch(i){throw new P(`Validation failed: ${i.message}`,i)}},{name:"validate",kind:"other",options:{schema:e}})}export{V as CircuitBreakerOpenError,x as DuplicatePolicyError,C as HANDLER_GRAPH,z as HttpError,S as INSPECTABLE_META,w as InvalidSlotError,k as MissingCapabilityError,B as NetworkError,G as NotAcceptableError,P as SerializationError,se as SlotType,N as TimeoutError,T as URLNormalizationError,u as UnireqError,F as UnsupportedAuthForTransport,K as UnsupportedMediaTypeError,ke as appendQueryParams,ve as assertHas,de as attachToGraph,ge as audit,Te as backoff,Je as circuitBreaker,Se as client,E as compose,he as createConsoleAuditLogger,Re as createLoggerAdapter,Ie as either,I as getHandlerGraph,Ae as getHeader,m as getInspectableMeta,Y as getSlotMetadata,xe as hasSlotType,A as inspect,U as inspectable,re as isBodyDescriptor,pe as isInspectable,qe as log,Oe as match,Ee as normalizeHeaders,Q as normalizeURL,p as policy,$ as redactOptions,oe as registerSlot,le as resetIdCounter,je as retry,W as serializationPolicy,Ce as setHeader,Pe as slot,wt as throttle,Ue as validate,J as validatePolicyChain};//# sourceMappingURL=index.js.map
1
+ import {randomUUID}from'crypto';var q=Symbol("unireq.handlerGraph"),L=Symbol("unireq.inspectable"),fe=["token","accessToken","refreshToken","clientSecret","clientId","password","privateKey","secret","apiKey","authorization"],se=0;function me(){se=0;}function ie(e){return process.env.NODE_ENV==="test"||process.env.VITEST?`${e}#${se++}`:`${e}#${randomUUID().slice(0,8)}`}function $(e){if(e===null||typeof e!="object")return e;if(Array.isArray(e))return e.map($);let t={};for(let[n,r]of Object.entries(e))t[n]=$(r);return t}function j(e,t=[]){if(!e||typeof e!="object")return {};let n=new Set([...fe,...t]),r=$(e);for(let o of Object.keys(r))n.has(o)&&(r[o]="***redacted***"),(o.toLowerCase().includes("secret")||o.toLowerCase().includes("token"))&&(r[o]="***redacted***");return r}function H(e,t,n){e[t]=n;}function B(e,t){return e[t]}function f(e,t){let n={...t,id:ie(t.name),options:t.options?j(t.options):void 0};return H(e,L,n),e}function ge(e,t){let n=B(e,q),r=n?[...n,t]:[t];H(e,q,r);}function D(e){return B(e,q)??[]}function N(e,t){let n={...t,id:ie(t.name),options:t.options?j(t.options):void 0};return H(e,L,n),e}function g(e){if(!(!e||typeof e!="function"&&typeof e!="object"))return B(e,L)}function he(e){return g(e)!==void 0}var Te=["authorization","cookie","set-cookie","x-api-key","x-auth-token","x-csrf-token","x-session-id"];function we(){return randomUUID()}function z(e){try{let t=new URL(e),n=["token","api_key","apikey","secret","password","auth"];for(let r of n)t.searchParams.has(r)&&t.searchParams.set(r,"[REDACTED]");return t.toString()}catch{return e}}function be(e){return e>=500?"error":e===401||e===403||e>=400?"warn":"info"}function Pe(e){switch(e){case 401:return ["auth_failure","warn"];case 403:return ["access_denied","warn"];case 429:return ["rate_limit_exceeded","warn"];default:return ["request_completed",be(e)]}}function Ee(e){let{logger:t,getUserId:n,getSessionId:r,getClientIp:o,redactHeaders:s=Te,logSuccess:a=true,correlationIdGenerator:c=we,detectSuspiciousActivity:l}=e;return f(async(i,u)=>{let p=c(),d=Date.now(),y=new Date().toISOString(),h=n?.(i),R=r?.(i),v=o?.(i),b=i.headers["user-agent"];await t.log({timestamp:y,correlationId:p,eventType:"request_started",severity:"info",method:i.method,url:z(i.url),userId:h,sessionId:R,clientIp:v,userAgent:b});try{let T=await u(i),M=Date.now()-d,[P,k]=Pe(T.status);return l?.(i,T,void 0)&&(P="suspicious_activity",k="critical"),(a||T.status>=400)&&await t.log({timestamp:new Date().toISOString(),correlationId:p,eventType:P,severity:k,method:i.method,url:z(i.url),statusCode:T.status,durationMs:M,userId:h,sessionId:R,clientIp:v,userAgent:b}),T}catch(T){let M=Date.now()-d,P=T instanceof Error?T:new Error(String(T)),k="request_failed",oe="error";throw l?.(i,void 0,P)&&(k="suspicious_activity",oe="critical"),await t.log({timestamp:new Date().toISOString(),correlationId:p,eventType:k,severity:oe,method:i.method,url:z(i.url),durationMs:M,userId:h,sessionId:R,clientIp:v,userAgent:b,errorMessage:P.message,errorCode:P.code}),T}},{name:"audit",kind:"other",options:{logSuccess:a,redactHeaders:s}})}function ke(){return {log:e=>{console.log(JSON.stringify(e));}}}function xe(e){return {log:t=>{let n=`[${t.eventType}] ${t.method} ${t.url}`,r={...t};switch(t.severity){case "critical":case "error":e.error(n,r);break;case "warn":e.warn(n,r);break;default:e.info(n,r);}}}}function Ae(e,t,n,r,o){let s=t*r**e,a=Math.min(s,n);return o?Math.floor(Math.random()*a):a}function Ce(e={}){let{initial:t=1e3,max:n=3e4,multiplier:r=2,jitter:o=true}=e;return N({getDelay:(a,c,l)=>Ae(l,t,n,r,o)},{name:"backoff",kind:"strategy",options:{initial:t,max:n,multiplier:r,jitter:o}})}var m=class extends Error{constructor(n,r,o){super(n);this.code=r;this.cause=o;this.name="UnireqError";}},F=class extends m{constructor(t,n){super(t,"NETWORK_ERROR",n),this.name="NetworkError";}},G=class extends m{constructor(n,r){super(`Request timed out after ${n}ms`,"TIMEOUT",r);this.timeoutMs=n;this.name="TimeoutError";}},K=class extends m{status;statusText;headers;data;constructor(t){super(`HTTP Error ${t.status}: ${t.statusText}`,"HTTP_ERROR"),this.name="HttpError",this.status=t.status,this.statusText=t.statusText,this.headers=t.headers,this.data=t.data;}},x=class extends m{constructor(t,n){super(t,"SERIALIZATION_ERROR",n),this.name="SerializationError";}},A=class extends m{constructor(n){super(`Duplicate policy detected: ${n}. Each policy can only be registered once in the chain.`,"DUPLICATE_POLICY");this.policyName=n;this.name="DuplicatePolicyError";}},V=class extends m{constructor(n,r){super(`Authentication type "${n}" is not supported by transport "${r}".`,"UNSUPPORTED_AUTH");this.authType=n;this.transportType=r;this.name="UnsupportedAuthForTransport";}},W=class extends m{constructor(n,r){let o=r?` (received: ${r})`:"";super(`Server cannot produce a response matching the Accept header. Accepted types: ${n.join(", ")}${o}`,"NOT_ACCEPTABLE");this.acceptedTypes=n;this.receivedType=r;this.name="NotAcceptableError";}},Y=class extends m{constructor(n,r){let o=r?` (sent: ${r})`:"";super(`Server cannot process the request payload media type. Supported types: ${n.join(", ")}${o}`,"UNSUPPORTED_MEDIA_TYPE");this.supportedTypes=n;this.sentType=r;this.name="UnsupportedMediaTypeError";}},C=class extends m{constructor(n,r){super(`Transport "${r}" does not support required capability: ${n}`,"MISSING_CAPABILITY");this.capability=n;this.transportType=r;this.name="MissingCapabilityError";}},E=class extends m{constructor(n,r){super(`Invalid slot configuration for ${n}: ${r}`,"INVALID_SLOT");this.slotType=n;this.name="InvalidSlotError";}},w=class extends m{constructor(n,r){super(`Failed to normalize URL "${n}": ${r}`,"URL_NORMALIZATION_FAILED");this.url=n;this.reason=r;this.name="URLNormalizationError";}};var J=class extends m{constructor(n){super("Circuit breaker is OPEN","CIRCUIT_BREAKER_OPEN");this.resetTime=n;this.name="CircuitBreakerOpenError";}};function lt(e={}){let{failureThreshold:t=5,resetTimeout:n=3e4,shouldFail:r=()=>true}=e,o="CLOSED",s=0,a=0;return f(async(c,l)=>{let i=Date.now();if(o==="OPEN")if(i>=a)o="HALF_OPEN";else throw new J(a);try{let u=await l(c);return o==="HALF_OPEN"&&(o="CLOSED"),s=0,u}catch(u){throw r(u)&&(o==="HALF_OPEN"?(o="OPEN",a=Date.now()+n):(s++,s>=t&&(o="OPEN",a=Date.now()+n))),u}},{name:"circuitBreaker",kind:"other",options:{failureThreshold:t,resetTimeout:n,state:o}})}function S(...e){if(e.length===0)return async(r,o)=>o(r);if(e.length===1){let r=e[0];return r||(async(o,s)=>s(o))}let t=async(r,o)=>{let s=async(a,c)=>{let l=e[a];return l?l(c,i=>s(a+1,i)):o(c)};return s(0,r)},n=e.filter(r=>r!==void 0).map(r=>g(r)).filter(r=>r!==void 0);return f(t,{name:"compose",kind:"other",children:n})}var Q=class{constructor(t){this.value=t;}_tag="Ok";isOk(){return true}isErr(){return false}map(t){return U(t(this.value))}flatMap(t){return t(this.value)}unwrap(){return this.value}unwrapOr(t){return this.value}unwrapErr(){throw new Error("Called unwrapErr on Ok value")}match(t){return t.ok(this.value)}},Z=class{constructor(t){this.error=t;}_tag="Err";isOk(){return false}isErr(){return true}map(t){return O(this.error)}flatMap(t){return O(this.error)}unwrap(){throw this.error}unwrapOr(t){return t}unwrapErr(){return this.error}match(t){return t.err(this.error)}};function U(e){return new Q(e)}function O(e){return new Z(e)}function Se(e){return e._tag==="Ok"}function Oe(e){return e._tag==="Err"}async function _(e){try{let t=await e;return U(t)}catch(t){return O(t)}}function Ie(e){try{return U(e())}catch(t){return O(t)}}function ae(e){return typeof e=="object"&&e!==null&&"__brand"in e&&e.__brand==="BodyDescriptor"&&"serialize"in e&&typeof e.serialize=="function"}function X(){return async(e,t)=>{if(ae(e.body)){let n=e.body,r=n.serialize(),o=n.contentType&&!e.headers["content-type"]&&!e.headers["Content-Type"]&&!(r instanceof FormData),s={...e,body:r,headers:{...e.headers,...o?{"content-type":n.contentType}:{}}};return t(s)}return t(e)}}var le=new WeakMap;function ce(e,t){le.set(e,t);}function ee(e){return le.get(e)}function te(e,t){let n=new Map;for(let r of e){let o=ee(r);if(!o)continue;let s=`${o.type}:${o.name}`;if(n.get(s))throw new A(o.name);if(n.set(s,o),t&&o.requiredCapabilities){for(let c of o.requiredCapabilities)if(!t[c])throw new C(c,"current")}}ve(Array.from(n.values()));}function ve(e){let t=-1,n=-1,r=-1;if(e.forEach((o,s)=>{switch(o.type){case "transport":t=s;break;case "auth":n=s;break;case "parser":r=s;break}}),t!==-1&&t!==e.length-1)throw new E("transport","Transport slot must be the last policy in the chain");if(n!==-1&&r!==-1&&n>r)throw new E("auth","Auth slot must come before parser slot in the chain")}function Me(e){return t=>(ce(t,e),t)}function qe(e,t){return ee(e)?.type===t}function re(e,t={}){let{base:n,defaultScheme:r="https"}=t;try{if(e.includes("://"))return new URL(e).toString();if(e.startsWith("//"))return new URL(`${r}:${e}`).toString();if(n){let s=n.includes("://")?n:`${r}://${n}`;return new URL(e,s).toString()}if(!e.match(/^[a-z][a-z0-9+.-]*:/i))throw new w(e,'Relative URL requires URI in transport. Use http("https://api.com") or provide absolute URL.');return new URL(e).toString()}catch(o){if(o instanceof w)throw o;let s=o instanceof Error?o.message:String(o);throw new w(e,s)}}function Le(e,t){let n=new URL(e);for(let[r,o]of Object.entries(t))o!==void 0&&n.searchParams.append(r,String(o));return n.toString()}function De(e){let t={};for(let[n,r]of Object.entries(e))t[n.toLowerCase()]=r;return t}function Ue(e,t){let n=t.toLowerCase();if(n in e)return e[n];for(let[r,o]of Object.entries(e))if(r.toLowerCase()===n)return o}function _e(e,t,n){let r=t.toLowerCase(),o={};for(let[s,a]of Object.entries(e))s.toLowerCase()!==r&&(o[s]=a);return o[r]=n,o}function $e(e){return new Headers(e)}function je(e){let t={};return e.forEach((n,r)=>{t[r]=n;}),t}function ue(e){if(e===null||typeof e!="object"||Array.isArray(e))return false;if(typeof e=="function")return false;let n=Object.keys(e),r=new Set(["body","policies","signal"]);if(n.length===0)return true;let o=n.some(a=>r.has(a)),s=n.every(a=>r.has(a));return o&&s}function He(e,...t){let n=typeof e=="function"?e:e.transport,r=typeof e=="function"?void 0:e.capabilities;te(t,r);let o=S(X(),...t),s=async(p,d,y,h,R)=>{if(!p||typeof p!="string"||p.trim().length===0)throw new m("URL must be a non-empty string","INVALID_URL");let b={url:p.includes("://")?re(p,{}):p,method:d,headers:{},body:y,signal:R};if(h.length>0){let T=S(...h);return S(o,T)(b,n)}return o(b,n)},a=p=>(d,...y)=>{let h=y[0];if(h!==void 0&&ue(h)){let R=h;return s(d,p,R.body,R.policies||[],R.signal)}return s(d,p,void 0,y)},c=p=>(d,y,...h)=>{if(y!==void 0&&ue(y)){let R=y;return s(d,p,R.body,R.policies||[],R.signal)}return s(d,p,y,h)},l=p=>(d,...y)=>_(a(p)(d,...y)),i=p=>(d,y,...h)=>_(c(p)(d,y,...h)),u={request:l("GET"),get:l("GET"),head:l("HEAD"),post:i("POST"),put:i("PUT"),delete:l("DELETE"),patch:i("PATCH"),options:l("OPTIONS")};return {request:a("GET"),get:a("GET"),head:a("HEAD"),post:c("POST"),put:c("PUT"),delete:a("DELETE"),patch:c("PATCH"),options:a("OPTIONS"),safe:u}}function Be(e,t,n){let r=async(i,u)=>await Promise.resolve(e(i))?t(i,u):n?n(i,u):u(i),o=e.name||"anonymous predicate",s=g(t),a=s?[s]:[],c=n?g(n):void 0;return f(r,{name:"either",kind:"other",branch:{predicate:o,thenBranch:a,elseBranch:c?[c]:[]}})}function Ne(e,t){let n=async(o,s)=>{for(let[a,c]of e)if(await Promise.resolve(a(o)))return c(o,s);return t?t(o,s):s(o)},r=e.map(([o,s],a)=>{let c=o.name||`branch ${a+1}`,l=g(s);return {id:`match-branch-${a}`,name:c,kind:"other",children:l?[l]:[]}});if(t){let o=g(t);r.push({id:"match-default",name:"default",kind:"other",children:o?[o]:[]});}return f(n,{name:"match",kind:"other",children:r})}function I(e,t={}){let{format:n="json"}=t,r=D(e);if(r.length===0){let o=g(e);o&&o.name!=="anonymous"&&(r=[o]);}return r.length===0?n==="tree"?"(empty policy chain)":"[]":n==="tree"?Fe(r):ze(r)}I.json=e=>I(e,{format:"json"});I.tree=e=>I(e,{format:"tree"});function ze(e){return JSON.stringify(e,null,2)}function Fe(e){let t=[],n=(r,o,s,a="")=>{let c="";o>0&&(c=s?"\u2514\u2500 ":"\u251C\u2500 ");let l=Ge(r.options);if(t.push(`${a}${c}${r.name} (${r.kind})${l}`),r.children&&r.children.length>0){let i=a+(o>0?s?" ":"\u2502 ":""),u=r.children.length;r.children.forEach((p,d)=>{n(p,o+1,d===u-1,i);});}if(r.branch){let i=a+(o>0?s?" ":"\u2502 ":"");if(t.push(`${i} ? ${r.branch.predicate}`),r.branch.thenBranch.length>0){t.push(`${i} \u251C\u2500 then:`);let u=`${i} \u2502 `,p=r.branch.thenBranch.length;r.branch.thenBranch.forEach((d,y)=>{n(d,o+2,y===p-1,u);});}if(r.branch.elseBranch.length>0){t.push(`${i} \u2514\u2500 else:`);let u=`${i} `,p=r.branch.elseBranch.length;r.branch.elseBranch.forEach((d,y)=>{n(d,o+2,y===p-1,u);});}}};return e.forEach((r,o)=>{n(r,0,o===e.length-1,"");}),t.join(`
2
+ `)}function Ge(e){return !e||Object.keys(e).length===0?"":` [${Object.entries(e).map(([n,r])=>`${n}=${ne(r)}`).join(", ")}]`}function ne(e){if(e==null)return "null";if(typeof e=="string")return `"${e}"`;if(typeof e=="boolean"||typeof e=="number")return String(e);if(Array.isArray(e))return e.length===0?"[]":e.length<=3?`[${e.map(ne).join(", ")}]`:`[${e.slice(0,2).map(ne).join(", ")}, ... +${e.length-2}]`;if(typeof e=="object"){let t=Object.keys(e);if(t.length===0)return "{}";let n=t.slice(0,2).join(", ");return t.length>2?`{${n}, ...}`:`{${n}}`}return String(e)}function Ke(e,t){let n=D(e);if(n.length===0){let o=g(e);o&&o.name!=="anonymous"&&(n=[o]);}let r=o=>{for(let s of o)if(s.kind===t||s.children&&r(s.children)||s.branch&&(r(s.branch.thenBranch)||r(s.branch.elseBranch)))return true;return false};if(!r(n))throw new Error(`Expected policy kind "${t}" not found in handler graph`)}var We=["authorization","cookie","set-cookie","x-api-key"];function Ye(e){let{logger:t,redactHeaders:n=We,logBody:r=false}=e,o=s=>{let a={...s};for(let c of n){let l=Object.keys(a).find(i=>i.toLowerCase()===c.toLowerCase());l&&(a[l]="[REDACTED]");}return a};return f(async(s,a)=>{let c=Date.now(),l=randomUUID();t.info(`Request ${l} started`,{requestId:l,method:s.method,url:s.url,headers:o(s.headers),body:r?s.body:void 0});try{let i=await a(s),u=Date.now()-c;return t.info(`Request ${l} completed`,{requestId:l,status:i.status,duration:u,headers:o(i.headers),data:r?i.data:void 0}),i}catch(i){let u=Date.now()-c;throw t.error(`Request ${l} failed`,{requestId:l,duration:u,error:i}),i}},{name:"log",kind:"other",options:{redactHeaders:n,logBody:r}})}async function pe(e,t,n,r){for(let o of e){let s=await o.getDelay(t,n,r);if(s!==void 0)return s}return 0}function Je(e,t,n){let{tries:r=3,onRetry:o}=n??{};return f(async(s,a)=>{let c,l=null;for(let i=0;i<r;i++)try{let u=await a(s);if(c=u,!await Promise.resolve(e(u,null,i,s))||i===r-1)return u;o&&await Promise.resolve(o(i+1,null,u));let d=await pe(t,u,null,i);d>0&&await new Promise(y=>setTimeout(y,d));}catch(u){if(l=u instanceof Error?u:new Error(String(u)),i===r-1||!await Promise.resolve(e(null,l,i,s)))throw l;o&&await Promise.resolve(o(i+1,l,null));let d=await pe(t,null,l,i);d>0&&await new Promise(y=>setTimeout(y,d));}return c},{name:"retry",kind:"retry",options:{tries:r},children:[...(()=>{let s=g(e);return s?[s]:[]})(),...t.map(s=>g(s)).filter(s=>s!==void 0)]})}function Dt(e){let{limit:t,interval:n=1e3}=e,r=t,o=Date.now(),s=t/n,a=()=>{let c=Date.now(),i=(c-o)*s;i>0&&(r=Math.min(t,r+i),o=c);};return f(async(c,l)=>{if(a(),r<1){let i=1-r,u=Math.ceil(i/s);await new Promise(p=>setTimeout(p,u)),a();}return r-=1,l(c)},{name:"throttle",kind:"other",options:{limit:t,interval:n}})}var de=(r=>(r.Transport="transport",r.Auth="auth",r.Parser="parser",r))(de||{});function Qe(e,t){return f(async(n,r)=>{let o=await r(n);try{let s=await t.validate(e,o.data);return {...o,data:s}}catch(s){throw new x(`Validation failed: ${s.message}`,s)}},{name:"validate",kind:"other",options:{schema:e}})}/* v8 ignore next 3 -- @preserve defensive: TypeScript prevents passing functions as RequestOptions */export{J as CircuitBreakerOpenError,A as DuplicatePolicyError,q as HANDLER_GRAPH,K as HttpError,L as INSPECTABLE_META,E as InvalidSlotError,C as MissingCapabilityError,F as NetworkError,W as NotAcceptableError,x as SerializationError,de as SlotType,G as TimeoutError,w as URLNormalizationError,m as UnireqError,V as UnsupportedAuthForTransport,Y as UnsupportedMediaTypeError,Le as appendQueryParams,Ke as assertHas,ge as attachToGraph,Ee as audit,Ce as backoff,lt as circuitBreaker,He as client,S as compose,ke as createConsoleAuditLogger,xe as createLoggerAdapter,Be as either,O as err,je as fromNativeHeaders,_ as fromPromise,Ie as fromTry,D as getHandlerGraph,Ue as getHeader,g as getInspectableMeta,ee as getSlotMetadata,qe as hasSlotType,I as inspect,N as inspectable,ae as isBodyDescriptor,Oe as isErr,he as isInspectable,Se as isOk,Ye as log,Ne as match,De as normalizeHeaders,re as normalizeURL,U as ok,f as policy,j as redactOptions,ce as registerSlot,me as resetIdCounter,Je as retry,X as serializationPolicy,_e as setHeader,Me as slot,Dt as throttle,$e as toNativeHeaders,Qe as validate,te as validatePolicyChain};//# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/introspection.ts","../src/audit.ts","../src/backoff.ts","../src/errors.ts","../src/circuit-breaker.ts","../src/compose.ts","../src/serialization.ts","../src/slots.ts","../src/url.ts","../src/client.ts","../src/either.ts","../src/inspect.ts","../src/logging.ts","../src/retry.ts","../src/throttle.ts","../src/types.ts","../src/validation.ts"],"names":["HANDLER_GRAPH","INSPECTABLE_META","SECRET_KEYS","idCounter","resetIdCounter","generateId","name","randomUUID","deepClone","obj","result","key","value","redactOptions","opts","extraKeys","keys","clone","attachMetadata","target","symbol","meta","getMetadata","policy","fn","withId","attachToGraph","handler","prev","next","getHandlerGraph","inspectable","getInspectableMeta","isInspectable","DEFAULT_REDACT_HEADERS","generateCorrelationId","sanitizeUrl","url","parsed","sensitiveParams","param","getSeverityFromStatus","status","audit","options","logger","getUserId","getSessionId","getClientIp","redactHeaders","logSuccess","correlationIdGenerator","detectSuspiciousActivity","ctx","correlationId","startTime","timestamp","userId","sessionId","clientIp","userAgent","response","durationMs","eventType","severity","error","err","createConsoleAuditLogger","entry","createLoggerAdapter","message","calculateBackoff","attempt","baseMs","maxMs","multiplier","jitter","exponential","capped","backoff","initial","max","_result","_error","UnireqError","code","cause","NetworkError","TimeoutError","timeoutMs","HttpError","SerializationError","DuplicatePolicyError","policyName","UnsupportedAuthForTransport","authType","transportType","NotAcceptableError","acceptedTypes","receivedType","received","UnsupportedMediaTypeError","supportedTypes","sentType","sent","MissingCapabilityError","capability","InvalidSlotError","slotType","URLNormalizationError","reason","CircuitBreakerOpenError","resetTime","circuitBreaker","failureThreshold","resetTimeout","shouldFail","state","failureCount","nextAttempt","now","compose","policies","_ctx","singlePolicy","impl","dispatch","i","context","nextCtx","children","p","m","isBodyDescriptor","serializationPolicy","descriptor","serialized","shouldSetContentType","updatedCtx","slotRegistry","registerSlot","metadata","getSlotMetadata","validatePolicyChain","transportCapabilities","seenSlots","slotKey","validateSlotOrdering","slots","transportIndex","authIndex","parserIndex","slot","hasSlotType","type","normalizeURL","base","defaultScheme","baseURL","appendQueryParams","params","urlObj","normalizeHeaders","headers","normalized","getHeader","lowerName","setHeader","newHeaders","val","client","transport","actualTransport","capabilities","composedPolicy","executeRequest","method","body","perRequestPolicies","perRequestPolicy","either","predicate","thenPolicy","elsePolicy","predicateDesc","thenMetaRaw","thenMeta","elseMetaRaw","match","branches","defaultPolicy","idx","defaultMeta","inspect","format","graph","toTree","toJson","nodes","lines","walk","depth","isLast","parentPrefix","prefix","optionsStr","formatOptions","childPrefix","childrenLength","child","branchPrefix","thenPrefix","thenLength","elsePrefix","elseLength","node","prettyValue","keyList","assertHas","kind","hasKind","log","logBody","redact","redacted","header","k","start","requestId","duration","calculateDelay","strategies","strategy","strategyDelay","retry","tries","onRetry","lastResponse","lastError","delay","resolve","s","throttle","limit","interval","tokens","lastRefill","refillRate","refill","newTokens","missingTokens","waitTime","SlotType","validate","schema","adapter","validatedData"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Policy introspection utilities\n * Enables inspection of policy composition for debugging and testing\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { Policy, RequestContext, Response } from './types.js';\n\n/**\n * Policy kind/slot classification\n */\nexport type Kind =\n | 'transport'\n | 'auth'\n | 'parser'\n | 'headers'\n | 'retry'\n | 'timeout'\n | 'files'\n | 'predicate'\n | 'strategy'\n | 'other';\n\n/**\n * Unified metadata for any inspectable (policy, predicate, strategy, etc.)\n * All inspectable items share this base structure\n */\nexport interface InspectableMeta {\n /** Unique stable ID for snapshot tests */\n readonly id: string;\n /** Function/object name */\n readonly name: string;\n /** Kind/type classification - unified for all inspectables */\n readonly kind: Kind;\n /** Redacted configuration options */\n readonly options?: Record<string, unknown>;\n /** Child inspectables (for composed functions) */\n readonly children?: ReadonlyArray<InspectableMeta>;\n /** Branch structure (for either/match policies) */\n readonly branch?: {\n readonly predicate: string;\n readonly thenBranch: ReadonlyArray<InspectableMeta>;\n readonly elseBranch: ReadonlyArray<InspectableMeta>;\n };\n}\n\n/**\n * Handler type (policy chain result)\n */\nexport type Handler = (ctx: RequestContext) => Promise<Response>;\n\n/**\n * Symbol for handler graph attachment\n */\nexport const HANDLER_GRAPH = Symbol('unireq.handlerGraph');\n\n/**\n * Symbol for inspectable metadata attachment\n */\nexport const INSPECTABLE_META = Symbol('unireq.inspectable');\n\n/**\n * Keys that should be redacted from options (secrets)\n */\nconst SECRET_KEYS = [\n 'token',\n 'accessToken',\n 'refreshToken',\n 'clientSecret',\n 'clientId',\n 'password',\n 'privateKey',\n 'secret',\n 'apiKey',\n 'authorization',\n] as const;\n\n/**\n * Counter for deterministic IDs in tests\n */\nlet idCounter = 0;\n\n/**\n * Reset ID counter (for deterministic tests)\n */\nexport function resetIdCounter(): void {\n idCounter = 0;\n}\n\n/**\n * Generate stable ID for policy metadata\n */\nfunction generateId(name: string): string {\n // In test mode, use deterministic counter\n if (process.env['NODE_ENV'] === 'test' || process.env['VITEST']) {\n return `${name}#${idCounter++}`;\n }\n // In production, use cryptographically secure random UUID\n return `${name}#${randomUUID().slice(0, 8)}`;\n}\n\n/**\n * Deep clones an object while preserving functions and other non-JSON types\n */\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(deepClone) as unknown as T;\n }\n\n const result = {} as Record<string, unknown>;\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[key] = deepClone(value);\n }\n return result as T;\n}\n\n/**\n * Redacts sensitive options for safe inspection\n * @param opts - Options object to redact\n * @param extraKeys - Additional keys to redact\n * @returns Redacted options clone\n */\nexport function redactOptions(opts: Record<string, unknown>, extraKeys: string[] = []): Record<string, unknown> {\n if (!opts || typeof opts !== 'object') {\n return {};\n }\n\n const keys = new Set([...SECRET_KEYS, ...extraKeys]);\n const clone = deepClone(opts);\n\n for (const key of Object.keys(clone)) {\n if (keys.has(key)) {\n clone[key] = '***redacted***';\n }\n // Also redact nested objects with \"secret\" in the key name\n if (key.toLowerCase().includes('secret') || key.toLowerCase().includes('token')) {\n clone[key] = '***redacted***';\n }\n }\n\n return clone;\n}\n\n/**\n * Type-safe metadata attachment helper\n * Attaches metadata to a function/object via symbol\n */\nfunction attachMetadata<T>(target: T, symbol: symbol, meta: InspectableMeta): void {\n // Use Record<symbol, unknown> to safely index with symbol\n (target as Record<symbol, unknown>)[symbol] = meta;\n}\n\n/**\n * Type-safe metadata retrieval helper\n * Retrieves metadata from a function/object via symbol\n */\nfunction getMetadata<T>(target: T, symbol: symbol): InspectableMeta | undefined {\n // Use Record<symbol, unknown> to safely index with symbol\n return (target as Record<symbol, unknown>)[symbol] as InspectableMeta | undefined;\n}\n\n/**\n * Creates a tagged policy with metadata for introspection\n * @param fn - Policy function\n * @param meta - Policy metadata (without id)\n * @returns Tagged policy function\n *\n * @example\n * ```ts\n * export function timeout(ms: number): Policy {\n * return policy(\n * async (ctx, next) => {\n * // ... implementation\n * },\n * { name: 'timeout', kind: 'timeout', options: { ms } }\n * );\n * }\n * ```\n */\nexport function policy<T extends Policy>(\n fn: T,\n meta: Omit<InspectableMeta, 'id'> & { options?: Record<string, unknown> },\n): T {\n const withId: InspectableMeta = {\n ...meta,\n id: generateId(meta.name),\n options: meta.options ? redactOptions(meta.options) : undefined,\n };\n\n // Attach metadata to function using typed helper\n attachMetadata(fn, INSPECTABLE_META, withId);\n\n return fn;\n}\n\n/**\n * Attaches policy metadata to handler graph\n * @param handler - Handler to attach graph to\n * @param meta - Policy metadata to attach\n */\nexport function attachToGraph(handler: Handler, meta: InspectableMeta): void {\n const prev = getMetadata(handler, HANDLER_GRAPH) as InspectableMeta[] | undefined;\n const next = prev ? [...prev, meta] : [meta];\n attachMetadata(handler, HANDLER_GRAPH, next as unknown as InspectableMeta);\n}\n\n/**\n * Gets handler graph\n * @param handler - Handler to get graph from\n * @returns Policy metadata graph\n */\nexport function getHandlerGraph(handler: Handler): ReadonlyArray<InspectableMeta> {\n const graph = getMetadata(handler, HANDLER_GRAPH);\n return (graph as unknown as InspectableMeta[]) ?? [];\n}\n\n/**\n * Creates an inspectable function/object with metadata\n * Generic version that works with any type of function (predicate, strategy, etc.)\n *\n * @param fn - Function to make inspectable\n * @param meta - Metadata (without id)\n * @returns Tagged function\n *\n * @example\n * ```ts\n * export function backoff(options: BackoffOptions): RetryDelayStrategy {\n * const strategy = {\n * getDelay: (result, error, attempt) => { ... }\n * };\n * return inspectable(strategy, {\n * name: 'backoff',\n * kind: 'strategy',\n * options: { initial: options.initial, max: options.max }\n * });\n * }\n * ```\n */\nexport function inspectable<T>(fn: T, meta: Omit<InspectableMeta, 'id'> & { options?: Record<string, unknown> }): T {\n const withId: InspectableMeta = {\n ...meta,\n id: generateId(meta.name),\n options: meta.options ? redactOptions(meta.options) : undefined,\n };\n\n // Attach metadata to function/object using typed helper\n attachMetadata(fn, INSPECTABLE_META, withId);\n\n return fn;\n}\n\n/**\n * Extracts inspectable metadata from any function/object\n * @param fn - Function/object to extract metadata from\n * @returns Metadata or undefined if not inspectable\n */\nexport function getInspectableMeta(fn: unknown): InspectableMeta | undefined {\n if (!fn || (typeof fn !== 'function' && typeof fn !== 'object')) {\n return undefined;\n }\n return getMetadata(fn, INSPECTABLE_META);\n}\n\n/**\n * Checks if a function/object has inspectable metadata\n * @param fn - Function/object to check\n * @returns True if inspectable\n */\nexport function isInspectable(fn: unknown): boolean {\n return getInspectableMeta(fn) !== undefined;\n}\n","/**\n * Structured audit logging for security events (OWASP A09:2021)\n *\n * @see https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { policy } from './introspection.js';\nimport type { Logger, Policy, RequestContext, Response } from './types.js';\n\n/**\n * Security event types for audit logging\n */\nexport type SecurityEventType =\n | 'auth_success'\n | 'auth_failure'\n | 'auth_token_refresh'\n | 'auth_token_expired'\n | 'access_denied'\n | 'rate_limit_exceeded'\n | 'validation_failure'\n | 'request_started'\n | 'request_completed'\n | 'request_failed'\n | 'suspicious_activity';\n\n/**\n * Structured audit log entry\n */\nexport interface AuditLogEntry {\n /** ISO 8601 timestamp */\n readonly timestamp: string;\n /** Unique correlation ID for request tracing */\n readonly correlationId: string;\n /** Security event type */\n readonly eventType: SecurityEventType;\n /** Event severity level */\n readonly severity: 'info' | 'warn' | 'error' | 'critical';\n /** HTTP method */\n readonly method: string;\n /** Request URL (sanitized) */\n readonly url: string;\n /** HTTP status code (if available) */\n readonly statusCode?: number;\n /** Request duration in milliseconds */\n readonly durationMs?: number;\n /** Client IP address (if available) */\n readonly clientIp?: string;\n /** User agent (if available) */\n readonly userAgent?: string;\n /** User ID or identifier (if authenticated) */\n readonly userId?: string;\n /** Session ID (if available) */\n readonly sessionId?: string;\n /** Additional context */\n readonly context?: Record<string, unknown>;\n /** Error message (sanitized, no stack traces) */\n readonly errorMessage?: string;\n /** Error code */\n readonly errorCode?: string;\n}\n\n/**\n * Audit logger interface\n */\nexport interface AuditLogger {\n readonly log: (entry: AuditLogEntry) => void | Promise<void>;\n}\n\n/**\n * Audit logging options\n */\nexport interface AuditOptions {\n /** Audit logger implementation */\n readonly logger: AuditLogger;\n /** Function to extract user ID from context */\n readonly getUserId?: (ctx: RequestContext) => string | undefined;\n /** Function to extract session ID from context */\n readonly getSessionId?: (ctx: RequestContext) => string | undefined;\n /** Function to extract client IP from context */\n readonly getClientIp?: (ctx: RequestContext) => string | undefined;\n /** Headers to redact from logs */\n readonly redactHeaders?: ReadonlyArray<string>;\n /** Log successful requests (default: true) */\n readonly logSuccess?: boolean;\n /** Log request bodies (default: false) */\n readonly logBody?: boolean;\n /** Custom correlation ID generator */\n readonly correlationIdGenerator?: () => string;\n /** Suspicious activity detector */\n readonly detectSuspiciousActivity?: (ctx: RequestContext, response?: Response, error?: Error) => boolean;\n}\n\n/**\n * Default headers to redact for security\n */\nconst DEFAULT_REDACT_HEADERS = [\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'x-auth-token',\n 'x-csrf-token',\n 'x-session-id',\n];\n\n/**\n * Generate a correlation ID using cryptographically secure random\n */\nfunction generateCorrelationId(): string {\n return randomUUID();\n}\n\n/**\n * Sanitize URL by removing sensitive query parameters\n */\nfunction sanitizeUrl(url: string): string {\n try {\n const parsed = new URL(url);\n const sensitiveParams = ['token', 'api_key', 'apikey', 'secret', 'password', 'auth'];\n\n for (const param of sensitiveParams) {\n if (parsed.searchParams.has(param)) {\n parsed.searchParams.set(param, '[REDACTED]');\n }\n }\n\n return parsed.toString();\n } catch {\n return url;\n }\n}\n\n/**\n * Get severity based on status code\n */\nfunction getSeverityFromStatus(status: number): 'info' | 'warn' | 'error' | 'critical' {\n if (status >= 500) return 'error';\n if (status === 401 || status === 403) return 'warn';\n if (status >= 400) return 'warn';\n return 'info';\n}\n\n/**\n * Creates a structured audit logging policy (OWASP A09:2021)\n *\n * Provides comprehensive security event logging with:\n * - Structured JSON format\n * - Correlation IDs for request tracing\n * - Sensitive data redaction\n * - Security event classification\n * - Suspicious activity detection\n *\n * @param options - Audit logging configuration\n * @returns Policy that logs security events\n *\n * @example\n * ```ts\n * const auditLogger: AuditLogger = {\n * log: (entry) => console.log(JSON.stringify(entry)),\n * };\n *\n * const api = client(\n * http('https://api.example.com'),\n * audit({\n * logger: auditLogger,\n * getUserId: (ctx) => ctx.headers['x-user-id'],\n * }),\n * parse.json()\n * );\n * ```\n */\nexport function audit(options: AuditOptions): Policy {\n const {\n logger,\n getUserId,\n getSessionId,\n getClientIp,\n redactHeaders = DEFAULT_REDACT_HEADERS,\n logSuccess = true,\n correlationIdGenerator = generateCorrelationId,\n detectSuspiciousActivity,\n } = options;\n\n return policy(\n async (ctx, next) => {\n const correlationId = correlationIdGenerator();\n const startTime = Date.now();\n const timestamp = new Date().toISOString();\n\n // Extract context information\n const userId = getUserId?.(ctx);\n const sessionId = getSessionId?.(ctx);\n const clientIp = getClientIp?.(ctx);\n const userAgent = ctx.headers['user-agent'];\n\n // Log request started\n await logger.log({\n timestamp,\n correlationId,\n eventType: 'request_started',\n severity: 'info',\n method: ctx.method,\n url: sanitizeUrl(ctx.url),\n userId,\n sessionId,\n clientIp,\n userAgent,\n });\n\n try {\n const response = await next(ctx);\n const durationMs = Date.now() - startTime;\n\n // Determine event type based on response\n let eventType: SecurityEventType = 'request_completed';\n let severity = getSeverityFromStatus(response.status);\n\n if (response.status === 401) {\n eventType = 'auth_failure';\n severity = 'warn';\n } else if (response.status === 403) {\n eventType = 'access_denied';\n severity = 'warn';\n } else if (response.status === 429) {\n eventType = 'rate_limit_exceeded';\n severity = 'warn';\n }\n\n // Check for suspicious activity\n if (detectSuspiciousActivity?.(ctx, response, undefined)) {\n eventType = 'suspicious_activity';\n severity = 'critical';\n }\n\n // Log completion (unless it's a success and logSuccess is false)\n if (logSuccess || response.status >= 400) {\n await logger.log({\n timestamp: new Date().toISOString(),\n correlationId,\n eventType,\n severity,\n method: ctx.method,\n url: sanitizeUrl(ctx.url),\n statusCode: response.status,\n durationMs,\n userId,\n sessionId,\n clientIp,\n userAgent,\n });\n }\n\n return response;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Check for suspicious activity\n let eventType: SecurityEventType = 'request_failed';\n let severity: 'error' | 'critical' = 'error';\n\n if (detectSuspiciousActivity?.(ctx, undefined, err)) {\n eventType = 'suspicious_activity';\n severity = 'critical';\n }\n\n // Log failure with sanitized error message (no stack trace)\n await logger.log({\n timestamp: new Date().toISOString(),\n correlationId,\n eventType,\n severity,\n method: ctx.method,\n url: sanitizeUrl(ctx.url),\n durationMs,\n userId,\n sessionId,\n clientIp,\n userAgent,\n errorMessage: err.message,\n errorCode: (err as { code?: string }).code,\n });\n\n throw error;\n }\n },\n {\n name: 'audit',\n kind: 'other',\n options: {\n logSuccess,\n redactHeaders,\n },\n },\n );\n}\n\n/**\n * Creates a console-based audit logger for development\n * @returns AuditLogger that logs to console in JSON format\n */\nexport function createConsoleAuditLogger(): AuditLogger {\n return {\n log: (entry) => {\n console.log(JSON.stringify(entry));\n },\n };\n}\n\n/**\n * Creates an audit logger that uses a standard Logger interface\n * @param logger - Standard logger implementation\n * @returns AuditLogger that delegates to the standard logger\n */\nexport function createLoggerAdapter(logger: Logger): AuditLogger {\n return {\n log: (entry) => {\n const message = `[${entry.eventType}] ${entry.method} ${entry.url}`;\n const meta: Record<string, unknown> = { ...entry };\n\n switch (entry.severity) {\n case 'critical':\n case 'error':\n logger.error(message, meta);\n break;\n case 'warn':\n logger.warn(message, meta);\n break;\n default:\n logger.info(message, meta);\n }\n },\n };\n}\n","/**\n * Exponential backoff strategy for retry delays\n * Provides exponential backoff with optional jitter\n * Transport-agnostic - works with any protocol\n */\n\nimport { inspectable } from './introspection.js';\nimport type { RetryDelayStrategy } from './retry.js';\n\n/**\n * Backoff options\n */\nexport interface BackoffOptions {\n /** Initial backoff in milliseconds (default: 1000) */\n readonly initial?: number;\n /** Maximum backoff in milliseconds (default: 30000) */\n readonly max?: number;\n /** Multiplier for exponential backoff (default: 2) */\n readonly multiplier?: number;\n /** Add jitter to backoff (default: true) */\n readonly jitter?: boolean;\n}\n\n/**\n * Calculates backoff delay with exponential backoff and optional jitter\n * @param attempt - Current attempt number (0-indexed)\n * @param baseMs - Base backoff in milliseconds\n * @param maxMs - Maximum backoff in milliseconds\n * @param multiplier - Exponential multiplier\n * @param jitter - Whether to add jitter\n * @returns Delay in milliseconds\n */\nfunction calculateBackoff(attempt: number, baseMs: number, maxMs: number, multiplier: number, jitter: boolean): number {\n const exponential = baseMs * multiplier ** attempt;\n const capped = Math.min(exponential, maxMs);\n\n if (!jitter) {\n return capped;\n }\n\n // Add jitter: random value between 0 and capped\n return Math.floor(Math.random() * capped);\n}\n\n/**\n * Creates an exponential backoff delay strategy\n *\n * @param options - Backoff configuration\n * @returns Delay strategy that calculates exponential backoff\n *\n * @example\n * ```ts\n * import { retry } from '@unireq/core';\n * import { backoff } from '@unireq/core';\n *\n * const policy = retry(\n * myPredicate,\n * [backoff({ initial: 100, max: 5000, jitter: true })],\n * { tries: 3 }\n * );\n * ```\n */\nexport function backoff<T = unknown>(options: BackoffOptions = {}): RetryDelayStrategy<T> {\n const { initial = 1000, max = 30000, multiplier = 2, jitter = true } = options;\n\n const strategy: RetryDelayStrategy<T> = {\n getDelay: (_result: T | null, _error: Error | null, attempt: number) => {\n return calculateBackoff(attempt, initial, max, multiplier, jitter);\n },\n };\n\n return inspectable(strategy, {\n name: 'backoff',\n kind: 'strategy',\n options: { initial, max, multiplier, jitter },\n });\n}\n","/**\n * DX-focused error types for unireq\n */\n\nimport type { Response } from './types.js';\n\n/** Base error class for unireq */\nexport class UnireqError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public override readonly cause?: unknown,\n ) {\n super(message);\n this.name = 'UnireqError';\n }\n}\n\n/** Error thrown when a network request fails (e.g. DNS, connection refused) */\nexport class NetworkError extends UnireqError {\n constructor(message: string, cause?: unknown) {\n super(message, 'NETWORK_ERROR', cause);\n this.name = 'NetworkError';\n }\n}\n\n/** Error thrown when a request times out */\nexport class TimeoutError extends UnireqError {\n constructor(\n public readonly timeoutMs: number,\n cause?: unknown,\n ) {\n super(`Request timed out after ${timeoutMs}ms`, 'TIMEOUT', cause);\n this.name = 'TimeoutError';\n }\n}\n\n/** Error thrown when an HTTP response has an error status code (4xx, 5xx) */\nexport class HttpError extends UnireqError {\n public readonly status: number;\n public readonly statusText: string;\n public readonly headers: Record<string, string | string[] | undefined>;\n public readonly data: unknown;\n\n constructor(response: Response) {\n super(`HTTP Error ${response.status}: ${response.statusText}`, 'HTTP_ERROR');\n this.name = 'HttpError';\n this.status = response.status;\n this.statusText = response.statusText;\n this.headers = response.headers;\n this.data = response.data;\n }\n}\n\n/** Error thrown when serialization or parsing fails */\nexport class SerializationError extends UnireqError {\n constructor(message: string, cause?: unknown) {\n super(message, 'SERIALIZATION_ERROR', cause);\n this.name = 'SerializationError';\n }\n}\n\n/** Error thrown when duplicate policies are detected */\nexport class DuplicatePolicyError extends UnireqError {\n constructor(public readonly policyName: string) {\n super(\n `Duplicate policy detected: ${policyName}. Each policy can only be registered once in the chain.`,\n 'DUPLICATE_POLICY',\n );\n this.name = 'DuplicatePolicyError';\n }\n}\n\n/** Error thrown when authentication is not supported by transport */\nexport class UnsupportedAuthForTransport extends UnireqError {\n constructor(\n public readonly authType: string,\n public readonly transportType: string,\n ) {\n super(`Authentication type \"${authType}\" is not supported by transport \"${transportType}\".`, 'UNSUPPORTED_AUTH');\n this.name = 'UnsupportedAuthForTransport';\n }\n}\n\n/** HTTP 406 Not Acceptable error */\nexport class NotAcceptableError extends UnireqError {\n constructor(\n public readonly acceptedTypes: ReadonlyArray<string>,\n public readonly receivedType?: string,\n ) {\n const received = receivedType ? ` (received: ${receivedType})` : '';\n super(\n `Server cannot produce a response matching the Accept header. Accepted types: ${acceptedTypes.join(', ')}${received}`,\n 'NOT_ACCEPTABLE',\n );\n this.name = 'NotAcceptableError';\n }\n}\n\n/** HTTP 415 Unsupported Media Type error */\nexport class UnsupportedMediaTypeError extends UnireqError {\n constructor(\n public readonly supportedTypes: ReadonlyArray<string>,\n public readonly sentType?: string,\n ) {\n const sent = sentType ? ` (sent: ${sentType})` : '';\n super(\n `Server cannot process the request payload media type. Supported types: ${supportedTypes.join(', ')}${sent}`,\n 'UNSUPPORTED_MEDIA_TYPE',\n );\n this.name = 'UnsupportedMediaTypeError';\n }\n}\n\n/** Error thrown when required capability is missing */\nexport class MissingCapabilityError extends UnireqError {\n constructor(\n public readonly capability: string,\n public readonly transportType: string,\n ) {\n super(`Transport \"${transportType}\" does not support required capability: ${capability}`, 'MISSING_CAPABILITY');\n this.name = 'MissingCapabilityError';\n }\n}\n\n/** Error thrown for invalid slot configuration */\nexport class InvalidSlotError extends UnireqError {\n constructor(\n public readonly slotType: string,\n message: string,\n ) {\n super(`Invalid slot configuration for ${slotType}: ${message}`, 'INVALID_SLOT');\n this.name = 'InvalidSlotError';\n }\n}\n\n/** Error thrown when URL normalization fails */\nexport class URLNormalizationError extends UnireqError {\n constructor(\n public readonly url: string,\n public readonly reason: string,\n ) {\n super(`Failed to normalize URL \"${url}\": ${reason}`, 'URL_NORMALIZATION_FAILED');\n this.name = 'URLNormalizationError';\n }\n}\n","/**\n * Circuit Breaker policy\n * Prevents cascading failures by stopping requests to a failing service\n */\n\nimport { UnireqError } from './errors.js';\nimport { policy } from './introspection.js';\nimport type { Policy } from './types.js';\n\nexport interface CircuitBreakerOptions {\n /** Number of failures before opening the circuit (default: 5) */\n readonly failureThreshold?: number;\n /** Time in ms to wait before trying again (default: 30000) */\n readonly resetTimeout?: number;\n /** Optional predicate to determine if an error should trigger failure */\n readonly shouldFail?: (error: unknown) => boolean;\n}\n\nenum CircuitState {\n CLOSED = 'CLOSED',\n OPEN = 'OPEN',\n HALF_OPEN = 'HALF_OPEN',\n}\n\n/** Error thrown when circuit is open */\nexport class CircuitBreakerOpenError extends UnireqError {\n constructor(public readonly resetTime: number) {\n super('Circuit breaker is OPEN', 'CIRCUIT_BREAKER_OPEN');\n this.name = 'CircuitBreakerOpenError';\n }\n}\n\nexport function circuitBreaker(options: CircuitBreakerOptions = {}): Policy {\n const { failureThreshold = 5, resetTimeout = 30000, shouldFail = () => true } = options;\n\n let state = CircuitState.CLOSED;\n let failureCount = 0;\n let nextAttempt = 0;\n\n return policy(\n async (ctx, next) => {\n const now = Date.now();\n\n if (state === CircuitState.OPEN) {\n if (now >= nextAttempt) {\n state = CircuitState.HALF_OPEN;\n } else {\n throw new CircuitBreakerOpenError(nextAttempt);\n }\n }\n\n try {\n const response = await next(ctx);\n\n if (state === CircuitState.HALF_OPEN) {\n /* v8 ignore next 2 */\n state = CircuitState.CLOSED;\n failureCount = 0;\n } else {\n // Must be CLOSED\n failureCount = 0; // Reset on success\n }\n\n return response;\n } catch (error) {\n if (shouldFail(error)) {\n if (state === CircuitState.HALF_OPEN) {\n /* v8 ignore next 2 */\n state = CircuitState.OPEN;\n nextAttempt = Date.now() + resetTimeout;\n } else {\n // Must be CLOSED\n failureCount++;\n if (failureCount >= failureThreshold) {\n state = CircuitState.OPEN;\n nextAttempt = Date.now() + resetTimeout;\n }\n }\n }\n throw error;\n }\n },\n {\n name: 'circuitBreaker',\n kind: 'other',\n options: { failureThreshold, resetTimeout, state },\n },\n );\n}\n","/**\n * Policy composition utilities\n */\n\nimport type { InspectableMeta } from './introspection.js';\nimport { getInspectableMeta, policy as tagPolicy } from './introspection.js';\nimport type { Policy, RequestContext, Response } from './types.js';\n\n/**\n * Composes multiple policies into a single policy chain (onion model)\n * @param policies - Array of policies to compose\n * @returns A single composed policy\n */\nexport function compose(...policies: ReadonlyArray<Policy>): Policy {\n if (policies.length === 0) {\n return async (_ctx, next) => next(_ctx);\n }\n\n if (policies.length === 1) {\n const singlePolicy = policies[0];\n if (!singlePolicy) {\n return async (_ctx, next) => next(_ctx);\n }\n return singlePolicy;\n }\n\n const impl = async (ctx: RequestContext, next: (ctx: RequestContext) => Promise<Response>) => {\n const dispatch = async (i: number, context: RequestContext): Promise<Response> => {\n const policy = policies[i];\n if (!policy) {\n return next(context);\n }\n\n return policy(context, (nextCtx) => dispatch(i + 1, nextCtx));\n };\n\n return dispatch(0, ctx);\n };\n\n // Build children metadata from input policies (filter out undefined)\n const children: InspectableMeta[] = policies\n .filter((p) => p !== undefined)\n .map((p) => getInspectableMeta(p))\n .filter((m): m is InspectableMeta => m !== undefined);\n\n return tagPolicy(impl, {\n name: 'compose',\n kind: 'other',\n children,\n });\n}\n","/**\n * Serialization middleware for BodyDescriptor handling\n */\n\nimport type { BodyDescriptor, Policy } from './types.js';\n\n/**\n * Type guard to check if a value is a BodyDescriptor\n * @param value - Value to check\n * @returns True if value is a BodyDescriptor\n */\nexport function isBodyDescriptor(value: unknown): value is BodyDescriptor {\n return (\n typeof value === 'object' &&\n value !== null &&\n '__brand' in value &&\n (value as BodyDescriptor).__brand === 'BodyDescriptor' &&\n 'serialize' in value &&\n typeof (value as BodyDescriptor).serialize === 'function'\n );\n}\n\n/**\n * Serialization policy that converts BodyDescriptor to serialized form\n * This middleware should be applied first in the policy chain to handle\n * body descriptors before other policies process the request\n *\n * @returns Policy that handles BodyDescriptor serialization\n */\nexport function serializationPolicy(): Policy {\n return async (ctx, next) => {\n // Check if body is a BodyDescriptor\n if (isBodyDescriptor(ctx.body)) {\n const descriptor = ctx.body;\n\n // Serialize the body\n const serialized = descriptor.serialize();\n\n // Create updated context with serialized body and Content-Type header\n // IMPORTANT: For FormData, we must NOT set Content-Type manually\n // because fetch needs to set it with the correct boundary\n const shouldSetContentType =\n descriptor.contentType &&\n !ctx.headers['content-type'] &&\n !ctx.headers['Content-Type'] &&\n !(serialized instanceof FormData); // Let fetch handle FormData Content-Type\n\n const updatedCtx = {\n ...ctx,\n body: serialized,\n headers: {\n ...ctx.headers,\n ...(shouldSetContentType ? { 'content-type': descriptor.contentType } : {}),\n },\n };\n\n return next(updatedCtx);\n }\n\n // Not a BodyDescriptor, pass through unchanged\n return next(ctx);\n };\n}\n","/**\n * Slot system for transport, auth, and parser policies\n * Provides compile-time and runtime checks for capability requirements\n */\n\nimport { DuplicatePolicyError, InvalidSlotError, MissingCapabilityError } from './errors.js';\nimport type { Policy, SlotMetadata, SlotType, TransportCapabilities } from './types.js';\n\n/** Registry for slot metadata */\nconst slotRegistry = new WeakMap<Policy, SlotMetadata>();\n\n/**\n * Registers slot metadata for a policy\n * @param policy - The policy to register\n * @param metadata - Slot metadata\n */\nexport function registerSlot(policy: Policy, metadata: SlotMetadata): void {\n slotRegistry.set(policy, metadata);\n}\n\n/**\n * Gets slot metadata for a policy\n * @param policy - The policy to query\n * @returns Slot metadata or undefined\n */\nexport function getSlotMetadata(policy: Policy): SlotMetadata | undefined {\n return slotRegistry.get(policy);\n}\n\n/**\n * Validates a policy chain for slot conflicts and capability requirements\n * @param policies - Array of policies to validate\n * @param transportCapabilities - Transport capabilities\n * @throws {DuplicatePolicyError} If duplicate slots detected\n * @throws {MissingCapabilityError} If required capabilities missing\n * @throws {InvalidSlotError} If slot configuration is invalid\n */\nexport function validatePolicyChain(\n policies: ReadonlyArray<Policy>,\n transportCapabilities?: TransportCapabilities,\n): void {\n const seenSlots = new Map<string, SlotMetadata>();\n\n for (const policy of policies) {\n const metadata = getSlotMetadata(policy);\n if (!metadata) continue;\n\n // Check for duplicate slots of the same type\n const slotKey = `${metadata.type}:${metadata.name}`;\n const existing = seenSlots.get(slotKey);\n if (existing) {\n throw new DuplicatePolicyError(metadata.name);\n }\n seenSlots.set(slotKey, metadata);\n\n // Validate required capabilities\n if (transportCapabilities && metadata.requiredCapabilities) {\n for (const capability of metadata.requiredCapabilities) {\n if (!transportCapabilities[capability]) {\n throw new MissingCapabilityError(capability, 'current');\n }\n }\n }\n }\n\n // Validate slot ordering constraints\n validateSlotOrdering(Array.from(seenSlots.values()));\n}\n\n/**\n * Validates slot ordering (e.g., auth before parser)\n * @param slots - Array of slot metadata\n * @throws {InvalidSlotError} If ordering is invalid\n */\nfunction validateSlotOrdering(slots: ReadonlyArray<SlotMetadata>): void {\n let transportIndex = -1;\n let authIndex = -1;\n let parserIndex = -1;\n\n slots.forEach((slot, i) => {\n switch (slot.type) {\n case 'transport':\n transportIndex = i;\n break;\n case 'auth':\n authIndex = i;\n break;\n case 'parser':\n parserIndex = i;\n break;\n }\n });\n\n // Transport should be last (if present)\n if (transportIndex !== -1 && transportIndex !== slots.length - 1) {\n throw new InvalidSlotError('transport', 'Transport slot must be the last policy in the chain');\n }\n\n // Auth should come before parser (if both present)\n if (authIndex !== -1 && parserIndex !== -1 && authIndex > parserIndex) {\n throw new InvalidSlotError('auth', 'Auth slot must come before parser slot in the chain');\n }\n}\n\n/**\n * Creates a slot decorator for policies\n * @param metadata - Slot metadata\n * @returns Decorator function\n */\nexport function slot(metadata: SlotMetadata) {\n return (policy: Policy): Policy => {\n registerSlot(policy, metadata);\n return policy;\n };\n}\n\n/**\n * Checks if a policy has a specific slot type\n * @param policy - The policy to check\n * @param type - The slot type to match\n * @returns True if policy has the slot type\n */\nexport function hasSlotType(policy: Policy, type: SlotType): boolean {\n const metadata = getSlotMetadata(policy);\n return metadata?.type === type;\n}\n","/**\n * URL normalization and header utilities\n */\n\nimport { URLNormalizationError } from './errors.js';\nimport type { ClientOptions } from './types.js';\n\n/**\n * Normalizes a URL with base and default scheme support\n * @param url - The URL to normalize (absolute or relative)\n * @param options - Client options containing base and defaultScheme\n * @returns Normalized absolute URL\n */\nexport function normalizeURL(url: string, options: ClientOptions = {}): string {\n const { base, defaultScheme = 'https' } = options;\n\n try {\n // If URL is already absolute, use it directly\n if (url.includes('://')) {\n return new URL(url).toString();\n }\n\n // If URL starts with //, treat as protocol-relative\n if (url.startsWith('//')) {\n return new URL(`${defaultScheme}:${url}`).toString();\n }\n\n // If base is provided, resolve relative to base\n if (base) {\n const baseURL = base.includes('://') ? base : `${defaultScheme}://${base}`;\n return new URL(url, baseURL).toString();\n }\n\n // If no base provided and URL is relative → Error\n // Check if URL is relative (starts with / or doesn't have scheme)\n const isRelative = !url.match(/^[a-z][a-z0-9+.-]*:/i);\n\n if (isRelative) {\n throw new URLNormalizationError(\n url,\n 'Relative URL requires URI in transport. Use http(\"https://api.com\") or provide absolute URL.',\n );\n }\n\n return new URL(url).toString();\n } catch (error) {\n if (error instanceof URLNormalizationError) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new URLNormalizationError(url, message);\n }\n}\n\n/**\n * Appends query parameters to a URL\n * @param url - The base URL\n * @param params - Query parameters to append\n * @returns URL with query parameters\n */\nexport function appendQueryParams(url: string, params: Record<string, string | number | boolean | undefined>): string {\n const urlObj = new URL(url);\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n urlObj.searchParams.append(key, String(value));\n }\n }\n return urlObj.toString();\n}\n\n/**\n * Normalizes HTTP headers to lowercase (per HTTP/2 spec)\n * HTTP/2 requires all header names to be lowercase\n * HTTP/1.1 is case-insensitive, so lowercase is safe everywhere\n *\n * @param headers - Headers object with potentially mixed case keys\n * @returns Headers object with all lowercase keys\n *\n * @example\n * ```ts\n * normalizeHeaders({ 'Content-Type': 'application/json', 'Accept': 'text/html' })\n * // Returns: { 'content-type': 'application/json', 'accept': 'text/html' }\n * ```\n */\nexport function normalizeHeaders(headers: Record<string, string>): Record<string, string> {\n const normalized: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n normalized[key.toLowerCase()] = value;\n }\n return normalized;\n}\n\n/**\n * Gets a header value with case-insensitive lookup\n * Useful for reading headers before normalization\n *\n * @param headers - Headers object\n * @param name - Header name (case-insensitive)\n * @returns Header value or undefined\n *\n * @example\n * ```ts\n * getHeader({ 'Content-Type': 'application/json' }, 'content-type') // 'application/json'\n * getHeader({ 'content-type': 'application/json' }, 'Content-Type') // 'application/json'\n * ```\n */\nexport function getHeader(headers: Record<string, string>, name: string): string | undefined {\n const lowerName = name.toLowerCase();\n // Try lowercase first (most common)\n if (lowerName in headers) {\n return headers[lowerName];\n }\n // Fallback: case-insensitive search\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) {\n return value;\n }\n }\n return undefined;\n}\n\n/**\n * Sets a header value, removing any existing variants with different casing\n * Prevents duplicate headers with different casing (e.g., 'accept' and 'Accept')\n *\n * @param headers - Headers object to modify\n * @param name - Header name (will be normalized to lowercase)\n * @param value - Header value\n * @returns New headers object with the header set (immutable)\n *\n * @example\n * ```ts\n * setHeader({ Accept: 'text/html' }, 'accept', 'application/json')\n * // Returns: { accept: 'application/json' } (removed 'Accept')\n * ```\n */\nexport function setHeader(headers: Record<string, string>, name: string, value: string): Record<string, string> {\n const lowerName = name.toLowerCase();\n const newHeaders: Record<string, string> = {};\n\n // Copy all headers except variants of the target header\n for (const [key, val] of Object.entries(headers)) {\n if (key.toLowerCase() !== lowerName) {\n newHeaders[key] = val;\n }\n }\n\n // Set the new header value with normalized lowercase name\n newHeaders[lowerName] = value;\n\n return newHeaders;\n}\n","/**\n * Client factory and request execution\n */\n\nimport { compose } from './compose.js';\nimport { UnireqError } from './errors.js';\nimport { serializationPolicy } from './serialization.js';\nimport { validatePolicyChain } from './slots.js';\nimport type { Client, Policy, RequestContext, Response, Transport, TransportWithCapabilities } from './types.js';\nimport { normalizeURL } from './url.js';\n\n/**\n * Creates a client with the given transport and policies\n * @param transport - The transport function or transport with capabilities\n * @param policies - Variadic list of policies to apply\n * @returns A configured client instance\n */\nexport function client(transport: Transport | TransportWithCapabilities, ...policies: ReadonlyArray<Policy>): Client {\n // Extract transport and capabilities\n const actualTransport = typeof transport === 'function' ? transport : transport.transport;\n const capabilities = typeof transport === 'function' ? undefined : transport.capabilities;\n\n // Validate policy chain\n validatePolicyChain(policies, capabilities);\n\n // Compose all policies with serialization middleware first\n const composedPolicy = compose(serializationPolicy(), ...policies);\n\n // Base request executor with per-request policies\n const executeRequest = async <T = unknown>(\n url: string,\n method: string,\n body: unknown | undefined,\n perRequestPolicies: ReadonlyArray<Policy>,\n ): Promise<Response<T>> => {\n // Validate URL input\n if (!url || typeof url !== 'string' || url.trim().length === 0) {\n throw new UnireqError('URL must be a non-empty string', 'INVALID_URL');\n }\n\n // Pass URL as-is to transport - let transport handle URI resolution\n // Only normalize absolute URLs to ensure consistent format\n const normalizedURL = url.includes('://') ? normalizeURL(url, {}) : url;\n\n const ctx: RequestContext = {\n url: normalizedURL,\n method,\n headers: {},\n body,\n };\n\n // Compose global policies with per-request policies\n if (perRequestPolicies.length > 0) {\n const perRequestPolicy = compose(...perRequestPolicies);\n const combinedPolicy = compose(composedPolicy, perRequestPolicy);\n return combinedPolicy(ctx, actualTransport) as Promise<Response<T>>;\n }\n\n // Execute through global policy chain only\n return composedPolicy(ctx, actualTransport) as Promise<Response<T>>;\n };\n\n // Return client interface with variadic policies\n return {\n request: <T = unknown>(url: string, ...perRequestPolicies: ReadonlyArray<Policy>) =>\n executeRequest<T>(url, 'GET', undefined, perRequestPolicies),\n\n get: <T = unknown>(url: string, ...perRequestPolicies: ReadonlyArray<Policy>) =>\n executeRequest<T>(url, 'GET', undefined, perRequestPolicies),\n\n head: <T = unknown>(url: string, ...perRequestPolicies: ReadonlyArray<Policy>) =>\n executeRequest<T>(url, 'HEAD', undefined, perRequestPolicies),\n\n post: <T = unknown>(url: string, body?: unknown, ...perRequestPolicies: ReadonlyArray<Policy>) =>\n executeRequest<T>(url, 'POST', body, perRequestPolicies),\n\n put: <T = unknown>(url: string, body?: unknown, ...perRequestPolicies: ReadonlyArray<Policy>) =>\n executeRequest<T>(url, 'PUT', body, perRequestPolicies),\n\n delete: <T = unknown>(url: string, ...perRequestPolicies: ReadonlyArray<Policy>) =>\n executeRequest<T>(url, 'DELETE', undefined, perRequestPolicies),\n\n patch: <T = unknown>(url: string, body?: unknown, ...perRequestPolicies: ReadonlyArray<Policy>) =>\n executeRequest<T>(url, 'PATCH', body, perRequestPolicies),\n\n options: <T = unknown>(url: string, ...perRequestPolicies: ReadonlyArray<Policy>) =>\n executeRequest<T>(url, 'OPTIONS', undefined, perRequestPolicies),\n };\n}\n","/**\n * Conditional branching utilities\n */\n\nimport { getInspectableMeta, policy as tagPolicy } from './introspection.js';\nimport type { Policy, Predicate, RequestContext, Response } from './types.js';\n\n/**\n * Creates a policy that executes different policies based on a predicate\n * Useful for conditional execution (e.g., content negotiation)\n *\n * @param predicate - Function that evaluates the condition\n * @param thenPolicy - Policy to execute if predicate is truthy\n * @param elsePolicy - Optional policy to execute if predicate is falsy\n * @returns A policy that branches based on the predicate\n *\n * @example\n * ```ts\n * const contentNegotiation = either(\n * (ctx) => ctx.headers['accept']?.includes('application/json'),\n * json(),\n * xml()\n * );\n * ```\n */\nexport function either<T = unknown>(predicate: Predicate<T>, thenPolicy: Policy, elsePolicy?: Policy): Policy {\n const impl = async (ctx: RequestContext, next: (ctx: RequestContext) => Promise<Response>) => {\n const result = await Promise.resolve(predicate(ctx));\n\n if (result) {\n return thenPolicy(ctx, next);\n }\n\n if (elsePolicy) {\n return elsePolicy(ctx, next);\n }\n\n // If no else branch and predicate is false, pass through\n return next(ctx);\n };\n\n // Build branch metadata\n const predicateDesc = predicate.name || 'anonymous predicate';\n const thenMetaRaw = getInspectableMeta(thenPolicy);\n const thenMeta = thenMetaRaw ? [thenMetaRaw] : [];\n const elseMetaRaw = elsePolicy ? getInspectableMeta(elsePolicy) : undefined;\n const elseMeta = elseMetaRaw ? [elseMetaRaw] : [];\n\n return tagPolicy(impl, {\n name: 'either',\n kind: 'other',\n branch: {\n predicate: predicateDesc,\n thenBranch: thenMeta,\n elseBranch: elseMeta,\n },\n });\n}\n\n/**\n * Creates a policy that matches against multiple predicates\n * Executes the first matching policy\n *\n * @param branches - Array of predicate-policy pairs\n * @param defaultPolicy - Optional default policy if no predicates match\n * @returns A policy that matches multiple conditions\n *\n * @example\n * ```ts\n * const parser = match(\n * [\n * [(ctx) => ctx.headers['content-type']?.includes('json'), json()],\n * [(ctx) => ctx.headers['content-type']?.includes('xml'), xml()],\n * ],\n * text() // default\n * );\n * ```\n */\nexport function match<T = unknown>(\n branches: ReadonlyArray<readonly [Predicate<T>, Policy]>,\n defaultPolicy?: Policy,\n): Policy {\n const impl = async (ctx: RequestContext, next: (ctx: RequestContext) => Promise<Response>) => {\n for (const [predicate, policy] of branches) {\n const result = await Promise.resolve(predicate(ctx));\n if (result) {\n return policy(ctx, next);\n }\n }\n\n if (defaultPolicy) {\n return defaultPolicy(ctx, next);\n }\n\n return next(ctx);\n };\n\n // Build children from all branches (match is a composition of either)\n const children = branches.map(([predicate, policy], idx) => {\n const predicateDesc = predicate.name || `branch ${idx + 1}`;\n const meta = getInspectableMeta(policy);\n return {\n id: `match-branch-${idx}`,\n name: predicateDesc,\n kind: 'other' as const,\n children: meta ? [meta] : [],\n };\n });\n\n if (defaultPolicy) {\n const defaultMeta = getInspectableMeta(defaultPolicy);\n children.push({\n id: 'match-default',\n name: 'default',\n kind: 'other' as const,\n children: defaultMeta ? [defaultMeta] : [],\n });\n }\n\n return tagPolicy(impl, {\n name: 'match',\n kind: 'other',\n children,\n });\n}\n","/**\n * Policy inspection and formatting utilities\n */\n\nimport type { Handler, InspectableMeta, Kind } from './introspection.js';\nimport { getHandlerGraph, getInspectableMeta } from './introspection.js';\nimport type { Policy } from './types.js';\n\n/**\n * Inspection output format\n */\nexport type InspectFormat = 'json' | 'tree';\n\n/**\n * Inspection options\n */\nexport interface InspectOptions {\n /** Output format (default: 'json') */\n readonly format?: InspectFormat;\n /** Whether to redact sensitive values (default: true) */\n readonly redact?: boolean;\n}\n\n/**\n * Inspects a policy or handler and returns its composition graph\n * @param target - Policy or handler to inspect\n * @param options - Inspection options\n * @returns Formatted policy graph\n *\n * @example\n * ```ts\n * const pipeline = compose(\n * retry(backoff(), { tries: 3 }),\n * json()\n * );\n *\n * // JSON format\n * console.log(inspect(pipeline));\n *\n * // Tree format\n * console.log(inspect(pipeline, { format: 'tree' }));\n * ```\n */\nexport function inspect(target: Handler | Policy, options: InspectOptions = {}): string {\n const { format = 'json' } = options;\n\n // Try to get graph from handler first\n let graph = getHandlerGraph(target as Handler);\n\n // If no graph, try to get metadata from policy\n if (graph.length === 0) {\n const meta = getInspectableMeta(target as Policy);\n if (meta && meta.name !== 'anonymous') {\n graph = [meta];\n }\n }\n\n if (graph.length === 0) {\n return format === 'tree' ? '(empty policy chain)' : '[]';\n }\n\n return format === 'tree' ? toTree(graph) : toJson(graph);\n}\n\n/**\n * Shortcut for JSON format inspection\n */\ninspect.json = (target: Handler | Policy): string => inspect(target, { format: 'json' });\n\n/**\n * Shortcut for tree format inspection\n */\ninspect.tree = (target: Handler | Policy): string => inspect(target, { format: 'tree' });\n\n/**\n * Converts policy graph to JSON string\n */\nfunction toJson(nodes: ReadonlyArray<InspectableMeta>): string {\n return JSON.stringify(nodes, null, 2);\n}\n\n/**\n * Converts policy graph to tree using box-drawing characters\n */\nfunction toTree(nodes: ReadonlyArray<InspectableMeta>): string {\n const lines: string[] = [];\n\n const walk = (meta: InspectableMeta, depth: number, isLast: boolean, parentPrefix = ''): void => {\n // Build indentation based on depth\n let prefix = '';\n if (depth > 0) {\n // For nested items, add proper tree connectors\n prefix = isLast ? '└─ ' : '├─ ';\n }\n\n const optionsStr = formatOptions(meta.options);\n lines.push(`${parentPrefix}${prefix}${meta.name} (${meta.kind})${optionsStr}`);\n\n // Handle children (compose)\n if (meta.children && meta.children.length > 0) {\n const childPrefix = parentPrefix + (depth > 0 ? (isLast ? ' ' : '│ ') : '');\n const childrenLength = meta.children.length;\n meta.children.forEach((child, idx) => {\n walk(child, depth + 1, idx === childrenLength - 1, childPrefix);\n });\n }\n\n // Handle branch (either/match)\n if (meta.branch) {\n const branchPrefix = parentPrefix + (depth > 0 ? (isLast ? ' ' : '│ ') : '');\n lines.push(`${branchPrefix} ? ${meta.branch.predicate}`);\n\n if (meta.branch.thenBranch.length > 0) {\n lines.push(`${branchPrefix} ├─ then:`);\n const thenPrefix = `${branchPrefix} │ `;\n const thenLength = meta.branch.thenBranch.length;\n meta.branch.thenBranch.forEach((child, idx) => {\n walk(child, depth + 2, idx === thenLength - 1, thenPrefix);\n });\n }\n\n if (meta.branch.elseBranch.length > 0) {\n lines.push(`${branchPrefix} └─ else:`);\n const elsePrefix = `${branchPrefix} `;\n const elseLength = meta.branch.elseBranch.length;\n meta.branch.elseBranch.forEach((child, idx) => {\n walk(child, depth + 2, idx === elseLength - 1, elsePrefix);\n });\n }\n }\n };\n\n nodes.forEach((node, idx) => {\n walk(node, 0, idx === nodes.length - 1, '');\n });\n\n return lines.join('\\n');\n}\n\n/**\n * Formats options for tree display\n */\nfunction formatOptions(opts?: Record<string, unknown>): string {\n if (!opts || Object.keys(opts).length === 0) {\n return '';\n }\n\n const entries = Object.entries(opts)\n .map(([key, value]) => `${key}=${prettyValue(value)}`)\n .join(', ');\n\n return ` [${entries}]`;\n}\n\n/**\n * Pretty-prints a value for tree display\n */\nfunction prettyValue(value: unknown): string {\n if (value === null || value === undefined) {\n return 'null';\n }\n\n if (typeof value === 'string') {\n return `\"${value}\"`;\n }\n\n if (typeof value === 'boolean' || typeof value === 'number') {\n return String(value);\n }\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return '[]';\n }\n if (value.length <= 3) {\n return `[${value.map(prettyValue).join(', ')}]`;\n }\n return `[${value.slice(0, 2).map(prettyValue).join(', ')}, ... +${value.length - 2}]`;\n }\n\n if (typeof value === 'object') {\n const keys = Object.keys(value);\n if (keys.length === 0) {\n return '{}';\n }\n const keyList = keys.slice(0, 2).join(', ');\n if (keys.length > 2) {\n return `{${keyList}, ...}`;\n }\n /* v8 ignore next 2 */\n return `{${keyList}}`;\n }\n\n // Unreachable in practice since JSON.parse() can't produce non-JSON types\n /* v8 ignore next 2 */\n return String(value);\n}\n\n/**\n * Asserts that a policy or handler has a specific policy kind\n * Useful for testing presets\n *\n * @param target - Policy or handler to check\n * @param kind - Expected policy kind\n * @throws Error if kind is not found\n *\n * @example\n * ```ts\n * const pipeline = compose(auth(), json());\n * assertHas(pipeline, 'auth'); // throws if no auth policy\n * assertHas(pipeline, 'parser'); // throws if no parser policy\n * ```\n */\nexport function assertHas(target: Handler | Policy, kind: Kind): void {\n // Try to get graph from handler first\n let graph = getHandlerGraph(target as Handler);\n\n // If no graph, try to get metadata from policy\n if (graph.length === 0) {\n const meta = getInspectableMeta(target as Policy);\n if (meta && meta.name !== 'anonymous') {\n graph = [meta];\n }\n }\n\n const hasKind = (nodes: ReadonlyArray<InspectableMeta>): boolean => {\n for (const node of nodes) {\n if (node.kind === kind) {\n return true;\n }\n if (node.children && hasKind(node.children)) {\n return true;\n }\n if (node.branch) {\n if (hasKind(node.branch.thenBranch) || hasKind(node.branch.elseBranch)) {\n return true;\n }\n }\n }\n return false;\n };\n\n if (!hasKind(graph)) {\n throw new Error(`Expected policy kind \"${kind}\" not found in handler graph`);\n }\n}\n","/**\n * Logging policy\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { policy } from './introspection.js';\nimport type { Logger, Policy } from './types.js';\n\nexport interface LogOptions {\n readonly logger: Logger;\n readonly redactHeaders?: ReadonlyArray<string>;\n readonly logBody?: boolean;\n}\n\nconst DEFAULT_REDACT_HEADERS = ['authorization', 'cookie', 'set-cookie', 'x-api-key'];\n\nexport function log(options: LogOptions): Policy {\n const { logger, redactHeaders = DEFAULT_REDACT_HEADERS, logBody = false } = options;\n\n const redact = (headers: Record<string, string>): Record<string, string> => {\n const redacted = { ...headers };\n for (const header of redactHeaders) {\n const key = Object.keys(redacted).find((k) => k.toLowerCase() === header.toLowerCase());\n if (key) {\n redacted[key] = '[REDACTED]';\n }\n }\n return redacted;\n };\n\n return policy(\n async (ctx, next) => {\n const start = Date.now();\n const requestId = randomUUID();\n\n logger.info(`Request ${requestId} started`, {\n requestId,\n method: ctx.method,\n url: ctx.url,\n headers: redact(ctx.headers),\n body: logBody ? ctx.body : undefined,\n });\n\n try {\n const response = await next(ctx);\n const duration = Date.now() - start;\n\n logger.info(`Request ${requestId} completed`, {\n requestId,\n status: response.status,\n duration,\n headers: redact(response.headers),\n data: logBody ? response.data : undefined,\n });\n\n return response;\n } catch (error) {\n const duration = Date.now() - start;\n logger.error(`Request ${requestId} failed`, {\n requestId,\n duration,\n error,\n });\n throw error;\n }\n },\n {\n name: 'log',\n kind: 'other',\n options: { redactHeaders, logBody },\n },\n );\n}\n","/**\n * Generic transport-agnostic retry flow control primitive\n */\n\nimport type { InspectableMeta } from './introspection.js';\nimport { getInspectableMeta, policy } from './introspection.js';\nimport type { Policy, RequestContext, Response } from './types.js';\n\n/**\n * Retry predicate function that determines if a retry should occur\n * Returns true to retry, false to stop retrying\n */\nexport type RetryPredicate<T = Response> = (\n result: T | null,\n error: Error | null,\n attempt: number,\n context: RequestContext,\n) => boolean | Promise<boolean>;\n\n/**\n * Retry delay strategy that calculates the delay before next retry\n * Returns delay in milliseconds, or undefined to skip delay calculation\n */\nexport type RetryDelayStrategy<T = Response> = {\n readonly getDelay: (\n result: T | null,\n error: Error | null,\n attempt: number,\n ) => number | undefined | Promise<number | undefined>;\n};\n\n/**\n * Generic retry options\n */\nexport interface RetryOptions<T = Response> {\n /** Number of retry attempts (default: 3) */\n readonly tries?: number;\n /** Callback before retry */\n readonly onRetry?: (attempt: number, error: Error | null, result: T | null) => void | Promise<void>;\n}\n\n/**\n * Calculate delay using strategies in order (first match wins)\n * @internal\n */\nasync function calculateDelay<T>(\n strategies: ReadonlyArray<RetryDelayStrategy<T>>,\n result: T | null,\n error: Error | null,\n attempt: number,\n): Promise<number> {\n for (const strategy of strategies) {\n const strategyDelay = await strategy.getDelay(result, error, attempt);\n if (strategyDelay !== undefined) {\n return strategyDelay;\n }\n }\n return 0;\n}\n\n/**\n * Creates a generic transport-agnostic retry policy\n *\n * This is a pure flow control primitive that works with any protocol (HTTP, FTP, IMAP, etc.).\n * It executes a predicate to determine if retry should occur, applies delay strategies,\n * and invokes callbacks.\n *\n * @param predicate - Function to determine if retry should occur\n * @param strategies - Delay strategies to calculate wait time before retry\n * @param options - Retry options\n * @returns Policy that retries based on predicate\n *\n * @example\n * ```ts\n * // HTTP-specific retry with status codes\n * const httpRetry = retry(\n * (result, error) => error !== null || (result?.status >= 500 && result?.status < 600),\n * [backoff({ initial: 100, max: 5000 })],\n * { tries: 3 }\n * );\n * ```\n *\n * @example\n * ```ts\n * // Generic retry for any operation\n * const genericRetry = retry(\n * (result, error) => error !== null,\n * [exponentialBackoff()],\n * { tries: 5 }\n * );\n * ```\n */\nexport function retry<T = Response>(\n predicate: RetryPredicate<T>,\n strategies: ReadonlyArray<RetryDelayStrategy<T>>,\n options?: RetryOptions<T>,\n): Policy {\n const { tries = 3, onRetry } = options ?? {};\n\n return policy(\n async (ctx, next) => {\n let lastResponse: Response | undefined;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < tries; attempt++) {\n try {\n const response = await next(ctx);\n lastResponse = response;\n\n // Check if we should retry using predicate\n const shouldRetry = await Promise.resolve(predicate(response as T, null, attempt, ctx));\n\n // If predicate says no retry, or last attempt, return response\n if (!shouldRetry || attempt === tries - 1) {\n return response;\n }\n\n // Call onRetry callback\n if (onRetry) {\n await Promise.resolve(onRetry(attempt + 1, null, response as T));\n }\n\n // Calculate and apply delay\n const delay = await calculateDelay(strategies, response as T, null, attempt);\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Last attempt - throw error\n if (attempt === tries - 1) {\n throw lastError;\n }\n\n // Check if we should retry on error using predicate\n const shouldRetry = await Promise.resolve(predicate(null, lastError, attempt, ctx));\n\n if (!shouldRetry) {\n throw lastError;\n }\n\n // Call onRetry callback\n if (onRetry) {\n await Promise.resolve(onRetry(attempt + 1, lastError, null));\n }\n\n // Calculate and apply delay\n const delay = await calculateDelay(strategies, null, lastError, attempt);\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n return lastResponse as Response;\n },\n {\n name: 'retry',\n kind: 'retry',\n options: {\n tries,\n },\n children: [\n // Add predicate metadata\n ...(() => {\n const meta = getInspectableMeta(predicate);\n return meta ? [meta] : [];\n })(),\n // Add strategies metadata\n ...strategies.map((s) => getInspectableMeta(s)).filter((m): m is InspectableMeta => m !== undefined),\n ],\n },\n );\n}\n","/**\n * Throttle policy (Client-side Rate Limiter)\n * Controls the rate of outgoing requests using Token Bucket algorithm\n */\n\nimport { policy } from './introspection.js';\nimport type { Policy } from './types.js';\n\nexport interface ThrottleOptions {\n /** Number of requests allowed per interval */\n readonly limit: number;\n /** Interval in milliseconds (default: 1000) */\n readonly interval?: number;\n}\n\nexport function throttle(options: ThrottleOptions): Policy {\n const { limit, interval = 1000 } = options;\n\n let tokens = limit;\n let lastRefill = Date.now();\n const refillRate = limit / interval; // tokens per ms\n\n const refill = () => {\n const now = Date.now();\n const elapsed = now - lastRefill;\n const newTokens = elapsed * refillRate;\n\n if (newTokens > 0) {\n tokens = Math.min(limit, tokens + newTokens);\n lastRefill = now;\n }\n };\n\n return policy(\n async (ctx, next) => {\n refill();\n\n if (tokens < 1) {\n // Calculate wait time\n const missingTokens = 1 - tokens;\n const waitTime = Math.ceil(missingTokens / refillRate);\n\n await new Promise((resolve) => setTimeout(resolve, waitTime));\n\n // Refill after wait\n refill();\n }\n\n tokens -= 1;\n return next(ctx);\n },\n {\n name: 'throttle',\n kind: 'other',\n options: { limit, interval },\n },\n );\n}\n","/**\n * Core types for the unireq framework\n */\n\n/** Request context passed through the policy chain */\nexport interface RequestContext {\n readonly url: string;\n readonly method: string;\n readonly headers: Record<string, string>;\n readonly body?: unknown;\n readonly signal?: AbortSignal;\n readonly policies?: ReadonlyArray<Policy>;\n readonly [key: string]: unknown;\n}\n\n/** Response from a transport or policy */\nexport interface Response<T = unknown> {\n readonly status: number;\n readonly statusText: string;\n readonly headers: Record<string, string>;\n readonly data: T;\n readonly ok: boolean;\n}\n\n/** Policy function that transforms request context */\nexport type Policy = (ctx: RequestContext, next: (ctx: RequestContext) => Promise<Response>) => Promise<Response>;\n\n/** Transport capabilities declaration */\nexport interface TransportCapabilities {\n readonly streams?: boolean;\n readonly multipartFormData?: boolean;\n readonly randomAccess?: boolean;\n readonly [key: string]: boolean | undefined;\n}\n\n/** Connector interface for pluggable transport implementations */\nexport interface Connector<TClient = unknown> {\n connect(uri: string): Promise<TClient> | TClient;\n request(client: TClient, ctx: RequestContext): Promise<Response>;\n disconnect(client: TClient): Promise<void> | void;\n}\n\n/** Transport function that executes the actual I/O */\nexport type Transport = (ctx: RequestContext) => Promise<Response>;\n\n/** Transport with capabilities metadata */\nexport interface TransportWithCapabilities {\n readonly transport: Transport;\n readonly capabilities: TransportCapabilities;\n}\n\n/** Slot types for compile/runtime checks */\nexport enum SlotType {\n Transport = 'transport',\n Auth = 'auth',\n Parser = 'parser',\n}\n\n/** Slot metadata for policy registration */\nexport interface SlotMetadata {\n readonly type: SlotType;\n readonly name: string;\n readonly requiredCapabilities?: ReadonlyArray<string>;\n}\n\n/** Client configuration options */\nexport interface ClientOptions {\n readonly base?: string;\n readonly defaultScheme?: 'http' | 'https' | 'ftp' | 'imap';\n readonly policies?: ReadonlyArray<Policy>;\n}\n\n/** Client instance */\nexport interface Client {\n readonly request: <T = unknown>(url: string, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;\n readonly get: <T = unknown>(url: string, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;\n readonly head: <T = unknown>(url: string, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;\n readonly post: <T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;\n readonly put: <T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;\n readonly delete: <T = unknown>(url: string, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;\n readonly patch: <T = unknown>(\n url: string,\n body?: unknown,\n ...policies: ReadonlyArray<Policy>\n ) => Promise<Response<T>>;\n readonly options: <T = unknown>(url: string, ...policies: ReadonlyArray<Policy>) => Promise<Response<T>>;\n}\n\n/** Predicate function for conditional branching */\nexport type Predicate<T = unknown> = (ctx: RequestContext) => T | Promise<T>;\n\n/** Either branch configuration */\nexport interface EitherBranch<T> {\n readonly predicate: Predicate<T>;\n readonly then: Policy;\n readonly else?: Policy;\n}\n\n/** Body descriptor for request serialization */\nexport interface BodyDescriptor {\n readonly __brand: 'BodyDescriptor';\n readonly data: unknown;\n readonly contentType?: string;\n readonly serialize: () => string | Blob | ArrayBuffer | FormData | ReadableStream<Uint8Array>;\n readonly filename?: string;\n readonly contentLength?: number;\n}\n\n/** Multipart part configuration */\nexport interface MultipartPart {\n readonly name: string;\n readonly part: BodyDescriptor;\n readonly filename?: string;\n}\n\n/** Logger interface for structured logging */\nexport interface Logger {\n debug(message: string, meta?: Record<string, unknown>): void;\n info(message: string, meta?: Record<string, unknown>): void;\n warn(message: string, meta?: Record<string, unknown>): void;\n error(message: string, meta?: Record<string, unknown>): void;\n}\n","/**\n * Validation policy using Adapter Pattern\n */\n\nimport { SerializationError } from './errors.js';\nimport { policy } from './introspection.js';\nimport type { Policy } from './types.js';\n\n/**\n * Validation adapter interface\n * Adapts any validation library (Zod, Valibot, Joi, etc.) to unireq\n */\nexport interface ValidationAdapter<TSchema, TOutput> {\n validate(schema: TSchema, data: unknown): Promise<TOutput> | TOutput;\n}\n\n/**\n * Validates response data using a schema and an adapter\n * @param schema - The schema to validate against\n * @param adapter - The adapter for the validation library\n * @returns Policy that validates response data\n */\nexport function validate<TSchema, TOutput>(schema: TSchema, adapter: ValidationAdapter<TSchema, TOutput>): Policy {\n return policy(\n async (ctx, next) => {\n const response = await next(ctx);\n\n try {\n const validatedData = await adapter.validate(schema, response.data);\n return {\n ...response,\n data: validatedData,\n };\n } catch (error) {\n throw new SerializationError(`Validation failed: ${(error as Error).message}`, error);\n }\n },\n {\n name: 'validate',\n kind: 'other',\n options: { schema },\n },\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/introspection.ts","../src/audit.ts","../src/backoff.ts","../src/errors.ts","../src/circuit-breaker.ts","../src/compose.ts","../src/result.ts","../src/serialization.ts","../src/slots.ts","../src/url.ts","../src/client.ts","../src/either.ts","../src/inspect.ts","../src/logging.ts","../src/retry.ts","../src/throttle.ts","../src/types.ts","../src/validation.ts"],"names":["HANDLER_GRAPH","INSPECTABLE_META","SECRET_KEYS","idCounter","resetIdCounter","generateId","name","randomUUID","deepClone","obj","result","key","value","redactOptions","opts","extraKeys","keys","clone","attachMetadata","target","symbol","meta","getMetadata","policy","fn","withId","attachToGraph","handler","prev","next","getHandlerGraph","inspectable","getInspectableMeta","isInspectable","DEFAULT_REDACT_HEADERS","generateCorrelationId","sanitizeUrl","url","parsed","sensitiveParams","param","getSeverityFromStatus","status","classifySecurityEvent","audit","options","logger","getUserId","getSessionId","getClientIp","redactHeaders","logSuccess","correlationIdGenerator","detectSuspiciousActivity","ctx","correlationId","startTime","timestamp","userId","sessionId","clientIp","userAgent","response","durationMs","eventType","severity","error","err","createConsoleAuditLogger","entry","createLoggerAdapter","message","calculateBackoff","attempt","baseMs","maxMs","multiplier","jitter","exponential","capped","backoff","initial","max","_result","_error","UnireqError","code","cause","NetworkError","TimeoutError","timeoutMs","HttpError","SerializationError","DuplicatePolicyError","policyName","UnsupportedAuthForTransport","authType","transportType","NotAcceptableError","acceptedTypes","receivedType","received","UnsupportedMediaTypeError","supportedTypes","sentType","sent","MissingCapabilityError","capability","InvalidSlotError","slotType","URLNormalizationError","reason","CircuitBreakerOpenError","resetTime","circuitBreaker","failureThreshold","resetTimeout","shouldFail","state","failureCount","nextAttempt","now","compose","policies","_ctx","singlePolicy","impl","dispatch","i","context","nextCtx","children","p","m","OkImpl","ok","_fallback","patterns","ErrImpl","_fn","fallback","isOk","isErr","fromPromise","promise","fromTry","isBodyDescriptor","serializationPolicy","descriptor","serialized","shouldSetContentType","updatedCtx","slotRegistry","registerSlot","metadata","getSlotMetadata","validatePolicyChain","transportCapabilities","seenSlots","slotKey","validateSlotOrdering","slots","transportIndex","authIndex","parserIndex","slot","hasSlotType","type","normalizeURL","base","defaultScheme","baseURL","appendQueryParams","params","urlObj","normalizeHeaders","headers","normalized","getHeader","lowerName","setHeader","newHeaders","val","toNativeHeaders","fromNativeHeaders","isRequestOptions","validKeys","hasValidKey","k","allKeysValid","client","transport","actualTransport","capabilities","composedPolicy","executeRequest","method","body","perRequestPolicies","signal","perRequestPolicy","createMethodWithoutBody","args","firstArg","createMethodWithBody","bodyOrOptions","restPolicies","createSafeMethodWithoutBody","createSafeMethodWithBody","safe","either","predicate","thenPolicy","elsePolicy","predicateDesc","thenMetaRaw","thenMeta","elseMetaRaw","match","branches","defaultPolicy","idx","defaultMeta","inspect","format","graph","toTree","toJson","nodes","lines","walk","depth","isLast","parentPrefix","prefix","optionsStr","formatOptions","childPrefix","childrenLength","child","branchPrefix","thenPrefix","thenLength","elsePrefix","elseLength","node","prettyValue","keyList","assertHas","kind","hasKind","log","logBody","redact","redacted","header","start","requestId","duration","calculateDelay","strategies","strategy","strategyDelay","retry","tries","onRetry","lastResponse","lastError","delay","resolve","throttle","limit","interval","tokens","lastRefill","refillRate","refill","newTokens","missingTokens","waitTime","SlotType","validate","schema","adapter","validatedData"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Policy introspection utilities\n * Enables inspection of policy composition for debugging and testing\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { Policy, RequestContext, Response } from './types.js';\n\n/**\n * Policy kind/slot classification\n */\nexport type Kind =\n | 'transport'\n | 'auth'\n | 'parser'\n | 'headers'\n | 'retry'\n | 'timeout'\n | 'files'\n | 'predicate'\n | 'strategy'\n | 'other';\n\n/**\n * Unified metadata for any inspectable (policy, predicate, strategy, etc.)\n * All inspectable items share this base structure\n */\nexport interface InspectableMeta {\n /** Unique stable ID for snapshot tests */\n readonly id: string;\n /** Function/object name */\n readonly name: string;\n /** Kind/type classification - unified for all inspectables */\n readonly kind: Kind;\n /** Redacted configuration options */\n readonly options?: Record<string, unknown>;\n /** Child inspectables (for composed functions) */\n readonly children?: ReadonlyArray<InspectableMeta>;\n /** Branch structure (for either/match policies) */\n readonly branch?: {\n readonly predicate: string;\n readonly thenBranch: ReadonlyArray<InspectableMeta>;\n readonly elseBranch: ReadonlyArray<InspectableMeta>;\n };\n}\n\n/**\n * Handler type (policy chain result)\n */\nexport type Handler = (ctx: RequestContext) => Promise<Response>;\n\n/**\n * Symbol for handler graph attachment\n */\nexport const HANDLER_GRAPH = Symbol('unireq.handlerGraph');\n\n/**\n * Symbol for inspectable metadata attachment\n */\nexport const INSPECTABLE_META = Symbol('unireq.inspectable');\n\n/**\n * Keys that should be redacted from options (secrets)\n */\nconst SECRET_KEYS = [\n 'token',\n 'accessToken',\n 'refreshToken',\n 'clientSecret',\n 'clientId',\n 'password',\n 'privateKey',\n 'secret',\n 'apiKey',\n 'authorization',\n] as const;\n\n/**\n * Counter for deterministic IDs in tests\n */\nlet idCounter = 0;\n\n/**\n * Reset ID counter (for deterministic tests)\n */\nexport function resetIdCounter(): void {\n idCounter = 0;\n}\n\n/**\n * Generate stable ID for policy metadata\n */\nfunction generateId(name: string): string {\n // In test mode, use deterministic counter\n if (process.env['NODE_ENV'] === 'test' || process.env['VITEST']) {\n return `${name}#${idCounter++}`;\n }\n // In production, use cryptographically secure random UUID\n return `${name}#${randomUUID().slice(0, 8)}`;\n}\n\n/**\n * Deep clones an object while preserving functions and other non-JSON types\n */\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(deepClone) as unknown as T;\n }\n\n const result = {} as Record<string, unknown>;\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[key] = deepClone(value);\n }\n return result as T;\n}\n\n/**\n * Redacts sensitive options for safe inspection\n * @param opts - Options object to redact\n * @param extraKeys - Additional keys to redact\n * @returns Redacted options clone\n */\nexport function redactOptions(opts: Record<string, unknown>, extraKeys: string[] = []): Record<string, unknown> {\n if (!opts || typeof opts !== 'object') {\n return {};\n }\n\n const keys = new Set([...SECRET_KEYS, ...extraKeys]);\n const clone = deepClone(opts);\n\n for (const key of Object.keys(clone)) {\n if (keys.has(key)) {\n clone[key] = '***redacted***';\n }\n // Also redact nested objects with \"secret\" in the key name\n if (key.toLowerCase().includes('secret') || key.toLowerCase().includes('token')) {\n clone[key] = '***redacted***';\n }\n }\n\n return clone;\n}\n\n/**\n * Type-safe metadata attachment helper\n * Attaches metadata to a function/object via symbol\n */\nfunction attachMetadata<T>(target: T, symbol: symbol, meta: InspectableMeta): void {\n // Use Record<symbol, unknown> to safely index with symbol\n (target as Record<symbol, unknown>)[symbol] = meta;\n}\n\n/**\n * Type-safe metadata retrieval helper\n * Retrieves metadata from a function/object via symbol\n */\nfunction getMetadata<T>(target: T, symbol: symbol): InspectableMeta | undefined {\n // Use Record<symbol, unknown> to safely index with symbol\n return (target as Record<symbol, unknown>)[symbol] as InspectableMeta | undefined;\n}\n\n/**\n * @frozen Signature change affects 46 files across 8 packages — requires monorepo-wide impact review.\n * Creates a tagged policy with metadata for introspection\n * @param fn - Policy function\n * @param meta - Policy metadata (without id)\n * @returns Tagged policy function\n *\n * @example\n * ```ts\n * export function timeout(ms: number): Policy {\n * return policy(\n * async (ctx, next) => {\n * // ... implementation\n * },\n * { name: 'timeout', kind: 'timeout', options: { ms } }\n * );\n * }\n * ```\n */\nexport function policy<T extends Policy>(\n fn: T,\n meta: Omit<InspectableMeta, 'id'> & { options?: Record<string, unknown> },\n): T {\n const withId: InspectableMeta = {\n ...meta,\n id: generateId(meta.name),\n options: meta.options ? redactOptions(meta.options) : undefined,\n };\n\n // Attach metadata to function using typed helper\n attachMetadata(fn, INSPECTABLE_META, withId);\n\n return fn;\n}\n\n/**\n * Attaches policy metadata to handler graph\n * @param handler - Handler to attach graph to\n * @param meta - Policy metadata to attach\n */\nexport function attachToGraph(handler: Handler, meta: InspectableMeta): void {\n const prev = getMetadata(handler, HANDLER_GRAPH) as InspectableMeta[] | undefined;\n const next = prev ? [...prev, meta] : [meta];\n attachMetadata(handler, HANDLER_GRAPH, next as unknown as InspectableMeta);\n}\n\n/**\n * Gets handler graph\n * @param handler - Handler to get graph from\n * @returns Policy metadata graph\n */\nexport function getHandlerGraph(handler: Handler): ReadonlyArray<InspectableMeta> {\n const graph = getMetadata(handler, HANDLER_GRAPH);\n return (graph as unknown as InspectableMeta[]) ?? [];\n}\n\n/**\n * Creates an inspectable function/object with metadata\n * Generic version that works with any type of function (predicate, strategy, etc.)\n *\n * @param fn - Function to make inspectable\n * @param meta - Metadata (without id)\n * @returns Tagged function\n *\n * @example\n * ```ts\n * export function backoff(options: BackoffOptions): RetryDelayStrategy {\n * const strategy = {\n * getDelay: (result, error, attempt) => { ... }\n * };\n * return inspectable(strategy, {\n * name: 'backoff',\n * kind: 'strategy',\n * options: { initial: options.initial, max: options.max }\n * });\n * }\n * ```\n */\nexport function inspectable<T>(fn: T, meta: Omit<InspectableMeta, 'id'> & { options?: Record<string, unknown> }): T {\n const withId: InspectableMeta = {\n ...meta,\n id: generateId(meta.name),\n options: meta.options ? redactOptions(meta.options) : undefined,\n };\n\n // Attach metadata to function/object using typed helper\n attachMetadata(fn, INSPECTABLE_META, withId);\n\n return fn;\n}\n\n/**\n * Extracts inspectable metadata from any function/object\n * @param fn - Function/object to extract metadata from\n * @returns Metadata or undefined if not inspectable\n */\nexport function getInspectableMeta(fn: unknown): InspectableMeta | undefined {\n if (!fn || (typeof fn !== 'function' && typeof fn !== 'object')) {\n return undefined;\n }\n return getMetadata(fn, INSPECTABLE_META);\n}\n\n/**\n * Checks if a function/object has inspectable metadata\n * @param fn - Function/object to check\n * @returns True if inspectable\n */\nexport function isInspectable(fn: unknown): boolean {\n return getInspectableMeta(fn) !== undefined;\n}\n","/**\n * Structured audit logging for security events (OWASP A09:2021)\n *\n * @see https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { policy } from './introspection.js';\nimport type { Logger, Policy, RequestContext, Response } from './types.js';\n\n/**\n * Security event types for audit logging\n */\nexport type SecurityEventType =\n | 'auth_success'\n | 'auth_failure'\n | 'auth_token_refresh'\n | 'auth_token_expired'\n | 'access_denied'\n | 'rate_limit_exceeded'\n | 'validation_failure'\n | 'request_started'\n | 'request_completed'\n | 'request_failed'\n | 'suspicious_activity';\n\n/**\n * Structured audit log entry\n */\nexport interface AuditLogEntry {\n /** ISO 8601 timestamp */\n readonly timestamp: string;\n /** Unique correlation ID for request tracing */\n readonly correlationId: string;\n /** Security event type */\n readonly eventType: SecurityEventType;\n /** Event severity level */\n readonly severity: 'info' | 'warn' | 'error' | 'critical';\n /** HTTP method */\n readonly method: string;\n /** Request URL (sanitized) */\n readonly url: string;\n /** HTTP status code (if available) */\n readonly statusCode?: number;\n /** Request duration in milliseconds */\n readonly durationMs?: number;\n /** Client IP address (if available) */\n readonly clientIp?: string;\n /** User agent (if available) */\n readonly userAgent?: string;\n /** User ID or identifier (if authenticated) */\n readonly userId?: string;\n /** Session ID (if available) */\n readonly sessionId?: string;\n /** Additional context */\n readonly context?: Record<string, unknown>;\n /** Error message (sanitized, no stack traces) */\n readonly errorMessage?: string;\n /** Error code */\n readonly errorCode?: string;\n}\n\n/**\n * Audit logger interface\n */\nexport interface AuditLogger {\n readonly log: (entry: AuditLogEntry) => void | Promise<void>;\n}\n\n/**\n * Audit logging options\n */\nexport interface AuditOptions {\n /** Audit logger implementation */\n readonly logger: AuditLogger;\n /** Function to extract user ID from context */\n readonly getUserId?: (ctx: RequestContext) => string | undefined;\n /** Function to extract session ID from context */\n readonly getSessionId?: (ctx: RequestContext) => string | undefined;\n /** Function to extract client IP from context */\n readonly getClientIp?: (ctx: RequestContext) => string | undefined;\n /** Headers to redact from logs */\n readonly redactHeaders?: ReadonlyArray<string>;\n /** Log successful requests (default: true) */\n readonly logSuccess?: boolean;\n /** Log request bodies (default: false) */\n readonly logBody?: boolean;\n /** Custom correlation ID generator */\n readonly correlationIdGenerator?: () => string;\n /** Suspicious activity detector */\n readonly detectSuspiciousActivity?: (ctx: RequestContext, response?: Response, error?: Error) => boolean;\n}\n\n/**\n * Default headers to redact for security\n */\nconst DEFAULT_REDACT_HEADERS = [\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'x-auth-token',\n 'x-csrf-token',\n 'x-session-id',\n];\n\n/**\n * Generate a correlation ID using cryptographically secure random\n */\nfunction generateCorrelationId(): string {\n return randomUUID();\n}\n\n/**\n * Sanitize URL by removing sensitive query parameters\n */\nfunction sanitizeUrl(url: string): string {\n try {\n const parsed = new URL(url);\n const sensitiveParams = ['token', 'api_key', 'apikey', 'secret', 'password', 'auth'];\n\n for (const param of sensitiveParams) {\n if (parsed.searchParams.has(param)) {\n parsed.searchParams.set(param, '[REDACTED]');\n }\n }\n\n return parsed.toString();\n } catch {\n return url;\n }\n}\n\n/**\n * Get severity based on status code\n */\nfunction getSeverityFromStatus(status: number): 'info' | 'warn' | 'error' | 'critical' {\n if (status >= 500) return 'error';\n if (status === 401 || status === 403) return 'warn';\n if (status >= 400) return 'warn';\n return 'info';\n}\n\n/**\n * Classify HTTP status into a security event type\n * @param status - HTTP response status code\n * @returns Tuple of [eventType, severity]\n */\nfunction classifySecurityEvent(status: number): [SecurityEventType, 'info' | 'warn' | 'error' | 'critical'] {\n switch (status) {\n case 401:\n return ['auth_failure', 'warn'];\n case 403:\n return ['access_denied', 'warn'];\n case 429:\n return ['rate_limit_exceeded', 'warn'];\n default:\n return ['request_completed', getSeverityFromStatus(status)];\n }\n}\n\n/**\n * Creates a structured audit logging policy (OWASP A09:2021)\n *\n * Provides comprehensive security event logging with:\n * - Structured JSON format\n * - Correlation IDs for request tracing\n * - Sensitive data redaction\n * - Security event classification\n * - Suspicious activity detection\n *\n * @param options - Audit logging configuration\n * @returns Policy that logs security events\n *\n * @example\n * ```ts\n * const auditLogger: AuditLogger = {\n * log: (entry) => console.log(JSON.stringify(entry)),\n * };\n *\n * const api = client(\n * http('https://api.example.com'),\n * audit({\n * logger: auditLogger,\n * getUserId: (ctx) => ctx.headers['x-user-id'],\n * }),\n * parse.json()\n * );\n * ```\n */\nexport function audit(options: AuditOptions): Policy {\n const {\n logger,\n getUserId,\n getSessionId,\n getClientIp,\n redactHeaders = DEFAULT_REDACT_HEADERS,\n logSuccess = true,\n correlationIdGenerator = generateCorrelationId,\n detectSuspiciousActivity,\n } = options;\n\n return policy(\n async (ctx, next) => {\n const correlationId = correlationIdGenerator();\n const startTime = Date.now();\n const timestamp = new Date().toISOString();\n\n // Extract context information\n const userId = getUserId?.(ctx);\n const sessionId = getSessionId?.(ctx);\n const clientIp = getClientIp?.(ctx);\n const userAgent = ctx.headers['user-agent'];\n\n // Log request started\n await logger.log({\n timestamp,\n correlationId,\n eventType: 'request_started',\n severity: 'info',\n method: ctx.method,\n url: sanitizeUrl(ctx.url),\n userId,\n sessionId,\n clientIp,\n userAgent,\n });\n\n try {\n const response = await next(ctx);\n const durationMs = Date.now() - startTime;\n\n // Classify response into security event\n let [eventType, severity] = classifySecurityEvent(response.status);\n\n // Check for suspicious activity\n if (detectSuspiciousActivity?.(ctx, response, undefined)) {\n eventType = 'suspicious_activity';\n severity = 'critical';\n }\n\n // Log completion (unless it's a success and logSuccess is false)\n if (logSuccess || response.status >= 400) {\n await logger.log({\n timestamp: new Date().toISOString(),\n correlationId,\n eventType,\n severity,\n method: ctx.method,\n url: sanitizeUrl(ctx.url),\n statusCode: response.status,\n durationMs,\n userId,\n sessionId,\n clientIp,\n userAgent,\n });\n }\n\n return response;\n } catch (error) {\n const durationMs = Date.now() - startTime;\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Check for suspicious activity\n let eventType: SecurityEventType = 'request_failed';\n let severity: 'error' | 'critical' = 'error';\n\n if (detectSuspiciousActivity?.(ctx, undefined, err)) {\n eventType = 'suspicious_activity';\n severity = 'critical';\n }\n\n // Log failure with sanitized error message (no stack trace)\n await logger.log({\n timestamp: new Date().toISOString(),\n correlationId,\n eventType,\n severity,\n method: ctx.method,\n url: sanitizeUrl(ctx.url),\n durationMs,\n userId,\n sessionId,\n clientIp,\n userAgent,\n errorMessage: err.message,\n errorCode: (err as { code?: string }).code,\n });\n\n throw error;\n }\n },\n {\n name: 'audit',\n kind: 'other',\n options: {\n logSuccess,\n redactHeaders,\n },\n },\n );\n}\n\n/**\n * Creates a console-based audit logger for development\n * @returns AuditLogger that logs to console in JSON format\n */\nexport function createConsoleAuditLogger(): AuditLogger {\n return {\n log: (entry) => {\n console.log(JSON.stringify(entry));\n },\n };\n}\n\n/**\n * Creates an audit logger that uses a standard Logger interface\n * @param logger - Standard logger implementation\n * @returns AuditLogger that delegates to the standard logger\n */\nexport function createLoggerAdapter(logger: Logger): AuditLogger {\n return {\n log: (entry) => {\n const message = `[${entry.eventType}] ${entry.method} ${entry.url}`;\n const meta: Record<string, unknown> = { ...entry };\n\n switch (entry.severity) {\n case 'critical':\n case 'error':\n logger.error(message, meta);\n break;\n case 'warn':\n logger.warn(message, meta);\n break;\n default:\n logger.info(message, meta);\n }\n },\n };\n}\n","/**\n * Exponential backoff strategy for retry delays\n * Provides exponential backoff with optional jitter\n * Transport-agnostic - works with any protocol\n */\n\nimport { inspectable } from './introspection.js';\nimport type { RetryDelayStrategy } from './retry.js';\n\n/**\n * Backoff options\n */\nexport interface BackoffOptions {\n /** Initial backoff in milliseconds (default: 1000) */\n readonly initial?: number;\n /** Maximum backoff in milliseconds (default: 30000) */\n readonly max?: number;\n /** Multiplier for exponential backoff (default: 2) */\n readonly multiplier?: number;\n /** Add jitter to backoff (default: true) */\n readonly jitter?: boolean;\n}\n\n/**\n * Calculates backoff delay with exponential backoff and optional jitter\n * @param attempt - Current attempt number (0-indexed)\n * @param baseMs - Base backoff in milliseconds\n * @param maxMs - Maximum backoff in milliseconds\n * @param multiplier - Exponential multiplier\n * @param jitter - Whether to add jitter\n * @returns Delay in milliseconds\n */\nfunction calculateBackoff(attempt: number, baseMs: number, maxMs: number, multiplier: number, jitter: boolean): number {\n const exponential = baseMs * multiplier ** attempt;\n const capped = Math.min(exponential, maxMs);\n\n if (!jitter) {\n return capped;\n }\n\n // Add jitter: random value between 0 and capped\n return Math.floor(Math.random() * capped);\n}\n\n/**\n * Creates an exponential backoff delay strategy\n *\n * @param options - Backoff configuration\n * @returns Delay strategy that calculates exponential backoff\n *\n * @example\n * ```ts\n * import { retry } from '@unireq/core';\n * import { backoff } from '@unireq/core';\n *\n * const policy = retry(\n * myPredicate,\n * [backoff({ initial: 100, max: 5000, jitter: true })],\n * { tries: 3 }\n * );\n * ```\n */\nexport function backoff<T = unknown>(options: BackoffOptions = {}): RetryDelayStrategy<T> {\n const { initial = 1000, max = 30000, multiplier = 2, jitter = true } = options;\n\n const strategy: RetryDelayStrategy<T> = {\n getDelay: (_result: T | null, _error: Error | null, attempt: number) => {\n return calculateBackoff(attempt, initial, max, multiplier, jitter);\n },\n };\n\n return inspectable(strategy, {\n name: 'backoff',\n kind: 'strategy',\n options: { initial, max, multiplier, jitter },\n });\n}\n","/**\n * DX-focused error types for unireq\n */\n\nimport type { Response } from './types.js';\n\n/** Base error class for unireq */\nexport class UnireqError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public override readonly cause?: unknown,\n ) {\n super(message);\n this.name = 'UnireqError';\n }\n}\n\n/** Error thrown when a network request fails (e.g. DNS, connection refused) */\nexport class NetworkError extends UnireqError {\n constructor(message: string, cause?: unknown) {\n super(message, 'NETWORK_ERROR', cause);\n this.name = 'NetworkError';\n }\n}\n\n/** Error thrown when a request times out */\nexport class TimeoutError extends UnireqError {\n constructor(\n public readonly timeoutMs: number,\n cause?: unknown,\n ) {\n super(`Request timed out after ${timeoutMs}ms`, 'TIMEOUT', cause);\n this.name = 'TimeoutError';\n }\n}\n\n/** Error thrown when an HTTP response has an error status code (4xx, 5xx) */\nexport class HttpError extends UnireqError {\n public readonly status: number;\n public readonly statusText: string;\n public readonly headers: Record<string, string | string[] | undefined>;\n public readonly data: unknown;\n\n constructor(response: Response) {\n super(`HTTP Error ${response.status}: ${response.statusText}`, 'HTTP_ERROR');\n this.name = 'HttpError';\n this.status = response.status;\n this.statusText = response.statusText;\n this.headers = response.headers;\n this.data = response.data;\n }\n}\n\n/** Error thrown when serialization or parsing fails */\nexport class SerializationError extends UnireqError {\n constructor(message: string, cause?: unknown) {\n super(message, 'SERIALIZATION_ERROR', cause);\n this.name = 'SerializationError';\n }\n}\n\n/** Error thrown when duplicate policies are detected */\nexport class DuplicatePolicyError extends UnireqError {\n constructor(public readonly policyName: string) {\n super(\n `Duplicate policy detected: ${policyName}. Each policy can only be registered once in the chain.`,\n 'DUPLICATE_POLICY',\n );\n this.name = 'DuplicatePolicyError';\n }\n}\n\n/** Error thrown when authentication is not supported by transport */\nexport class UnsupportedAuthForTransport extends UnireqError {\n constructor(\n public readonly authType: string,\n public readonly transportType: string,\n ) {\n super(`Authentication type \"${authType}\" is not supported by transport \"${transportType}\".`, 'UNSUPPORTED_AUTH');\n this.name = 'UnsupportedAuthForTransport';\n }\n}\n\n/** HTTP 406 Not Acceptable error */\nexport class NotAcceptableError extends UnireqError {\n constructor(\n public readonly acceptedTypes: ReadonlyArray<string>,\n public readonly receivedType?: string,\n ) {\n const received = receivedType ? ` (received: ${receivedType})` : '';\n super(\n `Server cannot produce a response matching the Accept header. Accepted types: ${acceptedTypes.join(', ')}${received}`,\n 'NOT_ACCEPTABLE',\n );\n this.name = 'NotAcceptableError';\n }\n}\n\n/** HTTP 415 Unsupported Media Type error */\nexport class UnsupportedMediaTypeError extends UnireqError {\n constructor(\n public readonly supportedTypes: ReadonlyArray<string>,\n public readonly sentType?: string,\n ) {\n const sent = sentType ? ` (sent: ${sentType})` : '';\n super(\n `Server cannot process the request payload media type. Supported types: ${supportedTypes.join(', ')}${sent}`,\n 'UNSUPPORTED_MEDIA_TYPE',\n );\n this.name = 'UnsupportedMediaTypeError';\n }\n}\n\n/** Error thrown when required capability is missing */\nexport class MissingCapabilityError extends UnireqError {\n constructor(\n public readonly capability: string,\n public readonly transportType: string,\n ) {\n super(`Transport \"${transportType}\" does not support required capability: ${capability}`, 'MISSING_CAPABILITY');\n this.name = 'MissingCapabilityError';\n }\n}\n\n/** Error thrown for invalid slot configuration */\nexport class InvalidSlotError extends UnireqError {\n constructor(\n public readonly slotType: string,\n message: string,\n ) {\n super(`Invalid slot configuration for ${slotType}: ${message}`, 'INVALID_SLOT');\n this.name = 'InvalidSlotError';\n }\n}\n\n/** Error thrown when URL normalization fails */\nexport class URLNormalizationError extends UnireqError {\n constructor(\n public readonly url: string,\n public readonly reason: string,\n ) {\n super(`Failed to normalize URL \"${url}\": ${reason}`, 'URL_NORMALIZATION_FAILED');\n this.name = 'URLNormalizationError';\n }\n}\n","/**\n * Circuit Breaker policy\n * Prevents cascading failures by stopping requests to a failing service\n */\n\nimport { UnireqError } from './errors.js';\nimport { policy } from './introspection.js';\nimport type { Policy } from './types.js';\n\nexport interface CircuitBreakerOptions {\n /** Number of failures before opening the circuit (default: 5) */\n readonly failureThreshold?: number;\n /** Time in ms to wait before trying again (default: 30000) */\n readonly resetTimeout?: number;\n /** Optional predicate to determine if an error should trigger failure */\n readonly shouldFail?: (error: unknown) => boolean;\n}\n\nenum CircuitState {\n CLOSED = 'CLOSED',\n OPEN = 'OPEN',\n HALF_OPEN = 'HALF_OPEN',\n}\n\n/** Error thrown when circuit is open */\nexport class CircuitBreakerOpenError extends UnireqError {\n constructor(public readonly resetTime: number) {\n super('Circuit breaker is OPEN', 'CIRCUIT_BREAKER_OPEN');\n this.name = 'CircuitBreakerOpenError';\n }\n}\n\nexport function circuitBreaker(options: CircuitBreakerOptions = {}): Policy {\n const { failureThreshold = 5, resetTimeout = 30000, shouldFail = () => true } = options;\n\n let state = CircuitState.CLOSED;\n let failureCount = 0;\n let nextAttempt = 0;\n\n return policy(\n async (ctx, next) => {\n const now = Date.now();\n\n if (state === CircuitState.OPEN) {\n if (now >= nextAttempt) {\n state = CircuitState.HALF_OPEN;\n } else {\n throw new CircuitBreakerOpenError(nextAttempt);\n }\n }\n\n try {\n const response = await next(ctx);\n\n if (state === CircuitState.HALF_OPEN) {\n /* v8 ignore next 2 */\n state = CircuitState.CLOSED;\n failureCount = 0;\n } else {\n // Must be CLOSED\n failureCount = 0; // Reset on success\n }\n\n return response;\n } catch (error) {\n if (shouldFail(error)) {\n if (state === CircuitState.HALF_OPEN) {\n /* v8 ignore next 2 */\n state = CircuitState.OPEN;\n nextAttempt = Date.now() + resetTimeout;\n } else {\n // Must be CLOSED\n failureCount++;\n if (failureCount >= failureThreshold) {\n state = CircuitState.OPEN;\n nextAttempt = Date.now() + resetTimeout;\n }\n }\n }\n throw error;\n }\n },\n {\n name: 'circuitBreaker',\n kind: 'other',\n options: { failureThreshold, resetTimeout, state },\n },\n );\n}\n","/**\n * Policy composition utilities\n */\n\nimport type { InspectableMeta } from './introspection.js';\nimport { getInspectableMeta, policy as tagPolicy } from './introspection.js';\nimport type { Policy, RequestContext, Response } from './types.js';\n\n/**\n * @frozen Core composition primitive — 28 files depend transitively. Changes require impact review.\n * Composes multiple policies into a single policy chain (onion model)\n * @param policies - Array of policies to compose\n * @returns A single composed policy\n */\nexport function compose(...policies: ReadonlyArray<Policy>): Policy {\n if (policies.length === 0) {\n return async (_ctx, next) => next(_ctx);\n }\n\n if (policies.length === 1) {\n const singlePolicy = policies[0];\n if (!singlePolicy) {\n return async (_ctx, next) => next(_ctx);\n }\n return singlePolicy;\n }\n\n const impl = async (ctx: RequestContext, next: (ctx: RequestContext) => Promise<Response>) => {\n const dispatch = async (i: number, context: RequestContext): Promise<Response> => {\n const policy = policies[i];\n if (!policy) {\n return next(context);\n }\n\n return policy(context, (nextCtx) => dispatch(i + 1, nextCtx));\n };\n\n return dispatch(0, ctx);\n };\n\n // Build children metadata from input policies (filter out undefined)\n const children: InspectableMeta[] = policies\n .filter((p) => p !== undefined)\n .map((p) => getInspectableMeta(p))\n .filter((m): m is InspectableMeta => m !== undefined);\n\n return tagPolicy(impl, {\n name: 'compose',\n kind: 'other',\n children,\n });\n}\n","/**\n * Result type for functional error handling\n *\n * Provides a type-safe way to handle success and error cases\n * without throwing exceptions. Inspired by Rust's Result type.\n *\n * @example\n * ```ts\n * const result = await client.safe.get<User>('/users/1');\n *\n * // Pattern matching\n * const name = result.match({\n * ok: (response) => response.data.name,\n * err: (error) => 'Unknown',\n * });\n *\n * // Chaining operations\n * const profile = result\n * .map(r => r.data)\n * .flatMap(user => fetchProfile(user.id));\n *\n * // Unwrapping with fallback\n * const data = result.unwrapOr(defaultResponse);\n * ```\n */\n\n/**\n * Pattern matching interface for Result\n */\nexport interface ResultPatterns<T, E, U> {\n readonly ok: (value: T) => U;\n readonly err: (error: E) => U;\n}\n\n/**\n * Ok variant - represents success\n */\nclass OkImpl<T, E> {\n readonly _tag = 'Ok' as const;\n\n constructor(readonly value: T) {}\n\n /**\n * Returns true if this is an Ok value\n */\n isOk(): this is OkImpl<T, E> {\n return true;\n }\n\n /**\n * Returns true if this is an Err value\n */\n isErr(): this is ErrImpl<T, E> {\n return false;\n }\n\n /**\n * Transforms the success value, leaving errors unchanged\n */\n map<U>(fn: (value: T) => U): Result<U, E> {\n return ok(fn(this.value));\n }\n\n /**\n * Maps a function that returns a Result, flattening the result\n */\n flatMap<U>(fn: (value: T) => Result<U, E>): Result<U, E> {\n return fn(this.value);\n }\n\n /**\n * Returns the success value or throws if this is an Err\n * @throws Error if this is an Err\n */\n unwrap(): T {\n return this.value;\n }\n\n /**\n * Returns the success value or the provided fallback\n */\n unwrapOr(_fallback: T): T {\n return this.value;\n }\n\n /**\n * Returns the error value or throws if this is an Ok\n * @throws Error if this is an Ok\n */\n unwrapErr(): never {\n throw new Error('Called unwrapErr on Ok value');\n }\n\n /**\n * Pattern matching on the result\n */\n match<U>(patterns: ResultPatterns<T, E, U>): U {\n return patterns.ok(this.value);\n }\n}\n\n/**\n * Err variant - represents failure\n */\nclass ErrImpl<T, E> {\n readonly _tag = 'Err' as const;\n\n constructor(readonly error: E) {}\n\n /**\n * Returns true if this is an Ok value\n */\n isOk(): this is OkImpl<T, E> {\n return false;\n }\n\n /**\n * Returns true if this is an Err value\n */\n isErr(): this is ErrImpl<T, E> {\n return true;\n }\n\n /**\n * Transforms the success value, leaving errors unchanged\n * For Err, this is a no-op that returns the same Err\n */\n map<U>(_fn: (value: T) => U): Result<U, E> {\n // Return new Err with same error but different T type\n return err(this.error);\n }\n\n /**\n * Maps a function that returns a Result, flattening the result\n * For Err, this is a no-op that returns the same Err\n */\n flatMap<U>(_fn: (value: T) => Result<U, E>): Result<U, E> {\n return err(this.error);\n }\n\n /**\n * Returns the success value or throws if this is an Err\n * @throws The error value\n */\n unwrap(): never {\n throw this.error;\n }\n\n /**\n * Returns the success value or the provided fallback\n */\n unwrapOr(fallback: T): T {\n return fallback;\n }\n\n /**\n * Returns the error value or throws if this is an Ok\n */\n unwrapErr(): E {\n return this.error;\n }\n\n /**\n * Pattern matching on the result\n */\n match<U>(patterns: ResultPatterns<T, E, U>): U {\n return patterns.err(this.error);\n }\n}\n\n/**\n * Result type - either Ok<T> or Err<E>\n */\nexport type Result<T, E> = OkImpl<T, E> | ErrImpl<T, E>;\n\n/**\n * Creates a successful Result containing the value\n *\n * @example\n * ```ts\n * const result = ok({ name: 'Alice' });\n * console.log(result.unwrap()); // { name: 'Alice' }\n * ```\n */\nexport function ok<T, E = never>(value: T): Result<T, E> {\n return new OkImpl<T, E>(value);\n}\n\n/**\n * Creates a failed Result containing the error\n *\n * @example\n * ```ts\n * const result = err(new Error('Not found'));\n * console.log(result.isErr()); // true\n * ```\n */\nexport function err<T = never, E = unknown>(error: E): Result<T, E> {\n return new ErrImpl<T, E>(error);\n}\n\n/**\n * Type guard to check if a Result is Ok\n */\nexport function isOk<T, E>(result: Result<T, E>): result is OkImpl<T, E> {\n return result._tag === 'Ok';\n}\n\n/**\n * Type guard to check if a Result is Err\n */\nexport function isErr<T, E>(result: Result<T, E>): result is ErrImpl<T, E> {\n return result._tag === 'Err';\n}\n\n/**\n * Wraps a Promise in a Result, catching any errors\n *\n * @example\n * ```ts\n * const result = await fromPromise(fetch('/api/users'));\n * if (result.isOk()) {\n * console.log(result.value);\n * }\n * ```\n */\nexport async function fromPromise<T, E = Error>(promise: Promise<T>): Promise<Result<T, E>> {\n try {\n const value = await promise;\n return ok(value);\n } catch (error) {\n return err(error as E);\n }\n}\n\n/**\n * Wraps a synchronous function in a Result, catching any errors\n *\n * @example\n * ```ts\n * const result = fromTry(() => JSON.parse(jsonString));\n * const data = result.unwrapOr({ default: true });\n * ```\n */\nexport function fromTry<T, E = Error>(fn: () => T): Result<T, E> {\n try {\n return ok(fn());\n } catch (error) {\n return err(error as E);\n }\n}\n","/**\n * Serialization middleware for BodyDescriptor handling\n */\n\nimport type { BodyDescriptor, Policy } from './types.js';\n\n/**\n * Type guard to check if a value is a BodyDescriptor\n * @param value - Value to check\n * @returns True if value is a BodyDescriptor\n */\nexport function isBodyDescriptor(value: unknown): value is BodyDescriptor {\n return (\n typeof value === 'object' &&\n value !== null &&\n '__brand' in value &&\n (value as BodyDescriptor).__brand === 'BodyDescriptor' &&\n 'serialize' in value &&\n typeof (value as BodyDescriptor).serialize === 'function'\n );\n}\n\n/**\n * Serialization policy that converts BodyDescriptor to serialized form\n * This middleware should be applied first in the policy chain to handle\n * body descriptors before other policies process the request\n *\n * @returns Policy that handles BodyDescriptor serialization\n */\nexport function serializationPolicy(): Policy {\n return async (ctx, next) => {\n // Check if body is a BodyDescriptor\n if (isBodyDescriptor(ctx.body)) {\n const descriptor = ctx.body;\n\n // Serialize the body\n const serialized = descriptor.serialize();\n\n // Create updated context with serialized body and Content-Type header\n // IMPORTANT: For FormData, we must NOT set Content-Type manually\n // because fetch needs to set it with the correct boundary\n const shouldSetContentType =\n descriptor.contentType &&\n !ctx.headers['content-type'] &&\n !ctx.headers['Content-Type'] &&\n !(serialized instanceof FormData); // Let fetch handle FormData Content-Type\n\n const updatedCtx = {\n ...ctx,\n body: serialized,\n headers: {\n ...ctx.headers,\n ...(shouldSetContentType ? { 'content-type': descriptor.contentType } : {}),\n },\n };\n\n return next(updatedCtx);\n }\n\n // Not a BodyDescriptor, pass through unchanged\n return next(ctx);\n };\n}\n","/**\n * Slot system for transport, auth, and parser policies\n * Provides compile-time and runtime checks for capability requirements\n */\n\nimport { DuplicatePolicyError, InvalidSlotError, MissingCapabilityError } from './errors.js';\nimport type { Policy, SlotMetadata, SlotType, TransportCapabilities } from './types.js';\n\n/** Registry for slot metadata */\nconst slotRegistry = new WeakMap<Policy, SlotMetadata>();\n\n/**\n * Registers slot metadata for a policy\n * @param policy - The policy to register\n * @param metadata - Slot metadata\n */\nexport function registerSlot(policy: Policy, metadata: SlotMetadata): void {\n slotRegistry.set(policy, metadata);\n}\n\n/**\n * Gets slot metadata for a policy\n * @param policy - The policy to query\n * @returns Slot metadata or undefined\n */\nexport function getSlotMetadata(policy: Policy): SlotMetadata | undefined {\n return slotRegistry.get(policy);\n}\n\n/**\n * Validates a policy chain for slot conflicts and capability requirements\n * @param policies - Array of policies to validate\n * @param transportCapabilities - Transport capabilities\n * @throws {DuplicatePolicyError} If duplicate slots detected\n * @throws {MissingCapabilityError} If required capabilities missing\n * @throws {InvalidSlotError} If slot configuration is invalid\n */\nexport function validatePolicyChain(\n policies: ReadonlyArray<Policy>,\n transportCapabilities?: TransportCapabilities,\n): void {\n const seenSlots = new Map<string, SlotMetadata>();\n\n for (const policy of policies) {\n const metadata = getSlotMetadata(policy);\n if (!metadata) continue;\n\n // Check for duplicate slots of the same type\n const slotKey = `${metadata.type}:${metadata.name}`;\n const existing = seenSlots.get(slotKey);\n if (existing) {\n throw new DuplicatePolicyError(metadata.name);\n }\n seenSlots.set(slotKey, metadata);\n\n // Validate required capabilities\n if (transportCapabilities && metadata.requiredCapabilities) {\n for (const capability of metadata.requiredCapabilities) {\n if (!transportCapabilities[capability]) {\n throw new MissingCapabilityError(capability, 'current');\n }\n }\n }\n }\n\n // Validate slot ordering constraints\n validateSlotOrdering(Array.from(seenSlots.values()));\n}\n\n/**\n * Validates slot ordering (e.g., auth before parser)\n * @param slots - Array of slot metadata\n * @throws {InvalidSlotError} If ordering is invalid\n */\nfunction validateSlotOrdering(slots: ReadonlyArray<SlotMetadata>): void {\n let transportIndex = -1;\n let authIndex = -1;\n let parserIndex = -1;\n\n slots.forEach((slot, i) => {\n switch (slot.type) {\n case 'transport':\n transportIndex = i;\n break;\n case 'auth':\n authIndex = i;\n break;\n case 'parser':\n parserIndex = i;\n break;\n }\n });\n\n // Transport should be last (if present)\n if (transportIndex !== -1 && transportIndex !== slots.length - 1) {\n throw new InvalidSlotError('transport', 'Transport slot must be the last policy in the chain');\n }\n\n // Auth should come before parser (if both present)\n if (authIndex !== -1 && parserIndex !== -1 && authIndex > parserIndex) {\n throw new InvalidSlotError('auth', 'Auth slot must come before parser slot in the chain');\n }\n}\n\n/**\n * Creates a slot decorator for policies\n * @param metadata - Slot metadata\n * @returns Decorator function\n */\nexport function slot(metadata: SlotMetadata) {\n return (policy: Policy): Policy => {\n registerSlot(policy, metadata);\n return policy;\n };\n}\n\n/**\n * Checks if a policy has a specific slot type\n * @param policy - The policy to check\n * @param type - The slot type to match\n * @returns True if policy has the slot type\n */\nexport function hasSlotType(policy: Policy, type: SlotType): boolean {\n const metadata = getSlotMetadata(policy);\n return metadata?.type === type;\n}\n","/**\n * URL normalization and header utilities\n */\n\nimport { URLNormalizationError } from './errors.js';\nimport type { ClientOptions } from './types.js';\n\n/**\n * Normalizes a URL with base and default scheme support\n * @param url - The URL to normalize (absolute or relative)\n * @param options - Client options containing base and defaultScheme\n * @returns Normalized absolute URL\n */\nexport function normalizeURL(url: string, options: ClientOptions = {}): string {\n const { base, defaultScheme = 'https' } = options;\n\n try {\n // If URL is already absolute, use it directly\n if (url.includes('://')) {\n return new URL(url).toString();\n }\n\n // If URL starts with //, treat as protocol-relative\n if (url.startsWith('//')) {\n return new URL(`${defaultScheme}:${url}`).toString();\n }\n\n // If base is provided, resolve relative to base\n if (base) {\n const baseURL = base.includes('://') ? base : `${defaultScheme}://${base}`;\n return new URL(url, baseURL).toString();\n }\n\n // If no base provided and URL is relative → Error\n // Check if URL is relative (starts with / or doesn't have scheme)\n const isRelative = !url.match(/^[a-z][a-z0-9+.-]*:/i);\n\n if (isRelative) {\n throw new URLNormalizationError(\n url,\n 'Relative URL requires URI in transport. Use http(\"https://api.com\") or provide absolute URL.',\n );\n }\n\n return new URL(url).toString();\n } catch (error) {\n if (error instanceof URLNormalizationError) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new URLNormalizationError(url, message);\n }\n}\n\n/**\n * Appends query parameters to a URL\n * @param url - The base URL\n * @param params - Query parameters to append\n * @returns URL with query parameters\n */\nexport function appendQueryParams(url: string, params: Record<string, string | number | boolean | undefined>): string {\n const urlObj = new URL(url);\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n urlObj.searchParams.append(key, String(value));\n }\n }\n return urlObj.toString();\n}\n\n/**\n * Normalizes HTTP headers to lowercase (per HTTP/2 spec)\n * HTTP/2 requires all header names to be lowercase\n * HTTP/1.1 is case-insensitive, so lowercase is safe everywhere\n *\n * @param headers - Headers object with potentially mixed case keys\n * @returns Headers object with all lowercase keys\n *\n * @example\n * ```ts\n * normalizeHeaders({ 'Content-Type': 'application/json', 'Accept': 'text/html' })\n * // Returns: { 'content-type': 'application/json', 'accept': 'text/html' }\n * ```\n */\nexport function normalizeHeaders(headers: Record<string, string>): Record<string, string> {\n const normalized: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n normalized[key.toLowerCase()] = value;\n }\n return normalized;\n}\n\n/**\n * Gets a header value with case-insensitive lookup\n * Useful for reading headers before normalization\n *\n * @param headers - Headers object\n * @param name - Header name (case-insensitive)\n * @returns Header value or undefined\n *\n * @example\n * ```ts\n * getHeader({ 'Content-Type': 'application/json' }, 'content-type') // 'application/json'\n * getHeader({ 'content-type': 'application/json' }, 'Content-Type') // 'application/json'\n * ```\n */\nexport function getHeader(headers: Record<string, string>, name: string): string | undefined {\n const lowerName = name.toLowerCase();\n // Try lowercase first (most common)\n if (lowerName in headers) {\n return headers[lowerName];\n }\n // Fallback: case-insensitive search\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) {\n return value;\n }\n }\n return undefined;\n}\n\n/**\n * Sets a header value, removing any existing variants with different casing\n * Prevents duplicate headers with different casing (e.g., 'accept' and 'Accept')\n *\n * @param headers - Headers object to modify\n * @param name - Header name (will be normalized to lowercase)\n * @param value - Header value\n * @returns New headers object with the header set (immutable)\n *\n * @example\n * ```ts\n * setHeader({ Accept: 'text/html' }, 'accept', 'application/json')\n * // Returns: { accept: 'application/json' } (removed 'Accept')\n * ```\n */\nexport function setHeader(headers: Record<string, string>, name: string, value: string): Record<string, string> {\n const lowerName = name.toLowerCase();\n const newHeaders: Record<string, string> = {};\n\n // Copy all headers except variants of the target header\n for (const [key, val] of Object.entries(headers)) {\n if (key.toLowerCase() !== lowerName) {\n newHeaders[key] = val;\n }\n }\n\n // Set the new header value with normalized lowercase name\n newHeaders[lowerName] = value;\n\n return newHeaders;\n}\n\n/**\n * Converts a Record<string, string> headers object to native Headers\n *\n * Use this helper when you need to pass headers to external libraries\n * that require the native Headers API (e.g., fetch, Web APIs).\n *\n * Note: In most cases, you should prefer Record<string, string> because:\n * - It's more efficient (no iteration overhead)\n * - It's JSON-serializable\n * - It works directly with unireq APIs\n *\n * @param headers - Headers object (Record<string, string>)\n * @returns Native Headers instance\n *\n * @example\n * ```ts\n * const headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer token' };\n * const nativeHeaders = toNativeHeaders(headers);\n *\n * // Pass to external API that requires native Headers\n * fetch(url, { headers: nativeHeaders });\n * ```\n */\nexport function toNativeHeaders(headers: Record<string, string>): Headers {\n return new Headers(headers);\n}\n\n/**\n * Converts native Headers to Record<string, string>\n *\n * Use this helper when receiving headers from external sources\n * (e.g., Response.headers from fetch) and need to use them with unireq APIs.\n *\n * @param headers - Native Headers instance\n * @returns Headers as Record<string, string>\n *\n * @example\n * ```ts\n * const response = await fetch(url);\n * const headers = fromNativeHeaders(response.headers);\n * console.log(headers['content-type']); // 'application/json'\n * ```\n */\nexport function fromNativeHeaders(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n}\n","/**\n * Client factory and request execution\n */\n\nimport { compose } from './compose.js';\nimport { UnireqError } from './errors.js';\nimport { fromPromise } from './result.js';\nimport { serializationPolicy } from './serialization.js';\nimport { validatePolicyChain } from './slots.js';\nimport type {\n Client,\n Policy,\n RequestContext,\n RequestOptions,\n Response,\n SafeClient,\n Transport,\n TransportWithCapabilities,\n} from './types.js';\nimport { normalizeURL } from './url.js';\n\n/**\n * Type guard to check if a value is a RequestOptions object\n * RequestOptions has specific keys: body, policies, signal\n * We detect it by checking for these keys AND ensuring it's a plain object\n *\n * Detection rules:\n * - Must be a non-null object (not array, not function)\n * - If empty object {}, it's treated as RequestOptions (no body, no policies, no signal)\n * - If non-empty, all keys must be valid RequestOptions keys\n */\nfunction isRequestOptions(value: unknown): value is RequestOptions {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n return false;\n }\n // Functions are not RequestOptions (policies are functions)\n /* v8 ignore next 3 -- @preserve defensive: TypeScript prevents passing functions as RequestOptions */\n if (typeof value === 'function') {\n return false;\n }\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj);\n const validKeys = new Set(['body', 'policies', 'signal']);\n\n // Empty object {} is valid RequestOptions (means no body, no policies, no signal)\n if (keys.length === 0) {\n return true;\n }\n\n // Non-empty: must have at least one valid key AND all keys must be valid\n const hasValidKey = keys.some((k) => validKeys.has(k));\n const allKeysValid = keys.every((k) => validKeys.has(k));\n return hasValidKey && allKeysValid;\n}\n\n/**\n * @frozen Public API contract — 51 files depend on this signature. Changes require impact review.\n * Creates a client with the given transport and policies\n * @param transport - The transport function or transport with capabilities\n * @param policies - Variadic list of policies to apply\n * @returns A configured client instance\n */\nexport function client(transport: Transport | TransportWithCapabilities, ...policies: ReadonlyArray<Policy>): Client {\n // Extract transport and capabilities\n const actualTransport = typeof transport === 'function' ? transport : transport.transport;\n const capabilities = typeof transport === 'function' ? undefined : transport.capabilities;\n\n // Validate policy chain\n validatePolicyChain(policies, capabilities);\n\n // Compose all policies with serialization middleware first\n const composedPolicy = compose(serializationPolicy(), ...policies);\n\n // Base request executor with per-request policies and optional signal\n const executeRequest = async <T = unknown>(\n url: string,\n method: string,\n body: unknown | undefined,\n perRequestPolicies: ReadonlyArray<Policy>,\n signal?: AbortSignal,\n ): Promise<Response<T>> => {\n // Validate URL input\n if (!url || typeof url !== 'string' || url.trim().length === 0) {\n throw new UnireqError('URL must be a non-empty string', 'INVALID_URL');\n }\n\n // Pass URL as-is to transport - let transport handle URI resolution\n // Only normalize absolute URLs to ensure consistent format\n const normalizedURL = url.includes('://') ? normalizeURL(url, {}) : url;\n\n const ctx: RequestContext = {\n url: normalizedURL,\n method,\n headers: {},\n body,\n signal,\n };\n\n // Compose global policies with per-request policies\n if (perRequestPolicies.length > 0) {\n const perRequestPolicy = compose(...perRequestPolicies);\n const combinedPolicy = compose(composedPolicy, perRequestPolicy);\n return combinedPolicy(ctx, actualTransport) as Promise<Response<T>>;\n }\n\n // Execute through global policy chain only\n return composedPolicy(ctx, actualTransport) as Promise<Response<T>>;\n };\n\n // Helper for methods without body (GET, HEAD, DELETE, OPTIONS)\n const createMethodWithoutBody =\n (method: string) =>\n <T = unknown>(url: string, ...args: Array<Policy | RequestOptions>): Promise<Response<T>> => {\n // Check if first arg is RequestOptions\n const firstArg = args[0];\n if (firstArg !== undefined && isRequestOptions(firstArg)) {\n const opts = firstArg;\n return executeRequest<T>(url, method, opts.body, opts.policies || [], opts.signal);\n }\n // Otherwise treat as variadic policies\n return executeRequest<T>(url, method, undefined, args as ReadonlyArray<Policy>);\n };\n\n // Helper for methods with body (POST, PUT, PATCH)\n const createMethodWithBody =\n (method: string) =>\n <T = unknown>(\n url: string,\n bodyOrOptions?: unknown,\n ...restPolicies: ReadonlyArray<Policy>\n ): Promise<Response<T>> => {\n // Check if second arg is RequestOptions\n if (bodyOrOptions !== undefined && isRequestOptions(bodyOrOptions)) {\n const opts = bodyOrOptions;\n return executeRequest<T>(url, method, opts.body, opts.policies || [], opts.signal);\n }\n // Otherwise treat as body + variadic policies\n return executeRequest<T>(url, method, bodyOrOptions, restPolicies);\n };\n\n // Create safe method wrappers that return Result instead of throwing\n const createSafeMethodWithoutBody =\n (method: string) =>\n <T = unknown>(url: string, ...args: Array<Policy | RequestOptions>) =>\n fromPromise<Response<T>, Error>(createMethodWithoutBody(method)<T>(url, ...args));\n\n const createSafeMethodWithBody =\n (method: string) =>\n <T = unknown>(url: string, bodyOrOptions?: unknown, ...policies: ReadonlyArray<Policy>) =>\n fromPromise<Response<T>, Error>(createMethodWithBody(method)<T>(url, bodyOrOptions, ...policies));\n\n // Build safe client namespace\n const safe: SafeClient = {\n request: createSafeMethodWithoutBody('GET'),\n get: createSafeMethodWithoutBody('GET'),\n head: createSafeMethodWithoutBody('HEAD'),\n post: createSafeMethodWithBody('POST'),\n put: createSafeMethodWithBody('PUT'),\n delete: createSafeMethodWithoutBody('DELETE'),\n patch: createSafeMethodWithBody('PATCH'),\n options: createSafeMethodWithoutBody('OPTIONS'),\n };\n\n // Return client interface supporting both APIs\n return {\n request: createMethodWithoutBody('GET'),\n get: createMethodWithoutBody('GET'),\n head: createMethodWithoutBody('HEAD'),\n post: createMethodWithBody('POST'),\n put: createMethodWithBody('PUT'),\n delete: createMethodWithoutBody('DELETE'),\n patch: createMethodWithBody('PATCH'),\n options: createMethodWithoutBody('OPTIONS'),\n safe,\n };\n}\n","/**\n * Conditional branching utilities\n */\n\nimport { getInspectableMeta, policy as tagPolicy } from './introspection.js';\nimport type { Policy, Predicate, RequestContext, Response } from './types.js';\n\n/**\n * Creates a policy that executes different policies based on a predicate\n * Useful for conditional execution (e.g., content negotiation)\n *\n * @param predicate - Function that evaluates the condition\n * @param thenPolicy - Policy to execute if predicate is truthy\n * @param elsePolicy - Optional policy to execute if predicate is falsy\n * @returns A policy that branches based on the predicate\n *\n * @example\n * ```ts\n * const contentNegotiation = either(\n * (ctx) => ctx.headers['accept']?.includes('application/json'),\n * json(),\n * xml()\n * );\n * ```\n */\nexport function either<T = unknown>(predicate: Predicate<T>, thenPolicy: Policy, elsePolicy?: Policy): Policy {\n const impl = async (ctx: RequestContext, next: (ctx: RequestContext) => Promise<Response>) => {\n const result = await Promise.resolve(predicate(ctx));\n\n if (result) {\n return thenPolicy(ctx, next);\n }\n\n if (elsePolicy) {\n return elsePolicy(ctx, next);\n }\n\n // If no else branch and predicate is false, pass through\n return next(ctx);\n };\n\n // Build branch metadata\n const predicateDesc = predicate.name || 'anonymous predicate';\n const thenMetaRaw = getInspectableMeta(thenPolicy);\n const thenMeta = thenMetaRaw ? [thenMetaRaw] : [];\n const elseMetaRaw = elsePolicy ? getInspectableMeta(elsePolicy) : undefined;\n const elseMeta = elseMetaRaw ? [elseMetaRaw] : [];\n\n return tagPolicy(impl, {\n name: 'either',\n kind: 'other',\n branch: {\n predicate: predicateDesc,\n thenBranch: thenMeta,\n elseBranch: elseMeta,\n },\n });\n}\n\n/**\n * Creates a policy that matches against multiple predicates\n * Executes the first matching policy\n *\n * @param branches - Array of predicate-policy pairs\n * @param defaultPolicy - Optional default policy if no predicates match\n * @returns A policy that matches multiple conditions\n *\n * @example\n * ```ts\n * const parser = match(\n * [\n * [(ctx) => ctx.headers['content-type']?.includes('json'), json()],\n * [(ctx) => ctx.headers['content-type']?.includes('xml'), xml()],\n * ],\n * text() // default\n * );\n * ```\n */\nexport function match<T = unknown>(\n branches: ReadonlyArray<readonly [Predicate<T>, Policy]>,\n defaultPolicy?: Policy,\n): Policy {\n const impl = async (ctx: RequestContext, next: (ctx: RequestContext) => Promise<Response>) => {\n for (const [predicate, policy] of branches) {\n const result = await Promise.resolve(predicate(ctx));\n if (result) {\n return policy(ctx, next);\n }\n }\n\n if (defaultPolicy) {\n return defaultPolicy(ctx, next);\n }\n\n return next(ctx);\n };\n\n // Build children from all branches (match is a composition of either)\n const children = branches.map(([predicate, policy], idx) => {\n const predicateDesc = predicate.name || `branch ${idx + 1}`;\n const meta = getInspectableMeta(policy);\n return {\n id: `match-branch-${idx}`,\n name: predicateDesc,\n kind: 'other' as const,\n children: meta ? [meta] : [],\n };\n });\n\n if (defaultPolicy) {\n const defaultMeta = getInspectableMeta(defaultPolicy);\n children.push({\n id: 'match-default',\n name: 'default',\n kind: 'other' as const,\n children: defaultMeta ? [defaultMeta] : [],\n });\n }\n\n return tagPolicy(impl, {\n name: 'match',\n kind: 'other',\n children,\n });\n}\n","/**\n * Policy inspection and formatting utilities\n */\n\nimport type { Handler, InspectableMeta, Kind } from './introspection.js';\nimport { getHandlerGraph, getInspectableMeta } from './introspection.js';\nimport type { Policy } from './types.js';\n\n/**\n * Inspection output format\n */\nexport type InspectFormat = 'json' | 'tree';\n\n/**\n * Inspection options\n */\nexport interface InspectOptions {\n /** Output format (default: 'json') */\n readonly format?: InspectFormat;\n /** Whether to redact sensitive values (default: true) */\n readonly redact?: boolean;\n}\n\n/**\n * Inspects a policy or handler and returns its composition graph\n * @param target - Policy or handler to inspect\n * @param options - Inspection options\n * @returns Formatted policy graph\n *\n * @example\n * ```ts\n * const pipeline = compose(\n * retry(backoff(), { tries: 3 }),\n * json()\n * );\n *\n * // JSON format\n * console.log(inspect(pipeline));\n *\n * // Tree format\n * console.log(inspect(pipeline, { format: 'tree' }));\n * ```\n */\nexport function inspect(target: Handler | Policy, options: InspectOptions = {}): string {\n const { format = 'json' } = options;\n\n // Try to get graph from handler first\n let graph = getHandlerGraph(target as Handler);\n\n // If no graph, try to get metadata from policy\n if (graph.length === 0) {\n const meta = getInspectableMeta(target as Policy);\n if (meta && meta.name !== 'anonymous') {\n graph = [meta];\n }\n }\n\n if (graph.length === 0) {\n return format === 'tree' ? '(empty policy chain)' : '[]';\n }\n\n return format === 'tree' ? toTree(graph) : toJson(graph);\n}\n\n/**\n * Shortcut for JSON format inspection\n */\ninspect.json = (target: Handler | Policy): string => inspect(target, { format: 'json' });\n\n/**\n * Shortcut for tree format inspection\n */\ninspect.tree = (target: Handler | Policy): string => inspect(target, { format: 'tree' });\n\n/**\n * Converts policy graph to JSON string\n */\nfunction toJson(nodes: ReadonlyArray<InspectableMeta>): string {\n return JSON.stringify(nodes, null, 2);\n}\n\n/**\n * Converts policy graph to tree using box-drawing characters\n */\nfunction toTree(nodes: ReadonlyArray<InspectableMeta>): string {\n const lines: string[] = [];\n\n const walk = (meta: InspectableMeta, depth: number, isLast: boolean, parentPrefix = ''): void => {\n // Build indentation based on depth\n let prefix = '';\n if (depth > 0) {\n // For nested items, add proper tree connectors\n prefix = isLast ? '└─ ' : '├─ ';\n }\n\n const optionsStr = formatOptions(meta.options);\n lines.push(`${parentPrefix}${prefix}${meta.name} (${meta.kind})${optionsStr}`);\n\n // Handle children (compose)\n if (meta.children && meta.children.length > 0) {\n const childPrefix = parentPrefix + (depth > 0 ? (isLast ? ' ' : '│ ') : '');\n const childrenLength = meta.children.length;\n meta.children.forEach((child, idx) => {\n walk(child, depth + 1, idx === childrenLength - 1, childPrefix);\n });\n }\n\n // Handle branch (either/match)\n if (meta.branch) {\n const branchPrefix = parentPrefix + (depth > 0 ? (isLast ? ' ' : '│ ') : '');\n lines.push(`${branchPrefix} ? ${meta.branch.predicate}`);\n\n if (meta.branch.thenBranch.length > 0) {\n lines.push(`${branchPrefix} ├─ then:`);\n const thenPrefix = `${branchPrefix} │ `;\n const thenLength = meta.branch.thenBranch.length;\n meta.branch.thenBranch.forEach((child, idx) => {\n walk(child, depth + 2, idx === thenLength - 1, thenPrefix);\n });\n }\n\n if (meta.branch.elseBranch.length > 0) {\n lines.push(`${branchPrefix} └─ else:`);\n const elsePrefix = `${branchPrefix} `;\n const elseLength = meta.branch.elseBranch.length;\n meta.branch.elseBranch.forEach((child, idx) => {\n walk(child, depth + 2, idx === elseLength - 1, elsePrefix);\n });\n }\n }\n };\n\n nodes.forEach((node, idx) => {\n walk(node, 0, idx === nodes.length - 1, '');\n });\n\n return lines.join('\\n');\n}\n\n/**\n * Formats options for tree display\n */\nfunction formatOptions(opts?: Record<string, unknown>): string {\n if (!opts || Object.keys(opts).length === 0) {\n return '';\n }\n\n const entries = Object.entries(opts)\n .map(([key, value]) => `${key}=${prettyValue(value)}`)\n .join(', ');\n\n return ` [${entries}]`;\n}\n\n/**\n * Pretty-prints a value for tree display\n */\nfunction prettyValue(value: unknown): string {\n if (value === null || value === undefined) {\n return 'null';\n }\n\n if (typeof value === 'string') {\n return `\"${value}\"`;\n }\n\n if (typeof value === 'boolean' || typeof value === 'number') {\n return String(value);\n }\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return '[]';\n }\n if (value.length <= 3) {\n return `[${value.map(prettyValue).join(', ')}]`;\n }\n return `[${value.slice(0, 2).map(prettyValue).join(', ')}, ... +${value.length - 2}]`;\n }\n\n if (typeof value === 'object') {\n const keys = Object.keys(value);\n if (keys.length === 0) {\n return '{}';\n }\n const keyList = keys.slice(0, 2).join(', ');\n if (keys.length > 2) {\n return `{${keyList}, ...}`;\n }\n /* v8 ignore next 2 */\n return `{${keyList}}`;\n }\n\n // Unreachable in practice since JSON.parse() can't produce non-JSON types\n /* v8 ignore next 2 */\n return String(value);\n}\n\n/**\n * Asserts that a policy or handler has a specific policy kind\n * Useful for testing presets\n *\n * @param target - Policy or handler to check\n * @param kind - Expected policy kind\n * @throws Error if kind is not found\n *\n * @example\n * ```ts\n * const pipeline = compose(auth(), json());\n * assertHas(pipeline, 'auth'); // throws if no auth policy\n * assertHas(pipeline, 'parser'); // throws if no parser policy\n * ```\n */\nexport function assertHas(target: Handler | Policy, kind: Kind): void {\n // Try to get graph from handler first\n let graph = getHandlerGraph(target as Handler);\n\n // If no graph, try to get metadata from policy\n if (graph.length === 0) {\n const meta = getInspectableMeta(target as Policy);\n if (meta && meta.name !== 'anonymous') {\n graph = [meta];\n }\n }\n\n const hasKind = (nodes: ReadonlyArray<InspectableMeta>): boolean => {\n for (const node of nodes) {\n if (node.kind === kind) {\n return true;\n }\n if (node.children && hasKind(node.children)) {\n return true;\n }\n if (node.branch) {\n if (hasKind(node.branch.thenBranch) || hasKind(node.branch.elseBranch)) {\n return true;\n }\n }\n }\n return false;\n };\n\n if (!hasKind(graph)) {\n throw new Error(`Expected policy kind \"${kind}\" not found in handler graph`);\n }\n}\n","/**\n * Logging policy\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { policy } from './introspection.js';\nimport type { Logger, Policy } from './types.js';\n\nexport interface LogOptions {\n readonly logger: Logger;\n readonly redactHeaders?: ReadonlyArray<string>;\n readonly logBody?: boolean;\n}\n\nconst DEFAULT_REDACT_HEADERS = ['authorization', 'cookie', 'set-cookie', 'x-api-key'];\n\nexport function log(options: LogOptions): Policy {\n const { logger, redactHeaders = DEFAULT_REDACT_HEADERS, logBody = false } = options;\n\n const redact = (headers: Record<string, string>): Record<string, string> => {\n const redacted = { ...headers };\n for (const header of redactHeaders) {\n const key = Object.keys(redacted).find((k) => k.toLowerCase() === header.toLowerCase());\n if (key) {\n redacted[key] = '[REDACTED]';\n }\n }\n return redacted;\n };\n\n return policy(\n async (ctx, next) => {\n const start = Date.now();\n const requestId = randomUUID();\n\n logger.info(`Request ${requestId} started`, {\n requestId,\n method: ctx.method,\n url: ctx.url,\n headers: redact(ctx.headers),\n body: logBody ? ctx.body : undefined,\n });\n\n try {\n const response = await next(ctx);\n const duration = Date.now() - start;\n\n logger.info(`Request ${requestId} completed`, {\n requestId,\n status: response.status,\n duration,\n headers: redact(response.headers),\n data: logBody ? response.data : undefined,\n });\n\n return response;\n } catch (error) {\n const duration = Date.now() - start;\n logger.error(`Request ${requestId} failed`, {\n requestId,\n duration,\n error,\n });\n throw error;\n }\n },\n {\n name: 'log',\n kind: 'other',\n options: { redactHeaders, logBody },\n },\n );\n}\n","/**\n * Generic transport-agnostic retry flow control primitive\n */\n\nimport type { InspectableMeta } from './introspection.js';\nimport { getInspectableMeta, policy } from './introspection.js';\nimport type { Policy, RequestContext, Response } from './types.js';\n\n/**\n * Retry predicate function that determines if a retry should occur\n * Returns true to retry, false to stop retrying\n */\nexport type RetryPredicate<T = Response> = (\n result: T | null,\n error: Error | null,\n attempt: number,\n context: RequestContext,\n) => boolean | Promise<boolean>;\n\n/**\n * Retry delay strategy that calculates the delay before next retry\n * Returns delay in milliseconds, or undefined to skip delay calculation\n */\nexport type RetryDelayStrategy<T = Response> = {\n readonly getDelay: (\n result: T | null,\n error: Error | null,\n attempt: number,\n ) => number | undefined | Promise<number | undefined>;\n};\n\n/**\n * Generic retry options\n */\nexport interface RetryOptions<T = Response> {\n /** Number of retry attempts (default: 3) */\n readonly tries?: number;\n /** Callback before retry */\n readonly onRetry?: (attempt: number, error: Error | null, result: T | null) => void | Promise<void>;\n}\n\n/**\n * Calculate delay using strategies in order (first match wins)\n * @internal\n */\nasync function calculateDelay<T>(\n strategies: ReadonlyArray<RetryDelayStrategy<T>>,\n result: T | null,\n error: Error | null,\n attempt: number,\n): Promise<number> {\n for (const strategy of strategies) {\n const strategyDelay = await strategy.getDelay(result, error, attempt);\n if (strategyDelay !== undefined) {\n return strategyDelay;\n }\n }\n return 0;\n}\n\n/**\n * Creates a generic transport-agnostic retry policy\n *\n * This is a pure flow control primitive that works with any protocol (HTTP, FTP, IMAP, etc.).\n * It executes a predicate to determine if retry should occur, applies delay strategies,\n * and invokes callbacks.\n *\n * @param predicate - Function to determine if retry should occur\n * @param strategies - Delay strategies to calculate wait time before retry\n * @param options - Retry options\n * @returns Policy that retries based on predicate\n *\n * @example\n * ```ts\n * // HTTP-specific retry with status codes\n * const httpRetry = retry(\n * (result, error) => error !== null || (result?.status >= 500 && result?.status < 600),\n * [backoff({ initial: 100, max: 5000 })],\n * { tries: 3 }\n * );\n * ```\n *\n * @example\n * ```ts\n * // Generic retry for any operation\n * const genericRetry = retry(\n * (result, error) => error !== null,\n * [exponentialBackoff()],\n * { tries: 5 }\n * );\n * ```\n */\nexport function retry<T = Response>(\n predicate: RetryPredicate<T>,\n strategies: ReadonlyArray<RetryDelayStrategy<T>>,\n options?: RetryOptions<T>,\n): Policy {\n const { tries = 3, onRetry } = options ?? {};\n\n return policy(\n async (ctx, next) => {\n let lastResponse: Response | undefined;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < tries; attempt++) {\n try {\n const response = await next(ctx);\n lastResponse = response;\n\n // Check if we should retry using predicate\n const shouldRetry = await Promise.resolve(predicate(response as T, null, attempt, ctx));\n\n // If predicate says no retry, or last attempt, return response\n if (!shouldRetry || attempt === tries - 1) {\n return response;\n }\n\n // Call onRetry callback\n if (onRetry) {\n await Promise.resolve(onRetry(attempt + 1, null, response as T));\n }\n\n // Calculate and apply delay\n const delay = await calculateDelay(strategies, response as T, null, attempt);\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Last attempt - throw error\n if (attempt === tries - 1) {\n throw lastError;\n }\n\n // Check if we should retry on error using predicate\n const shouldRetry = await Promise.resolve(predicate(null, lastError, attempt, ctx));\n\n if (!shouldRetry) {\n throw lastError;\n }\n\n // Call onRetry callback\n if (onRetry) {\n await Promise.resolve(onRetry(attempt + 1, lastError, null));\n }\n\n // Calculate and apply delay\n const delay = await calculateDelay(strategies, null, lastError, attempt);\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n return lastResponse as Response;\n },\n {\n name: 'retry',\n kind: 'retry',\n options: {\n tries,\n },\n children: [\n // Add predicate metadata\n ...(() => {\n const meta = getInspectableMeta(predicate);\n return meta ? [meta] : [];\n })(),\n // Add strategies metadata\n ...strategies.map((s) => getInspectableMeta(s)).filter((m): m is InspectableMeta => m !== undefined),\n ],\n },\n );\n}\n","/**\n * Throttle policy (Client-side Rate Limiter)\n * Controls the rate of outgoing requests using Token Bucket algorithm\n */\n\nimport { policy } from './introspection.js';\nimport type { Policy } from './types.js';\n\nexport interface ThrottleOptions {\n /** Number of requests allowed per interval */\n readonly limit: number;\n /** Interval in milliseconds (default: 1000) */\n readonly interval?: number;\n}\n\nexport function throttle(options: ThrottleOptions): Policy {\n const { limit, interval = 1000 } = options;\n\n let tokens = limit;\n let lastRefill = Date.now();\n const refillRate = limit / interval; // tokens per ms\n\n const refill = () => {\n const now = Date.now();\n const elapsed = now - lastRefill;\n const newTokens = elapsed * refillRate;\n\n if (newTokens > 0) {\n tokens = Math.min(limit, tokens + newTokens);\n lastRefill = now;\n }\n };\n\n return policy(\n async (ctx, next) => {\n refill();\n\n if (tokens < 1) {\n // Calculate wait time\n const missingTokens = 1 - tokens;\n const waitTime = Math.ceil(missingTokens / refillRate);\n\n await new Promise((resolve) => setTimeout(resolve, waitTime));\n\n // Refill after wait\n refill();\n }\n\n tokens -= 1;\n return next(ctx);\n },\n {\n name: 'throttle',\n kind: 'other',\n options: { limit, interval },\n },\n );\n}\n","/**\n * Core types for the unireq framework\n */\n\nimport type { Result } from './result.js';\n\n/**\n * Request context passed through the policy chain\n * @frozen Adding required fields is a breaking change across 55 importer files. All new fields must be optional.\n */\nexport interface RequestContext {\n readonly url: string;\n readonly method: string;\n readonly headers: Record<string, string>;\n readonly body?: unknown;\n readonly signal?: AbortSignal;\n readonly policies?: ReadonlyArray<Policy>;\n readonly [key: string]: unknown;\n}\n\n/** Response from a transport or policy */\nexport interface Response<T = unknown> {\n readonly status: number;\n readonly statusText: string;\n readonly headers: Record<string, string>;\n readonly data: T;\n readonly ok: boolean;\n}\n\n/** Policy function that transforms request context */\nexport type Policy = (ctx: RequestContext, next: (ctx: RequestContext) => Promise<Response>) => Promise<Response>;\n\n/** Transport capabilities declaration */\nexport interface TransportCapabilities {\n readonly streams?: boolean;\n readonly multipartFormData?: boolean;\n readonly randomAccess?: boolean;\n readonly [key: string]: boolean | undefined;\n}\n\n/** Connector interface for pluggable transport implementations */\nexport interface Connector<TClient = unknown> {\n connect(uri: string): Promise<TClient> | TClient;\n request(client: TClient, ctx: RequestContext): Promise<Response>;\n disconnect(client: TClient): Promise<void> | void;\n}\n\n/** Transport function that executes the actual I/O */\nexport type Transport = (ctx: RequestContext) => Promise<Response>;\n\n/** Transport with capabilities metadata */\nexport interface TransportWithCapabilities {\n readonly transport: Transport;\n readonly capabilities: TransportCapabilities;\n}\n\n/** Slot types for compile/runtime checks */\nexport enum SlotType {\n Transport = 'transport',\n Auth = 'auth',\n Parser = 'parser',\n}\n\n/** Slot metadata for policy registration */\nexport interface SlotMetadata {\n readonly type: SlotType;\n readonly name: string;\n readonly requiredCapabilities?: ReadonlyArray<string>;\n}\n\n/** Client configuration options */\nexport interface ClientOptions {\n readonly base?: string;\n readonly defaultScheme?: 'http' | 'https' | 'ftp' | 'imap';\n readonly policies?: ReadonlyArray<Policy>;\n}\n\n/**\n * Per-request options for client methods\n * Allows passing body, policies, and signal in a structured way\n *\n * @example\n * ```ts\n * // Using options object\n * await client.post('/users', {\n * body: { name: 'Alice' },\n * policies: [timeout(5000)],\n * signal: AbortSignal.timeout(10000),\n * });\n *\n * // Equivalent to variadic API\n * await client.post('/users', { name: 'Alice' }, timeout(5000));\n * ```\n */\nexport interface RequestOptions {\n /** Request body (for POST, PUT, PATCH) */\n readonly body?: unknown;\n /** Per-request policies to compose with client-level policies */\n readonly policies?: ReadonlyArray<Policy>;\n /** AbortSignal for request cancellation */\n readonly signal?: AbortSignal;\n}\n\n/**\n * Safe client methods that return Result<Response<T>, Error> instead of throwing\n *\n * @example\n * ```ts\n * const result = await client.safe.get<User>('/users/1');\n *\n * // Pattern matching\n * result.match({\n * ok: (response) => console.log(response.data),\n * err: (error) => console.error(error.message),\n * });\n *\n * // Chaining\n * const name = result\n * .map(r => r.data)\n * .map(user => user.name)\n * .unwrapOr('Unknown');\n * ```\n */\nexport interface SafeClient {\n /** Request with variadic policies */\n request<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;\n /** Request with options object */\n request<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;\n\n /** GET with variadic policies */\n get<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;\n /** GET with options object */\n get<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;\n\n /** HEAD with variadic policies */\n head<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;\n /** HEAD with options object */\n head<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;\n\n /** POST with body and variadic policies */\n post<T = unknown>(\n url: string,\n body?: unknown,\n ...policies: ReadonlyArray<Policy>\n ): Promise<Result<Response<T>, Error>>;\n /** POST with options object */\n post<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;\n\n /** PUT with body and variadic policies */\n put<T = unknown>(\n url: string,\n body?: unknown,\n ...policies: ReadonlyArray<Policy>\n ): Promise<Result<Response<T>, Error>>;\n /** PUT with options object */\n put<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;\n\n /** DELETE with variadic policies */\n delete<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;\n /** DELETE with options object */\n delete<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;\n\n /** PATCH with body and variadic policies */\n patch<T = unknown>(\n url: string,\n body?: unknown,\n ...policies: ReadonlyArray<Policy>\n ): Promise<Result<Response<T>, Error>>;\n /** PATCH with options object */\n patch<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;\n\n /** OPTIONS with variadic policies */\n options<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Result<Response<T>, Error>>;\n /** OPTIONS with options object */\n options<T = unknown>(url: string, options: RequestOptions): Promise<Result<Response<T>, Error>>;\n}\n\n/** Client instance */\nexport interface Client {\n /** Make a request with variadic policies */\n request<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;\n /** Make a request with options object */\n request<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;\n\n /** GET with variadic policies */\n get<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;\n /** GET with options object */\n get<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;\n\n /** HEAD with variadic policies */\n head<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;\n /** HEAD with options object */\n head<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;\n\n /** POST with body and variadic policies */\n post<T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;\n /** POST with options object */\n post<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;\n\n /** PUT with body and variadic policies */\n put<T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;\n /** PUT with options object */\n put<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;\n\n /** DELETE with variadic policies */\n delete<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;\n /** DELETE with options object */\n delete<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;\n\n /** PATCH with body and variadic policies */\n patch<T = unknown>(url: string, body?: unknown, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;\n /** PATCH with options object */\n patch<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;\n\n /** OPTIONS with variadic policies */\n options<T = unknown>(url: string, ...policies: ReadonlyArray<Policy>): Promise<Response<T>>;\n /** OPTIONS with options object */\n options<T = unknown>(url: string, options: RequestOptions): Promise<Response<T>>;\n\n /** Safe methods that return Result instead of throwing */\n readonly safe: SafeClient;\n}\n\n/** Predicate function for conditional branching */\nexport type Predicate<T = unknown> = (ctx: RequestContext) => T | Promise<T>;\n\n/** Either branch configuration */\nexport interface EitherBranch<T> {\n readonly predicate: Predicate<T>;\n readonly then: Policy;\n readonly else?: Policy;\n}\n\n/** Body descriptor for request serialization */\nexport interface BodyDescriptor {\n readonly __brand: 'BodyDescriptor';\n readonly data: unknown;\n readonly contentType?: string;\n readonly serialize: () => string | Blob | ArrayBuffer | FormData | ReadableStream<Uint8Array>;\n readonly filename?: string;\n readonly contentLength?: number;\n}\n\n/** Multipart part configuration */\nexport interface MultipartPart {\n readonly name: string;\n readonly part: BodyDescriptor;\n readonly filename?: string;\n}\n\n/** Logger interface for structured logging */\nexport interface Logger {\n debug(message: string, meta?: Record<string, unknown>): void;\n info(message: string, meta?: Record<string, unknown>): void;\n warn(message: string, meta?: Record<string, unknown>): void;\n error(message: string, meta?: Record<string, unknown>): void;\n}\n","/**\n * Validation policy using Adapter Pattern\n */\n\nimport { SerializationError } from './errors.js';\nimport { policy } from './introspection.js';\nimport type { Policy } from './types.js';\n\n/**\n * Validation adapter interface\n * Adapts any validation library (Zod, Valibot, Joi, etc.) to unireq\n */\nexport interface ValidationAdapter<TSchema, TOutput> {\n validate(schema: TSchema, data: unknown): Promise<TOutput> | TOutput;\n}\n\n/**\n * Validates response data using a schema and an adapter\n * @param schema - The schema to validate against\n * @param adapter - The adapter for the validation library\n * @returns Policy that validates response data\n */\nexport function validate<TSchema, TOutput>(schema: TSchema, adapter: ValidationAdapter<TSchema, TOutput>): Policy {\n return policy(\n async (ctx, next) => {\n const response = await next(ctx);\n\n try {\n const validatedData = await adapter.validate(schema, response.data);\n return {\n ...response,\n data: validatedData,\n };\n } catch (error) {\n throw new SerializationError(`Validation failed: ${(error as Error).message}`, error);\n }\n },\n {\n name: 'validate',\n kind: 'other',\n options: { schema },\n },\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unireq/core",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Core client and composition utilities for unireq",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,7 +26,7 @@
26
26
  "devDependencies": {
27
27
  "typescript": "^5.9.3",
28
28
  "tsup": "^8.5.1",
29
- "vitest": "^4.0.16"
29
+ "vitest": "^4.0.18"
30
30
  },
31
31
  "engines": {
32
32
  "node": ">=18.0.0"