@igniter-js/caller 0.1.4 → 0.1.5

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,4 +1,4 @@
1
- import { IgniterError } from '@igniter-js/core';
1
+ import { IgniterError } from '@igniter-js/common';
2
2
  import { z } from 'zod';
3
3
 
4
4
  // src/errors/caller.error.ts
@@ -503,6 +503,7 @@ var IgniterCallerRequestBuilder = class {
503
503
  this.eventEmitter = params.eventEmitter;
504
504
  this.schemas = params.schemas;
505
505
  this.schemaValidation = params.schemaValidation;
506
+ this.mock = params.mock;
506
507
  }
507
508
  /**
508
509
  * Sets the HTTP method for this request.
@@ -527,7 +528,7 @@ var IgniterCallerRequestBuilder = class {
527
528
  /**
528
529
  * Overrides the logger for this request chain.
529
530
  *
530
- * @param logger - Logger implementation from `@igniter-js/core`.
531
+ * @param logger - Logger implementation from `@igniter-js/common`.
531
532
  */
532
533
  withLogger(logger) {
533
534
  this.logger = logger;
@@ -1024,6 +1025,11 @@ var IgniterCallerRequestBuilder = class {
1024
1025
  }
1025
1026
  }
1026
1027
  }
1028
+ const mockResult = await this.executeMockRequest(url, safeUrl);
1029
+ if (mockResult) {
1030
+ clearTimeout(timeoutId);
1031
+ return mockResult;
1032
+ }
1027
1033
  try {
1028
1034
  const httpResponse = await fetch(url, {
1029
1035
  ...requestInit,
@@ -1344,6 +1350,296 @@ var IgniterCallerRequestBuilder = class {
1344
1350
  timeoutId
1345
1351
  };
1346
1352
  }
1353
+ /**
1354
+ * Normalizes a URL into a path for mock matching.
1355
+ */
1356
+ normalizeMockPath(url, baseURL) {
1357
+ if (/^https?:\/\//i.test(url)) {
1358
+ try {
1359
+ return new URL(url).pathname;
1360
+ } catch {
1361
+ return url;
1362
+ }
1363
+ }
1364
+ if (baseURL && /^https?:\/\//i.test(baseURL)) {
1365
+ try {
1366
+ const resolved = IgniterCallerUrlUtils.buildUrl({ url, baseURL });
1367
+ return new URL(resolved).pathname;
1368
+ } catch {
1369
+ return url;
1370
+ }
1371
+ }
1372
+ return url.startsWith("/") ? url : `/${url}`;
1373
+ }
1374
+ /**
1375
+ * Resolves the final query object (merges GET/HEAD body into params).
1376
+ */
1377
+ getFinalQuery() {
1378
+ const { method, body, params } = this.options;
1379
+ if ((method === "GET" || method === "HEAD") && body && typeof body === "object") {
1380
+ const bodyParams = {};
1381
+ for (const [key, value] of Object.entries(body)) {
1382
+ if (value !== void 0 && value !== null) {
1383
+ bodyParams[key] = String(value);
1384
+ }
1385
+ }
1386
+ return { ...bodyParams, ...params || {} };
1387
+ }
1388
+ return params || {};
1389
+ }
1390
+ /**
1391
+ * Executes a mock handler when enabled and matched.
1392
+ */
1393
+ async executeMockRequest(url, safeUrl) {
1394
+ if (!this.mock?.enabled) return null;
1395
+ const path = this.normalizeMockPath(this.options.url, this.options.baseURL);
1396
+ const method = this.options.method;
1397
+ const resolved = this.mock.mock.resolve(path, method);
1398
+ if (!resolved) return null;
1399
+ const query = this.getFinalQuery();
1400
+ const mockRequest = {
1401
+ method,
1402
+ path: resolved.path,
1403
+ url,
1404
+ safeUrl,
1405
+ baseURL: this.options.baseURL,
1406
+ headers: this.options.headers || {},
1407
+ query,
1408
+ params: resolved.params || {},
1409
+ body: this.options.body,
1410
+ timeoutMs: this.options.timeout,
1411
+ cache: this.options.cache,
1412
+ cacheKey: this.cacheKey,
1413
+ staleTime: this.staleTime,
1414
+ responseTypeSchema: this.responseTypeSchema
1415
+ };
1416
+ const response = await this.resolveMockResponse(
1417
+ resolved.handler,
1418
+ mockRequest
1419
+ );
1420
+ if (response.delayMs && response.delayMs > 0) {
1421
+ await new Promise((resolve) => setTimeout(resolve, response.delayMs));
1422
+ }
1423
+ const status = response.status;
1424
+ const headers = new Headers(response.headers);
1425
+ if (status >= 400) {
1426
+ return {
1427
+ data: void 0,
1428
+ error: new IgniterCallerError({
1429
+ code: "IGNITER_CALLER_MOCK_HTTP_ERROR",
1430
+ operation: "execute",
1431
+ message: response.errorMessage || `Mocked request failed with status ${status}`,
1432
+ statusCode: status,
1433
+ logger: this.logger,
1434
+ metadata: {
1435
+ method,
1436
+ url
1437
+ }
1438
+ }),
1439
+ status,
1440
+ headers
1441
+ };
1442
+ }
1443
+ let data = response.response;
1444
+ if (this.schemas) {
1445
+ const { schema: endpointSchema } = IgniterCallerSchemaUtils.findSchema(
1446
+ this.schemas,
1447
+ path,
1448
+ method
1449
+ );
1450
+ const responseSchema = endpointSchema?.responses?.[status];
1451
+ if (responseSchema) {
1452
+ try {
1453
+ data = await IgniterCallerSchemaUtils.validateResponse(
1454
+ data,
1455
+ responseSchema,
1456
+ status,
1457
+ this.schemaValidation,
1458
+ { url: safeUrl, method },
1459
+ this.logger
1460
+ );
1461
+ } catch (error) {
1462
+ const err = error;
1463
+ this.telemetry?.emit(
1464
+ "igniter.caller.validation.response.error",
1465
+ {
1466
+ level: "error",
1467
+ attributes: {
1468
+ "ctx.request.method": method,
1469
+ "ctx.request.url": safeUrl,
1470
+ "ctx.validation.type": "response",
1471
+ "ctx.validation.error": err.message,
1472
+ "ctx.response.status": status
1473
+ }
1474
+ }
1475
+ );
1476
+ this.logger?.error("IgniterCaller.response.validation failed", {
1477
+ method,
1478
+ url: safeUrl,
1479
+ status,
1480
+ error: err
1481
+ });
1482
+ return {
1483
+ data: void 0,
1484
+ error: err,
1485
+ status,
1486
+ headers
1487
+ };
1488
+ }
1489
+ }
1490
+ }
1491
+ if (this.responseTypeSchema) {
1492
+ if ("safeParse" in this.responseTypeSchema) {
1493
+ const zodSchema = this.responseTypeSchema;
1494
+ const result = zodSchema.safeParse(data);
1495
+ if (!result.success) {
1496
+ const err = new IgniterCallerError({
1497
+ code: "IGNITER_CALLER_RESPONSE_VALIDATION_FAILED",
1498
+ operation: "parseResponse",
1499
+ message: `Response validation failed: ${result.error.message}`,
1500
+ logger: this.logger,
1501
+ statusCode: status,
1502
+ metadata: {
1503
+ method,
1504
+ url
1505
+ },
1506
+ cause: result.error
1507
+ });
1508
+ this.telemetry?.emit(
1509
+ "igniter.caller.validation.response.error",
1510
+ {
1511
+ level: "error",
1512
+ attributes: {
1513
+ "ctx.request.method": method,
1514
+ "ctx.request.url": safeUrl,
1515
+ "ctx.validation.type": "response",
1516
+ "ctx.validation.error": err.message,
1517
+ "ctx.response.status": status
1518
+ }
1519
+ }
1520
+ );
1521
+ this.logger?.error("IgniterCaller.response.validation failed", {
1522
+ method,
1523
+ url: safeUrl,
1524
+ status,
1525
+ error: err
1526
+ });
1527
+ return {
1528
+ data: void 0,
1529
+ error: err,
1530
+ status,
1531
+ headers
1532
+ };
1533
+ }
1534
+ data = result.data;
1535
+ } else if ("~standard" in this.responseTypeSchema) {
1536
+ try {
1537
+ const standardSchema = this.responseTypeSchema;
1538
+ const result = await standardSchema["~standard"].validate(data);
1539
+ if (result.issues) {
1540
+ const err = new IgniterCallerError({
1541
+ code: "IGNITER_CALLER_RESPONSE_VALIDATION_FAILED",
1542
+ operation: "parseResponse",
1543
+ message: `Response validation failed`,
1544
+ logger: this.logger,
1545
+ statusCode: status,
1546
+ metadata: {
1547
+ method,
1548
+ url,
1549
+ issues: result.issues
1550
+ }
1551
+ });
1552
+ this.telemetry?.emit(
1553
+ "igniter.caller.validation.response.error",
1554
+ {
1555
+ level: "error",
1556
+ attributes: {
1557
+ "ctx.request.method": method,
1558
+ "ctx.request.url": safeUrl,
1559
+ "ctx.validation.type": "response",
1560
+ "ctx.validation.error": err.message,
1561
+ "ctx.response.status": status
1562
+ }
1563
+ }
1564
+ );
1565
+ this.logger?.error("IgniterCaller.response.validation failed", {
1566
+ method,
1567
+ url: safeUrl,
1568
+ status,
1569
+ error: err
1570
+ });
1571
+ return {
1572
+ data: void 0,
1573
+ error: err,
1574
+ status,
1575
+ headers
1576
+ };
1577
+ }
1578
+ data = result.value;
1579
+ } catch (error) {
1580
+ const err = error;
1581
+ this.telemetry?.emit(
1582
+ "igniter.caller.validation.response.error",
1583
+ {
1584
+ level: "error",
1585
+ attributes: {
1586
+ "ctx.request.method": method,
1587
+ "ctx.request.url": safeUrl,
1588
+ "ctx.validation.type": "response",
1589
+ "ctx.validation.error": err.message,
1590
+ "ctx.response.status": status
1591
+ }
1592
+ }
1593
+ );
1594
+ this.logger?.error("IgniterCaller.response.validation failed", {
1595
+ method,
1596
+ url: safeUrl,
1597
+ status,
1598
+ error: err
1599
+ });
1600
+ return {
1601
+ data: void 0,
1602
+ error: err,
1603
+ status,
1604
+ headers
1605
+ };
1606
+ }
1607
+ }
1608
+ }
1609
+ let responseResult = {
1610
+ data,
1611
+ error: void 0,
1612
+ status,
1613
+ headers
1614
+ };
1615
+ if (this.responseInterceptors && this.responseInterceptors.length > 0) {
1616
+ for (const interceptor of this.responseInterceptors) {
1617
+ responseResult = await interceptor(responseResult);
1618
+ }
1619
+ }
1620
+ return responseResult;
1621
+ }
1622
+ /**
1623
+ * Normalizes a mock handler result into a response payload with status.
1624
+ */
1625
+ async resolveMockResponse(handler, request) {
1626
+ const result = typeof handler === "function" ? await handler(request) : handler;
1627
+ const hasStatus = typeof result.status === "number";
1628
+ if (hasStatus) {
1629
+ return result;
1630
+ }
1631
+ const schemas = this.schemas;
1632
+ const schemaMatch = schemas ? IgniterCallerSchemaUtils.findSchema(
1633
+ schemas,
1634
+ request.path,
1635
+ request.method
1636
+ ).schema : void 0;
1637
+ const fallbackStatus = schemaMatch?.responses?.[200] ? 200 : schemaMatch?.responses?.[201] ? 201 : 200;
1638
+ return {
1639
+ ...result,
1640
+ status: fallbackStatus
1641
+ };
1642
+ }
1347
1643
  /**
1348
1644
  * Emits event for this response using injected emitter.
1349
1645
  */
@@ -1510,6 +1806,7 @@ var _IgniterCallerManager = class _IgniterCallerManager {
1510
1806
  this.responseInterceptors = opts?.responseInterceptors;
1511
1807
  this.schemas = opts?.schemas;
1512
1808
  this.schemaValidation = opts?.schemaValidation;
1809
+ this.mock = opts?.mock;
1513
1810
  }
1514
1811
  /**
1515
1812
  * Creates common request builder params.
@@ -1527,7 +1824,8 @@ var _IgniterCallerManager = class _IgniterCallerManager {
1527
1824
  await _IgniterCallerManager.emitEvent(url, method, result);
1528
1825
  },
1529
1826
  schemas: this.schemas,
1530
- schemaValidation: this.schemaValidation
1827
+ schemaValidation: this.schemaValidation,
1828
+ mock: this.mock
1531
1829
  };
1532
1830
  }
1533
1831
  get(url) {
@@ -1733,6 +2031,45 @@ var _IgniterCallerManager = class _IgniterCallerManager {
1733
2031
  _IgniterCallerManager.events = new IgniterCallerEvents();
1734
2032
  var IgniterCallerManager = _IgniterCallerManager;
1735
2033
 
2034
+ // src/core/mock.ts
2035
+ var IgniterCallerMockManager = class {
2036
+ constructor(registry) {
2037
+ this.registry = registry;
2038
+ }
2039
+ /**
2040
+ * Resolves a mock handler for a path+method pair.
2041
+ *
2042
+ * @param path - Request path (normalized).
2043
+ * @param method - HTTP method.
2044
+ * @returns Resolved handler info or null when no match is found.
2045
+ */
2046
+ resolve(path, method) {
2047
+ const direct = this.registry[path]?.[method];
2048
+ if (direct) {
2049
+ return {
2050
+ handler: direct,
2051
+ params: {},
2052
+ path,
2053
+ method
2054
+ };
2055
+ }
2056
+ for (const [registeredPath, methods] of Object.entries(this.registry)) {
2057
+ if (!methods) continue;
2058
+ const match = IgniterCallerSchemaUtils.matchPath(path, registeredPath);
2059
+ if (!match.matched) continue;
2060
+ const handler = methods[method];
2061
+ if (!handler) continue;
2062
+ return {
2063
+ handler,
2064
+ params: match.params || {},
2065
+ path: registeredPath,
2066
+ method
2067
+ };
2068
+ }
2069
+ return null;
2070
+ }
2071
+ };
2072
+
1736
2073
  // src/builders/main.builder.ts
1737
2074
  var IgniterCallerBuilder = class _IgniterCallerBuilder {
1738
2075
  constructor(state) {
@@ -1773,7 +2110,7 @@ var IgniterCallerBuilder = class _IgniterCallerBuilder {
1773
2110
  /**
1774
2111
  * Attaches a logger instance.
1775
2112
  *
1776
- * @param logger - Logger implementation from `@igniter-js/core`.
2113
+ * @param logger - Logger implementation from `@igniter-js/common`.
1777
2114
  */
1778
2115
  withLogger(logger) {
1779
2116
  return new _IgniterCallerBuilder({ ...this.state, logger });
@@ -1895,6 +2232,16 @@ var IgniterCallerBuilder = class _IgniterCallerBuilder {
1895
2232
  withTelemetry(telemetry) {
1896
2233
  return new _IgniterCallerBuilder({ ...this.state, telemetry });
1897
2234
  }
2235
+ /**
2236
+ * Enables request mocking using a mock registry.
2237
+ *
2238
+ * When enabled, matching requests are routed to the mock handlers instead of fetch.
2239
+ *
2240
+ * @param config - Mock configuration with registry and enable flag.
2241
+ */
2242
+ withMock(config) {
2243
+ return new _IgniterCallerBuilder({ ...this.state, mock: config });
2244
+ }
1898
2245
  /**
1899
2246
  * Builds the `IgniterCaller` instance.
1900
2247
  *
@@ -1912,7 +2259,8 @@ var IgniterCallerBuilder = class _IgniterCallerBuilder {
1912
2259
  requestInterceptors: this.state.requestInterceptors,
1913
2260
  responseInterceptors: this.state.responseInterceptors,
1914
2261
  schemas: this.state.schemas,
1915
- schemaValidation: this.state.schemaValidation
2262
+ schemaValidation: this.state.schemaValidation,
2263
+ mock: this.state.mock
1916
2264
  });
1917
2265
  this.state.logger?.info("IgniterCaller initialized", {
1918
2266
  baseURL: this.state.baseURL,
@@ -2152,6 +2500,54 @@ function ensureValidSchemaKey(key) {
2152
2500
  }
2153
2501
  }
2154
2502
 
2503
+ // src/builders/mock.builder.ts
2504
+ var IgniterCallerMockBuilder = class _IgniterCallerMockBuilder {
2505
+ constructor(state) {
2506
+ this.state = state;
2507
+ }
2508
+ /**
2509
+ * Creates a new mock builder.
2510
+ */
2511
+ static create() {
2512
+ return new _IgniterCallerMockBuilder({ registry: {} });
2513
+ }
2514
+ /**
2515
+ * Sets schemas to enable typed mock definitions.
2516
+ *
2517
+ * @param _schemas - Schema map or build result.
2518
+ */
2519
+ withSchemas(_schemas) {
2520
+ return new _IgniterCallerMockBuilder({
2521
+ registry: this.state.registry
2522
+ });
2523
+ }
2524
+ /**
2525
+ * Registers mock handlers for a path.
2526
+ *
2527
+ * @param path - Schema path.
2528
+ * @param handlers - Method handlers or static responses.
2529
+ */
2530
+ mock(path, handlers) {
2531
+ const registry = {
2532
+ ...this.state.registry,
2533
+ [path]: {
2534
+ ...this.state.registry[path] || {},
2535
+ ...handlers
2536
+ }
2537
+ };
2538
+ return new _IgniterCallerMockBuilder({ registry });
2539
+ }
2540
+ /**
2541
+ * Builds a mock manager instance.
2542
+ */
2543
+ build() {
2544
+ return new IgniterCallerMockManager(this.state.registry);
2545
+ }
2546
+ };
2547
+ var IgniterCallerMock = {
2548
+ create: IgniterCallerMockBuilder.create
2549
+ };
2550
+
2155
2551
  // src/adapters/mock.adapter.ts
2156
2552
  var MockCallerStoreAdapter = class _MockCallerStoreAdapter {
2157
2553
  constructor() {
@@ -2239,7 +2635,7 @@ var MockCallerStoreAdapter = class _MockCallerStoreAdapter {
2239
2635
  };
2240
2636
 
2241
2637
  // src/utils/testing.ts
2242
- var IgniterCallerMock = class {
2638
+ var IgniterCallerHttpMock = class {
2243
2639
  /**
2244
2640
  * Creates a successful mock response.
2245
2641
  *
@@ -2292,6 +2688,6 @@ var IgniterCallerMock = class {
2292
2688
  }
2293
2689
  };
2294
2690
 
2295
- export { IgniterCaller, IgniterCallerBodyUtils, IgniterCallerBuilder, IgniterCallerCacheUtils, IgniterCallerError, IgniterCallerEvents, IgniterCallerManager, IgniterCallerMock, IgniterCallerRequestBuilder, IgniterCallerSchema, IgniterCallerSchemaPathBuilder, IgniterCallerSchemaUtils, IgniterCallerUrlUtils, MockCallerStoreAdapter };
2691
+ export { IgniterCaller, IgniterCallerBodyUtils, IgniterCallerBuilder, IgniterCallerCacheUtils, IgniterCallerError, IgniterCallerEvents, IgniterCallerHttpMock, IgniterCallerManager, IgniterCallerMock, IgniterCallerMockBuilder, IgniterCallerMockManager, IgniterCallerRequestBuilder, IgniterCallerSchema, IgniterCallerSchemaPathBuilder, IgniterCallerSchemaUtils, IgniterCallerUrlUtils, MockCallerStoreAdapter };
2296
2692
  //# sourceMappingURL=index.mjs.map
2297
2693
  //# sourceMappingURL=index.mjs.map