@igniter-js/caller 0.1.4 → 0.1.51

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,297 @@ 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
+ const delayMs = response.delayMs ?? this.mock?.delay;
1421
+ if (delayMs && delayMs > 0) {
1422
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
1423
+ }
1424
+ const status = response.status;
1425
+ const headers = new Headers(response.headers);
1426
+ if (status >= 400) {
1427
+ return {
1428
+ data: void 0,
1429
+ error: new IgniterCallerError({
1430
+ code: "IGNITER_CALLER_MOCK_HTTP_ERROR",
1431
+ operation: "execute",
1432
+ message: response.errorMessage || `Mocked request failed with status ${status}`,
1433
+ statusCode: status,
1434
+ logger: this.logger,
1435
+ metadata: {
1436
+ method,
1437
+ url
1438
+ }
1439
+ }),
1440
+ status,
1441
+ headers
1442
+ };
1443
+ }
1444
+ let data = response.response;
1445
+ if (this.schemas) {
1446
+ const { schema: endpointSchema } = IgniterCallerSchemaUtils.findSchema(
1447
+ this.schemas,
1448
+ path,
1449
+ method
1450
+ );
1451
+ const responseSchema = endpointSchema?.responses?.[status];
1452
+ if (responseSchema) {
1453
+ try {
1454
+ data = await IgniterCallerSchemaUtils.validateResponse(
1455
+ data,
1456
+ responseSchema,
1457
+ status,
1458
+ this.schemaValidation,
1459
+ { url: safeUrl, method },
1460
+ this.logger
1461
+ );
1462
+ } catch (error) {
1463
+ const err = error;
1464
+ this.telemetry?.emit(
1465
+ "igniter.caller.validation.response.error",
1466
+ {
1467
+ level: "error",
1468
+ attributes: {
1469
+ "ctx.request.method": method,
1470
+ "ctx.request.url": safeUrl,
1471
+ "ctx.validation.type": "response",
1472
+ "ctx.validation.error": err.message,
1473
+ "ctx.response.status": status
1474
+ }
1475
+ }
1476
+ );
1477
+ this.logger?.error("IgniterCaller.response.validation failed", {
1478
+ method,
1479
+ url: safeUrl,
1480
+ status,
1481
+ error: err
1482
+ });
1483
+ return {
1484
+ data: void 0,
1485
+ error: err,
1486
+ status,
1487
+ headers
1488
+ };
1489
+ }
1490
+ }
1491
+ }
1492
+ if (this.responseTypeSchema) {
1493
+ if ("safeParse" in this.responseTypeSchema) {
1494
+ const zodSchema = this.responseTypeSchema;
1495
+ const result = zodSchema.safeParse(data);
1496
+ if (!result.success) {
1497
+ const err = new IgniterCallerError({
1498
+ code: "IGNITER_CALLER_RESPONSE_VALIDATION_FAILED",
1499
+ operation: "parseResponse",
1500
+ message: `Response validation failed: ${result.error.message}`,
1501
+ logger: this.logger,
1502
+ statusCode: status,
1503
+ metadata: {
1504
+ method,
1505
+ url
1506
+ },
1507
+ cause: result.error
1508
+ });
1509
+ this.telemetry?.emit(
1510
+ "igniter.caller.validation.response.error",
1511
+ {
1512
+ level: "error",
1513
+ attributes: {
1514
+ "ctx.request.method": method,
1515
+ "ctx.request.url": safeUrl,
1516
+ "ctx.validation.type": "response",
1517
+ "ctx.validation.error": err.message,
1518
+ "ctx.response.status": status
1519
+ }
1520
+ }
1521
+ );
1522
+ this.logger?.error("IgniterCaller.response.validation failed", {
1523
+ method,
1524
+ url: safeUrl,
1525
+ status,
1526
+ error: err
1527
+ });
1528
+ return {
1529
+ data: void 0,
1530
+ error: err,
1531
+ status,
1532
+ headers
1533
+ };
1534
+ }
1535
+ data = result.data;
1536
+ } else if ("~standard" in this.responseTypeSchema) {
1537
+ try {
1538
+ const standardSchema = this.responseTypeSchema;
1539
+ const result = await standardSchema["~standard"].validate(data);
1540
+ if (result.issues) {
1541
+ const err = new IgniterCallerError({
1542
+ code: "IGNITER_CALLER_RESPONSE_VALIDATION_FAILED",
1543
+ operation: "parseResponse",
1544
+ message: `Response validation failed`,
1545
+ logger: this.logger,
1546
+ statusCode: status,
1547
+ metadata: {
1548
+ method,
1549
+ url,
1550
+ issues: result.issues
1551
+ }
1552
+ });
1553
+ this.telemetry?.emit(
1554
+ "igniter.caller.validation.response.error",
1555
+ {
1556
+ level: "error",
1557
+ attributes: {
1558
+ "ctx.request.method": method,
1559
+ "ctx.request.url": safeUrl,
1560
+ "ctx.validation.type": "response",
1561
+ "ctx.validation.error": err.message,
1562
+ "ctx.response.status": status
1563
+ }
1564
+ }
1565
+ );
1566
+ this.logger?.error("IgniterCaller.response.validation failed", {
1567
+ method,
1568
+ url: safeUrl,
1569
+ status,
1570
+ error: err
1571
+ });
1572
+ return {
1573
+ data: void 0,
1574
+ error: err,
1575
+ status,
1576
+ headers
1577
+ };
1578
+ }
1579
+ data = result.value;
1580
+ } catch (error) {
1581
+ const err = error;
1582
+ this.telemetry?.emit(
1583
+ "igniter.caller.validation.response.error",
1584
+ {
1585
+ level: "error",
1586
+ attributes: {
1587
+ "ctx.request.method": method,
1588
+ "ctx.request.url": safeUrl,
1589
+ "ctx.validation.type": "response",
1590
+ "ctx.validation.error": err.message,
1591
+ "ctx.response.status": status
1592
+ }
1593
+ }
1594
+ );
1595
+ this.logger?.error("IgniterCaller.response.validation failed", {
1596
+ method,
1597
+ url: safeUrl,
1598
+ status,
1599
+ error: err
1600
+ });
1601
+ return {
1602
+ data: void 0,
1603
+ error: err,
1604
+ status,
1605
+ headers
1606
+ };
1607
+ }
1608
+ }
1609
+ }
1610
+ let responseResult = {
1611
+ data,
1612
+ error: void 0,
1613
+ status,
1614
+ headers
1615
+ };
1616
+ if (this.responseInterceptors && this.responseInterceptors.length > 0) {
1617
+ for (const interceptor of this.responseInterceptors) {
1618
+ responseResult = await interceptor(responseResult);
1619
+ }
1620
+ }
1621
+ return responseResult;
1622
+ }
1623
+ /**
1624
+ * Normalizes a mock handler result into a response payload with status.
1625
+ */
1626
+ async resolveMockResponse(handler, request) {
1627
+ const result = typeof handler === "function" ? await handler(request) : handler;
1628
+ const hasStatus = typeof result.status === "number";
1629
+ if (hasStatus) {
1630
+ return result;
1631
+ }
1632
+ const schemas = this.schemas;
1633
+ const schemaMatch = schemas ? IgniterCallerSchemaUtils.findSchema(
1634
+ schemas,
1635
+ request.path,
1636
+ request.method
1637
+ ).schema : void 0;
1638
+ const fallbackStatus = schemaMatch?.responses?.[200] ? 200 : schemaMatch?.responses?.[201] ? 201 : 200;
1639
+ return {
1640
+ ...result,
1641
+ status: fallbackStatus
1642
+ };
1643
+ }
1347
1644
  /**
1348
1645
  * Emits event for this response using injected emitter.
1349
1646
  */
@@ -1510,6 +1807,7 @@ var _IgniterCallerManager = class _IgniterCallerManager {
1510
1807
  this.responseInterceptors = opts?.responseInterceptors;
1511
1808
  this.schemas = opts?.schemas;
1512
1809
  this.schemaValidation = opts?.schemaValidation;
1810
+ this.mock = opts?.mock;
1513
1811
  }
1514
1812
  /**
1515
1813
  * Creates common request builder params.
@@ -1527,7 +1825,8 @@ var _IgniterCallerManager = class _IgniterCallerManager {
1527
1825
  await _IgniterCallerManager.emitEvent(url, method, result);
1528
1826
  },
1529
1827
  schemas: this.schemas,
1530
- schemaValidation: this.schemaValidation
1828
+ schemaValidation: this.schemaValidation,
1829
+ mock: this.mock
1531
1830
  };
1532
1831
  }
1533
1832
  get(url) {
@@ -1733,6 +2032,45 @@ var _IgniterCallerManager = class _IgniterCallerManager {
1733
2032
  _IgniterCallerManager.events = new IgniterCallerEvents();
1734
2033
  var IgniterCallerManager = _IgniterCallerManager;
1735
2034
 
2035
+ // src/core/mock.ts
2036
+ var IgniterCallerMockManager = class {
2037
+ constructor(registry) {
2038
+ this.registry = registry;
2039
+ }
2040
+ /**
2041
+ * Resolves a mock handler for a path+method pair.
2042
+ *
2043
+ * @param path - Request path (normalized).
2044
+ * @param method - HTTP method.
2045
+ * @returns Resolved handler info or null when no match is found.
2046
+ */
2047
+ resolve(path, method) {
2048
+ const direct = this.registry[path]?.[method];
2049
+ if (direct) {
2050
+ return {
2051
+ handler: direct,
2052
+ params: {},
2053
+ path,
2054
+ method
2055
+ };
2056
+ }
2057
+ for (const [registeredPath, methods] of Object.entries(this.registry)) {
2058
+ if (!methods) continue;
2059
+ const match = IgniterCallerSchemaUtils.matchPath(path, registeredPath);
2060
+ if (!match.matched) continue;
2061
+ const handler = methods[method];
2062
+ if (!handler) continue;
2063
+ return {
2064
+ handler,
2065
+ params: match.params || {},
2066
+ path: registeredPath,
2067
+ method
2068
+ };
2069
+ }
2070
+ return null;
2071
+ }
2072
+ };
2073
+
1736
2074
  // src/builders/main.builder.ts
1737
2075
  var IgniterCallerBuilder = class _IgniterCallerBuilder {
1738
2076
  constructor(state) {
@@ -1773,7 +2111,7 @@ var IgniterCallerBuilder = class _IgniterCallerBuilder {
1773
2111
  /**
1774
2112
  * Attaches a logger instance.
1775
2113
  *
1776
- * @param logger - Logger implementation from `@igniter-js/core`.
2114
+ * @param logger - Logger implementation from `@igniter-js/common`.
1777
2115
  */
1778
2116
  withLogger(logger) {
1779
2117
  return new _IgniterCallerBuilder({ ...this.state, logger });
@@ -1895,6 +2233,16 @@ var IgniterCallerBuilder = class _IgniterCallerBuilder {
1895
2233
  withTelemetry(telemetry) {
1896
2234
  return new _IgniterCallerBuilder({ ...this.state, telemetry });
1897
2235
  }
2236
+ /**
2237
+ * Enables request mocking using a mock registry.
2238
+ *
2239
+ * When enabled, matching requests are routed to the mock handlers instead of fetch.
2240
+ *
2241
+ * @param config - Mock configuration with registry and enable flag.
2242
+ */
2243
+ withMock(config) {
2244
+ return new _IgniterCallerBuilder({ ...this.state, mock: config });
2245
+ }
1898
2246
  /**
1899
2247
  * Builds the `IgniterCaller` instance.
1900
2248
  *
@@ -1912,7 +2260,8 @@ var IgniterCallerBuilder = class _IgniterCallerBuilder {
1912
2260
  requestInterceptors: this.state.requestInterceptors,
1913
2261
  responseInterceptors: this.state.responseInterceptors,
1914
2262
  schemas: this.state.schemas,
1915
- schemaValidation: this.state.schemaValidation
2263
+ schemaValidation: this.state.schemaValidation,
2264
+ mock: this.state.mock
1916
2265
  });
1917
2266
  this.state.logger?.info("IgniterCaller initialized", {
1918
2267
  baseURL: this.state.baseURL,
@@ -2152,6 +2501,54 @@ function ensureValidSchemaKey(key) {
2152
2501
  }
2153
2502
  }
2154
2503
 
2504
+ // src/builders/mock.builder.ts
2505
+ var IgniterCallerMockBuilder = class _IgniterCallerMockBuilder {
2506
+ constructor(state) {
2507
+ this.state = state;
2508
+ }
2509
+ /**
2510
+ * Creates a new mock builder.
2511
+ */
2512
+ static create() {
2513
+ return new _IgniterCallerMockBuilder({ registry: {} });
2514
+ }
2515
+ /**
2516
+ * Sets schemas to enable typed mock definitions.
2517
+ *
2518
+ * @param _schemas - Schema map or build result.
2519
+ */
2520
+ withSchemas(_schemas) {
2521
+ return new _IgniterCallerMockBuilder({
2522
+ registry: this.state.registry
2523
+ });
2524
+ }
2525
+ /**
2526
+ * Registers mock handlers for a path.
2527
+ *
2528
+ * @param path - Schema path.
2529
+ * @param handlers - Method handlers or static responses.
2530
+ */
2531
+ mock(path, handlers) {
2532
+ const registry = {
2533
+ ...this.state.registry,
2534
+ [path]: {
2535
+ ...this.state.registry[path] || {},
2536
+ ...handlers
2537
+ }
2538
+ };
2539
+ return new _IgniterCallerMockBuilder({ registry });
2540
+ }
2541
+ /**
2542
+ * Builds a mock manager instance.
2543
+ */
2544
+ build() {
2545
+ return new IgniterCallerMockManager(this.state.registry);
2546
+ }
2547
+ };
2548
+ var IgniterCallerMock = {
2549
+ create: IgniterCallerMockBuilder.create
2550
+ };
2551
+
2155
2552
  // src/adapters/mock.adapter.ts
2156
2553
  var MockCallerStoreAdapter = class _MockCallerStoreAdapter {
2157
2554
  constructor() {
@@ -2239,7 +2636,7 @@ var MockCallerStoreAdapter = class _MockCallerStoreAdapter {
2239
2636
  };
2240
2637
 
2241
2638
  // src/utils/testing.ts
2242
- var IgniterCallerMock = class {
2639
+ var IgniterCallerHttpMock = class {
2243
2640
  /**
2244
2641
  * Creates a successful mock response.
2245
2642
  *
@@ -2292,6 +2689,6 @@ var IgniterCallerMock = class {
2292
2689
  }
2293
2690
  };
2294
2691
 
2295
- export { IgniterCaller, IgniterCallerBodyUtils, IgniterCallerBuilder, IgniterCallerCacheUtils, IgniterCallerError, IgniterCallerEvents, IgniterCallerManager, IgniterCallerMock, IgniterCallerRequestBuilder, IgniterCallerSchema, IgniterCallerSchemaPathBuilder, IgniterCallerSchemaUtils, IgniterCallerUrlUtils, MockCallerStoreAdapter };
2692
+ export { IgniterCaller, IgniterCallerBodyUtils, IgniterCallerBuilder, IgniterCallerCacheUtils, IgniterCallerError, IgniterCallerEvents, IgniterCallerHttpMock, IgniterCallerManager, IgniterCallerMock, IgniterCallerMockBuilder, IgniterCallerMockManager, IgniterCallerRequestBuilder, IgniterCallerSchema, IgniterCallerSchemaPathBuilder, IgniterCallerSchemaUtils, IgniterCallerUrlUtils, MockCallerStoreAdapter };
2296
2693
  //# sourceMappingURL=index.mjs.map
2297
2694
  //# sourceMappingURL=index.mjs.map