@adobe-commerce/aio-toolkit 1.2.5 → 1.2.7

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.
Files changed (31) hide show
  1. package/CHANGELOG.md +250 -0
  2. package/README.md +450 -1
  3. package/dist/aio-toolkit-cli-workflow/bin/cli.js +2048 -0
  4. package/dist/aio-toolkit-cli-workflow/bin/cli.js.map +1 -0
  5. package/dist/aio-toolkit-cursor-context/bin/cli.js +16 -0
  6. package/dist/aio-toolkit-cursor-context/bin/cli.js.map +1 -1
  7. package/dist/index.d.mts +61 -6
  8. package/dist/index.d.ts +61 -6
  9. package/dist/index.js +600 -42
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +610 -38
  12. package/dist/index.mjs.map +1 -1
  13. package/files/cursor-context/commands/aio-toolkit-analyze-adobe-commerce-module.md +612 -0
  14. package/files/cursor-context/commands/aio-toolkit-create-amazon-sqs-consumer.md +445 -0
  15. package/files/cursor-context/commands/aio-toolkit-create-event-consumer-action.md +6 -0
  16. package/files/cursor-context/commands/aio-toolkit-create-graphql-action.md +21 -7
  17. package/files/cursor-context/commands/aio-toolkit-create-openwhisk-action.md +326 -0
  18. package/files/cursor-context/commands/aio-toolkit-create-runtime-action.md +15 -5
  19. package/files/cursor-context/commands/aio-toolkit-create-shipping-carrier.md +681 -0
  20. package/files/cursor-context/commands/aio-toolkit-create-webhook-action.md +22 -9
  21. package/files/cursor-context/rules/aio-toolkit-create-adobe-commerce-client.mdc +252 -116
  22. package/files/cursor-context/rules/aio-toolkit-oop-best-practices.mdc +10 -4
  23. package/files/cursor-context/rules/aio-toolkit-setup-new-relic-telemetry.mdc +167 -2
  24. package/files/cursor-context/rules/aio-toolkit-use-abdb-collection.mdc +610 -0
  25. package/files/cursor-context/rules/aio-toolkit-use-abdb-repository.mdc +705 -0
  26. package/files/cursor-context/rules/aio-toolkit-use-adobe-auth.mdc +442 -0
  27. package/files/cursor-context/rules/aio-toolkit-use-amazon-sqs-publish.mdc +397 -0
  28. package/files/cursor-context/rules/aio-toolkit-use-file-repository.mdc +502 -0
  29. package/files/cursor-context/rules/aio-toolkit-use-publish-event.mdc +510 -0
  30. package/files/cursor-context/rules/aio-toolkit-use-runtime-api-gateway-service.mdc +542 -0
  31. package/package.json +4 -2
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ __export(index_exports, {
38
38
  AdminUiSdk: () => AdminUiSdk,
39
39
  AdobeAuth: () => adobe_auth_default,
40
40
  AdobeCommerceClient: () => adobe_commerce_client_default,
41
+ AmazonSQSClient: () => amazon_sqs_client_default,
41
42
  BasicAuthConnection: () => basic_auth_connection_default,
42
43
  BearerToken: () => bearer_token_default,
43
44
  CreateEvents: () => create_events_default,
@@ -55,6 +56,7 @@ __export(index_exports, {
55
56
  InfiniteLoopBreaker: () => infinite_loop_breaker_default,
56
57
  IoEventsGlobals: () => IoEventsGlobals,
57
58
  JsonMessageProcessor: () => JsonMessageProcessor,
59
+ LogSanitizer: () => LogSanitizer,
58
60
  Oauth1aConnection: () => oauth1a_connection_default,
59
61
  OnboardCommerce: () => onboard_commerce_default,
60
62
  OnboardEvents: () => onboard_events_default,
@@ -213,7 +215,7 @@ var validator_default = Validator;
213
215
 
214
216
  // src/framework/telemetry/index.ts
215
217
  var import_aio_sdk = require("@adobe/aio-sdk");
216
- var import_aio_lib_telemetry3 = require("@adobe/aio-lib-telemetry");
218
+ var import_aio_lib_telemetry4 = require("@adobe/aio-lib-telemetry");
217
219
 
218
220
  // src/framework/telemetry/new-relic/index.ts
219
221
  var import_aio_lib_telemetry2 = require("@adobe/aio-lib-telemetry");
@@ -767,6 +769,334 @@ __name(_NewRelicTelemetry, "NewRelicTelemetry");
767
769
  var NewRelicTelemetry = _NewRelicTelemetry;
768
770
  var new_relic_default = NewRelicTelemetry;
769
771
 
772
+ // src/framework/telemetry/grafana/index.ts
773
+ var import_aio_lib_telemetry3 = require("@adobe/aio-lib-telemetry");
774
+
775
+ // src/framework/telemetry/grafana/validator/index.ts
776
+ var _GrafanaTelemetryValidator = class _GrafanaTelemetryValidator {
777
+ /**
778
+ * Checks if Grafana telemetry is configured
779
+ *
780
+ * Returns true when:
781
+ * - ENABLE_TELEMETRY is explicitly set to true
782
+ * - GRAFANA_TELEMETRY is explicitly set to true
783
+ *
784
+ * This method does NOT validate configuration completeness —
785
+ * it only checks whether the provider should be active.
786
+ *
787
+ * @param params - Runtime parameters to check
788
+ * @returns true if Grafana telemetry is enabled, false otherwise
789
+ *
790
+ * @example
791
+ * ```typescript
792
+ * const validator = new GrafanaTelemetryValidator();
793
+ * const params = { ENABLE_TELEMETRY: true, GRAFANA_TELEMETRY: true };
794
+ * if (validator.isConfigured(params)) {
795
+ * // Grafana telemetry is enabled, proceed with initialization
796
+ * }
797
+ * ```
798
+ */
799
+ isConfigured(params) {
800
+ return params.ENABLE_TELEMETRY === true && params.GRAFANA_TELEMETRY === true;
801
+ }
802
+ /**
803
+ * Validates Grafana-specific parameters
804
+ *
805
+ * IMPORTANT: Only call this method after isConfigured() returns true.
806
+ *
807
+ * Validation rules by mode:
808
+ * - GRAFANA_DEV=true → all parameters optional; localhost defaults are used
809
+ * - GRAFANA_CLOUD=true → GRAFANA_ENDPOINT, GRAFANA_SERVICE_NAME, GRAFANA_INSTANCE_ID,
810
+ * and GRAFANA_API_KEY are all required
811
+ * - Default (tunnel) → GRAFANA_ENDPOINT and GRAFANA_SERVICE_NAME are required;
812
+ * no credentials needed
813
+ *
814
+ * @param params - Runtime parameters to validate
815
+ * @throws {TelemetryInputError} If required parameters are missing for the selected mode
816
+ *
817
+ * @example Dev mode — all optional
818
+ * ```typescript
819
+ * validator.validateConfiguration({ GRAFANA_DEV: true }); // passes
820
+ * ```
821
+ *
822
+ * @example Grafana Cloud — credentials required
823
+ * ```typescript
824
+ * validator.validateConfiguration({
825
+ * GRAFANA_CLOUD: true,
826
+ * GRAFANA_ENDPOINT: 'https://otlp-gateway-prod-us-east-0.grafana.net/otlp',
827
+ * GRAFANA_SERVICE_NAME: 'my-app',
828
+ * GRAFANA_INSTANCE_ID: '123456',
829
+ * GRAFANA_API_KEY: 'glc_xxx',
830
+ * }); // passes
831
+ * ```
832
+ *
833
+ * @example Tunnel (deployed) mode — endpoint + service name required
834
+ * ```typescript
835
+ * validator.validateConfiguration({
836
+ * GRAFANA_ENDPOINT: 'https://abc123.trycloudflare.com',
837
+ * GRAFANA_SERVICE_NAME: 'my-app',
838
+ * }); // passes
839
+ * ```
840
+ */
841
+ validateConfiguration(params) {
842
+ if (params.GRAFANA_DEV === true) {
843
+ return;
844
+ }
845
+ if (!params.GRAFANA_ENDPOINT) {
846
+ throw new TelemetryInputError(
847
+ "GRAFANA_ENDPOINT is required when GRAFANA_DEV is not true. Provide a reachable OTLP/HTTP collector URL."
848
+ );
849
+ }
850
+ if (!params.GRAFANA_SERVICE_NAME) {
851
+ throw new TelemetryInputError(
852
+ "GRAFANA_SERVICE_NAME is required when GRAFANA_DEV is not true."
853
+ );
854
+ }
855
+ if (params.GRAFANA_CLOUD === true) {
856
+ if (!params.GRAFANA_INSTANCE_ID) {
857
+ throw new TelemetryInputError(
858
+ "GRAFANA_INSTANCE_ID is required when GRAFANA_CLOUD is true."
859
+ );
860
+ }
861
+ if (!params.GRAFANA_API_KEY) {
862
+ throw new TelemetryInputError("GRAFANA_API_KEY is required when GRAFANA_CLOUD is true.");
863
+ }
864
+ }
865
+ }
866
+ };
867
+ __name(_GrafanaTelemetryValidator, "GrafanaTelemetryValidator");
868
+ var GrafanaTelemetryValidator = _GrafanaTelemetryValidator;
869
+
870
+ // src/framework/telemetry/grafana/index.ts
871
+ var import_otel2 = require("@adobe/aio-lib-telemetry/otel");
872
+ var DEFAULT_DEV_URL = "http://localhost:4318";
873
+ var DEFAULT_SERVICE_NAME = "app-builder-app";
874
+ var _GrafanaTelemetry = class _GrafanaTelemetry {
875
+ constructor() {
876
+ this.validator = new GrafanaTelemetryValidator();
877
+ this.successChecker = new SuccessChecker();
878
+ }
879
+ /**
880
+ * Checks if Grafana telemetry can be initialized
881
+ *
882
+ * Returns true when ENABLE_TELEMETRY=true and GRAFANA_TELEMETRY=true.
883
+ *
884
+ * @param params - Runtime parameters to check
885
+ * @returns true if Grafana telemetry is enabled and can be initialized
886
+ *
887
+ * @example
888
+ * ```typescript
889
+ * const telemetry = new GrafanaTelemetry();
890
+ * if (telemetry.canInitialize(params)) {
891
+ * // Grafana is configured, use it
892
+ * } else {
893
+ * // Skip to next provider
894
+ * }
895
+ * ```
896
+ */
897
+ canInitialize(params) {
898
+ return this.validator.isConfigured(params);
899
+ }
900
+ /**
901
+ * Get the OpenTelemetry instrumentation configuration for Grafana
902
+ *
903
+ * Builds and returns the complete instrumentation configuration:
904
+ * - Service name (from GRAFANA_SERVICE_NAME or default)
905
+ * - Preset simple instrumentations
906
+ * - Adobe I/O Runtime resource attributes
907
+ * - OTLP/HTTP exporters (localhost in dev, GRAFANA_ENDPOINT in deployed)
908
+ * - Success/failure detection for actions
909
+ *
910
+ * @returns Complete entrypoint instrumentation configuration
911
+ * @throws {TelemetryInputError} If GRAFANA_ENDPOINT or GRAFANA_SERVICE_NAME is missing in non-dev mode
912
+ */
913
+ getConfig() {
914
+ return {
915
+ ...(0, import_aio_lib_telemetry3.defineTelemetryConfig)((params) => {
916
+ this.validator.validateConfiguration(params);
917
+ const serviceName = params.GRAFANA_DEV === true ? params.GRAFANA_SERVICE_NAME || DEFAULT_SERVICE_NAME : params.GRAFANA_SERVICE_NAME;
918
+ return {
919
+ sdkConfig: {
920
+ serviceName,
921
+ instrumentations: (0, import_aio_lib_telemetry3.getPresetInstrumentations)("simple"),
922
+ resource: (0, import_aio_lib_telemetry3.getAioRuntimeResource)(),
923
+ ...this.buildExportersConfig(params)
924
+ }
925
+ };
926
+ }),
927
+ isSuccessful: this.successChecker.execute.bind(this.successChecker)
928
+ };
929
+ }
930
+ /**
931
+ * Build OTLP/HTTP exporters for traces, metrics, and logs
932
+ *
933
+ * Selects the collector base URL and optional auth header based on mode:
934
+ * - GRAFANA_DEV=true → http://localhost:4318, no auth
935
+ * - GRAFANA_CLOUD=true → GRAFANA_ENDPOINT with Basic auth (instance ID + API key)
936
+ * - Default (tunnel) → GRAFANA_ENDPOINT, no auth
937
+ *
938
+ * All three signal types share the same factory that appends the signal-specific
939
+ * path (/v1/traces, /v1/metrics, /v1/logs) to the base URL.
940
+ *
941
+ * @param params - Runtime parameters
942
+ * @returns Configuration object with traceExporter, metricReaders, logRecordProcessors
943
+ * @private
944
+ */
945
+ buildExportersConfig(params) {
946
+ const exportUrl = (params.GRAFANA_DEV === true ? DEFAULT_DEV_URL : params.GRAFANA_ENDPOINT).replace(/\/$/, "");
947
+ const authHeader = params.GRAFANA_CLOUD === true ? {
948
+ Authorization: `Basic ${Buffer.from(
949
+ `${params.GRAFANA_INSTANCE_ID}:${params.GRAFANA_API_KEY}`
950
+ ).toString("base64")}`
951
+ } : void 0;
952
+ const makeExporterConfig = /* @__PURE__ */ __name((path) => authHeader ? { url: `${exportUrl}/${path}`, headers: authHeader } : { url: `${exportUrl}/${path}` }, "makeExporterConfig");
953
+ return {
954
+ traceExporter: new import_otel2.OTLPTraceExporterProto(makeExporterConfig("v1/traces")),
955
+ metricReaders: [
956
+ new import_otel2.PeriodicExportingMetricReader({
957
+ exporter: new import_otel2.OTLPMetricExporterProto(makeExporterConfig("v1/metrics"))
958
+ })
959
+ ],
960
+ logRecordProcessors: [
961
+ new import_otel2.SimpleLogRecordProcessor(new import_otel2.OTLPLogExporterProto(makeExporterConfig("v1/logs")))
962
+ ]
963
+ };
964
+ }
965
+ /**
966
+ * Initialize telemetry instrumentation for a runtime action
967
+ *
968
+ * Wraps the provided action with OpenTelemetry instrumentation using
969
+ * the Grafana LGTM stack as the backend.
970
+ *
971
+ * @param action - The runtime action function to instrument
972
+ * @returns The instrumented action function with Grafana telemetry enabled
973
+ * @throws {TelemetryInputError} If required configuration is missing
974
+ *
975
+ * @example
976
+ * ```typescript
977
+ * async function myAction(params: Record<string, unknown>) {
978
+ * return { statusCode: 200, body: { success: true } };
979
+ * }
980
+ *
981
+ * const telemetry = new GrafanaTelemetry();
982
+ * export const main = telemetry.initialize(myAction);
983
+ * ```
984
+ */
985
+ initialize(action) {
986
+ return (0, import_aio_lib_telemetry3.instrumentEntrypoint)(action, this.getConfig());
987
+ }
988
+ };
989
+ __name(_GrafanaTelemetry, "GrafanaTelemetry");
990
+ var GrafanaTelemetry = _GrafanaTelemetry;
991
+ var grafana_default = GrafanaTelemetry;
992
+
993
+ // src/framework/telemetry/helpers/log-sanitizer/types.ts
994
+ var EXACT_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
995
+ "authorization",
996
+ "x-api-key",
997
+ "cookie",
998
+ "set-cookie"
999
+ ]);
1000
+ var SENSITIVE_KEY_SUFFIXES = [
1001
+ "_api_key",
1002
+ "_secret",
1003
+ "_token",
1004
+ "_password",
1005
+ "_key",
1006
+ "-token",
1007
+ "-secret",
1008
+ "-key"
1009
+ ];
1010
+
1011
+ // src/framework/telemetry/helpers/log-sanitizer/index.ts
1012
+ var _LogSanitizer = class _LogSanitizer {
1013
+ constructor(additionalSensitiveKeys = /* @__PURE__ */ new Set(), maxDepth = 10) {
1014
+ this.additionalSensitiveKeys = additionalSensitiveKeys;
1015
+ this.maxDepth = maxDepth;
1016
+ }
1017
+ /**
1018
+ * Parses the `SENSITIVE_KEYS` runtime param into a `ReadonlySet<string>`.
1019
+ *
1020
+ * Accepts three forms:
1021
+ * - **Array** — each string element becomes a key (`["my_key", "x-secret"]`)
1022
+ * - **Comma-separated string** — split on `,`, each trimmed segment becomes a key (`"my_key, x-secret"`)
1023
+ * - **Single string** — treated as one key (`"my_key"`)
1024
+ *
1025
+ * Non-string array elements and non-string / non-array values are silently ignored.
1026
+ * All keys are lowercased for case-insensitive matching.
1027
+ *
1028
+ * @param raw - The raw `params.SENSITIVE_KEYS` value
1029
+ * @returns A set of lowercased key strings safe to pass to the constructor
1030
+ */
1031
+ static parseSensitiveKeys(raw) {
1032
+ if (Array.isArray(raw)) {
1033
+ return new Set(
1034
+ raw.filter((v) => typeof v === "string").map((v) => v.trim().toLowerCase()).filter(Boolean)
1035
+ );
1036
+ }
1037
+ if (typeof raw === "string") {
1038
+ return new Set(
1039
+ raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean)
1040
+ );
1041
+ }
1042
+ return /* @__PURE__ */ new Set();
1043
+ }
1044
+ /**
1045
+ * Sanitizes `data` and returns a deep copy with sensitive values replaced.
1046
+ *
1047
+ * @param data - The value to sanitize
1048
+ * @returns A sanitized deep copy of `data`
1049
+ */
1050
+ execute(data) {
1051
+ return this.sanitize(data, this.maxDepth);
1052
+ }
1053
+ /**
1054
+ * Returns `true` when `key` should have its value replaced with `"[REDACTED]"`.
1055
+ *
1056
+ * Matches exact keys from {@link EXACT_SENSITIVE_KEYS} and the caller's
1057
+ * `additionalSensitiveKeys`, then falls back to suffix matching via
1058
+ * {@link SENSITIVE_KEY_SUFFIXES}. All comparisons are case-insensitive.
1059
+ *
1060
+ * @param key - Object property name to evaluate
1061
+ */
1062
+ isSensitiveKey(key) {
1063
+ const lower = key.toLowerCase();
1064
+ if (EXACT_SENSITIVE_KEYS.has(lower) || this.additionalSensitiveKeys.has(lower)) {
1065
+ return true;
1066
+ }
1067
+ return SENSITIVE_KEY_SUFFIXES.some((suffix) => lower.endsWith(suffix));
1068
+ }
1069
+ /**
1070
+ * Recursively sanitizes `data` up to `depth` levels deep.
1071
+ *
1072
+ * Arrays are mapped element-by-element; plain objects have each value checked
1073
+ * with {@link isSensitiveKey} and replaced or recursed accordingly. All other
1074
+ * value types (primitives, `null`, `undefined`) are returned unchanged.
1075
+ * When `depth` reaches zero the node is returned as-is to cap recursion.
1076
+ *
1077
+ * @param data - Value to sanitize
1078
+ * @param depth - Remaining recursion budget
1079
+ */
1080
+ sanitize(data, depth) {
1081
+ if (depth === 0 || data === null || data === void 0) {
1082
+ return data;
1083
+ }
1084
+ if (Array.isArray(data)) {
1085
+ return data.map((item) => this.sanitize(item, depth - 1));
1086
+ }
1087
+ if (typeof data === "object") {
1088
+ const sanitized = {};
1089
+ for (const [key, value] of Object.entries(data)) {
1090
+ sanitized[key] = this.isSensitiveKey(key) ? "[REDACTED]" : this.sanitize(value, depth - 1);
1091
+ }
1092
+ return sanitized;
1093
+ }
1094
+ return data;
1095
+ }
1096
+ };
1097
+ __name(_LogSanitizer, "LogSanitizer");
1098
+ var LogSanitizer = _LogSanitizer;
1099
+
770
1100
  // src/framework/telemetry/index.ts
771
1101
  var _Telemetry = class _Telemetry {
772
1102
  /**
@@ -885,7 +1215,7 @@ var _Telemetry = class _Telemetry {
885
1215
  level: this.params?.LOG_LEVEL || "info"
886
1216
  });
887
1217
  if (this.params?.ENABLE_TELEMETRY === true) {
888
- const helpers = (0, import_aio_lib_telemetry3.getInstrumentationHelpers)();
1218
+ const helpers = (0, import_aio_lib_telemetry4.getInstrumentationHelpers)();
889
1219
  baseLogger = helpers.logger;
890
1220
  }
891
1221
  const metadata = {};
@@ -898,38 +1228,24 @@ var _Telemetry = class _Telemetry {
898
1228
  if (actionType && actionType !== "") {
899
1229
  metadata["action.type"] = actionType;
900
1230
  }
1231
+ const logSanitizer = new LogSanitizer(
1232
+ LogSanitizer.parseSensitiveKeys(this.params?.SENSITIVE_KEYS)
1233
+ );
901
1234
  if (Object.keys(metadata).length === 0) {
1235
+ this.logger = baseLogger;
902
1236
  return baseLogger;
903
1237
  }
1238
+ const sanitizeAndMerge = /* @__PURE__ */ __name((message) => {
1239
+ if (typeof message === "object" && message !== null) {
1240
+ return logSanitizer.execute({ ...metadata, ...message });
1241
+ }
1242
+ return message;
1243
+ }, "sanitizeAndMerge");
904
1244
  const wrapper = {
905
- debug: /* @__PURE__ */ __name((message) => {
906
- if (typeof message === "object" && message !== null) {
907
- baseLogger.debug({ ...metadata, ...message });
908
- } else {
909
- baseLogger.debug(message);
910
- }
911
- }, "debug"),
912
- info: /* @__PURE__ */ __name((message) => {
913
- if (typeof message === "object" && message !== null) {
914
- baseLogger.info({ ...metadata, ...message });
915
- } else {
916
- baseLogger.info(message);
917
- }
918
- }, "info"),
919
- warn: /* @__PURE__ */ __name((message) => {
920
- if (typeof message === "object" && message !== null) {
921
- baseLogger.warn({ ...metadata, ...message });
922
- } else {
923
- baseLogger.warn(message);
924
- }
925
- }, "warn"),
926
- error: /* @__PURE__ */ __name((message) => {
927
- if (typeof message === "object" && message !== null) {
928
- baseLogger.error({ ...metadata, ...message });
929
- } else {
930
- baseLogger.error(message);
931
- }
932
- }, "error")
1245
+ debug: /* @__PURE__ */ __name((message) => baseLogger.debug(sanitizeAndMerge(message)), "debug"),
1246
+ info: /* @__PURE__ */ __name((message) => baseLogger.info(sanitizeAndMerge(message)), "info"),
1247
+ warn: /* @__PURE__ */ __name((message) => baseLogger.warn(sanitizeAndMerge(message)), "warn"),
1248
+ error: /* @__PURE__ */ __name((message) => baseLogger.error(sanitizeAndMerge(message)), "error")
933
1249
  };
934
1250
  this.logger = {
935
1251
  ...baseLogger,
@@ -992,7 +1308,7 @@ var _Telemetry = class _Telemetry {
992
1308
  */
993
1309
  getCurrentSpan() {
994
1310
  if (this.params?.ENABLE_TELEMETRY === true) {
995
- const helpers = (0, import_aio_lib_telemetry3.getInstrumentationHelpers)();
1311
+ const helpers = (0, import_aio_lib_telemetry4.getInstrumentationHelpers)();
996
1312
  return helpers?.currentSpan || null;
997
1313
  }
998
1314
  return null;
@@ -1030,7 +1346,7 @@ var _Telemetry = class _Telemetry {
1030
1346
  * ```
1031
1347
  */
1032
1348
  instrument(spanName, fn) {
1033
- return (0, import_aio_lib_telemetry3.instrument)(fn, {
1349
+ return (0, import_aio_lib_telemetry4.instrument)(fn, {
1034
1350
  spanConfig: {
1035
1351
  spanName
1036
1352
  }
@@ -1041,7 +1357,7 @@ var _Telemetry = class _Telemetry {
1041
1357
  *
1042
1358
  * Attempts to initialize telemetry providers in the following order:
1043
1359
  * 1. New Relic (if NEW_RELIC_TELEMETRY=true)
1044
- * 2. Grafana (if GRAFANA_TELEMETRY=true) - Future support
1360
+ * 2. Grafana LGTM (if GRAFANA_TELEMETRY=true)
1045
1361
  * 3. Original action (no telemetry)
1046
1362
  *
1047
1363
  * Telemetry initialization is deferred to runtime when params are available.
@@ -1056,13 +1372,30 @@ var _Telemetry = class _Telemetry {
1056
1372
  * @param action - The runtime action function to instrument
1057
1373
  * @returns The instrumented action ready for export
1058
1374
  *
1059
- * @example
1375
+ * @example New Relic provider
1060
1376
  * ```typescript
1061
1377
  * const telemetry = new Telemetry();
1062
1378
  * export const main = telemetry.initialize(myAction);
1063
1379
  * // Environment: ENABLE_TELEMETRY=true, NEW_RELIC_TELEMETRY=true
1064
1380
  * // Result: Uses New Relic telemetry with full distributed tracing
1065
1381
  * ```
1382
+ *
1383
+ * @example Grafana LGTM provider (dev mode)
1384
+ * ```typescript
1385
+ * const telemetry = new Telemetry();
1386
+ * export const main = telemetry.initialize(myAction);
1387
+ * // Environment: ENABLE_TELEMETRY=true, GRAFANA_TELEMETRY=true, GRAFANA_DEV=true
1388
+ * // Result: Sends all signals to http://localhost:4318 (Grafana LGTM Docker stack)
1389
+ * ```
1390
+ *
1391
+ * @example Grafana LGTM provider (deployed mode)
1392
+ * ```typescript
1393
+ * const telemetry = new Telemetry();
1394
+ * export const main = telemetry.initialize(myAction);
1395
+ * // Environment: ENABLE_TELEMETRY=true, GRAFANA_TELEMETRY=true,
1396
+ * // GRAFANA_ENDPOINT=https://abc123.trycloudflare.com
1397
+ * // Result: Sends all signals to the Cloudflare tunnel URL
1398
+ * ```
1066
1399
  */
1067
1400
  initialize(action) {
1068
1401
  return async (params) => {
@@ -1080,6 +1413,19 @@ var _Telemetry = class _Telemetry {
1080
1413
  );
1081
1414
  }
1082
1415
  }
1416
+ const grafanaTelemetry = new grafana_default();
1417
+ if (grafanaTelemetry.canInitialize(params)) {
1418
+ try {
1419
+ const instrumentedAction = grafanaTelemetry.initialize(action);
1420
+ return await instrumentedAction(params);
1421
+ } catch (error) {
1422
+ const errorMessage = error instanceof Error ? error.message : "Telemetry initialization failed";
1423
+ return response_default.error(
1424
+ 500 /* INTERNAL_ERROR */,
1425
+ `Telemetry configuration error: ${errorMessage}`
1426
+ );
1427
+ }
1428
+ }
1083
1429
  return action(params);
1084
1430
  };
1085
1431
  }
@@ -1190,6 +1536,7 @@ var _RuntimeAction = class _RuntimeAction {
1190
1536
  }
1191
1537
  telemetry.setParams(params);
1192
1538
  const logger = telemetry.createLogger(name);
1539
+ const logSanitizer = new LogSanitizer(LogSanitizer.parseSensitiveKeys(params.SENSITIVE_KEYS));
1193
1540
  try {
1194
1541
  logger.debug({
1195
1542
  message: `${_RuntimeAction.getActionTypeName()} execution started`
@@ -1197,13 +1544,13 @@ var _RuntimeAction = class _RuntimeAction {
1197
1544
  logger.debug(
1198
1545
  JSON.stringify({
1199
1546
  message: `${_RuntimeAction.getActionTypeName()} headers received`,
1200
- headers: params.__ow_headers || {}
1547
+ headers: logSanitizer.execute(params.__ow_headers || {})
1201
1548
  })
1202
1549
  );
1203
1550
  logger.debug(
1204
1551
  JSON.stringify({
1205
1552
  message: `${_RuntimeAction.getActionTypeName()} body received`,
1206
- body: params.__ow_body || {}
1553
+ body: logSanitizer.execute(params.__ow_body || {})
1207
1554
  })
1208
1555
  );
1209
1556
  const validationError = _RuntimeAction.validateRequestWithInstrumentation(
@@ -1229,7 +1576,7 @@ var _RuntimeAction = class _RuntimeAction {
1229
1576
  logger.debug(
1230
1577
  JSON.stringify({
1231
1578
  message: `${_RuntimeAction.getActionTypeName()} execution completed`,
1232
- result
1579
+ result: logSanitizer.execute(result)
1233
1580
  })
1234
1581
  );
1235
1582
  return result;
@@ -1487,17 +1834,18 @@ var _EventConsumerAction = class _EventConsumerAction {
1487
1834
  const eventConsumerAction = /* @__PURE__ */ __name(async (params) => {
1488
1835
  params.action_type = "event-consumer-action";
1489
1836
  const logger = telemetry.createLogger(name);
1837
+ const logSanitizer = new LogSanitizer(LogSanitizer.parseSensitiveKeys(params.SENSITIVE_KEYS));
1490
1838
  try {
1491
1839
  logger.debug({
1492
1840
  message: "Event consumer action execution started"
1493
1841
  });
1494
1842
  logger.debug({
1495
1843
  message: "Event consumer action headers received",
1496
- headers: params.__ow_headers || {}
1844
+ headers: logSanitizer.execute(params.__ow_headers || {})
1497
1845
  });
1498
1846
  logger.debug({
1499
1847
  message: "Event consumer action parameters received",
1500
- parameters: params
1848
+ parameters: logSanitizer.execute(params)
1501
1849
  });
1502
1850
  const errorMessage = _EventConsumerAction.validateWithInstrumentation(
1503
1851
  name,
@@ -1523,7 +1871,7 @@ var _EventConsumerAction = class _EventConsumerAction {
1523
1871
  );
1524
1872
  logger.debug({
1525
1873
  message: "Event consumer action execution completed",
1526
- result
1874
+ result: logSanitizer.execute(result)
1527
1875
  });
1528
1876
  return result;
1529
1877
  } catch (error) {
@@ -2151,13 +2499,14 @@ var _OpenwhiskAction = class _OpenwhiskAction {
2151
2499
  const openwhiskAction = /* @__PURE__ */ __name(async (params) => {
2152
2500
  params.action_type = "openwhisk-action";
2153
2501
  const logger = telemetry.createLogger(name);
2502
+ const logSanitizer = new LogSanitizer(LogSanitizer.parseSensitiveKeys(params.SENSITIVE_KEYS));
2154
2503
  try {
2155
2504
  logger.debug({
2156
2505
  message: "OpenWhisk action execution started"
2157
2506
  });
2158
2507
  logger.debug({
2159
2508
  message: "OpenWhisk action parameters received",
2160
- params
2509
+ params: logSanitizer.execute(params)
2161
2510
  });
2162
2511
  const result = await _OpenwhiskAction.executeActionWithInstrumentation(
2163
2512
  name,
@@ -2169,7 +2518,7 @@ var _OpenwhiskAction = class _OpenwhiskAction {
2169
2518
  );
2170
2519
  logger.debug({
2171
2520
  message: "OpenWhisk action execution completed",
2172
- result
2521
+ result: logSanitizer.execute(result)
2173
2522
  });
2174
2523
  return result;
2175
2524
  } catch (error) {
@@ -11940,6 +12289,213 @@ __name(_RabbitMQClient, "RabbitMQClient");
11940
12289
  var RabbitMQClient = _RabbitMQClient;
11941
12290
  var rabbit_mq_client_default = RabbitMQClient;
11942
12291
 
12292
+ // src/integration/amazon-sqs-client/index.ts
12293
+ var import_crypto5 = require("crypto");
12294
+ var import_client_sqs = require("@aws-sdk/client-sqs");
12295
+
12296
+ // src/integration/amazon-sqs-client/types.ts
12297
+ var SQS_MAX_BATCH_SIZE = 10;
12298
+ var DEFAULT_VISIBILITY_TIMEOUT = 30;
12299
+ var DEFAULT_WAIT_TIME_SECONDS = 0;
12300
+ var DEFAULT_MESSAGE_GROUP_ID = "default";
12301
+
12302
+ // src/integration/amazon-sqs-client/index.ts
12303
+ var _AmazonSQSClient = class _AmazonSQSClient {
12304
+ /**
12305
+ * @param config - AWS region, IAM credentials, target queue URL, and optional
12306
+ * consumer/FIFO settings (visibilityTimeout, waitTimeSeconds, messageGroupId).
12307
+ */
12308
+ constructor(config) {
12309
+ this.queueUrl = config.queueUrl;
12310
+ this.isFifo = config.queueUrl.endsWith(".fifo");
12311
+ this.visibilityTimeout = config.visibilityTimeout ?? DEFAULT_VISIBILITY_TIMEOUT;
12312
+ this.waitTimeSeconds = config.waitTimeSeconds ?? DEFAULT_WAIT_TIME_SECONDS;
12313
+ this.messageGroupId = config.messageGroupId ?? DEFAULT_MESSAGE_GROUP_ID;
12314
+ this.client = new import_client_sqs.SQSClient({
12315
+ region: config.region,
12316
+ credentials: {
12317
+ accessKeyId: config.accessKeyId,
12318
+ secretAccessKey: config.secretAccessKey
12319
+ }
12320
+ });
12321
+ }
12322
+ /**
12323
+ * Publishes all `payloads` to the bound queue in batches of 10 (the SQS hard limit
12324
+ * per `SendMessageBatch` call). Failed entries reported by SQS within a successful
12325
+ * batch response are tracked individually in `stats.errors`. Transport errors
12326
+ * (thrown exceptions) are captured per-batch so a single failing batch does not
12327
+ * abort the rest.
12328
+ *
12329
+ * For FIFO queues (URL ends in `.fifo`), every entry is stamped with:
12330
+ * - `MessageGroupId` — the value set at construction time (`config.messageGroupId`,
12331
+ * defaulting to `'default'`).
12332
+ * - `MessageDeduplicationId` — a unique `randomUUID()` per entry, ensuring
12333
+ * at-most-once delivery within the 5-minute SQS deduplication window regardless
12334
+ * of whether content-based deduplication is enabled on the queue.
12335
+ *
12336
+ * @param payloads - Array of string message bodies to send.
12337
+ * @returns Counts of published and failed messages plus per-failure details.
12338
+ */
12339
+ async publish(payloads) {
12340
+ const stats = { published: 0, failed: 0, errors: [] };
12341
+ if (payloads.length === 0) return stats;
12342
+ const chunks = this.chunk(payloads, SQS_MAX_BATCH_SIZE);
12343
+ let globalIndex = 0;
12344
+ for (const chunk of chunks) {
12345
+ await this.sendBatch(chunk, globalIndex, stats);
12346
+ globalIndex += chunk.length;
12347
+ }
12348
+ return stats;
12349
+ }
12350
+ /**
12351
+ * Pulls up to `batchSize` messages from the bound queue and processes each
12352
+ * one via `handler`.
12353
+ *
12354
+ * Processing steps:
12355
+ * 1. Receive messages in ReceiveMessage loops (≤ 10 per call) until `batchSize`
12356
+ * messages are accumulated or the queue returns empty.
12357
+ * 2. Invoke `handler` concurrently for all received messages.
12358
+ * 3. Delete every message whose handler resolved successfully.
12359
+ * Messages whose handler threw are left in the queue and become visible again
12360
+ * after the configured `visibilityTimeout` (set at construction time).
12361
+ *
12362
+ * @param batchSize - Total number of messages to pull per invocation.
12363
+ * @param handler - Callback invoked with the SQS MessageId and raw body string.
12364
+ * @returns Receive / process / delete / fail counts plus per-error details.
12365
+ * @throws Propagates SQS transport errors (receive, delete) to the caller.
12366
+ */
12367
+ async consume(batchSize, handler) {
12368
+ const stats = {
12369
+ received: 0,
12370
+ processed: 0,
12371
+ deleted: 0,
12372
+ failed: 0,
12373
+ errors: []
12374
+ };
12375
+ const messages = await this.receiveMessages(batchSize);
12376
+ stats.received = messages.length;
12377
+ if (messages.length === 0) return stats;
12378
+ const toDelete = [];
12379
+ await Promise.all(
12380
+ messages.map(async (msg) => {
12381
+ stats.processed++;
12382
+ try {
12383
+ await handler(msg.MessageId ?? "", msg.Body ?? "");
12384
+ toDelete.push(msg);
12385
+ } catch (error) {
12386
+ stats.failed++;
12387
+ stats.errors.push({ messageId: msg.MessageId ?? "", error });
12388
+ }
12389
+ })
12390
+ );
12391
+ if (toDelete.length > 0) {
12392
+ await this.deleteMessages(toDelete);
12393
+ stats.deleted += toDelete.length;
12394
+ }
12395
+ return stats;
12396
+ }
12397
+ /**
12398
+ * Loops ReceiveMessage calls until `batchSize` messages are accumulated
12399
+ * or the queue returns an empty response (queue drained).
12400
+ *
12401
+ * The first call uses `this.waitTimeSeconds` (enabling long polling when > 0).
12402
+ * Subsequent fill-up calls always use waitTimeSeconds = 0 to avoid blocking —
12403
+ * if the first call returned messages the queue is clearly non-empty.
12404
+ */
12405
+ async receiveMessages(batchSize) {
12406
+ const messages = [];
12407
+ let isFirstCall = true;
12408
+ while (messages.length < batchSize) {
12409
+ const remaining = batchSize - messages.length;
12410
+ const maxMessages = Math.min(remaining, SQS_MAX_BATCH_SIZE);
12411
+ const response = await this.client.send(
12412
+ new import_client_sqs.ReceiveMessageCommand({
12413
+ QueueUrl: this.queueUrl,
12414
+ MaxNumberOfMessages: maxMessages,
12415
+ VisibilityTimeout: this.visibilityTimeout,
12416
+ WaitTimeSeconds: isFirstCall ? this.waitTimeSeconds : 0
12417
+ })
12418
+ );
12419
+ isFirstCall = false;
12420
+ const received = response.Messages ?? [];
12421
+ if (received.length === 0) break;
12422
+ messages.push(...received);
12423
+ if (received.length < maxMessages) break;
12424
+ }
12425
+ return messages;
12426
+ }
12427
+ /**
12428
+ * Deletes messages from the queue after successful processing.
12429
+ * Uses DeleteMessageBatch (≤ 10 per call).
12430
+ */
12431
+ async deleteMessages(messages) {
12432
+ const chunks = this.chunk(messages, SQS_MAX_BATCH_SIZE);
12433
+ for (const chunk of chunks) {
12434
+ const entries = chunk.map((msg, i) => ({
12435
+ Id: String(i),
12436
+ ReceiptHandle: msg.ReceiptHandle ?? ""
12437
+ }));
12438
+ await this.client.send(
12439
+ new import_client_sqs.DeleteMessageBatchCommand({ QueueUrl: this.queueUrl, Entries: entries })
12440
+ );
12441
+ }
12442
+ }
12443
+ /**
12444
+ * Sends a single publish batch via `SendMessageBatchCommand`.
12445
+ * `globalOffset` generates unique, stable entry IDs across consecutive batches
12446
+ * (SQS requires unique IDs within each request, not globally).
12447
+ * For FIFO queues, each entry is stamped with a `MessageGroupId` and a unique
12448
+ * `MessageDeduplicationId` (randomUUID).
12449
+ * Per-entry SQS failures are captured in `stats.errors`; transport exceptions
12450
+ * mark all entries in the chunk as failed without re-throwing.
12451
+ */
12452
+ async sendBatch(chunk, globalOffset, stats) {
12453
+ const entries = chunk.map((payload, i) => ({
12454
+ Id: String(globalOffset + i),
12455
+ MessageBody: payload,
12456
+ ...this.isFifo && {
12457
+ MessageGroupId: this.messageGroupId,
12458
+ MessageDeduplicationId: (0, import_crypto5.randomUUID)()
12459
+ }
12460
+ }));
12461
+ try {
12462
+ const response = await this.client.send(
12463
+ new import_client_sqs.SendMessageBatchCommand({ QueueUrl: this.queueUrl, Entries: entries })
12464
+ );
12465
+ const successful = response.Successful ?? [];
12466
+ const failed = response.Failed ?? [];
12467
+ stats.published += successful.length;
12468
+ for (const failure of failed) {
12469
+ const entryIndex = Number(failure.Id) - globalOffset;
12470
+ const payload = chunk[entryIndex] ?? "";
12471
+ const error = new Error(
12472
+ `SQS batch entry failed [Id=${failure.Id}] Code=${failure.Code}: ${failure.Message ?? "unknown"}`
12473
+ );
12474
+ stats.failed++;
12475
+ stats.errors.push({ payload, error });
12476
+ }
12477
+ } catch (error) {
12478
+ for (const payload of chunk) {
12479
+ stats.failed++;
12480
+ stats.errors.push({ payload, error });
12481
+ }
12482
+ }
12483
+ }
12484
+ /**
12485
+ * Splits `items` into sequential chunks of at most `size` elements.
12486
+ */
12487
+ chunk(items, size) {
12488
+ const chunks = [];
12489
+ for (let i = 0; i < items.length; i += size) {
12490
+ chunks.push(items.slice(i, i + size));
12491
+ }
12492
+ return chunks;
12493
+ }
12494
+ };
12495
+ __name(_AmazonSQSClient, "AmazonSQSClient");
12496
+ var AmazonSQSClient = _AmazonSQSClient;
12497
+ var amazon_sqs_client_default = AmazonSQSClient;
12498
+
11943
12499
  // src/commerce/adobe-commerce-client/index.ts
11944
12500
  var import_got = __toESM(require("got"));
11945
12501
  var _AdobeCommerceClient = class _AdobeCommerceClient {
@@ -12934,6 +13490,7 @@ var AdminUiSdk = _AdminUiSdk;
12934
13490
  AdminUiSdk,
12935
13491
  AdobeAuth,
12936
13492
  AdobeCommerceClient,
13493
+ AmazonSQSClient,
12937
13494
  BasicAuthConnection,
12938
13495
  BearerToken,
12939
13496
  CreateEvents,
@@ -12951,6 +13508,7 @@ var AdminUiSdk = _AdminUiSdk;
12951
13508
  InfiniteLoopBreaker,
12952
13509
  IoEventsGlobals,
12953
13510
  JsonMessageProcessor,
13511
+ LogSanitizer,
12954
13512
  Oauth1aConnection,
12955
13513
  OnboardCommerce,
12956
13514
  OnboardEvents,