@adobe-commerce/aio-toolkit 1.0.13 → 1.0.15

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.js CHANGED
@@ -1321,7 +1321,96 @@ var new_relic_default = NewRelicTelemetry;
1321
1321
  // src/framework/telemetry/index.ts
1322
1322
  var _Telemetry = class _Telemetry {
1323
1323
  /**
1324
- * Create a logger with standard configuration and automatic metadata injection.
1324
+ * Create a new Telemetry instance
1325
+ *
1326
+ * Initializes the telemetry manager with optional runtime parameters.
1327
+ * Parameters can be set later using setParams() if not provided during construction.
1328
+ *
1329
+ * @param params - Optional runtime parameters containing telemetry configuration
1330
+ *
1331
+ * @example
1332
+ * ```typescript
1333
+ * const telemetry = new Telemetry({
1334
+ * ENABLE_TELEMETRY: true,
1335
+ * LOG_LEVEL: 'debug',
1336
+ * action_type: 'webhook'
1337
+ * });
1338
+ * ```
1339
+ */
1340
+ constructor(params = void 0) {
1341
+ /**
1342
+ * Runtime parameters containing telemetry configuration
1343
+ *
1344
+ * Stores the action parameters including telemetry flags (ENABLE_TELEMETRY),
1345
+ * provider configuration (NEW_RELIC_*), log levels, and request context.
1346
+ *
1347
+ * @private
1348
+ */
1349
+ this.params = void 0;
1350
+ /**
1351
+ * Logger instance
1352
+ *
1353
+ * Stores the logger instance for the telemetry instance.
1354
+ *
1355
+ * @private
1356
+ */
1357
+ this.logger = void 0;
1358
+ this.params = params;
1359
+ }
1360
+ /**
1361
+ * Get the current runtime parameters
1362
+ *
1363
+ * Returns the parameters stored in this telemetry instance, or an empty object
1364
+ * if no parameters have been set. These parameters control telemetry behavior
1365
+ * and provide context for logging and tracing.
1366
+ *
1367
+ * @returns Runtime parameters object (never undefined, returns empty object if not set)
1368
+ *
1369
+ * @example
1370
+ * ```typescript
1371
+ * const currentParams = telemetry.getParams();
1372
+ * if (currentParams.ENABLE_TELEMETRY) {
1373
+ * // telemetry is enabled
1374
+ * }
1375
+ * ```
1376
+ */
1377
+ getParams() {
1378
+ return this.params || {};
1379
+ }
1380
+ /**
1381
+ * Set or update the runtime parameters
1382
+ *
1383
+ * Updates the parameters used by this telemetry instance. This is typically
1384
+ * called by the initialize() method when the action receives runtime parameters,
1385
+ * but can be used to update configuration at any time.
1386
+ *
1387
+ * @param params - New runtime parameters to store
1388
+ *
1389
+ * @example
1390
+ * ```typescript
1391
+ * telemetry.setParams({
1392
+ * ENABLE_TELEMETRY: true,
1393
+ * NEW_RELIC_TELEMETRY: true,
1394
+ * LOG_LEVEL: 'info'
1395
+ * });
1396
+ * const logger = telemetry.createLogger('my-action');
1397
+ * ```
1398
+ */
1399
+ setParams(params) {
1400
+ this.params = params;
1401
+ }
1402
+ /**
1403
+ * Get the logger instance
1404
+ *
1405
+ * Returns the logger instance for the telemetry instance.
1406
+ *
1407
+ * @returns Logger instance
1408
+ */
1409
+ getLogger() {
1410
+ return this.logger;
1411
+ }
1412
+ /**
1413
+ * Create a logger with standard configuration and automatic metadata injection
1325
1414
  *
1326
1415
  * This method creates a structured logger and wraps it to automatically add
1327
1416
  * contextual metadata to all log calls:
@@ -1333,50 +1422,30 @@ var _Telemetry = class _Telemetry {
1333
1422
  * automatically appear in all logs sent to New Relic if ENABLE_TELEMETRY is true.
1334
1423
  *
1335
1424
  * @param name - Logger name (typically action name)
1336
- * @param params - Runtime parameters containing LOG_LEVEL, optional ENABLE_TELEMETRY, ENVIRONMENT, action_type, and __ow_headers
1337
1425
  * @returns Configured logger instance with automatic metadata injection
1338
1426
  *
1339
- * @example Basic string message
1340
- * ```typescript
1341
- * const logger = Telemetry.createLogger("my-action", params);
1342
- * logger.info("Processing started");
1343
- * // Logs: "Processing started"
1344
- * ```
1345
- *
1346
- * @example JSON object message with automatic metadata
1427
+ * @example
1347
1428
  * ```typescript
1348
- * const logger = Telemetry.createLogger("my-action", {
1349
- * ...params,
1350
- * action_type: "webhook",
1351
- * __ow_headers: { 'x-adobe-commerce-request-id': 'req-123' }
1352
- * });
1353
- * logger.info({
1354
- * message: "User action completed",
1355
- * user_id: "123"
1356
- * });
1357
- * // In New Relic: {
1358
- * // message: "User action completed",
1359
- * // user_id: "123",
1360
- * // "x-adobe-commerce-request-id": "req-123",
1361
- * // "action.type": "webhook",
1362
- * // environment: "development",
1363
- * // ...
1364
- * // }
1429
+ * const logger = telemetry.createLogger('my-action');
1430
+ * logger.info({ message: 'User action completed', user_id: '123' });
1431
+ * // In New Relic: includes message, user_id, request-id, action.type, environment
1365
1432
  * ```
1366
1433
  */
1367
- static createLogger(name, params) {
1368
- const baseLogger = !params.ENABLE_TELEMETRY ? import_aio_sdk.Core.Logger(name, {
1369
- level: params.LOG_LEVEL || "info"
1370
- }) : (0, import_aio_lib_telemetry3.getLogger)(name, {
1371
- level: params.LOG_LEVEL || "info"
1434
+ createLogger(name) {
1435
+ let baseLogger = import_aio_sdk.Core.Logger(name, {
1436
+ level: this.params?.LOG_LEVEL || "info"
1372
1437
  });
1438
+ if (this.params?.ENABLE_TELEMETRY === true) {
1439
+ const helpers = (0, import_aio_lib_telemetry3.getInstrumentationHelpers)();
1440
+ baseLogger = helpers.logger;
1441
+ }
1373
1442
  const metadata = {};
1374
- const headers = params.__ow_headers;
1443
+ const headers = this.params?.__ow_headers;
1375
1444
  const requestId = headers?.["x-adobe-commerce-request-id"];
1376
1445
  if (requestId && requestId !== "") {
1377
1446
  metadata["x-adobe-commerce-request-id"] = requestId;
1378
1447
  }
1379
- const actionType = params.action_type;
1448
+ const actionType = this.params?.action_type;
1380
1449
  if (actionType && actionType !== "") {
1381
1450
  metadata["action.type"] = actionType;
1382
1451
  }
@@ -1413,10 +1482,11 @@ var _Telemetry = class _Telemetry {
1413
1482
  }
1414
1483
  }, "error")
1415
1484
  };
1416
- return {
1485
+ this.logger = {
1417
1486
  ...baseLogger,
1418
1487
  ...wrapper
1419
1488
  };
1489
+ return this.logger;
1420
1490
  }
1421
1491
  /**
1422
1492
  * Extract structured error information for logging
@@ -1430,13 +1500,13 @@ var _Telemetry = class _Telemetry {
1430
1500
  * @example
1431
1501
  * ```typescript
1432
1502
  * try {
1433
- * // some operation
1503
+ * await processOrder(orderId);
1434
1504
  * } catch (error) {
1435
- * logger.error("Operation failed", Telemetry.formatError(error));
1505
+ * logger.error({ message: 'Operation failed', ...telemetry.formatError(error) });
1436
1506
  * }
1437
1507
  * ```
1438
1508
  */
1439
- static formatError(error) {
1509
+ formatError(error) {
1440
1510
  if (error instanceof Error) {
1441
1511
  return {
1442
1512
  error_name: error.name,
@@ -1446,6 +1516,77 @@ var _Telemetry = class _Telemetry {
1446
1516
  }
1447
1517
  return { error: String(error) };
1448
1518
  }
1519
+ /**
1520
+ * Get the current OpenTelemetry span for adding attributes and events
1521
+ *
1522
+ * This method provides access to the currently active span, allowing you to
1523
+ * add custom attributes and events for enhanced observability in New Relic.
1524
+ *
1525
+ * **IMPORTANT**: This must be called within an active trace context
1526
+ * (i.e., inside a function that has been wrapped by Telemetry.initialize()).
1527
+ * If called outside an active context or if ENABLE_TELEMETRY is false, it returns null.
1528
+ *
1529
+ * @returns The current span object, or null if no active span exists
1530
+ *
1531
+ * @example
1532
+ * ```typescript
1533
+ * const processOrder = async (orderId: string) => {
1534
+ * const span = telemetry.getCurrentSpan();
1535
+ * if (span) {
1536
+ * span.setAttribute('order.id', orderId);
1537
+ * span.setAttribute('order.status', 'processing');
1538
+ * span.addEvent('processing-started');
1539
+ * }
1540
+ * // ... process order logic ...
1541
+ * };
1542
+ * ```
1543
+ */
1544
+ getCurrentSpan() {
1545
+ if (this.params?.ENABLE_TELEMETRY === true) {
1546
+ const helpers = (0, import_aio_lib_telemetry3.getInstrumentationHelpers)();
1547
+ return helpers?.currentSpan || null;
1548
+ }
1549
+ return null;
1550
+ }
1551
+ /**
1552
+ * Instrument a function with OpenTelemetry for distributed tracing
1553
+ *
1554
+ * This method wraps any function with OpenTelemetry instrumentation,
1555
+ * creating a child span in the current trace context. Use this to create
1556
+ * detailed spans for specific operations within your action.
1557
+ *
1558
+ * **IMPORTANT**: The instrumented function must be called within an active trace context
1559
+ * (i.e., inside a function that has been wrapped by Telemetry.initialize()).
1560
+ * If called outside an active context, the span will not be created or exported.
1561
+ *
1562
+ * @param spanName - Name for the span (appears in New Relic as the span name)
1563
+ * @param fn - The function to instrument (sync or async)
1564
+ * @returns The instrumented function that creates a child span when called
1565
+ *
1566
+ * @example
1567
+ * ```typescript
1568
+ * const fetchUserData = telemetry.instrument(
1569
+ * 'fetch-user-data',
1570
+ * async (userId: string) => {
1571
+ * const span = telemetry.getCurrentSpan();
1572
+ * if (span) {
1573
+ * span.setAttribute('user.id', userId);
1574
+ * span.addEvent('fetching-started');
1575
+ * }
1576
+ * const response = await fetch(`/api/users/${userId}`);
1577
+ * return response.json();
1578
+ * }
1579
+ * );
1580
+ * const userData = await fetchUserData('123');
1581
+ * ```
1582
+ */
1583
+ instrument(spanName, fn) {
1584
+ return (0, import_aio_lib_telemetry3.instrument)(fn, {
1585
+ spanConfig: {
1586
+ spanName
1587
+ }
1588
+ });
1589
+ }
1449
1590
  /**
1450
1591
  * Initialize telemetry for a runtime action with provider fallback chain
1451
1592
  *
@@ -1463,60 +1604,20 @@ var _Telemetry = class _Telemetry {
1463
1604
  * is returned to alert you of the misconfiguration. This prevents silently running
1464
1605
  * without telemetry when you expect it to be working.
1465
1606
  *
1466
- * Environment Configuration:
1467
- * Pass params.ENVIRONMENT to set the environment field in all logs.
1468
- * This can be set via environment variables in your action configuration.
1469
- *
1470
1607
  * @param action - The runtime action function to instrument
1471
1608
  * @returns The instrumented action ready for export
1472
1609
  *
1473
- * @example Single provider (New Relic) - Valid configuration
1474
- * ```typescript
1475
- * // Environment:
1476
- * // ENABLE_TELEMETRY=true
1477
- * // NEW_RELIC_TELEMETRY=true
1478
- * // NEW_RELIC_SERVICE_NAME=my-service
1479
- * // NEW_RELIC_LICENSE_KEY=xxxxx
1480
- *
1481
- * export const main = Telemetry.initialize(myAction);
1482
- * // ✅ Uses New Relic telemetry
1483
- * ```
1484
- *
1485
- * @example New Relic enabled but misconfigured - Returns error response
1486
- * ```typescript
1487
- * // Environment:
1488
- * // ENABLE_TELEMETRY=true
1489
- * // NEW_RELIC_TELEMETRY=true
1490
- * // NEW_RELIC_SERVICE_NAME=my-service
1491
- * // Missing NEW_RELIC_LICENSE_KEY!
1492
- *
1493
- * export const main = Telemetry.initialize(myAction);
1494
- * // ❌ Returns { error: { statusCode: 500, body: { error: "Telemetry configuration error: NEW_RELIC_LICENSE_KEY is required" } } }
1495
- * // This is intentional - you want to know your telemetry config is broken!
1496
- * ```
1497
- *
1498
- * @example Multi-provider fallback
1499
- * ```typescript
1500
- * // Environment:
1501
- * // ENABLE_TELEMETRY=true
1502
- * // NEW_RELIC_TELEMETRY=false // Skip New Relic
1503
- * // GRAFANA_TELEMETRY=true // Use Grafana instead
1504
- *
1505
- * export const main = Telemetry.initialize(myAction);
1506
- * // ✅ Skips New Relic, tries Grafana (when implemented)
1507
- * ```
1508
- *
1509
- * @example No telemetry
1610
+ * @example
1510
1611
  * ```typescript
1511
- * // Environment:
1512
- * // ENABLE_TELEMETRY=false (or not set)
1513
- *
1514
- * export const main = Telemetry.initialize(myAction);
1515
- * // ✅ Returns original action without instrumentation
1612
+ * const telemetry = new Telemetry();
1613
+ * export const main = telemetry.initialize(myAction);
1614
+ * // Environment: ENABLE_TELEMETRY=true, NEW_RELIC_TELEMETRY=true
1615
+ * // Result: Uses New Relic telemetry with full distributed tracing
1516
1616
  * ```
1517
1617
  */
1518
- static initialize(action) {
1618
+ initialize(action) {
1519
1619
  return async (params) => {
1620
+ this.setParams(params);
1520
1621
  const newRelicTelemetry = new new_relic_default();
1521
1622
  if (newRelicTelemetry.canInitialize(params)) {
1522
1623
  try {
@@ -1591,7 +1692,7 @@ var _RuntimeAction = class _RuntimeAction {
1591
1692
  * @param requiredHeaders - Required header names (case-insensitive)
1592
1693
  * @param action - User-defined action function that receives:
1593
1694
  * - params: All request parameters including __ow_* runtime parameters
1594
- * - ctx: Context object with logger and headers
1695
+ * - ctx: Context object with logger, headers, and telemetry
1595
1696
  * @returns Wrapped action function ready to be exported as Adobe I/O Runtime entrypoint
1596
1697
  *
1597
1698
  * @example With All Options
@@ -1601,10 +1702,16 @@ var _RuntimeAction = class _RuntimeAction {
1601
1702
  * [HttpMethod.POST],
1602
1703
  * ['orderId', 'customerId'],
1603
1704
  * ['authorization', 'x-api-key'],
1604
- * async (params, { logger, headers }) => {
1705
+ * async (params, { logger, headers, telemetry }) => {
1605
1706
  * const { orderId, customerId } = params;
1606
1707
  * logger.info({ message: 'Processing order', orderId, customerId });
1607
1708
  *
1709
+ * // Access current span for custom instrumentation
1710
+ * const span = telemetry.getCurrentSpan();
1711
+ * if (span) {
1712
+ * span.setAttribute('order.id', orderId);
1713
+ * }
1714
+ *
1608
1715
  * // Your business logic here
1609
1716
  * const result = await processOrderLogic(orderId, customerId);
1610
1717
  *
@@ -1627,11 +1734,13 @@ var _RuntimeAction = class _RuntimeAction {
1627
1734
  static execute(name = "main", httpMethods = [], requiredParams = [], requiredHeaders = [], action = async (_params) => {
1628
1735
  return { statusCode: 200 /* OK */, body: {} };
1629
1736
  }) {
1737
+ const telemetry = new telemetry_default();
1630
1738
  const runtimeAction = /* @__PURE__ */ __name(async (params) => {
1631
1739
  if (!params.action_type) {
1632
1740
  params.action_type = _RuntimeAction.getActionType();
1633
1741
  }
1634
- const logger = telemetry_default.createLogger(name, params);
1742
+ telemetry.setParams(params);
1743
+ const logger = telemetry.createLogger(name);
1635
1744
  try {
1636
1745
  logger.debug({
1637
1746
  message: `${_RuntimeAction.getActionTypeName()} execution started`
@@ -1648,18 +1757,26 @@ var _RuntimeAction = class _RuntimeAction {
1648
1757
  body: params.__ow_body || {}
1649
1758
  })
1650
1759
  );
1651
- const validationError = _RuntimeAction.validateRequest(
1760
+ const validationError = _RuntimeAction.validateRequestWithInstrumentation(
1761
+ name,
1652
1762
  params,
1653
1763
  requiredParams,
1654
1764
  requiredHeaders,
1655
1765
  httpMethods,
1656
- logger,
1657
- name
1766
+ telemetry,
1767
+ logger
1658
1768
  );
1659
1769
  if (validationError) {
1660
1770
  return validationError;
1661
1771
  }
1662
- const result = await action(params, { logger, headers: params.__ow_headers || {} });
1772
+ const result = await _RuntimeAction.executeActionWithInstrumentation(
1773
+ name,
1774
+ params.__ow_headers || {},
1775
+ params,
1776
+ action,
1777
+ telemetry,
1778
+ logger
1779
+ );
1663
1780
  logger.debug(
1664
1781
  JSON.stringify({
1665
1782
  message: `${_RuntimeAction.getActionTypeName()} execution completed`,
@@ -1683,7 +1800,108 @@ var _RuntimeAction = class _RuntimeAction {
1683
1800
  return response_default.error(500 /* INTERNAL_ERROR */, "server error");
1684
1801
  }
1685
1802
  }, "runtimeAction");
1686
- return telemetry_default.initialize(runtimeAction);
1803
+ return telemetry.initialize(runtimeAction);
1804
+ }
1805
+ /**
1806
+ * Executes user's action with OpenTelemetry instrumentation
1807
+ *
1808
+ * This method wraps the action execution with distributed tracing instrumentation,
1809
+ * creating a span that tracks execution metrics and results in New Relic.
1810
+ *
1811
+ * @param name - Action name used for span naming
1812
+ * @param headers - Request headers
1813
+ * @param params - Request parameters
1814
+ * @param action - The user's action function to execute
1815
+ * @param telemetry - Telemetry instance for instrumentation
1816
+ * @param logger - Logger instance
1817
+ * @returns Promise resolving to the action result
1818
+ *
1819
+ * @private
1820
+ */
1821
+ static async executeActionWithInstrumentation(name, headers, params, action, telemetry, logger) {
1822
+ const instrumentedAction = telemetry.instrument(
1823
+ `runtime.action.${name}.execute`,
1824
+ async (actionName, actionHeaders, actionParams, actionFn, actionTelemetry, actionLogger) => {
1825
+ const span = actionTelemetry.getCurrentSpan();
1826
+ if (span) {
1827
+ span.setAttribute("action.name", actionName);
1828
+ span.setAttribute("action.type", actionParams.action_type || "unknown");
1829
+ span.setAttribute("action.has_headers", !!actionHeaders);
1830
+ span.addEvent("action-execution-started");
1831
+ }
1832
+ const result = await actionFn(actionParams, {
1833
+ logger: actionLogger,
1834
+ headers: actionHeaders,
1835
+ telemetry: actionTelemetry
1836
+ });
1837
+ if (span) {
1838
+ const statusCode = "statusCode" in result ? result.statusCode : result.error.statusCode;
1839
+ span.setAttribute("action.response_status", statusCode);
1840
+ span.setAttribute("action.has_error", "error" in result);
1841
+ span.addEvent("action-execution-completed", {
1842
+ statusCode
1843
+ });
1844
+ }
1845
+ return result;
1846
+ }
1847
+ );
1848
+ return instrumentedAction(name, headers, params, action, telemetry, logger);
1849
+ }
1850
+ /**
1851
+ * Validates incoming request with OpenTelemetry instrumentation
1852
+ *
1853
+ * This method wraps the validation logic with distributed tracing instrumentation,
1854
+ * creating a span that tracks validation metrics and results in New Relic.
1855
+ *
1856
+ * @param name - Action name used for span naming
1857
+ * @param params - Request parameters including __ow_* runtime parameters
1858
+ * @param requiredParams - List of required parameter names to validate
1859
+ * @param requiredHeaders - List of required header names to validate (case-insensitive)
1860
+ * @param httpMethods - Allowed HTTP methods. Empty array skips HTTP method validation
1861
+ * @param telemetry - Telemetry instance for instrumentation
1862
+ * @param logger - Logger instance for error logging
1863
+ * @returns RuntimeActionResponseType with error details if validation fails, null if valid
1864
+ *
1865
+ * @private
1866
+ */
1867
+ static validateRequestWithInstrumentation(name, params, requiredParams, requiredHeaders, httpMethods, telemetry, logger) {
1868
+ const instrumentedValidate = telemetry.instrument(
1869
+ `runtime.action.${name}.validate`,
1870
+ (actionName, actionParams, actionRequiredParams, actionRequiredHeaders, actionHttpMethods, actionTelemetry, actionLogger) => {
1871
+ const span = actionTelemetry.getCurrentSpan();
1872
+ if (span) {
1873
+ span.setAttribute("validation.required_params", actionRequiredParams.join(","));
1874
+ span.setAttribute("validation.required_headers", actionRequiredHeaders.join(","));
1875
+ span.setAttribute("validation.allowed_methods", actionHttpMethods.join(","));
1876
+ span.setAttribute("validation.request_method", actionParams.__ow_method || "unknown");
1877
+ }
1878
+ const validationResult = _RuntimeAction.validateRequest(
1879
+ actionName,
1880
+ actionParams,
1881
+ actionRequiredParams,
1882
+ actionRequiredHeaders,
1883
+ actionHttpMethods,
1884
+ actionLogger
1885
+ );
1886
+ if (span) {
1887
+ span.setAttribute("validation.passed", validationResult === null);
1888
+ if (validationResult) {
1889
+ const statusCode = "statusCode" in validationResult ? validationResult.statusCode : validationResult.error.statusCode;
1890
+ span.setAttribute("validation.error_code", statusCode);
1891
+ }
1892
+ }
1893
+ return validationResult;
1894
+ }
1895
+ );
1896
+ return instrumentedValidate(
1897
+ name,
1898
+ params,
1899
+ requiredParams,
1900
+ requiredHeaders,
1901
+ httpMethods,
1902
+ telemetry,
1903
+ logger
1904
+ );
1687
1905
  }
1688
1906
  /**
1689
1907
  * Validates incoming request against required parameters, headers, and HTTP methods
@@ -1693,12 +1911,12 @@ var _RuntimeAction = class _RuntimeAction {
1693
1911
  * 2. Checks for missing required headers (case-insensitive)
1694
1912
  * 3. Validates the HTTP method against allowed methods list
1695
1913
  *
1914
+ * @param _name - Action name for logging (currently unused)
1696
1915
  * @param params - Request parameters including __ow_* runtime parameters
1697
1916
  * @param requiredParams - List of required parameter names to validate
1698
1917
  * @param requiredHeaders - List of required header names to validate (case-insensitive)
1699
1918
  * @param httpMethods - Allowed HTTP methods. Empty array skips HTTP method validation
1700
1919
  * @param logger - Logger instance for error logging (child logger with request ID if present)
1701
- * @param name - Action name for logging
1702
1920
  * @returns RuntimeActionResponseType with error details if validation fails, null if valid
1703
1921
  *
1704
1922
  * @private
@@ -1718,7 +1936,7 @@ var _RuntimeAction = class _RuntimeAction {
1718
1936
  * }
1719
1937
  * ```
1720
1938
  */
1721
- static validateRequest(params, requiredParams, requiredHeaders, httpMethods, logger, _name) {
1939
+ static validateRequest(_name, params, requiredParams, requiredHeaders, httpMethods, logger) {
1722
1940
  const errorMessage = validator_default.checkMissingRequestInputs(params, requiredParams, requiredHeaders) ?? "";
1723
1941
  if (errorMessage) {
1724
1942
  logger.error({
@@ -1798,11 +2016,15 @@ var _EventConsumerAction = class _EventConsumerAction {
1798
2016
  * 'webhook-processor',
1799
2017
  * ['eventType', 'eventData'],
1800
2018
  * ['x-webhook-signature'],
1801
- * async (params, { logger, headers }) => {
2019
+ * async (params, { logger, headers, telemetry }) => {
1802
2020
  * logger.info({
1803
2021
  * message: 'Processing webhook',
1804
2022
  * eventType: params.eventType
1805
2023
  * });
2024
+ * const span = telemetry.getCurrentSpan();
2025
+ * if (span) {
2026
+ * span.setAttribute('event.type', params.eventType);
2027
+ * }
1806
2028
  * // Process event...
1807
2029
  * return { statusCode: 200, body: { processed: true } };
1808
2030
  * }
@@ -1812,9 +2034,10 @@ var _EventConsumerAction = class _EventConsumerAction {
1812
2034
  static execute(name = "main", requiredParams = [], requiredHeaders = [], action = async (_params) => {
1813
2035
  return { statusCode: 200 /* OK */, body: {} };
1814
2036
  }) {
2037
+ const telemetry = new telemetry_default();
1815
2038
  const eventConsumerAction = /* @__PURE__ */ __name(async (params) => {
1816
2039
  params.action_type = "event-consumer-action";
1817
- const logger = telemetry_default.createLogger(name, params);
2040
+ const logger = telemetry.createLogger(name);
1818
2041
  try {
1819
2042
  logger.debug({
1820
2043
  message: "Event consumer action execution started"
@@ -1827,7 +2050,13 @@ var _EventConsumerAction = class _EventConsumerAction {
1827
2050
  message: "Event consumer action parameters received",
1828
2051
  parameters: params
1829
2052
  });
1830
- const errorMessage = validator_default.checkMissingRequestInputs(params, requiredParams, requiredHeaders) || "";
2053
+ const errorMessage = _EventConsumerAction.validateWithInstrumentation(
2054
+ name,
2055
+ params,
2056
+ requiredParams,
2057
+ requiredHeaders,
2058
+ telemetry
2059
+ );
1831
2060
  if (errorMessage) {
1832
2061
  logger.error({
1833
2062
  message: "Event consumer action validation failed",
@@ -1835,7 +2064,14 @@ var _EventConsumerAction = class _EventConsumerAction {
1835
2064
  });
1836
2065
  return response_default.error(400 /* BAD_REQUEST */, errorMessage);
1837
2066
  }
1838
- const result = await action(params, { logger, headers: params.__ow_headers || {} });
2067
+ const result = await _EventConsumerAction.executeActionWithInstrumentation(
2068
+ name,
2069
+ params.__ow_headers || {},
2070
+ params,
2071
+ action,
2072
+ telemetry,
2073
+ logger
2074
+ );
1839
2075
  logger.debug({
1840
2076
  message: "Event consumer action execution completed",
1841
2077
  result
@@ -1844,16 +2080,109 @@ var _EventConsumerAction = class _EventConsumerAction {
1844
2080
  } catch (error) {
1845
2081
  if (error instanceof Error) {
1846
2082
  logger.error({
2083
+ message: "Event consumer action execution failed",
1847
2084
  error: error.message,
1848
2085
  stack: error.stack
1849
2086
  });
1850
2087
  } else {
1851
- logger.error({ error });
2088
+ logger.error({
2089
+ message: "Event consumer action execution failed",
2090
+ error
2091
+ });
1852
2092
  }
1853
2093
  return response_default.error(500 /* INTERNAL_ERROR */, "server error");
1854
2094
  }
1855
2095
  }, "eventConsumerAction");
1856
- return telemetry_default.initialize(eventConsumerAction);
2096
+ return telemetry.initialize(eventConsumerAction);
2097
+ }
2098
+ /**
2099
+ * Validates event consumer request with OpenTelemetry instrumentation
2100
+ *
2101
+ * This method wraps the validation logic with distributed tracing instrumentation,
2102
+ * creating a span that tracks validation metrics and results in New Relic.
2103
+ *
2104
+ * @param name - Action name used for span naming
2105
+ * @param params - Request parameters
2106
+ * @param requiredParams - List of required parameter names to validate
2107
+ * @param requiredHeaders - List of required header names to validate
2108
+ * @returns Error message if validation fails, empty string if successful
2109
+ *
2110
+ * @private
2111
+ */
2112
+ static validateWithInstrumentation(name, params, requiredParams, requiredHeaders, telemetry) {
2113
+ const instrumentedValidate = telemetry.instrument(
2114
+ `event.consumer.action.${name}.validate`,
2115
+ (actionName, actionParams, actionRequiredParams, actionRequiredHeaders, actionTelemetry) => {
2116
+ const span = actionTelemetry.getCurrentSpan();
2117
+ if (span) {
2118
+ span.setAttribute(
2119
+ "event.consumer.validation.required_params",
2120
+ actionRequiredParams.join(",")
2121
+ );
2122
+ span.setAttribute(
2123
+ "event.consumer.validation.required_headers",
2124
+ actionRequiredHeaders.join(",")
2125
+ );
2126
+ }
2127
+ const errorMessage = validator_default.checkMissingRequestInputs(
2128
+ actionParams,
2129
+ actionRequiredParams,
2130
+ actionRequiredHeaders
2131
+ ) || "";
2132
+ if (span) {
2133
+ span.setAttribute("event.consumer.validation.passed", errorMessage === "");
2134
+ if (errorMessage) {
2135
+ span.setAttribute("event.consumer.validation.error", errorMessage);
2136
+ }
2137
+ }
2138
+ return errorMessage;
2139
+ }
2140
+ );
2141
+ return instrumentedValidate(name, params, requiredParams, requiredHeaders, telemetry);
2142
+ }
2143
+ /**
2144
+ * Executes event consumer action with OpenTelemetry instrumentation
2145
+ *
2146
+ * This method wraps the action execution with distributed tracing instrumentation,
2147
+ * creating a span that tracks execution metrics and results in New Relic.
2148
+ *
2149
+ * @param name - Action name used for span naming
2150
+ * @param headers - Request headers
2151
+ * @param params - Request parameters
2152
+ * @param action - The user's action function to execute
2153
+ * @param telemetry - Telemetry instance for instrumentation
2154
+ * @param logger - Logger instance
2155
+ * @returns Promise resolving to the action result
2156
+ *
2157
+ * @private
2158
+ */
2159
+ static async executeActionWithInstrumentation(name, headers, params, action, telemetry, logger) {
2160
+ const instrumentedAction = telemetry.instrument(
2161
+ `event.consumer.action.${name}.execute`,
2162
+ async (actionName, actionHeaders, actionParams, actionFn, actionTelemetry, actionLogger) => {
2163
+ const span = actionTelemetry.getCurrentSpan();
2164
+ if (span) {
2165
+ span.setAttribute("event.consumer.action.name", actionName);
2166
+ span.setAttribute("event.consumer.action.has_headers", !!actionHeaders);
2167
+ span.addEvent("event-consumer-execution-started");
2168
+ }
2169
+ const result = await actionFn(actionParams, {
2170
+ logger: actionLogger,
2171
+ headers: actionHeaders,
2172
+ telemetry: actionTelemetry
2173
+ });
2174
+ if (span) {
2175
+ const statusCode = "statusCode" in result ? result.statusCode : result.error.statusCode;
2176
+ span.setAttribute("event.consumer.action.response_status", statusCode);
2177
+ span.setAttribute("event.consumer.action.has_error", "error" in result);
2178
+ span.addEvent("event-consumer-execution-completed", {
2179
+ statusCode
2180
+ });
2181
+ }
2182
+ return result;
2183
+ }
2184
+ );
2185
+ return instrumentedAction(name, headers, params, action, telemetry, logger);
1857
2186
  }
1858
2187
  };
1859
2188
  __name(_EventConsumerAction, "EventConsumerAction");
@@ -1863,6 +2192,116 @@ var event_consumer_action_default = EventConsumerAction;
1863
2192
  // src/framework/graphql-action/index.ts
1864
2193
  var import_graphql = require("graphql");
1865
2194
  var _GraphQlAction = class _GraphQlAction {
2195
+ /**
2196
+ * Creates a GraphQL action handler with schema validation and telemetry
2197
+ *
2198
+ * This method wraps a GraphQL API with standardized runtime functionality:
2199
+ * 1. Builds and validates the GraphQL schema
2200
+ * 2. Parses incoming GraphQL queries
2201
+ * 3. Validates queries against the schema
2202
+ * 4. Optionally blocks introspection queries for security
2203
+ * 5. Executes queries with provided resolvers
2204
+ * 6. Integrates with OpenTelemetry for distributed tracing
2205
+ * 7. Provides structured logging with request ID correlation
2206
+ *
2207
+ * All operations are instrumented with OpenTelemetry spans for observability in New Relic.
2208
+ *
2209
+ * @param schema - GraphQL schema definition string (SDL format)
2210
+ * @param resolvers - Function that returns resolver map with access to logger, headers, and params
2211
+ * @param name - Action name for logging and telemetry spans (default: 'main')
2212
+ * @param disableIntrospection - If true, blocks introspection queries (__schema, __type) for security (default: false)
2213
+ * @returns Wrapped action function ready to be exported as Adobe I/O Runtime entrypoint
2214
+ *
2215
+ * @example Complete GraphQL API with authentication
2216
+ * ```typescript
2217
+ * const schema = `
2218
+ * type Query {
2219
+ * me: User
2220
+ * orders: [Order]
2221
+ * }
2222
+ *
2223
+ * type User {
2224
+ * id: ID!
2225
+ * email: String!
2226
+ * name: String!
2227
+ * }
2228
+ *
2229
+ * type Order {
2230
+ * id: ID!
2231
+ * total: Float!
2232
+ * items: [OrderItem]
2233
+ * }
2234
+ *
2235
+ * type OrderItem {
2236
+ * productId: ID!
2237
+ * quantity: Int!
2238
+ * price: Float!
2239
+ * }
2240
+ * `;
2241
+ *
2242
+ * const resolvers = async ({ logger, headers, params, telemetry }) => {
2243
+ * // Access authentication from headers
2244
+ * const userId = headers['x-user-id'];
2245
+ *
2246
+ * return {
2247
+ * me: async () => {
2248
+ * logger.info({ message: 'Fetching current user', userId });
2249
+ * const span = telemetry.getCurrentSpan();
2250
+ * if (span) {
2251
+ * span.setAttribute('user.id', userId);
2252
+ * }
2253
+ * return getUserById(userId);
2254
+ * },
2255
+ * orders: async () => {
2256
+ * logger.info({ message: 'Fetching user orders', userId });
2257
+ * return getOrdersByUserId(userId);
2258
+ * }
2259
+ * };
2260
+ * };
2261
+ *
2262
+ * // Disable introspection in production for security
2263
+ * const handler = GraphQlAction.execute(
2264
+ * schema,
2265
+ * resolvers,
2266
+ * 'customer-api',
2267
+ * process.env.NODE_ENV === 'production'
2268
+ * );
2269
+ *
2270
+ * export const main = handler;
2271
+ * ```
2272
+ *
2273
+ * @example GraphQL action with variables and operation names
2274
+ * ```typescript
2275
+ * // Client sends:
2276
+ * // POST /api/graphql
2277
+ * // {
2278
+ * // "query": "query GetUser($id: ID!) { user(id: $id) { name email } }",
2279
+ * // "variables": { "id": "123" },
2280
+ * // "operationName": "GetUser"
2281
+ * // }
2282
+ *
2283
+ * const schema = `
2284
+ * type Query {
2285
+ * user(id: ID!): User
2286
+ * }
2287
+ * type User {
2288
+ * id: ID!
2289
+ * name: String!
2290
+ * email: String!
2291
+ * }
2292
+ * `;
2293
+ *
2294
+ * const resolvers = async ({ logger, headers, params, telemetry }) => ({
2295
+ * user: async ({ id }) => {
2296
+ * logger.info({ message: 'Query executed', operationName: 'GetUser', userId: id });
2297
+ * return { id, name: 'John Doe', email: 'john@example.com' };
2298
+ * }
2299
+ * });
2300
+ *
2301
+ * const handler = GraphQlAction.execute(schema, resolvers, 'graphql-api');
2302
+ * export const main = handler;
2303
+ * ```
2304
+ */
1866
2305
  static execute(schema = `
1867
2306
  type Query {
1868
2307
  hello: String
@@ -1872,15 +2311,11 @@ var _GraphQlAction = class _GraphQlAction {
1872
2311
  hello: /* @__PURE__ */ __name(() => "Hello World!", "hello")
1873
2312
  };
1874
2313
  }, name = "main", disableIntrospection = false) {
1875
- runtime_action_default.setActionType("graphql-action");
1876
- runtime_action_default.setActionTypeName("GraphQL action");
1877
2314
  const callback = /* @__PURE__ */ __name(async (params, ctx) => {
1878
- const { logger } = ctx;
1879
- let graphqlSchema;
1880
- try {
1881
- graphqlSchema = (0, import_graphql.buildSchema)(schema);
1882
- } catch (error) {
1883
- return response_default.error(400 /* BAD_REQUEST */, error.message);
2315
+ const { logger, telemetry } = ctx;
2316
+ const { schema: graphqlSchema, error: schemaError } = _GraphQlAction.buildSchemaWithInstrumentation(name, schema, telemetry);
2317
+ if (schemaError) {
2318
+ return response_default.error(400 /* BAD_REQUEST */, schemaError);
1884
2319
  }
1885
2320
  const graphqlResolvers = await resolvers({
1886
2321
  ...ctx,
@@ -1890,11 +2325,9 @@ var _GraphQlAction = class _GraphQlAction {
1890
2325
  });
1891
2326
  const context2 = {};
1892
2327
  const query = params.query;
1893
- let parsedQuery;
1894
- try {
1895
- parsedQuery = (0, import_graphql.parse)(query);
1896
- } catch (error) {
1897
- return response_default.error(400 /* BAD_REQUEST */, error.message);
2328
+ const { parsed: parsedQuery, error: parseError } = _GraphQlAction.parseQueryWithInstrumentation(name, query, telemetry);
2329
+ if (parseError) {
2330
+ return response_default.error(400 /* BAD_REQUEST */, parseError);
1898
2331
  }
1899
2332
  logger.debug(
1900
2333
  JSON.stringify({
@@ -1902,7 +2335,12 @@ var _GraphQlAction = class _GraphQlAction {
1902
2335
  query: parsedQuery
1903
2336
  })
1904
2337
  );
1905
- const validationErrors = (0, import_graphql.validate)(graphqlSchema, parsedQuery);
2338
+ const validationErrors = _GraphQlAction.validateQueryWithInstrumentation(
2339
+ name,
2340
+ graphqlSchema,
2341
+ parsedQuery,
2342
+ telemetry
2343
+ );
1906
2344
  if (validationErrors.length) {
1907
2345
  logger.error({
1908
2346
  message: "GraphQL action query validation failed",
@@ -1917,10 +2355,10 @@ var _GraphQlAction = class _GraphQlAction {
1917
2355
  logger.debug({
1918
2356
  message: "GraphQL action introspection check disabled"
1919
2357
  });
1920
- const isIntrospectionQuery = parsedQuery.definitions.some(
1921
- (definition) => definition.selectionSet.selections.some(
1922
- (selection) => selection.name.value.startsWith("__")
1923
- )
2358
+ const isIntrospectionQuery = _GraphQlAction.checkIntrospectionWithInstrumentation(
2359
+ name,
2360
+ parsedQuery,
2361
+ telemetry
1924
2362
  );
1925
2363
  if (isIntrospectionQuery) {
1926
2364
  logger.error({
@@ -1939,24 +2377,240 @@ var _GraphQlAction = class _GraphQlAction {
1939
2377
  variables
1940
2378
  });
1941
2379
  return response_default.success(
1942
- await (0, import_graphql.graphql)({
1943
- schema: graphqlSchema,
1944
- source: query,
1945
- rootValue: graphqlResolvers,
1946
- contextValue: context2,
1947
- variableValues: variables,
1948
- operationName: params.operationName
1949
- })
2380
+ await _GraphQlAction.executeGraphQLWithInstrumentation(
2381
+ name,
2382
+ {
2383
+ schema: graphqlSchema,
2384
+ source: query,
2385
+ rootValue: graphqlResolvers,
2386
+ contextValue: context2,
2387
+ variableValues: variables,
2388
+ operationName: params.operationName
2389
+ },
2390
+ telemetry
2391
+ )
1950
2392
  );
1951
2393
  }, "callback");
1952
- const graphqlAction = runtime_action_default.execute(
2394
+ runtime_action_default.setActionType("graphql-action");
2395
+ runtime_action_default.setActionTypeName("GraphQL action");
2396
+ return runtime_action_default.execute(
1953
2397
  `graphql-${name}`,
1954
2398
  ["GET" /* GET */, "POST" /* POST */],
1955
2399
  ["query"],
1956
2400
  [],
1957
2401
  callback
1958
2402
  );
1959
- return telemetry_default.initialize(graphqlAction);
2403
+ }
2404
+ /**
2405
+ * Builds GraphQL schema with OpenTelemetry instrumentation
2406
+ *
2407
+ * This method wraps the schema building logic with distributed tracing instrumentation,
2408
+ * creating a span that tracks schema metrics and results in New Relic.
2409
+ *
2410
+ * @param name - Action name used for span naming
2411
+ * @param schemaString - GraphQL schema definition string
2412
+ * @param params - Request parameters for telemetry context
2413
+ * @returns Object with built schema or error message
2414
+ *
2415
+ * @private
2416
+ */
2417
+ static buildSchemaWithInstrumentation(name, schemaString, telemetry) {
2418
+ const instrumentedBuildSchema = telemetry.instrument(
2419
+ `graphql.action.${name}.build-schema`,
2420
+ (actionName, actionSchemaString, actionTelemetry) => {
2421
+ const span = actionTelemetry.getCurrentSpan();
2422
+ if (span) {
2423
+ span.setAttribute("graphql.schema.length", actionSchemaString.length);
2424
+ span.addEvent("schema-building-started");
2425
+ }
2426
+ try {
2427
+ const builtSchema = (0, import_graphql.buildSchema)(actionSchemaString);
2428
+ if (span) {
2429
+ span.setAttribute("graphql.schema.built", true);
2430
+ span.addEvent("schema-building-completed");
2431
+ }
2432
+ return { schema: builtSchema, error: null };
2433
+ } catch (error) {
2434
+ if (span) {
2435
+ span.setAttribute("graphql.schema.built", false);
2436
+ span.setAttribute("graphql.schema.error", error.message);
2437
+ }
2438
+ return { schema: null, error: error.message };
2439
+ }
2440
+ }
2441
+ );
2442
+ return instrumentedBuildSchema(name, schemaString, telemetry);
2443
+ }
2444
+ /**
2445
+ * Parses GraphQL query with OpenTelemetry instrumentation
2446
+ *
2447
+ * This method wraps the query parsing logic with distributed tracing instrumentation,
2448
+ * creating a span that tracks parsing metrics and results in New Relic.
2449
+ *
2450
+ * @param name - Action name used for span naming
2451
+ * @param queryString - GraphQL query string to parse
2452
+ * @param params - Request parameters for telemetry context
2453
+ * @returns Object with parsed query document or error message
2454
+ *
2455
+ * @private
2456
+ */
2457
+ static parseQueryWithInstrumentation(name, queryString, telemetry) {
2458
+ const instrumentedParseQuery = telemetry.instrument(
2459
+ `graphql.action.${name}.parse-query`,
2460
+ (actionName, actionQueryString, actionTelemetry) => {
2461
+ const span = actionTelemetry.getCurrentSpan();
2462
+ if (span) {
2463
+ span.setAttribute("graphql.query.length", actionQueryString.length);
2464
+ span.addEvent("query-parsing-started");
2465
+ }
2466
+ try {
2467
+ const parsed = (0, import_graphql.parse)(actionQueryString);
2468
+ if (span) {
2469
+ span.setAttribute("graphql.query.parsed", true);
2470
+ span.setAttribute("graphql.query.definitions_count", parsed.definitions.length);
2471
+ span.addEvent("query-parsing-completed");
2472
+ }
2473
+ return { parsed, error: null };
2474
+ } catch (error) {
2475
+ if (span) {
2476
+ span.setAttribute("graphql.query.parsed", false);
2477
+ span.setAttribute("graphql.query.error", error.message);
2478
+ }
2479
+ return { parsed: null, error: error.message };
2480
+ }
2481
+ }
2482
+ );
2483
+ return instrumentedParseQuery(name, queryString, telemetry);
2484
+ }
2485
+ /**
2486
+ * Validates GraphQL query with OpenTelemetry instrumentation
2487
+ *
2488
+ * This method wraps the query validation logic with distributed tracing instrumentation,
2489
+ * creating a span that tracks validation metrics and results in New Relic.
2490
+ *
2491
+ * @param name - Action name used for span naming
2492
+ * @param schema - GraphQL schema to validate against
2493
+ * @param parsedQuery - Parsed GraphQL query document
2494
+ * @param params - Request parameters for telemetry context
2495
+ * @returns Array of validation errors (empty if valid)
2496
+ *
2497
+ * @private
2498
+ */
2499
+ static validateQueryWithInstrumentation(name, schema, parsedQuery, telemetry) {
2500
+ const instrumentedValidateQuery = telemetry.instrument(
2501
+ `graphql.action.${name}.validate-query`,
2502
+ (actionName, actionSchema, actionParsedQuery, actionTelemetry) => {
2503
+ const span = actionTelemetry.getCurrentSpan();
2504
+ if (span) {
2505
+ span.addEvent("query-validation-started");
2506
+ }
2507
+ const errors = (0, import_graphql.validate)(actionSchema, actionParsedQuery);
2508
+ if (span) {
2509
+ span.setAttribute("graphql.query.valid", errors.length === 0);
2510
+ span.setAttribute("graphql.query.validation_errors_count", errors.length);
2511
+ if (errors.length > 0) {
2512
+ span.setAttribute(
2513
+ "graphql.query.validation_errors",
2514
+ errors.map((e) => e.message).join("; ")
2515
+ );
2516
+ }
2517
+ span.addEvent("query-validation-completed", {
2518
+ valid: errors.length === 0,
2519
+ errorCount: errors.length
2520
+ });
2521
+ }
2522
+ return errors;
2523
+ }
2524
+ );
2525
+ return instrumentedValidateQuery(name, schema, parsedQuery, telemetry);
2526
+ }
2527
+ /**
2528
+ * Checks for introspection query with OpenTelemetry instrumentation
2529
+ *
2530
+ * This method wraps the introspection detection logic with distributed tracing instrumentation,
2531
+ * creating a span that tracks whether an introspection query was detected in New Relic.
2532
+ *
2533
+ * @param name - Action name used for span naming
2534
+ * @param parsedQuery - Parsed GraphQL query document
2535
+ * @param params - Request parameters for telemetry context
2536
+ * @returns True if introspection query detected, false otherwise
2537
+ *
2538
+ * @private
2539
+ */
2540
+ static checkIntrospectionWithInstrumentation(name, parsedQuery, telemetry) {
2541
+ const instrumentedIntrospectionCheck = telemetry.instrument(
2542
+ `graphql.action.${name}.introspection-check`,
2543
+ (actionName, actionParsedQuery, actionTelemetry) => {
2544
+ const span = actionTelemetry.getCurrentSpan();
2545
+ if (span) {
2546
+ span.setAttribute("graphql.introspection.disabled", true);
2547
+ span.addEvent("introspection-check-started");
2548
+ }
2549
+ const isIntrospection = actionParsedQuery.definitions.some(
2550
+ (definition) => definition.selectionSet.selections.some(
2551
+ (selection) => selection.name.value.startsWith("__")
2552
+ )
2553
+ );
2554
+ if (span) {
2555
+ span.setAttribute("graphql.introspection.detected", isIntrospection);
2556
+ span.addEvent("introspection-check-completed", {
2557
+ detected: isIntrospection
2558
+ });
2559
+ }
2560
+ return isIntrospection;
2561
+ }
2562
+ );
2563
+ return instrumentedIntrospectionCheck(name, parsedQuery, telemetry);
2564
+ }
2565
+ /**
2566
+ * Executes GraphQL query with OpenTelemetry instrumentation
2567
+ *
2568
+ * This method wraps the GraphQL execution with distributed tracing instrumentation,
2569
+ * creating a span that tracks execution metrics and results in New Relic.
2570
+ *
2571
+ * @param name - Action name used for span naming
2572
+ * @param executionParams - GraphQL execution parameters (schema, source, rootValue, etc.)
2573
+ * @param params - Request parameters for telemetry context
2574
+ * @returns Promise resolving to the GraphQL execution result
2575
+ *
2576
+ * @private
2577
+ */
2578
+ static async executeGraphQLWithInstrumentation(name, executionParams, telemetry) {
2579
+ const instrumentedGraphQLExecution = telemetry.instrument(
2580
+ `graphql.action.${name}.execute`,
2581
+ async (actionName, actionExecutionParams, actionTelemetry) => {
2582
+ const span = actionTelemetry.getCurrentSpan();
2583
+ if (span) {
2584
+ span.setAttribute(
2585
+ "graphql.execution.operation_name",
2586
+ actionExecutionParams.operationName || "default"
2587
+ );
2588
+ span.setAttribute(
2589
+ "graphql.execution.has_variables",
2590
+ !!actionExecutionParams.variableValues
2591
+ );
2592
+ span.addEvent("graphql-execution-started");
2593
+ }
2594
+ const result = await (0, import_graphql.graphql)(actionExecutionParams);
2595
+ if (span) {
2596
+ span.setAttribute("graphql.execution.has_errors", !!result.errors);
2597
+ if (result.errors) {
2598
+ span.setAttribute("graphql.execution.errors_count", result.errors.length);
2599
+ span.setAttribute(
2600
+ "graphql.execution.errors",
2601
+ result.errors.map((e) => e.message).join("; ")
2602
+ );
2603
+ }
2604
+ span.setAttribute("graphql.execution.has_data", !!result.data);
2605
+ span.addEvent("graphql-execution-completed", {
2606
+ hasErrors: !!result.errors,
2607
+ hasData: !!result.data
2608
+ });
2609
+ }
2610
+ return result;
2611
+ }
2612
+ );
2613
+ return instrumentedGraphQLExecution(name, executionParams, telemetry);
1960
2614
  }
1961
2615
  };
1962
2616
  __name(_GraphQlAction, "GraphQlAction");
@@ -2021,9 +2675,15 @@ var _OpenwhiskAction = class _OpenwhiskAction {
2021
2675
  * ```typescript
2022
2676
  * const handler = OpenwhiskAction.execute(
2023
2677
  * 'processWebhook',
2024
- * async (params, { logger, headers }) => {
2678
+ * async (params, { logger, headers, telemetry }) => {
2025
2679
  * logger.info({ message: 'Webhook received', event: params.event });
2026
2680
  *
2681
+ * // Access current span for custom instrumentation
2682
+ * const span = telemetry.getCurrentSpan();
2683
+ * if (span) {
2684
+ * span.setAttribute('webhook.event', params.event);
2685
+ * }
2686
+ *
2027
2687
  * // Your webhook processing logic here
2028
2688
  * const result = await processWebhookData(params);
2029
2689
  *
@@ -2038,9 +2698,10 @@ var _OpenwhiskAction = class _OpenwhiskAction {
2038
2698
  static execute(name = "main", action = async (_params) => {
2039
2699
  return { statusCode: 200 /* OK */, body: {} };
2040
2700
  }) {
2701
+ const telemetry = new telemetry_default();
2041
2702
  const openwhiskAction = /* @__PURE__ */ __name(async (params) => {
2042
2703
  params.action_type = "openwhisk-action";
2043
- const logger = telemetry_default.createLogger(name, params);
2704
+ const logger = telemetry.createLogger(name);
2044
2705
  try {
2045
2706
  logger.debug({
2046
2707
  message: "OpenWhisk action execution started"
@@ -2049,7 +2710,14 @@ var _OpenwhiskAction = class _OpenwhiskAction {
2049
2710
  message: "OpenWhisk action parameters received",
2050
2711
  params
2051
2712
  });
2052
- const result = await action(params, { logger, headers: params.__ow_headers || {} });
2713
+ const result = await _OpenwhiskAction.executeActionWithInstrumentation(
2714
+ name,
2715
+ params.__ow_headers || {},
2716
+ params,
2717
+ action,
2718
+ telemetry,
2719
+ logger
2720
+ );
2053
2721
  logger.debug({
2054
2722
  message: "OpenWhisk action execution completed",
2055
2723
  result
@@ -2071,7 +2739,51 @@ var _OpenwhiskAction = class _OpenwhiskAction {
2071
2739
  return response_default.error(500 /* INTERNAL_ERROR */, "server error");
2072
2740
  }
2073
2741
  }, "openwhiskAction");
2074
- return telemetry_default.initialize(openwhiskAction);
2742
+ return telemetry.initialize(openwhiskAction);
2743
+ }
2744
+ /**
2745
+ * Executes OpenWhisk action with OpenTelemetry instrumentation
2746
+ *
2747
+ * This method wraps the action execution with distributed tracing instrumentation,
2748
+ * creating a span that tracks execution metrics and results in New Relic.
2749
+ *
2750
+ * @param name - Action name used for span naming
2751
+ * @param headers - Request headers
2752
+ * @param params - Request parameters
2753
+ * @param action - The user's action function to execute
2754
+ * @param telemetry - Telemetry instance for instrumentation
2755
+ * @param logger - Logger instance
2756
+ * @returns Promise resolving to the action result
2757
+ *
2758
+ * @private
2759
+ */
2760
+ static async executeActionWithInstrumentation(name, headers, params, action, telemetry, logger) {
2761
+ const instrumentedAction = telemetry.instrument(
2762
+ `openwhisk.action.${name}.execute`,
2763
+ async (actionName, actionHeaders, actionParams, actionFn, actionTelemetry, actionLogger) => {
2764
+ const span = actionTelemetry.getCurrentSpan();
2765
+ if (span) {
2766
+ span.setAttribute("openwhisk.action.name", actionName);
2767
+ span.setAttribute("openwhisk.action.has_headers", !!actionHeaders);
2768
+ span.addEvent("openwhisk-execution-started");
2769
+ }
2770
+ const result = await actionFn(actionParams, {
2771
+ logger: actionLogger,
2772
+ headers: actionHeaders,
2773
+ telemetry: actionTelemetry
2774
+ });
2775
+ if (span) {
2776
+ const statusCode = "statusCode" in result ? result.statusCode : result.error.statusCode;
2777
+ span.setAttribute("openwhisk.action.response_status", statusCode);
2778
+ span.setAttribute("openwhisk.action.has_error", "error" in result);
2779
+ span.addEvent("openwhisk-execution-completed", {
2780
+ statusCode
2781
+ });
2782
+ }
2783
+ return result;
2784
+ }
2785
+ );
2786
+ return instrumentedAction(name, headers, params, action, telemetry, logger);
2075
2787
  }
2076
2788
  };
2077
2789
  __name(_OpenwhiskAction, "OpenwhiskAction");
@@ -2595,41 +3307,17 @@ var _WebhookAction = class _WebhookAction {
2595
3307
  */
2596
3308
  static execute(name = "webhook", requiredParams = [], requiredHeaders = [], signatureVerification = "disabled" /* DISABLED */, action = async () => response_default2.success()) {
2597
3309
  const httpMethods = ["POST" /* POST */];
2598
- const verifySignature = /* @__PURE__ */ __name(async (params) => {
2599
- const signature = params.__ow_headers["x-adobe-commerce-webhook-signature"] || "";
2600
- if (!signature) {
2601
- return "Header `x-adobe-commerce-webhook-signature` not found. Make sure Webhooks signature is enabled in the Commerce instance.";
2602
- }
2603
- const body = params.__ow_body;
2604
- if (!body) {
2605
- return "Request body not found. Make sure the action is configured with `raw-http: true`.";
2606
- }
2607
- let publicKey = params.PUBLIC_KEY;
2608
- if (!publicKey && params.PUBLIC_KEY_BASE64) {
2609
- publicKey = atob(params.PUBLIC_KEY_BASE64);
2610
- }
2611
- if (!publicKey) {
2612
- return "Public key not found. Make sure the action is configured with the input `PUBLIC_KEY` or `PUBLIC_KEY_BASE64` and it is defined in .env file.";
2613
- }
2614
- try {
2615
- const verifier = import_crypto.default.createVerify("SHA256");
2616
- verifier.update(body);
2617
- const isSignatureValid = verifier.verify(publicKey, signature, "base64");
2618
- if (!isSignatureValid) {
2619
- return "The signature is invalid.";
2620
- }
2621
- } catch (error) {
2622
- return "The signature is invalid.";
2623
- }
2624
- return null;
2625
- }, "verifySignature");
2626
3310
  const callback = /* @__PURE__ */ __name(async (params, ctx) => {
2627
- const { logger } = ctx;
3311
+ const { logger, telemetry } = ctx;
2628
3312
  if (signatureVerification === "enabled" /* ENABLED */) {
2629
- const verificationErrorMessage = await verifySignature(params);
3313
+ const verificationErrorMessage = await _WebhookAction.verifySignatureWithInstrumentation(
3314
+ name,
3315
+ params,
3316
+ telemetry
3317
+ );
2630
3318
  if (verificationErrorMessage) {
2631
3319
  logger.error({
2632
- message: "Webhook actionsignature verification failed",
3320
+ message: "Webhook action signature verification failed",
2633
3321
  error: verificationErrorMessage
2634
3322
  });
2635
3323
  const verificationErrorResponse = response_default2.exception(verificationErrorMessage);
@@ -2640,7 +3328,13 @@ var _WebhookAction = class _WebhookAction {
2640
3328
  ...JSON.parse(atob(params.__ow_body))
2641
3329
  };
2642
3330
  }
2643
- const errorMessage = validator_default.checkMissingRequestInputs(params, requiredParams, requiredHeaders) ?? "";
3331
+ const errorMessage = _WebhookAction.validateWithInstrumentation(
3332
+ name,
3333
+ params,
3334
+ requiredParams,
3335
+ requiredHeaders,
3336
+ telemetry
3337
+ );
2644
3338
  if (errorMessage) {
2645
3339
  logger.error({
2646
3340
  message: "Webhook action validation failed",
@@ -2649,13 +3343,183 @@ var _WebhookAction = class _WebhookAction {
2649
3343
  const errorMessageResponse = response_default2.exception(errorMessage);
2650
3344
  return response_default.success(JSON.stringify(errorMessageResponse));
2651
3345
  }
2652
- const response = await action(params, ctx);
3346
+ const response = await _WebhookAction.executeActionWithInstrumentation(
3347
+ name,
3348
+ action,
3349
+ params,
3350
+ ctx
3351
+ );
2653
3352
  return response_default.success(JSON.stringify(response));
2654
3353
  }, "callback");
2655
3354
  runtime_action_default.setActionType("webhook-action");
2656
3355
  runtime_action_default.setActionTypeName("Webhook action");
2657
3356
  return runtime_action_default.execute(name, httpMethods, [], [], callback);
2658
3357
  }
3358
+ /**
3359
+ * Executes webhook action with OpenTelemetry instrumentation
3360
+ *
3361
+ * This method wraps the webhook action execution with distributed tracing instrumentation,
3362
+ * creating a span that tracks execution metrics and results in New Relic.
3363
+ *
3364
+ * @param name - Webhook action name used for span naming
3365
+ * @param action - The webhook action function to execute
3366
+ * @param params - Request parameters
3367
+ * @param ctx - Context object with logger, headers, and telemetry
3368
+ * @returns Promise resolving to the webhook action response(s)
3369
+ *
3370
+ * @private
3371
+ */
3372
+ static async executeActionWithInstrumentation(name, action, params, ctx) {
3373
+ const instrumentedWebhookAction = ctx.telemetry.instrument(
3374
+ `webhook.action.${name}.execute`,
3375
+ async (actionName, actionFn, actionParams, context2) => {
3376
+ const span = context2.telemetry.getCurrentSpan();
3377
+ if (span) {
3378
+ span.setAttribute("webhook.action.name", actionName);
3379
+ span.setAttribute("webhook.action.has_headers", !!context2.headers);
3380
+ span.addEvent("webhook-execution-started");
3381
+ }
3382
+ const response = await actionFn(actionParams, context2);
3383
+ if (span) {
3384
+ span.setAttribute("webhook.action.response_is_array", Array.isArray(response));
3385
+ if (Array.isArray(response)) {
3386
+ span.setAttribute("webhook.action.response_count", response.length);
3387
+ }
3388
+ span.addEvent("webhook-execution-completed", {
3389
+ isArray: Array.isArray(response),
3390
+ count: Array.isArray(response) ? response.length : 1
3391
+ });
3392
+ }
3393
+ return response;
3394
+ }
3395
+ );
3396
+ return instrumentedWebhookAction(name, action, params, ctx);
3397
+ }
3398
+ /**
3399
+ * Verifies webhook signature with OpenTelemetry instrumentation
3400
+ *
3401
+ * This method wraps the signature verification logic with distributed tracing instrumentation,
3402
+ * creating a span that tracks verification metrics and results in New Relic.
3403
+ *
3404
+ * @param name - Webhook action name used for span naming
3405
+ * @param params - Request parameters including headers, body, and public key
3406
+ * @param telemetry - Telemetry instance for instrumentation
3407
+ * @returns Error message if verification fails, null if successful
3408
+ *
3409
+ * @private
3410
+ */
3411
+ static async verifySignatureWithInstrumentation(name, params, telemetry) {
3412
+ const instrumentedVerifySignature = telemetry.instrument(
3413
+ `webhook.action.${name}.verify-signature`,
3414
+ async (actionName, actionParams, actionTelemetry) => {
3415
+ const span = actionTelemetry.getCurrentSpan();
3416
+ if (span) {
3417
+ span.setAttribute("webhook.signature.enabled", true);
3418
+ span.setAttribute(
3419
+ "webhook.signature.header_present",
3420
+ !!actionParams.__ow_headers?.["x-adobe-commerce-webhook-signature"]
3421
+ );
3422
+ span.setAttribute("webhook.signature.has_body", !!actionParams.__ow_body);
3423
+ span.setAttribute(
3424
+ "webhook.signature.has_public_key",
3425
+ !!(actionParams.PUBLIC_KEY || actionParams.PUBLIC_KEY_BASE64)
3426
+ );
3427
+ span.addEvent("signature-verification-started");
3428
+ }
3429
+ const verificationError = await _WebhookAction.verifySignature(actionParams);
3430
+ if (span) {
3431
+ span.setAttribute("webhook.signature.valid", verificationError === null);
3432
+ if (verificationError) {
3433
+ span.setAttribute("webhook.signature.error", verificationError);
3434
+ }
3435
+ span.addEvent("signature-verification-completed", {
3436
+ valid: verificationError === null
3437
+ });
3438
+ }
3439
+ return verificationError;
3440
+ }
3441
+ );
3442
+ return instrumentedVerifySignature(name, params, telemetry);
3443
+ }
3444
+ /**
3445
+ * Validates webhook parameters and headers with OpenTelemetry instrumentation
3446
+ *
3447
+ * This method wraps the validation logic with distributed tracing instrumentation,
3448
+ * creating a span that tracks validation metrics and results in New Relic.
3449
+ *
3450
+ * @param name - Webhook action name used for span naming
3451
+ * @param params - Request parameters
3452
+ * @param requiredParams - List of required parameter names to validate
3453
+ * @param requiredHeaders - List of required header names to validate
3454
+ * @param telemetry - Telemetry instance for instrumentation
3455
+ * @returns Error message if validation fails, empty string if successful
3456
+ *
3457
+ * @private
3458
+ */
3459
+ static validateWithInstrumentation(name, params, requiredParams, requiredHeaders, telemetry) {
3460
+ const instrumentedValidate = telemetry.instrument(
3461
+ `webhook.action.${name}.validate`,
3462
+ (actionName, actionParams, actionRequiredParams, actionRequiredHeaders, actionTelemetry) => {
3463
+ const span = actionTelemetry.getCurrentSpan();
3464
+ if (span) {
3465
+ span.setAttribute("webhook.validation.required_params", actionRequiredParams.join(","));
3466
+ span.setAttribute("webhook.validation.required_headers", actionRequiredHeaders.join(","));
3467
+ }
3468
+ const errorMessage = validator_default.checkMissingRequestInputs(
3469
+ actionParams,
3470
+ actionRequiredParams,
3471
+ actionRequiredHeaders
3472
+ ) ?? "";
3473
+ if (span) {
3474
+ span.setAttribute("webhook.validation.passed", errorMessage === "");
3475
+ if (errorMessage) {
3476
+ span.setAttribute("webhook.validation.error", errorMessage);
3477
+ }
3478
+ }
3479
+ return errorMessage;
3480
+ }
3481
+ );
3482
+ return instrumentedValidate(name, params, requiredParams, requiredHeaders, telemetry);
3483
+ }
3484
+ /**
3485
+ * Verify webhook signature using a public key and SHA256
3486
+ *
3487
+ * This method validates the authenticity of webhook requests by verifying
3488
+ * the signature against the request body using the provided public key.
3489
+ *
3490
+ * @param params - Request parameters including headers, body, and public key
3491
+ * @returns Error message if verification fails, null if successful
3492
+ *
3493
+ * @private
3494
+ */
3495
+ static async verifySignature(params) {
3496
+ const signature = params.__ow_headers["x-adobe-commerce-webhook-signature"] || "";
3497
+ if (!signature) {
3498
+ return "Header `x-adobe-commerce-webhook-signature` not found. Make sure Webhooks signature is enabled in the Commerce instance.";
3499
+ }
3500
+ const body = params.__ow_body;
3501
+ if (!body) {
3502
+ return "Request body not found. Make sure the action is configured with `raw-http: true`.";
3503
+ }
3504
+ let publicKey = params.PUBLIC_KEY;
3505
+ if (!publicKey && params.PUBLIC_KEY_BASE64) {
3506
+ publicKey = atob(params.PUBLIC_KEY_BASE64);
3507
+ }
3508
+ if (!publicKey) {
3509
+ return "Public key not found. Make sure the action is configured with the input `PUBLIC_KEY` or `PUBLIC_KEY_BASE64` and it is defined in .env file.";
3510
+ }
3511
+ try {
3512
+ const verifier = import_crypto.default.createVerify("SHA256");
3513
+ verifier.update(body);
3514
+ const isSignatureValid = verifier.verify(publicKey, signature, "base64");
3515
+ if (!isSignatureValid) {
3516
+ return "The signature is invalid.";
3517
+ }
3518
+ } catch (error) {
3519
+ return "The signature is invalid.";
3520
+ }
3521
+ return null;
3522
+ }
2659
3523
  };
2660
3524
  __name(_WebhookAction, "WebhookAction");
2661
3525
  var WebhookAction = _WebhookAction;