@igniter-js/caller 0.1.3 → 0.1.4

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/dist/index.mjs CHANGED
@@ -1,110 +1,13 @@
1
1
  import { IgniterError } from '@igniter-js/core';
2
+ import { z } from 'zod';
2
3
 
3
- // src/builder/igniter-caller.builder.ts
4
- var IgniterCallerBuilder = class _IgniterCallerBuilder {
5
- constructor(state, factory) {
6
- this.state = state;
7
- this.factory = factory;
8
- }
9
- /**
10
- * Creates a new builder instance.
11
- */
12
- static create(factory) {
13
- return new _IgniterCallerBuilder({}, factory);
14
- }
15
- /** Sets the base URL for all requests. */
16
- withBaseUrl(baseURL) {
17
- return new _IgniterCallerBuilder({ ...this.state, baseURL }, this.factory);
18
- }
19
- /** Merges default headers for all requests. */
20
- withHeaders(headers) {
21
- return new _IgniterCallerBuilder({ ...this.state, headers }, this.factory);
22
- }
23
- /** Sets default cookies (sent as the `Cookie` header). */
24
- withCookies(cookies) {
25
- return new _IgniterCallerBuilder({ ...this.state, cookies }, this.factory);
26
- }
27
- /** Attaches a logger instance. */
28
- withLogger(logger) {
29
- return new _IgniterCallerBuilder({ ...this.state, logger }, this.factory);
30
- }
31
- /** Adds a request interceptor that runs before each request. */
32
- withRequestInterceptor(interceptor) {
33
- const requestInterceptors = [
34
- ...this.state.requestInterceptors || [],
35
- interceptor
36
- ];
37
- return new _IgniterCallerBuilder(
38
- { ...this.state, requestInterceptors },
39
- this.factory
40
- );
41
- }
42
- /** Adds a response interceptor that runs after each request. */
43
- withResponseInterceptor(interceptor) {
44
- const responseInterceptors = [
45
- ...this.state.responseInterceptors || [],
46
- interceptor
47
- ];
48
- return new _IgniterCallerBuilder(
49
- { ...this.state, responseInterceptors },
50
- this.factory
51
- );
52
- }
53
- /**
54
- * Configures a persistent store adapter for caching.
55
- *
56
- * When configured, cache operations will use the store (e.g., Redis)
57
- * instead of in-memory cache, enabling persistent cache across deployments.
58
- */
59
- withStore(store, options) {
60
- return new _IgniterCallerBuilder(
61
- { ...this.state, store, storeOptions: options },
62
- this.factory
63
- );
64
- }
4
+ // src/errors/caller.error.ts
5
+ var IgniterCallerError = class _IgniterCallerError extends IgniterError {
65
6
  /**
66
- * Configures schema-based type safety and validation.
7
+ * Creates a new typed caller error.
67
8
  *
68
- * Enables automatic type inference for requests/responses based on
69
- * route and method, with optional runtime validation via Zod.
70
- *
71
- * @example
72
- * ```ts
73
- * const api = IgniterCaller.create()
74
- * .withSchemas({
75
- * '/users': {
76
- * GET: {
77
- * responses: {
78
- * 200: z.array(UserSchema),
79
- * 401: ErrorSchema,
80
- * },
81
- * },
82
- * POST: {
83
- * request: CreateUserSchema,
84
- * responses: {
85
- * 201: UserSchema,
86
- * 400: ValidationErrorSchema,
87
- * },
88
- * },
89
- * },
90
- * })
91
- * .build()
92
- * ```
9
+ * @param payload - Error payload with code, message, and metadata.
93
10
  */
94
- withSchemas(schemas, validation) {
95
- return new _IgniterCallerBuilder(
96
- { ...this.state, schemas, schemaValidation: validation },
97
- this.factory
98
- );
99
- }
100
- /**
101
- * Builds the `IgniterCaller` instance.
102
- */
103
- build() {
104
- return this.factory(this.state);
105
- }
106
- };
107
- var IgniterCallerError = class _IgniterCallerError extends IgniterError {
108
11
  constructor(payload) {
109
12
  const metadata = {
110
13
  ...payload.metadata,
@@ -119,13 +22,19 @@ var IgniterCallerError = class _IgniterCallerError extends IgniterError {
119
22
  causer: "@igniter-js/caller",
120
23
  details,
121
24
  metadata,
122
- logger: payload.logger
25
+ logger: payload.logger,
26
+ cause: payload.cause
123
27
  });
124
28
  this.name = "IgniterCallerError";
125
29
  this.operation = payload.operation;
126
30
  this.statusText = payload.statusText;
127
31
  this.cause = payload.cause;
128
32
  }
33
+ /**
34
+ * Type guard for `IgniterCallerError`.
35
+ *
36
+ * @param error - Value to check.
37
+ */
129
38
  static is(error) {
130
39
  return error instanceof _IgniterCallerError;
131
40
  }
@@ -135,6 +44,9 @@ var IgniterCallerError = class _IgniterCallerError extends IgniterError {
135
44
  var IgniterCallerBodyUtils = class {
136
45
  /**
137
46
  * Returns true when the request body should be passed to `fetch` as-is.
47
+ *
48
+ * @param body - Request body to inspect.
49
+ * @returns True if the body should be sent as raw data.
138
50
  */
139
51
  static isRawBody(body) {
140
52
  if (!body) return false;
@@ -153,6 +65,10 @@ var IgniterCallerBodyUtils = class {
153
65
  }
154
66
  /**
155
67
  * Removes Content-Type for FormData so fetch can set boundaries automatically.
68
+ *
69
+ * @param headers - Request headers map.
70
+ * @param body - Request body.
71
+ * @returns Updated headers without Content-Type when needed.
156
72
  */
157
73
  static normalizeHeadersForBody(headers, body) {
158
74
  if (!headers) return headers;
@@ -171,6 +87,9 @@ var _IgniterCallerCacheUtils = class _IgniterCallerCacheUtils {
171
87
  *
172
88
  * When configured, cache operations will use the store (e.g., Redis)
173
89
  * instead of in-memory cache, enabling persistent cache across deployments.
90
+ *
91
+ * @param store - Store adapter implementation.
92
+ * @param options - Store options such as ttl and key prefix.
174
93
  */
175
94
  static setStore(store, options) {
176
95
  _IgniterCallerCacheUtils.store = store;
@@ -183,12 +102,18 @@ var _IgniterCallerCacheUtils = class _IgniterCallerCacheUtils {
183
102
  }
184
103
  /**
185
104
  * Gets the configured store adapter.
105
+ *
106
+ * @returns Store adapter or null when unset.
186
107
  */
187
108
  static getStore() {
188
109
  return _IgniterCallerCacheUtils.store;
189
110
  }
190
111
  /**
191
112
  * Gets cached data if it exists and is not stale.
113
+ *
114
+ * @param key - Cache key (without prefix).
115
+ * @param staleTime - Optional stale time in milliseconds.
116
+ * @returns Cached value or undefined when missing/stale.
192
117
  */
193
118
  static async get(key, staleTime) {
194
119
  const prefixedKey = _IgniterCallerCacheUtils.getPrefixedKey(key);
@@ -212,6 +137,10 @@ var _IgniterCallerCacheUtils = class _IgniterCallerCacheUtils {
212
137
  }
213
138
  /**
214
139
  * Stores data in cache with current timestamp.
140
+ *
141
+ * @param key - Cache key (without prefix).
142
+ * @param data - Data to cache.
143
+ * @param ttl - Optional TTL override in seconds.
215
144
  */
216
145
  static async set(key, data, ttl) {
217
146
  const prefixedKey = _IgniterCallerCacheUtils.getPrefixedKey(key);
@@ -233,6 +162,8 @@ var _IgniterCallerCacheUtils = class _IgniterCallerCacheUtils {
233
162
  }
234
163
  /**
235
164
  * Clears a specific cache entry.
165
+ *
166
+ * @param key - Cache key (without prefix).
236
167
  */
237
168
  static async clear(key) {
238
169
  const prefixedKey = _IgniterCallerCacheUtils.getPrefixedKey(key);
@@ -266,6 +197,8 @@ var _IgniterCallerCacheUtils = class _IgniterCallerCacheUtils {
266
197
  }
267
198
  /**
268
199
  * Clears all cache entries.
200
+ *
201
+ * @returns Promise that resolves when in-memory cache is cleared.
269
202
  */
270
203
  static async clearAll() {
271
204
  _IgniterCallerCacheUtils.cache.clear();
@@ -304,6 +237,10 @@ var IgniterCallerSchemaUtils = class _IgniterCallerSchemaUtils {
304
237
  /**
305
238
  * Matches a URL path against schema map paths (supports path parameters).
306
239
  *
240
+ * @param actualPath - Incoming request path.
241
+ * @param schemaPath - Schema path pattern.
242
+ * @returns Match result with params when matched.
243
+ *
307
244
  * @example
308
245
  * ```ts
309
246
  * matchPath('/users/123', '/users/:id') // { matched: true, params: { id: '123' } }
@@ -332,6 +269,11 @@ var IgniterCallerSchemaUtils = class _IgniterCallerSchemaUtils {
332
269
  }
333
270
  /**
334
271
  * Finds the schema for a given path and method from the schema map.
272
+ *
273
+ * @param schemaMap - Schema map from the builder.
274
+ * @param path - Request path to match.
275
+ * @param method - HTTP method to match.
276
+ * @returns Matching schema and extracted params.
335
277
  */
336
278
  static findSchema(schemaMap, path, method) {
337
279
  if (!schemaMap) {
@@ -357,6 +299,10 @@ var IgniterCallerSchemaUtils = class _IgniterCallerSchemaUtils {
357
299
  *
358
300
  * If the schema provides `~standard.validate`, it will be used.
359
301
  * Otherwise, returns the input as-is.
302
+ *
303
+ * @param schema - StandardSchema instance.
304
+ * @param input - Input value to validate.
305
+ * @returns Validated input value.
360
306
  */
361
307
  static async validateWithStandardSchema(schema, input) {
362
308
  const standard = schema?.["~standard"];
@@ -375,7 +321,12 @@ var IgniterCallerSchemaUtils = class _IgniterCallerSchemaUtils {
375
321
  /**
376
322
  * Validates request body against schema.
377
323
  *
378
- * @returns Validated data or throws/logs error based on validation mode
324
+ * @param data - Request body data.
325
+ * @param schema - Request schema (if any).
326
+ * @param options - Validation options.
327
+ * @param context - Request context for error reporting.
328
+ * @param logger - Optional logger instance.
329
+ * @returns Validated data or throws/logs error based on validation mode.
379
330
  */
380
331
  static async validateRequest(data, schema, options, context, logger) {
381
332
  if (!schema || options?.mode === "off") {
@@ -408,7 +359,13 @@ var IgniterCallerSchemaUtils = class _IgniterCallerSchemaUtils {
408
359
  /**
409
360
  * Validates response data against schema.
410
361
  *
411
- * @returns Validated data or throws/logs error based on validation mode
362
+ * @param data - Response payload to validate.
363
+ * @param schema - Response schema (if any).
364
+ * @param statusCode - HTTP status code.
365
+ * @param options - Validation options.
366
+ * @param context - Request context for error reporting.
367
+ * @param logger - Optional logger instance.
368
+ * @returns Validated data or throws/logs error based on validation mode.
412
369
  */
413
370
  static async validateResponse(data, schema, statusCode, options, context, logger) {
414
371
  if (!schema || options?.mode === "off") {
@@ -443,6 +400,12 @@ var IgniterCallerSchemaUtils = class _IgniterCallerSchemaUtils {
443
400
 
444
401
  // src/utils/url.ts
445
402
  var IgniterCallerUrlUtils = class {
403
+ /**
404
+ * Builds a full URL with optional base URL and query parameters.
405
+ *
406
+ * @param params - URL construction parameters.
407
+ * @returns Full URL string.
408
+ */
446
409
  static buildUrl(params) {
447
410
  const { url, baseURL, query } = params;
448
411
  let fullUrl = url;
@@ -459,7 +422,7 @@ var IgniterCallerUrlUtils = class {
459
422
  }
460
423
  };
461
424
 
462
- // src/builder/igniter-caller-request.builder.ts
425
+ // src/builders/request.builder.ts
463
426
  var VALIDATABLE_CONTENT_TYPES = [
464
427
  "json",
465
428
  "xml",
@@ -507,6 +470,11 @@ async function parseResponseByContentType(response, contentType) {
507
470
  }
508
471
  }
509
472
  var IgniterCallerRequestBuilder = class {
473
+ /**
474
+ * Creates a new request builder instance.
475
+ *
476
+ * @param params - Builder configuration from the manager.
477
+ */
510
478
  constructor(params) {
511
479
  this.options = {
512
480
  method: "GET",
@@ -529,6 +497,7 @@ var IgniterCallerRequestBuilder = class {
529
497
  this.options.headers = { ...this.options.headers, Cookie: cookieStr };
530
498
  }
531
499
  this.logger = params.logger;
500
+ this.telemetry = params.telemetry;
532
501
  this.requestInterceptors = params.requestInterceptors;
533
502
  this.responseInterceptors = params.responseInterceptors;
534
503
  this.eventEmitter = params.eventEmitter;
@@ -538,6 +507,8 @@ var IgniterCallerRequestBuilder = class {
538
507
  /**
539
508
  * Sets the HTTP method for this request.
540
509
  * @internal Used by IgniterCaller.request() for generic requests.
510
+ *
511
+ * @param method - HTTP method for the request.
541
512
  */
542
513
  _setMethod(method) {
543
514
  this.options.method = method;
@@ -546,6 +517,8 @@ var IgniterCallerRequestBuilder = class {
546
517
  /**
547
518
  * Sets the URL for this request.
548
519
  * @internal Used when URL is passed to HTTP method directly.
520
+ *
521
+ * @param url - Request URL or path.
549
522
  */
550
523
  _setUrl(url) {
551
524
  this.options.url = url;
@@ -553,6 +526,8 @@ var IgniterCallerRequestBuilder = class {
553
526
  }
554
527
  /**
555
528
  * Overrides the logger for this request chain.
529
+ *
530
+ * @param logger - Logger implementation from `@igniter-js/core`.
556
531
  */
557
532
  withLogger(logger) {
558
533
  this.logger = logger;
@@ -560,6 +535,8 @@ var IgniterCallerRequestBuilder = class {
560
535
  }
561
536
  /**
562
537
  * Sets the request URL.
538
+ *
539
+ * @param url - Request URL or path.
563
540
  */
564
541
  url(url) {
565
542
  this.options.url = url;
@@ -568,6 +545,8 @@ var IgniterCallerRequestBuilder = class {
568
545
  /**
569
546
  * Sets the request body.
570
547
  * For GET/HEAD requests, body will be automatically converted to query params.
548
+ *
549
+ * @param body - Body payload for the request.
571
550
  */
572
551
  body(body) {
573
552
  this.options.body = body;
@@ -575,6 +554,8 @@ var IgniterCallerRequestBuilder = class {
575
554
  }
576
555
  /**
577
556
  * Sets URL query parameters.
557
+ *
558
+ * @param params - Query string parameters.
578
559
  */
579
560
  params(params) {
580
561
  this.options.params = params;
@@ -582,6 +563,8 @@ var IgniterCallerRequestBuilder = class {
582
563
  }
583
564
  /**
584
565
  * Merges additional headers into the request.
566
+ *
567
+ * @param headers - Header map merged into existing headers.
585
568
  */
586
569
  headers(headers) {
587
570
  this.options.headers = { ...this.options.headers, ...headers };
@@ -589,6 +572,8 @@ var IgniterCallerRequestBuilder = class {
589
572
  }
590
573
  /**
591
574
  * Sets request timeout in milliseconds.
575
+ *
576
+ * @param timeout - Timeout in milliseconds.
592
577
  */
593
578
  timeout(timeout) {
594
579
  this.options.timeout = timeout;
@@ -596,6 +581,9 @@ var IgniterCallerRequestBuilder = class {
596
581
  }
597
582
  /**
598
583
  * Sets cache strategy and optional cache key.
584
+ *
585
+ * @param cache - Cache strategy for the request.
586
+ * @param key - Optional cache key override.
599
587
  */
600
588
  cache(cache, key) {
601
589
  this.options.cache = cache;
@@ -604,6 +592,9 @@ var IgniterCallerRequestBuilder = class {
604
592
  }
605
593
  /**
606
594
  * Configures retry behavior for failed requests.
595
+ *
596
+ * @param maxAttempts - Maximum number of attempts.
597
+ * @param options - Retry options excluding `maxAttempts`.
607
598
  */
608
599
  retry(maxAttempts, options) {
609
600
  this.retryOptions = { maxAttempts, ...options };
@@ -611,6 +602,8 @@ var IgniterCallerRequestBuilder = class {
611
602
  }
612
603
  /**
613
604
  * Provides a fallback value if the request fails.
605
+ *
606
+ * @param fn - Fallback factory called when the request fails.
614
607
  */
615
608
  fallback(fn) {
616
609
  this.fallbackFn = fn;
@@ -618,6 +611,8 @@ var IgniterCallerRequestBuilder = class {
618
611
  }
619
612
  /**
620
613
  * Sets cache stale time in milliseconds.
614
+ *
615
+ * @param milliseconds - Stale time in milliseconds.
621
616
  */
622
617
  stale(milliseconds) {
623
618
  this.staleTime = milliseconds;
@@ -631,6 +626,8 @@ var IgniterCallerRequestBuilder = class {
631
626
  *
632
627
  * The actual parsing is based on Content-Type headers, not this setting.
633
628
  *
629
+ * @param schema - Zod/StandardSchema instance for validation (optional).
630
+ *
634
631
  * @example
635
632
  * ```ts
636
633
  * // With Zod schema (validates JSON response)
@@ -649,6 +646,8 @@ var IgniterCallerRequestBuilder = class {
649
646
  /**
650
647
  * Downloads a file via GET request.
651
648
  * @deprecated Use `.responseType<File>().execute()` instead. The response type is auto-detected.
649
+ *
650
+ * @param url - URL or path to download.
652
651
  */
653
652
  getFile(url) {
654
653
  this.options.method = "GET";
@@ -723,7 +722,7 @@ var IgniterCallerRequestBuilder = class {
723
722
  statusCode: 408,
724
723
  logger: this.logger,
725
724
  metadata: { url: finalUrl },
726
- cause: error
725
+ cause: error instanceof Error ? error : void 0
727
726
  })
728
727
  };
729
728
  }
@@ -735,7 +734,7 @@ var IgniterCallerRequestBuilder = class {
735
734
  message: error?.message || "Failed to download file",
736
735
  logger: this.logger,
737
736
  metadata: { url: finalUrl },
738
- cause: error
737
+ cause: error instanceof Error ? error : void 0
739
738
  })
740
739
  };
741
740
  }
@@ -754,8 +753,32 @@ var IgniterCallerRequestBuilder = class {
754
753
  * - `application/octet-stream` → returned as Blob
755
754
  *
756
755
  * Schema validation (if configured) only runs for validatable content types (JSON, XML, CSV).
756
+ *
757
+ * @returns Response envelope with data or error.
757
758
  */
758
759
  async execute() {
760
+ const startTime = Date.now();
761
+ const { safeUrl } = this.resolveUrl();
762
+ const method = this.options.method;
763
+ const baseURL = this.options.baseURL;
764
+ const timeoutMs = this.options.timeout;
765
+ this.telemetry?.emit(
766
+ "igniter.caller.request.execute.started",
767
+ {
768
+ level: "debug",
769
+ attributes: {
770
+ "ctx.request.method": method,
771
+ "ctx.request.url": safeUrl,
772
+ "ctx.request.baseUrl": baseURL,
773
+ "ctx.request.timeoutMs": timeoutMs
774
+ }
775
+ }
776
+ );
777
+ this.logger?.debug("IgniterCaller.request.execute started", {
778
+ method,
779
+ url: safeUrl,
780
+ baseURL
781
+ });
759
782
  const effectiveCacheKey = this.cacheKey || this.options.url;
760
783
  if (effectiveCacheKey && this.staleTime) {
761
784
  const cached = await IgniterCallerCacheUtils.get(
@@ -763,8 +786,36 @@ var IgniterCallerRequestBuilder = class {
763
786
  this.staleTime
764
787
  );
765
788
  if (cached !== void 0) {
766
- this.logger?.debug("IgniterCaller.execute cache hit", {
767
- key: effectiveCacheKey
789
+ this.telemetry?.emit(
790
+ "igniter.caller.cache.read.hit",
791
+ {
792
+ level: "debug",
793
+ attributes: {
794
+ "ctx.request.method": method,
795
+ "ctx.request.url": safeUrl,
796
+ "ctx.cache.key": effectiveCacheKey,
797
+ "ctx.cache.staleTime": this.staleTime
798
+ }
799
+ }
800
+ );
801
+ const durationMs2 = Date.now() - startTime;
802
+ this.telemetry?.emit(
803
+ "igniter.caller.request.execute.success",
804
+ {
805
+ level: "info",
806
+ attributes: {
807
+ "ctx.request.method": method,
808
+ "ctx.request.url": safeUrl,
809
+ "ctx.request.durationMs": durationMs2,
810
+ "ctx.cache.hit": true
811
+ }
812
+ }
813
+ );
814
+ this.logger?.info("IgniterCaller.request.execute success (cache)", {
815
+ key: effectiveCacheKey,
816
+ method,
817
+ url: safeUrl,
818
+ durationMs: durationMs2
768
819
  });
769
820
  const cachedResult = {
770
821
  data: cached,
@@ -776,13 +827,33 @@ var IgniterCallerRequestBuilder = class {
776
827
  }
777
828
  const result = await this.executeWithRetry();
778
829
  if (result.error && this.fallbackFn) {
779
- this.logger?.debug("IgniterCaller.execute applying fallback", {
830
+ this.logger?.debug("IgniterCaller.request.execute applying fallback", {
831
+ method,
832
+ url: safeUrl,
780
833
  error: result.error
781
834
  });
782
835
  const fallbackResult = {
783
836
  data: this.fallbackFn(),
784
837
  error: void 0
785
838
  };
839
+ const durationMs2 = Date.now() - startTime;
840
+ this.telemetry?.emit(
841
+ "igniter.caller.request.execute.success",
842
+ {
843
+ level: "info",
844
+ attributes: {
845
+ "ctx.request.method": method,
846
+ "ctx.request.url": safeUrl,
847
+ "ctx.request.durationMs": durationMs2,
848
+ "ctx.request.fallback": true
849
+ }
850
+ }
851
+ );
852
+ this.logger?.info("IgniterCaller.request.execute success (fallback)", {
853
+ method,
854
+ url: safeUrl,
855
+ durationMs: durationMs2
856
+ });
786
857
  await this.emitEvent(fallbackResult);
787
858
  return fallbackResult;
788
859
  }
@@ -793,12 +864,57 @@ var IgniterCallerRequestBuilder = class {
793
864
  this.staleTime
794
865
  );
795
866
  }
867
+ const durationMs = Date.now() - startTime;
868
+ if (result.error) {
869
+ this.telemetry?.emit(
870
+ "igniter.caller.request.execute.error",
871
+ {
872
+ level: "error",
873
+ attributes: {
874
+ "ctx.request.method": method,
875
+ "ctx.request.url": safeUrl,
876
+ "ctx.request.durationMs": durationMs,
877
+ "ctx.error.code": result.error instanceof IgniterCallerError ? result.error.code : "IGNITER_CALLER_UNKNOWN_ERROR",
878
+ "ctx.error.message": result.error.message,
879
+ "ctx.response.status": result.status
880
+ }
881
+ }
882
+ );
883
+ this.logger?.error("IgniterCaller.request.execute failed", {
884
+ method,
885
+ url: safeUrl,
886
+ durationMs,
887
+ error: result.error
888
+ });
889
+ } else {
890
+ const contentType = result.headers?.get("content-type") || void 0;
891
+ this.telemetry?.emit(
892
+ "igniter.caller.request.execute.success",
893
+ {
894
+ level: "info",
895
+ attributes: {
896
+ "ctx.request.method": method,
897
+ "ctx.request.url": safeUrl,
898
+ "ctx.request.durationMs": durationMs,
899
+ "ctx.response.status": result.status,
900
+ "ctx.response.contentType": contentType,
901
+ "ctx.cache.hit": false
902
+ }
903
+ }
904
+ );
905
+ this.logger?.info("IgniterCaller.request.execute success", {
906
+ method,
907
+ url: safeUrl,
908
+ durationMs,
909
+ status: result.status
910
+ });
911
+ }
796
912
  await this.emitEvent(result);
797
913
  return result;
798
914
  }
799
915
  async executeWithRetry() {
800
916
  const maxAttempts = this.retryOptions?.maxAttempts || 1;
801
- const baseDelay = this.retryOptions?.baseDelay || 1e3;
917
+ const baseDelay = this.retryOptions?.baseDelay ?? 1e3;
802
918
  const backoff = this.retryOptions?.backoff || "linear";
803
919
  const retryOnStatus = this.retryOptions?.retryOnStatus || [
804
920
  408,
@@ -808,13 +924,30 @@ var IgniterCallerRequestBuilder = class {
808
924
  503,
809
925
  504
810
926
  ];
927
+ const { safeUrl } = this.resolveUrl();
928
+ const method = this.options.method;
811
929
  let lastError;
812
930
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
813
931
  if (attempt > 0) {
814
932
  const delay = backoff === "exponential" ? baseDelay * 2 ** (attempt - 1) : baseDelay * attempt;
815
- this.logger?.debug("IgniterCaller.execute retrying", {
816
- attempt,
817
- delay
933
+ this.telemetry?.emit(
934
+ "igniter.caller.retry.attempt.started",
935
+ {
936
+ level: "debug",
937
+ attributes: {
938
+ "ctx.request.method": method,
939
+ "ctx.request.url": safeUrl,
940
+ "ctx.retry.attempt": attempt + 1,
941
+ "ctx.retry.maxAttempts": maxAttempts,
942
+ "ctx.retry.delayMs": delay
943
+ }
944
+ }
945
+ );
946
+ this.logger?.debug("IgniterCaller.request.execute retrying", {
947
+ method,
948
+ url: safeUrl,
949
+ attempt: attempt + 1,
950
+ delayMs: delay
818
951
  });
819
952
  await new Promise((resolve) => setTimeout(resolve, delay));
820
953
  }
@@ -835,10 +968,8 @@ var IgniterCallerRequestBuilder = class {
835
968
  }
836
969
  async executeSingleRequest() {
837
970
  let { url, requestInit, controller, timeoutId } = this.buildRequest();
838
- this.logger?.debug("IgniterCaller.execute started", {
839
- method: this.options.method,
840
- url
841
- });
971
+ const { safeUrl } = this.resolveUrl();
972
+ const method = this.options.method;
842
973
  if (this.requestInterceptors && this.requestInterceptors.length > 0) {
843
974
  let modifiedOptions = { ...this.options, url };
844
975
  for (const interceptor of this.requestInterceptors) {
@@ -868,9 +999,27 @@ var IgniterCallerRequestBuilder = class {
868
999
  );
869
1000
  } catch (error) {
870
1001
  clearTimeout(timeoutId);
1002
+ const err = error;
1003
+ this.telemetry?.emit(
1004
+ "igniter.caller.validation.request.error",
1005
+ {
1006
+ level: "error",
1007
+ attributes: {
1008
+ "ctx.request.method": method,
1009
+ "ctx.request.url": safeUrl,
1010
+ "ctx.validation.type": "request",
1011
+ "ctx.validation.error": err.message
1012
+ }
1013
+ }
1014
+ );
1015
+ this.logger?.error("IgniterCaller.request.validation failed", {
1016
+ method,
1017
+ url: safeUrl,
1018
+ error: err
1019
+ });
871
1020
  return {
872
1021
  data: void 0,
873
- error
1022
+ error: err
874
1023
  };
875
1024
  }
876
1025
  }
@@ -924,9 +1073,29 @@ var IgniterCallerRequestBuilder = class {
924
1073
  this.logger
925
1074
  );
926
1075
  } catch (error) {
1076
+ const err = error;
1077
+ this.telemetry?.emit(
1078
+ "igniter.caller.validation.response.error",
1079
+ {
1080
+ level: "error",
1081
+ attributes: {
1082
+ "ctx.request.method": method,
1083
+ "ctx.request.url": safeUrl,
1084
+ "ctx.validation.type": "response",
1085
+ "ctx.validation.error": err.message,
1086
+ "ctx.response.status": httpResponse.status
1087
+ }
1088
+ }
1089
+ );
1090
+ this.logger?.error("IgniterCaller.response.validation failed", {
1091
+ method,
1092
+ url: safeUrl,
1093
+ status: httpResponse.status,
1094
+ error: err
1095
+ });
927
1096
  return {
928
1097
  data: void 0,
929
- error,
1098
+ error: err,
930
1099
  status: httpResponse.status,
931
1100
  headers: httpResponse.headers
932
1101
  };
@@ -938,20 +1107,40 @@ var IgniterCallerRequestBuilder = class {
938
1107
  const zodSchema = this.responseTypeSchema;
939
1108
  const result = zodSchema.safeParse(data);
940
1109
  if (!result.success) {
1110
+ const err = new IgniterCallerError({
1111
+ code: "IGNITER_CALLER_RESPONSE_VALIDATION_FAILED",
1112
+ operation: "parseResponse",
1113
+ message: `Response validation failed: ${result.error.message}`,
1114
+ logger: this.logger,
1115
+ statusCode: httpResponse.status,
1116
+ metadata: {
1117
+ method: this.options.method,
1118
+ url
1119
+ },
1120
+ cause: result.error
1121
+ });
1122
+ this.telemetry?.emit(
1123
+ "igniter.caller.validation.response.error",
1124
+ {
1125
+ level: "error",
1126
+ attributes: {
1127
+ "ctx.request.method": method,
1128
+ "ctx.request.url": safeUrl,
1129
+ "ctx.validation.type": "response",
1130
+ "ctx.validation.error": err.message,
1131
+ "ctx.response.status": httpResponse.status
1132
+ }
1133
+ }
1134
+ );
1135
+ this.logger?.error("IgniterCaller.response.validation failed", {
1136
+ method,
1137
+ url: safeUrl,
1138
+ status: httpResponse.status,
1139
+ error: err
1140
+ });
941
1141
  return {
942
1142
  data: void 0,
943
- error: new IgniterCallerError({
944
- code: "IGNITER_CALLER_RESPONSE_VALIDATION_FAILED",
945
- operation: "parseResponse",
946
- message: `Response validation failed: ${result.error.message}`,
947
- logger: this.logger,
948
- statusCode: httpResponse.status,
949
- metadata: {
950
- method: this.options.method,
951
- url
952
- },
953
- cause: result.error
954
- }),
1143
+ error: err,
955
1144
  status: httpResponse.status,
956
1145
  headers: httpResponse.headers
957
1146
  };
@@ -962,40 +1151,80 @@ var IgniterCallerRequestBuilder = class {
962
1151
  const standardSchema = this.responseTypeSchema;
963
1152
  const result = await standardSchema["~standard"].validate(data);
964
1153
  if (result.issues) {
1154
+ const err = new IgniterCallerError({
1155
+ code: "IGNITER_CALLER_RESPONSE_VALIDATION_FAILED",
1156
+ operation: "parseResponse",
1157
+ message: `Response validation failed`,
1158
+ logger: this.logger,
1159
+ statusCode: httpResponse.status,
1160
+ metadata: {
1161
+ method: this.options.method,
1162
+ url,
1163
+ issues: result.issues
1164
+ }
1165
+ });
1166
+ this.telemetry?.emit(
1167
+ "igniter.caller.validation.response.error",
1168
+ {
1169
+ level: "error",
1170
+ attributes: {
1171
+ "ctx.request.method": method,
1172
+ "ctx.request.url": safeUrl,
1173
+ "ctx.validation.type": "response",
1174
+ "ctx.validation.error": err.message,
1175
+ "ctx.response.status": httpResponse.status
1176
+ }
1177
+ }
1178
+ );
1179
+ this.logger?.error("IgniterCaller.response.validation failed", {
1180
+ method,
1181
+ url: safeUrl,
1182
+ status: httpResponse.status,
1183
+ error: err
1184
+ });
965
1185
  return {
966
1186
  data: void 0,
967
- error: new IgniterCallerError({
968
- code: "IGNITER_CALLER_RESPONSE_VALIDATION_FAILED",
969
- operation: "parseResponse",
970
- message: `Response validation failed`,
971
- logger: this.logger,
972
- statusCode: httpResponse.status,
973
- metadata: {
974
- method: this.options.method,
975
- url,
976
- issues: result.issues
977
- }
978
- }),
1187
+ error: err,
979
1188
  status: httpResponse.status,
980
1189
  headers: httpResponse.headers
981
1190
  };
982
1191
  }
983
1192
  data = result.value;
984
1193
  } catch (error) {
1194
+ const err = new IgniterCallerError({
1195
+ code: "IGNITER_CALLER_RESPONSE_VALIDATION_FAILED",
1196
+ operation: "parseResponse",
1197
+ message: error?.message || "Response validation failed",
1198
+ logger: this.logger,
1199
+ statusCode: httpResponse.status,
1200
+ metadata: {
1201
+ method: this.options.method,
1202
+ url
1203
+ },
1204
+ cause: error instanceof Error ? error : void 0
1205
+ });
1206
+ this.telemetry?.emit(
1207
+ "igniter.caller.validation.response.error",
1208
+ {
1209
+ level: "error",
1210
+ attributes: {
1211
+ "ctx.request.method": method,
1212
+ "ctx.request.url": safeUrl,
1213
+ "ctx.validation.type": "response",
1214
+ "ctx.validation.error": err.message,
1215
+ "ctx.response.status": httpResponse.status
1216
+ }
1217
+ }
1218
+ );
1219
+ this.logger?.error("IgniterCaller.response.validation failed", {
1220
+ method,
1221
+ url: safeUrl,
1222
+ status: httpResponse.status,
1223
+ error: err
1224
+ });
985
1225
  return {
986
1226
  data: void 0,
987
- error: new IgniterCallerError({
988
- code: "IGNITER_CALLER_RESPONSE_VALIDATION_FAILED",
989
- operation: "parseResponse",
990
- message: error?.message || "Response validation failed",
991
- logger: this.logger,
992
- statusCode: httpResponse.status,
993
- metadata: {
994
- method: this.options.method,
995
- url
996
- },
997
- cause: error
998
- }),
1227
+ error: err,
999
1228
  status: httpResponse.status,
1000
1229
  headers: httpResponse.headers
1001
1230
  };
@@ -1018,20 +1247,38 @@ var IgniterCallerRequestBuilder = class {
1018
1247
  } catch (error) {
1019
1248
  clearTimeout(timeoutId);
1020
1249
  if (error instanceof Error && error.name === "AbortError") {
1250
+ const err = new IgniterCallerError({
1251
+ code: "IGNITER_CALLER_TIMEOUT",
1252
+ operation: "execute",
1253
+ message: `Request timeout after ${this.options.timeout || 3e4}ms`,
1254
+ statusCode: 408,
1255
+ logger: this.logger,
1256
+ metadata: {
1257
+ method: this.options.method,
1258
+ url
1259
+ },
1260
+ cause: error instanceof Error ? error : void 0
1261
+ });
1262
+ this.telemetry?.emit(
1263
+ "igniter.caller.request.timeout.error",
1264
+ {
1265
+ level: "error",
1266
+ attributes: {
1267
+ "ctx.request.method": method,
1268
+ "ctx.request.url": safeUrl,
1269
+ "ctx.request.timeoutMs": this.options.timeout || 3e4
1270
+ }
1271
+ }
1272
+ );
1273
+ this.logger?.error("IgniterCaller.request.execute timeout", {
1274
+ method,
1275
+ url: safeUrl,
1276
+ timeoutMs: this.options.timeout || 3e4,
1277
+ error: err
1278
+ });
1021
1279
  return {
1022
1280
  data: void 0,
1023
- error: new IgniterCallerError({
1024
- code: "IGNITER_CALLER_TIMEOUT",
1025
- operation: "execute",
1026
- message: `Request timeout after ${this.options.timeout || 3e4}ms`,
1027
- statusCode: 408,
1028
- logger: this.logger,
1029
- metadata: {
1030
- method: this.options.method,
1031
- url
1032
- },
1033
- cause: error
1034
- })
1281
+ error: err
1035
1282
  };
1036
1283
  }
1037
1284
  return {
@@ -1046,13 +1293,13 @@ var IgniterCallerRequestBuilder = class {
1046
1293
  method: this.options.method,
1047
1294
  url
1048
1295
  },
1049
- cause: error
1296
+ cause: error instanceof Error ? error : void 0
1050
1297
  })
1051
1298
  };
1052
1299
  }
1053
1300
  }
1054
- buildRequest() {
1055
- const { method, url, body, params, headers, timeout, baseURL, cache } = this.options;
1301
+ resolveUrl() {
1302
+ const { method, url, body, params, baseURL } = this.options;
1056
1303
  let finalParams = params;
1057
1304
  if ((method === "GET" || method === "HEAD") && body && typeof body === "object") {
1058
1305
  const bodyParams = {};
@@ -1068,6 +1315,14 @@ var IgniterCallerRequestBuilder = class {
1068
1315
  baseURL,
1069
1316
  query: finalParams
1070
1317
  });
1318
+ return {
1319
+ url: fullUrl,
1320
+ safeUrl: fullUrl.split("?")[0]
1321
+ };
1322
+ }
1323
+ buildRequest() {
1324
+ const { method, body, headers, timeout, cache } = this.options;
1325
+ const { url } = this.resolveUrl();
1071
1326
  const shouldIncludeBody = body && method !== "GET" && method !== "HEAD";
1072
1327
  const rawBody = shouldIncludeBody && IgniterCallerBodyUtils.isRawBody(body);
1073
1328
  const finalHeaders = IgniterCallerBodyUtils.normalizeHeadersForBody(
@@ -1083,7 +1338,7 @@ var IgniterCallerRequestBuilder = class {
1083
1338
  const controller = new AbortController();
1084
1339
  const timeoutId = setTimeout(() => controller.abort(), timeout || 3e4);
1085
1340
  return {
1086
- url: fullUrl,
1341
+ url,
1087
1342
  requestInit,
1088
1343
  controller,
1089
1344
  timeoutId
@@ -1103,7 +1358,7 @@ var IgniterCallerRequestBuilder = class {
1103
1358
  }
1104
1359
  };
1105
1360
 
1106
- // src/core/igniter-caller-events.ts
1361
+ // src/core/events.ts
1107
1362
  var IgniterCallerEvents = class {
1108
1363
  constructor() {
1109
1364
  this.listeners = /* @__PURE__ */ new Map();
@@ -1170,6 +1425,9 @@ var IgniterCallerEvents = class {
1170
1425
  }
1171
1426
  /**
1172
1427
  * Removes a specific listener or all listeners for a pattern.
1428
+ *
1429
+ * @param pattern - URL string or RegExp pattern.
1430
+ * @param callback - Optional specific callback to remove.
1173
1431
  */
1174
1432
  off(pattern, callback) {
1175
1433
  if (typeof pattern === "string") {
@@ -1190,6 +1448,10 @@ var IgniterCallerEvents = class {
1190
1448
  * Emits an event to all matching listeners.
1191
1449
  *
1192
1450
  * @internal
1451
+ *
1452
+ * @param url - Request URL to match listeners against.
1453
+ * @param method - HTTP method.
1454
+ * @param result - Response envelope.
1193
1455
  */
1194
1456
  async emit(url, method, result) {
1195
1457
  const context = {
@@ -1221,6 +1483,8 @@ var IgniterCallerEvents = class {
1221
1483
  }
1222
1484
  /**
1223
1485
  * Removes all listeners.
1486
+ *
1487
+ * @returns Nothing.
1224
1488
  */
1225
1489
  clear() {
1226
1490
  this.listeners.clear();
@@ -1228,65 +1492,25 @@ var IgniterCallerEvents = class {
1228
1492
  }
1229
1493
  };
1230
1494
 
1231
- // src/core/igniter-caller.ts
1232
- var _IgniterCaller = class _IgniterCaller {
1495
+ // src/core/manager.ts
1496
+ var _IgniterCallerManager = class _IgniterCallerManager {
1497
+ /**
1498
+ * Creates a new manager instance.
1499
+ *
1500
+ * @param baseURL - Base URL prefix for requests.
1501
+ * @param opts - Optional configuration (headers, cookies, telemetry, schemas).
1502
+ */
1233
1503
  constructor(baseURL, opts) {
1234
1504
  this.baseURL = baseURL;
1235
1505
  this.headers = opts?.headers;
1236
1506
  this.cookies = opts?.cookies;
1237
1507
  this.logger = opts?.logger;
1508
+ this.telemetry = opts?.telemetry;
1238
1509
  this.requestInterceptors = opts?.requestInterceptors;
1239
1510
  this.responseInterceptors = opts?.responseInterceptors;
1240
1511
  this.schemas = opts?.schemas;
1241
1512
  this.schemaValidation = opts?.schemaValidation;
1242
1513
  }
1243
- /**
1244
- * Canonical initialization entrypoint.
1245
- *
1246
- * This is designed to remain stable when extracted to `@igniter-js/caller`.
1247
- */
1248
- static create() {
1249
- return IgniterCallerBuilder.create((state) => {
1250
- if (state.store) {
1251
- IgniterCallerCacheUtils.setStore(state.store, state.storeOptions);
1252
- }
1253
- return new _IgniterCaller(state.baseURL, {
1254
- headers: state.headers,
1255
- cookies: state.cookies,
1256
- logger: state.logger,
1257
- requestInterceptors: state.requestInterceptors,
1258
- responseInterceptors: state.responseInterceptors,
1259
- schemas: state.schemas,
1260
- schemaValidation: state.schemaValidation
1261
- });
1262
- });
1263
- }
1264
- /**
1265
- * Returns a new client with the same config and a new logger.
1266
- */
1267
- withLogger(logger) {
1268
- return new _IgniterCaller(this.baseURL, {
1269
- headers: this.headers,
1270
- cookies: this.cookies,
1271
- logger,
1272
- requestInterceptors: this.requestInterceptors,
1273
- responseInterceptors: this.responseInterceptors,
1274
- schemas: this.schemas,
1275
- schemaValidation: this.schemaValidation
1276
- });
1277
- }
1278
- setBaseURL(baseURL) {
1279
- this.baseURL = baseURL;
1280
- return this;
1281
- }
1282
- setHeaders(headers) {
1283
- this.headers = headers;
1284
- return this;
1285
- }
1286
- setCookies(cookies) {
1287
- this.cookies = cookies;
1288
- return this;
1289
- }
1290
1514
  /**
1291
1515
  * Creates common request builder params.
1292
1516
  */
@@ -1296,24 +1520,16 @@ var _IgniterCaller = class _IgniterCaller {
1296
1520
  defaultHeaders: this.headers,
1297
1521
  defaultCookies: this.cookies,
1298
1522
  logger: this.logger,
1523
+ telemetry: this.telemetry,
1299
1524
  requestInterceptors: this.requestInterceptors,
1300
1525
  responseInterceptors: this.responseInterceptors,
1301
1526
  eventEmitter: async (url, method, result) => {
1302
- await _IgniterCaller.emitEvent(url, method, result);
1527
+ await _IgniterCallerManager.emitEvent(url, method, result);
1303
1528
  },
1304
1529
  schemas: this.schemas,
1305
1530
  schemaValidation: this.schemaValidation
1306
1531
  };
1307
1532
  }
1308
- /**
1309
- * Resolves the full URL path by prepending baseURL if needed.
1310
- */
1311
- resolveSchemaPath(url) {
1312
- if (this.baseURL && !url.startsWith("http")) {
1313
- return `${this.baseURL}${url}`;
1314
- }
1315
- return url;
1316
- }
1317
1533
  get(url) {
1318
1534
  const builder = new IgniterCallerRequestBuilder(this.createBuilderParams());
1319
1535
  builder._setMethod("GET");
@@ -1356,6 +1572,9 @@ var _IgniterCaller = class _IgniterCaller {
1356
1572
  * This is a convenience method for making requests without using the builder pattern.
1357
1573
  * Useful for dynamic requests where options are constructed programmatically.
1358
1574
  *
1575
+ * @param options - Request configuration for method, url, and behavior.
1576
+ * @returns Response envelope with data or error.
1577
+ *
1359
1578
  * @example
1360
1579
  * ```ts
1361
1580
  * const result = await api.request({
@@ -1422,6 +1641,9 @@ var _IgniterCaller = class _IgniterCaller {
1422
1641
  * Executes multiple requests in parallel and returns results as an array.
1423
1642
  *
1424
1643
  * This is useful for batching independent API calls.
1644
+ *
1645
+ * @param requests - Array of request promises.
1646
+ * @returns Array of resolved results in the same order.
1425
1647
  */
1426
1648
  static async batch(requests) {
1427
1649
  return Promise.all(requests);
@@ -1442,7 +1664,7 @@ var _IgniterCaller = class _IgniterCaller {
1442
1664
  * @example
1443
1665
  * ```ts
1444
1666
  * // Listen to all user endpoints
1445
- * const cleanup = IgniterCaller.on(/^\/users/, (result, context) => {
1667
+ * const cleanup = IgniterCallerManager.on(/^\/users/, (result, context) => {
1446
1668
  * console.log(`${context.method} ${context.url}`, result)
1447
1669
  * })
1448
1670
  *
@@ -1451,24 +1673,29 @@ var _IgniterCaller = class _IgniterCaller {
1451
1673
  * ```
1452
1674
  */
1453
1675
  static on(pattern, callback) {
1454
- return _IgniterCaller.events.on(pattern, callback);
1676
+ return _IgniterCallerManager.events.on(pattern, callback);
1455
1677
  }
1456
1678
  /**
1457
1679
  * Removes event listeners for a pattern.
1680
+ *
1681
+ * @param pattern - URL string or RegExp pattern.
1682
+ * @param callback - Callback to remove (optional).
1458
1683
  */
1459
1684
  static off(pattern, callback) {
1460
- _IgniterCaller.events.off(pattern, callback);
1685
+ _IgniterCallerManager.events.off(pattern, callback);
1461
1686
  }
1462
1687
  /**
1463
1688
  * Invalidates a specific cache entry.
1464
1689
  *
1465
1690
  * This is useful after mutations to ensure fresh data on next fetch.
1466
1691
  *
1692
+ * @param key - Cache key to invalidate.
1693
+ *
1467
1694
  * @example
1468
1695
  * ```ts
1469
1696
  * // After creating a user
1470
1697
  * await api.post('/users').body(newUser).execute()
1471
- * await IgniterCaller.invalidate('/users') // Clear users list cache
1698
+ * await IgniterCallerManager.invalidate('/users') // Clear users list cache
1472
1699
  * ```
1473
1700
  */
1474
1701
  static async invalidate(key) {
@@ -1478,11 +1705,12 @@ var _IgniterCaller = class _IgniterCaller {
1478
1705
  * Invalidates all cache entries matching a pattern.
1479
1706
  *
1480
1707
  * @param pattern Glob pattern (e.g., '/users/*') or exact key
1708
+ * @returns Promise that resolves when invalidation completes.
1481
1709
  *
1482
1710
  * @example
1483
1711
  * ```ts
1484
1712
  * // Invalidate all user-related caches
1485
- * await IgniterCaller.invalidatePattern('/users/*')
1713
+ * await IgniterCallerManager.invalidatePattern('/users/*')
1486
1714
  * ```
1487
1715
  */
1488
1716
  static async invalidatePattern(pattern) {
@@ -1492,25 +1720,539 @@ var _IgniterCaller = class _IgniterCaller {
1492
1720
  * Emits an event to all registered listeners.
1493
1721
  *
1494
1722
  * @internal
1723
+ *
1724
+ * @param url - Request URL (resolved).
1725
+ * @param method - HTTP method.
1726
+ * @param result - Response envelope.
1495
1727
  */
1496
1728
  static async emitEvent(url, method, result) {
1497
- await _IgniterCaller.events.emit(url, method, result);
1729
+ await _IgniterCallerManager.events.emit(url, method, result);
1498
1730
  }
1499
1731
  };
1500
1732
  /** Global event emitter for observing HTTP responses */
1501
- _IgniterCaller.events = new IgniterCallerEvents();
1502
- var IgniterCaller = _IgniterCaller;
1733
+ _IgniterCallerManager.events = new IgniterCallerEvents();
1734
+ var IgniterCallerManager = _IgniterCallerManager;
1735
+
1736
+ // src/builders/main.builder.ts
1737
+ var IgniterCallerBuilder = class _IgniterCallerBuilder {
1738
+ constructor(state) {
1739
+ this.state = state;
1740
+ }
1741
+ /**
1742
+ * Creates a new builder instance.
1743
+ *
1744
+ * @returns New builder instance with empty state.
1745
+ */
1746
+ static create() {
1747
+ return new _IgniterCallerBuilder({});
1748
+ }
1749
+ /**
1750
+ * Sets the base URL for all requests.
1751
+ *
1752
+ * @param baseURL - Base URL prefix for outgoing requests.
1753
+ */
1754
+ withBaseUrl(baseURL) {
1755
+ return new _IgniterCallerBuilder({ ...this.state, baseURL });
1756
+ }
1757
+ /**
1758
+ * Merges default headers for all requests.
1759
+ *
1760
+ * @param headers - Header map merged into every request.
1761
+ */
1762
+ withHeaders(headers) {
1763
+ return new _IgniterCallerBuilder({ ...this.state, headers });
1764
+ }
1765
+ /**
1766
+ * Sets default cookies (sent as the `Cookie` header).
1767
+ *
1768
+ * @param cookies - Cookie key/value pairs.
1769
+ */
1770
+ withCookies(cookies) {
1771
+ return new _IgniterCallerBuilder({ ...this.state, cookies });
1772
+ }
1773
+ /**
1774
+ * Attaches a logger instance.
1775
+ *
1776
+ * @param logger - Logger implementation from `@igniter-js/core`.
1777
+ */
1778
+ withLogger(logger) {
1779
+ return new _IgniterCallerBuilder({ ...this.state, logger });
1780
+ }
1781
+ /**
1782
+ * Adds a request interceptor that runs before each request.
1783
+ *
1784
+ * @param interceptor - Interceptor called with request options.
1785
+ */
1786
+ withRequestInterceptor(interceptor) {
1787
+ const requestInterceptors = [
1788
+ ...this.state.requestInterceptors || [],
1789
+ interceptor
1790
+ ];
1791
+ return new _IgniterCallerBuilder({ ...this.state, requestInterceptors });
1792
+ }
1793
+ /**
1794
+ * Adds a response interceptor that runs after each request.
1795
+ *
1796
+ * @param interceptor - Interceptor called with the response result.
1797
+ */
1798
+ withResponseInterceptor(interceptor) {
1799
+ const responseInterceptors = [
1800
+ ...this.state.responseInterceptors || [],
1801
+ interceptor
1802
+ ];
1803
+ return new _IgniterCallerBuilder({ ...this.state, responseInterceptors });
1804
+ }
1805
+ /**
1806
+ * Configures a persistent store adapter for caching.
1807
+ *
1808
+ * When configured, cache operations will use the store (e.g., Redis)
1809
+ * instead of in-memory cache, enabling persistent cache across deployments.
1810
+ *
1811
+ * @param store - Store adapter implementation.
1812
+ * @param options - Store options (ttl, keyPrefix, fallback).
1813
+ */
1814
+ withStore(store, options) {
1815
+ return new _IgniterCallerBuilder({
1816
+ ...this.state,
1817
+ store,
1818
+ storeOptions: options
1819
+ });
1820
+ }
1821
+ /**
1822
+ * Configures schema-based type safety and validation.
1823
+ *
1824
+ * Enables automatic type inference for requests/responses based on
1825
+ * route and method, with optional runtime validation via StandardSchemaV1
1826
+ * (Zod is supported).
1827
+ *
1828
+ * @param schemas - Schema map keyed by URL path and method.
1829
+ * @param validation - Validation options for request/response checks.
1830
+ *
1831
+ * @example
1832
+ * ```ts
1833
+ * const api = IgniterCaller.create()
1834
+ * .withSchemas({
1835
+ * '/users': {
1836
+ * GET: {
1837
+ * responses: {
1838
+ * 200: z.array(UserSchema),
1839
+ * 401: ErrorSchema,
1840
+ * },
1841
+ * },
1842
+ * POST: {
1843
+ * request: CreateUserSchema,
1844
+ * responses: {
1845
+ * 201: UserSchema,
1846
+ * 400: ValidationErrorSchema,
1847
+ * },
1848
+ * },
1849
+ * },
1850
+ * })
1851
+ * .build()
1852
+ * ```
1853
+ */
1854
+ withSchemas(schemas, validation) {
1855
+ const nextState = {
1856
+ ...this.state,
1857
+ schemas,
1858
+ schemaValidation: validation
1859
+ };
1860
+ return new _IgniterCallerBuilder(nextState);
1861
+ }
1862
+ /**
1863
+ * Attaches telemetry for request monitoring and observability.
1864
+ *
1865
+ * Telemetry is optional and only emits events when a manager is provided.
1866
+ *
1867
+ * Telemetry events emitted by the caller package include:
1868
+ * - `request.execute.started`
1869
+ * - `request.execute.success`
1870
+ * - `request.execute.error`
1871
+ * - `request.timeout.error`
1872
+ * - `cache.read.hit`
1873
+ * - `retry.attempt.started`
1874
+ * - `validation.request.error`
1875
+ * - `validation.response.error`
1876
+ *
1877
+ * @param telemetry - Telemetry manager instance.
1878
+ *
1879
+ * @example
1880
+ * ```ts
1881
+ * import { IgniterTelemetry } from '@igniter-js/telemetry'
1882
+ * import { IgniterCallerTelemetryEvents } from '@igniter-js/caller/telemetry'
1883
+ *
1884
+ * const telemetry = IgniterTelemetry.create()
1885
+ * .withService('my-api')
1886
+ * .addEvents(IgniterCallerTelemetryEvents)
1887
+ * .build()
1888
+ *
1889
+ * const api = IgniterCaller.create()
1890
+ * .withBaseUrl('https://api.example.com')
1891
+ * .withTelemetry(telemetry)
1892
+ * .build()
1893
+ * ```
1894
+ */
1895
+ withTelemetry(telemetry) {
1896
+ return new _IgniterCallerBuilder({ ...this.state, telemetry });
1897
+ }
1898
+ /**
1899
+ * Builds the `IgniterCaller` instance.
1900
+ *
1901
+ * @returns Configured manager instance.
1902
+ */
1903
+ build() {
1904
+ if (this.state.store) {
1905
+ IgniterCallerCacheUtils.setStore(this.state.store, this.state.storeOptions);
1906
+ }
1907
+ const manager = new IgniterCallerManager(this.state.baseURL, {
1908
+ headers: this.state.headers,
1909
+ cookies: this.state.cookies,
1910
+ logger: this.state.logger,
1911
+ telemetry: this.state.telemetry,
1912
+ requestInterceptors: this.state.requestInterceptors,
1913
+ responseInterceptors: this.state.responseInterceptors,
1914
+ schemas: this.state.schemas,
1915
+ schemaValidation: this.state.schemaValidation
1916
+ });
1917
+ this.state.logger?.info("IgniterCaller initialized", {
1918
+ baseURL: this.state.baseURL,
1919
+ hasTelemetry: Boolean(this.state.telemetry),
1920
+ hasStore: Boolean(this.state.store),
1921
+ hasSchemas: Boolean(this.state.schemas)
1922
+ });
1923
+ return manager;
1924
+ }
1925
+ };
1926
+ var IgniterCaller = {
1927
+ create: IgniterCallerBuilder.create
1928
+ };
1929
+ var IgniterCallerSchemaPathBuilder = class _IgniterCallerSchemaPathBuilder {
1930
+ constructor(methods, registry) {
1931
+ this.methods = methods;
1932
+ this.registry = registry;
1933
+ }
1934
+ /**
1935
+ * Creates a new path builder for the provided registry.
1936
+ */
1937
+ static create(registry) {
1938
+ return new _IgniterCallerSchemaPathBuilder({}, registry);
1939
+ }
1940
+ /**
1941
+ * Returns a registry reference helper for a given key.
1942
+ * The helper exposes optional Zod-based wrappers (array/optional/nullable/record).
1943
+ */
1944
+ ref(key) {
1945
+ const schema = this.registry[key];
1946
+ const zodSchema = schema;
1947
+ return {
1948
+ schema,
1949
+ array: () => z.array(zodSchema),
1950
+ nullable: () => zodSchema.nullable(),
1951
+ optional: () => zodSchema.optional(),
1952
+ record: (keyType) => z.record(
1953
+ z.any(),
1954
+ zodSchema
1955
+ )
1956
+ };
1957
+ }
1958
+ /**
1959
+ * Defines a GET endpoint.
1960
+ */
1961
+ get(config) {
1962
+ return this.addMethod("GET", config);
1963
+ }
1964
+ /**
1965
+ * Defines a POST endpoint.
1966
+ */
1967
+ post(config) {
1968
+ return this.addMethod("POST", config);
1969
+ }
1970
+ /**
1971
+ * Defines a PUT endpoint.
1972
+ */
1973
+ put(config) {
1974
+ return this.addMethod("PUT", config);
1975
+ }
1976
+ /**
1977
+ * Defines a PATCH endpoint.
1978
+ */
1979
+ patch(config) {
1980
+ return this.addMethod("PATCH", config);
1981
+ }
1982
+ /**
1983
+ * Defines a DELETE endpoint.
1984
+ */
1985
+ delete(config) {
1986
+ return this.addMethod("DELETE", config);
1987
+ }
1988
+ /**
1989
+ * Defines a HEAD endpoint.
1990
+ */
1991
+ head(config) {
1992
+ return this.addMethod("HEAD", config);
1993
+ }
1994
+ /**
1995
+ * Builds the accumulated method map for the path.
1996
+ */
1997
+ build() {
1998
+ return this.methods;
1999
+ }
2000
+ addMethod(method, config) {
2001
+ if (method in this.methods) {
2002
+ throw new IgniterCallerError({
2003
+ code: "IGNITER_CALLER_SCHEMA_DUPLICATE",
2004
+ operation: "buildSchema",
2005
+ message: `Schema for method "${method}" is already defined on this path.`,
2006
+ statusCode: 400,
2007
+ metadata: { method }
2008
+ });
2009
+ }
2010
+ return new _IgniterCallerSchemaPathBuilder(
2011
+ {
2012
+ ...this.methods,
2013
+ [method]: {
2014
+ ...config
2015
+ }
2016
+ },
2017
+ this.registry
2018
+ );
2019
+ }
2020
+ };
2021
+
2022
+ // src/builders/schema.builder.ts
2023
+ var IgniterCallerSchema = class _IgniterCallerSchema {
2024
+ constructor(schemas, registry) {
2025
+ this.schemas = schemas;
2026
+ this.registry = registry;
2027
+ }
2028
+ /**
2029
+ * Creates a new empty schema builder.
2030
+ */
2031
+ static create() {
2032
+ return new _IgniterCallerSchema({}, {});
2033
+ }
2034
+ /**
2035
+ * Registers a reusable schema in the registry.
2036
+ */
2037
+ schema(key, schema, options) {
2038
+ ensureValidSchemaKey(key);
2039
+ if (key in this.registry) {
2040
+ throw new IgniterCallerError({
2041
+ code: "IGNITER_CALLER_SCHEMA_DUPLICATE",
2042
+ operation: "buildSchema",
2043
+ message: `Schema registry key "${key}" is already defined.`,
2044
+ statusCode: 400,
2045
+ metadata: { key }
2046
+ });
2047
+ }
2048
+ const nextRegistry = {
2049
+ ...this.registry,
2050
+ [key]: schema
2051
+ };
2052
+ void options?.internal;
2053
+ return new _IgniterCallerSchema(this.schemas, nextRegistry);
2054
+ }
2055
+ /**
2056
+ * Defines a path with its methods using a fluent builder.
2057
+ */
2058
+ path(path, builder) {
2059
+ ensureValidPath(path);
2060
+ const pathBuilder = IgniterCallerSchemaPathBuilder.create(this.registry);
2061
+ const builtMethods = builder(pathBuilder).build();
2062
+ const existing = this.schemas[path] ?? {};
2063
+ for (const method of Object.keys(
2064
+ builtMethods
2065
+ )) {
2066
+ if (method in existing) {
2067
+ throw new IgniterCallerError({
2068
+ code: "IGNITER_CALLER_SCHEMA_DUPLICATE",
2069
+ operation: "buildSchema",
2070
+ message: `Schema for "${path}" with method "${method}" is already defined.`,
2071
+ statusCode: 400,
2072
+ metadata: { path, method }
2073
+ });
2074
+ }
2075
+ }
2076
+ const merged = {
2077
+ ...existing,
2078
+ ...builtMethods
2079
+ };
2080
+ const nextSchemas = {
2081
+ ...this.schemas,
2082
+ [path]: merged
2083
+ };
2084
+ return new _IgniterCallerSchema(nextSchemas, this.registry);
2085
+ }
2086
+ /**
2087
+ * Builds the schema map and attaches inference + runtime helpers.
2088
+ */
2089
+ build() {
2090
+ const result = {
2091
+ ...this.schemas
2092
+ };
2093
+ const inferHelpers = createInferHelpers();
2094
+ const getHelpers = createGetHelpers(this.schemas, this.registry);
2095
+ Object.defineProperty(result, "$Infer", {
2096
+ value: inferHelpers,
2097
+ enumerable: false
2098
+ });
2099
+ Object.defineProperty(result, "get", {
2100
+ value: getHelpers,
2101
+ enumerable: false
2102
+ });
2103
+ return result;
2104
+ }
2105
+ };
2106
+ function createInferHelpers() {
2107
+ return {
2108
+ Path: void 0,
2109
+ Endpoint: (() => void 0),
2110
+ Request: (() => void 0),
2111
+ Response: (() => void 0),
2112
+ Responses: (() => void 0),
2113
+ Schema: (() => void 0)
2114
+ };
2115
+ }
2116
+ function createGetHelpers(schemas, registry) {
2117
+ return {
2118
+ path: (path) => schemas[path],
2119
+ endpoint: (path, method) => schemas[path][method],
2120
+ request: (path, method) => schemas[path][method]?.request,
2121
+ response: (path, method, status) => schemas[path][method]?.responses?.[status],
2122
+ schema: (key) => registry[key]
2123
+ };
2124
+ }
2125
+ function ensureValidPath(path) {
2126
+ if (!path || path.trim().length === 0) {
2127
+ throw new IgniterCallerError({
2128
+ code: "IGNITER_CALLER_SCHEMA_INVALID",
2129
+ operation: "buildSchema",
2130
+ message: "Path cannot be empty.",
2131
+ statusCode: 400
2132
+ });
2133
+ }
2134
+ if (!path.startsWith("/")) {
2135
+ throw new IgniterCallerError({
2136
+ code: "IGNITER_CALLER_SCHEMA_INVALID",
2137
+ operation: "buildSchema",
2138
+ message: `Path "${path}" must start with "/".`,
2139
+ statusCode: 400,
2140
+ metadata: { path }
2141
+ });
2142
+ }
2143
+ }
2144
+ function ensureValidSchemaKey(key) {
2145
+ if (!key || key.trim().length === 0) {
2146
+ throw new IgniterCallerError({
2147
+ code: "IGNITER_CALLER_SCHEMA_INVALID",
2148
+ operation: "buildSchema",
2149
+ message: "Schema registry key cannot be empty.",
2150
+ statusCode: 400
2151
+ });
2152
+ }
2153
+ }
2154
+
2155
+ // src/adapters/mock.adapter.ts
2156
+ var MockCallerStoreAdapter = class _MockCallerStoreAdapter {
2157
+ constructor() {
2158
+ /** Underlying in-memory store. */
2159
+ this.client = /* @__PURE__ */ new Map();
2160
+ /** Tracks all calls for assertions. */
2161
+ this.calls = {
2162
+ get: 0,
2163
+ set: 0,
2164
+ delete: 0,
2165
+ has: 0
2166
+ };
2167
+ /** Captures recent operations. */
2168
+ this.history = {
2169
+ get: [],
2170
+ set: [],
2171
+ delete: [],
2172
+ has: []
2173
+ };
2174
+ }
2175
+ /** Creates a new mock adapter instance. */
2176
+ static create() {
2177
+ return new _MockCallerStoreAdapter();
2178
+ }
2179
+ /**
2180
+ * Retrieves a cached value by key.
2181
+ *
2182
+ * @param key - Cache key (without prefix).
2183
+ * @returns Cached value or null.
2184
+ */
2185
+ async get(key) {
2186
+ this.calls.get += 1;
2187
+ this.history.get.push(key);
2188
+ return this.client.has(key) ? this.client.get(key) : null;
2189
+ }
2190
+ /**
2191
+ * Stores a cached value.
2192
+ *
2193
+ * @param key - Cache key (without prefix).
2194
+ * @param value - Value to store.
2195
+ * @param options - Cache options (ttl, etc).
2196
+ */
2197
+ async set(key, value, options) {
2198
+ this.calls.set += 1;
2199
+ this.history.set.push({ key, value, options });
2200
+ this.client.set(key, value);
2201
+ }
2202
+ /**
2203
+ * Removes a cached value.
2204
+ *
2205
+ * @param key - Cache key (without prefix).
2206
+ */
2207
+ async delete(key) {
2208
+ this.calls.delete += 1;
2209
+ this.history.delete.push(key);
2210
+ this.client.delete(key);
2211
+ }
2212
+ /**
2213
+ * Checks if a cached value exists.
2214
+ *
2215
+ * @param key - Cache key (without prefix).
2216
+ * @returns True when the key exists.
2217
+ */
2218
+ async has(key) {
2219
+ this.calls.has += 1;
2220
+ this.history.has.push(key);
2221
+ return this.client.has(key);
2222
+ }
2223
+ /**
2224
+ * Clears all tracked state.
2225
+ *
2226
+ * @returns Nothing.
2227
+ */
2228
+ clear() {
2229
+ this.client.clear();
2230
+ this.calls.get = 0;
2231
+ this.calls.set = 0;
2232
+ this.calls.delete = 0;
2233
+ this.calls.has = 0;
2234
+ this.history.get = [];
2235
+ this.history.set = [];
2236
+ this.history.delete = [];
2237
+ this.history.has = [];
2238
+ }
2239
+ };
1503
2240
 
1504
2241
  // src/utils/testing.ts
1505
2242
  var IgniterCallerMock = class {
1506
2243
  /**
1507
2244
  * Creates a successful mock response.
2245
+ *
2246
+ * @param data - Mock response data.
1508
2247
  */
1509
2248
  static mockResponse(data) {
1510
2249
  return { data, error: void 0 };
1511
2250
  }
1512
2251
  /**
1513
2252
  * Creates an error mock response.
2253
+ *
2254
+ * @param code - Error code to use.
2255
+ * @param message - Optional error message.
1514
2256
  */
1515
2257
  static mockError(code, message = "Mock error") {
1516
2258
  return {
@@ -1524,6 +2266,9 @@ var IgniterCallerMock = class {
1524
2266
  }
1525
2267
  /**
1526
2268
  * Creates a successful file download mock.
2269
+ *
2270
+ * @param filename - File name for the mock.
2271
+ * @param content - File contents as string or Blob.
1527
2272
  */
1528
2273
  static mockFile(filename, content) {
1529
2274
  const blob = typeof content === "string" ? new Blob([content]) : content;
@@ -1532,6 +2277,8 @@ var IgniterCallerMock = class {
1532
2277
  }
1533
2278
  /**
1534
2279
  * Creates a failed file download mock.
2280
+ *
2281
+ * @param message - Optional error message.
1535
2282
  */
1536
2283
  static mockFileError(message = "Mock file error") {
1537
2284
  return {
@@ -1545,6 +2292,6 @@ var IgniterCallerMock = class {
1545
2292
  }
1546
2293
  };
1547
2294
 
1548
- export { IgniterCaller, IgniterCallerBodyUtils, IgniterCallerBuilder, IgniterCallerCacheUtils, IgniterCallerError, IgniterCallerEvents, IgniterCallerMock, IgniterCallerRequestBuilder, IgniterCallerSchemaUtils, IgniterCallerUrlUtils };
2295
+ export { IgniterCaller, IgniterCallerBodyUtils, IgniterCallerBuilder, IgniterCallerCacheUtils, IgniterCallerError, IgniterCallerEvents, IgniterCallerManager, IgniterCallerMock, IgniterCallerRequestBuilder, IgniterCallerSchema, IgniterCallerSchemaPathBuilder, IgniterCallerSchemaUtils, IgniterCallerUrlUtils, MockCallerStoreAdapter };
1549
2296
  //# sourceMappingURL=index.mjs.map
1550
2297
  //# sourceMappingURL=index.mjs.map