@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
@@ -70,7 +70,7 @@ class OrderProcessor {
70
70
  **Keep implementation details private:**
71
71
 
72
72
  ```javascript
73
- // JavaScript (Node.js 12+)
73
+ // JavaScript
74
74
  class UserManager {
75
75
  #apiKey; // Private field
76
76
 
@@ -197,7 +197,13 @@ class GetUserResolver {
197
197
 
198
198
  async execute() {
199
199
  return async (args) => {
200
- const { logger } = this.ctx;
200
+ // args = GraphQL field arguments (e.g. { id } from getUser(id: ID!))
201
+ const { logger, headers, telemetry, params } = this.ctx;
202
+ // logger - structured logger with auto-correlation
203
+ // headers - incoming HTTP request headers
204
+ // telemetry - OpenTelemetry helper for custom spans
205
+ // params - full OpenWhisk params (query, variables, operationName, action inputs)
206
+
201
207
  logger.info({ message: 'getUser-execution', args });
202
208
 
203
209
  try {
@@ -205,7 +211,7 @@ class GetUserResolver {
205
211
  return result;
206
212
  } catch (error) {
207
213
  logger.error({ message: 'getUser-error', error: error.message });
208
- throw error;
214
+ throw error; // GraphQL returns this as a field error (HTTP 200 with errors[])
209
215
  }
210
216
  };
211
217
  }
@@ -308,7 +314,7 @@ class HelloWorld {
308
314
 
309
315
  async execute() {
310
316
  return async (args) => {
311
- const { logger } = this.ctx;
317
+ const { logger, headers, telemetry, params } = this.ctx;
312
318
  // Implementation
313
319
  };
314
320
  }
@@ -8,7 +8,7 @@ alwaysApply: false
8
8
 
9
9
  ## Trigger Conditions
10
10
 
11
- This rule applies when the user requests to add New Relic telemetry to their actions using any of these phrases:
11
+ This rule applies when the user requests to add New Relic telemetry, use structured logging, or add custom spans to their actions using any of these phrases:
12
12
 
13
13
  - "Add New Relic telemetry"
14
14
  - "Enable New Relic monitoring"
@@ -18,6 +18,16 @@ This rule applies when the user requests to add New Relic telemetry to their act
18
18
  - "Enable observability with New Relic"
19
19
  - "Setup New Relic OTLP"
20
20
  - "Add telemetry monitoring"
21
+ - "How to use ctx.logger"
22
+ - "Structured logging in my action"
23
+ - "Log object fields as separate attributes"
24
+ - "Add custom spans"
25
+ - "Instrument a function with telemetry"
26
+ - "Use ctx.telemetry"
27
+ - "Add spans to my action"
28
+ - "How to use formatError"
29
+ - "Structured error logging"
30
+ - "How does ctx.logger work"
21
31
 
22
32
  ### Supported Action Types
23
33
 
@@ -42,7 +52,150 @@ The `@adobe-commerce/aio-toolkit` library includes built-in New Relic telemetry
42
52
  5. **Tags data** - Adds environment, service name, and custom resource attributes
43
53
  6. **Detects success/failure** - Automatically detects action outcomes
44
54
 
45
- **No code changes required** - Telemetry is configured entirely through action inputs and environment variables.
55
+ **No code changes required for New Relic setup** - Telemetry is configured entirely through action inputs and environment variables. However, `ctx.logger` and `ctx.telemetry` are available in every action handler and benefit from intentional usage patterns described below.
56
+
57
+ ---
58
+
59
+ ## Using `ctx.logger`
60
+
61
+ `ctx.logger` is injected automatically into every action handler. It is available in all action types regardless of whether New Relic telemetry is enabled. All log methods accept a plain string or **an object with a `message` key**:
62
+
63
+ ```javascript
64
+ const { logger } = ctx;
65
+
66
+ logger.debug({ message: 'debug-message', key: 'value' });
67
+ logger.info({ message: 'info-message', key: 'value' });
68
+ logger.warn({ message: 'warn-message', key: 'value' });
69
+ logger.error({ message: 'error-message', key: 'value' });
70
+ ```
71
+
72
+ ### Always log objects, not plain strings
73
+
74
+ When telemetry is enabled, the toolkit's `JsonMessageProcessor` automatically **promotes each key in a logged object to a separate, queryable attribute in New Relic**. Plain string logs are stored as a single `message` attribute only:
75
+
76
+ ```javascript
77
+ // Preferred — each field becomes a queryable attribute in New Relic
78
+ logger.info({
79
+ message: 'order-created',
80
+ order_id: 'ORD-123',
81
+ customer_id: 'CUST-456',
82
+ total: 99.99,
83
+ });
84
+ // New Relic receives: message, order_id, customer_id, total as separate attributes
85
+
86
+ // Avoid — entire message is a single string, not queryable by field
87
+ logger.info('order created for CUST-456');
88
+ ```
89
+
90
+ ### Nested objects are flattened to dot-notation
91
+
92
+ ```javascript
93
+ logger.debug({
94
+ message: 'ACTION_HEADERS',
95
+ headers: {
96
+ accept: 'application/json',
97
+ authorization: 'Bearer token',
98
+ },
99
+ });
100
+ // New Relic attributes: headers.accept, headers.authorization
101
+ ```
102
+
103
+ ### Automatic metadata injection
104
+
105
+ The logger automatically merges contextual metadata into every object log call — you do not need to add these fields yourself:
106
+
107
+ | Metadata key | Source | Description |
108
+ |---|---|---|
109
+ | `x-adobe-commerce-request-id` | `__ow_headers['x-adobe-commerce-request-id']` | Adobe Commerce request correlation ID |
110
+ | `action.type` | `params.action_type` | Action category set by the framework (e.g. `runtime`, `webhook`, `event-consumer`) |
111
+
112
+ When `ENABLE_TELEMETRY: true` and New Relic is configured, the `ENVIRONMENT` param is attached as a **resource attribute** on every log, trace, and metric automatically.
113
+
114
+ ### Log level
115
+
116
+ Controlled by the `LOG_LEVEL` action input. Valid values: `trace`, `debug`, `info`, `warn`, `error`. Defaults to `info` if not set.
117
+
118
+ ---
119
+
120
+ ## Using `ctx.telemetry`
121
+
122
+ `ctx.telemetry` is injected alongside `ctx.logger`. Its span methods are only functional when `ENABLE_TELEMETRY: true`; when disabled, `getCurrentSpan()` always returns `null`. It exposes three methods relevant to action developers.
123
+
124
+ ### `instrument(spanName, fn)` — create child spans
125
+
126
+ Wraps any function (sync or async) to produce a **child span** within the current trace context. The instrumented function has the same signature as the original — it must be called to execute:
127
+
128
+ ```javascript
129
+ const { logger, telemetry } = ctx;
130
+
131
+ const fetchOrders = telemetry.instrument(
132
+ 'runtime.action.my-action.fetchOrders', // span name as it appears in New Relic
133
+ async (customerId) => {
134
+ const span = telemetry.getCurrentSpan();
135
+ if (span) {
136
+ span.setAttribute('customer.id', customerId);
137
+ span.addEvent('fetch-started');
138
+ }
139
+
140
+ const orders = await orderService.findByCustomer(customerId);
141
+
142
+ if (span) {
143
+ span.setAttribute('orders.count', orders.length);
144
+ }
145
+
146
+ return orders;
147
+ }
148
+ );
149
+
150
+ // instrument() wraps the function — call it to execute and produce the span
151
+ const orders = await fetchOrders(params.customer_id);
152
+ ```
153
+
154
+ > `instrument()` must be called **inside** an active trace context — i.e., inside the action handler. Calling it outside will produce no span.
155
+
156
+ **Span naming convention**: use `[action-type].action.[action-name].[operation]` — e.g. `runtime.action.order-create.validatePayload`.
157
+
158
+ ### `getCurrentSpan()` — attach attributes and events
159
+
160
+ Returns the currently active OpenTelemetry span, or `null` when telemetry is disabled or called outside an active context. **Always guard with `if (span)`**:
161
+
162
+ ```javascript
163
+ const span = telemetry.getCurrentSpan();
164
+
165
+ if (span) {
166
+ span.setAttribute('order.id', orderId);
167
+ span.setAttribute('order.status', 'processing');
168
+ span.addEvent('validation-passed');
169
+ }
170
+ ```
171
+
172
+ ### `formatError(error)` — structured error logging
173
+
174
+ Converts an `Error` object (or any thrown value) into a structured object safe for logging. Use it inside `catch` blocks to promote error fields to individual New Relic attributes:
175
+
176
+ ```javascript
177
+ try {
178
+ await processOrder(orderId);
179
+ } catch (error) {
180
+ logger.error({
181
+ message: 'order-processing-failed',
182
+ order_id: orderId,
183
+ ...telemetry.formatError(error), // spreads error_name, error_message, error_stack
184
+ });
185
+ }
186
+ ```
187
+
188
+ **Returns:**
189
+
190
+ | Key | Value |
191
+ |---|---|
192
+ | `error_name` | `error.name` |
193
+ | `error_message` | `error.message` |
194
+ | `error_stack` | `error.stack` |
195
+
196
+ If the thrown value is not an `Error`, returns `{ error: String(error) }`.
197
+
198
+ ---
46
199
 
47
200
  ## Step 1: Verify Prerequisites and Existing Configuration
48
201
 
@@ -208,6 +361,8 @@ Locate the action under `application.runtimeManifest.packages.[package-name].act
208
361
  final: true
209
362
  ```
210
363
 
364
+ > **Misconfiguration is fatal.** If `NEW_RELIC_TELEMETRY: true` is set but `NEW_RELIC_SERVICE_NAME` or `NEW_RELIC_LICENSE_KEY` is missing or blank, the action returns **HTTP 500 immediately** with a `Telemetry configuration error: ...` message. This is intentional — it prevents silently running without telemetry when you expect it to be active. Always ensure both variables are set in `.env` before deploying with `NEW_RELIC_TELEMETRY: true`.
365
+
211
366
  #### Packaged Actions (in `actions/[package]/actions.config.yaml`)
212
367
 
213
368
  For actions defined in separate config files:
@@ -296,6 +451,16 @@ NEW_RELIC_URL=[custom-endpoint] # Optional: EU only
296
451
 
297
452
  **Alerting**: Set up alerts for high error rates, slow execution, failed invocations, or missing telemetry data.
298
453
 
454
+ **PublishEvent Logger Integration**: When an action uses `PublishEvent` to publish CloudEvents, pass the action's `ctx.logger` to the constructor so publisher-level logs appear in the same New Relic trace:
455
+ ```javascript
456
+ const publisher = new PublishEvent(
457
+ params.IMS_ORG_ID,
458
+ params.API_KEY,
459
+ accessToken,
460
+ logger // ← pass ctx.logger here for correlated logs in New Relic
461
+ );
462
+ ```
463
+
299
464
  **Multiple Environments**: Use different `ENVIRONMENT` values (dev/staging/prod) in separate `.env.{env}` files in project root. Filter New Relic data by environment tag.
300
465
 
301
466
  **CI/CD Integration**: Store `NEW_RELIC_LICENSE_KEY` in CI/CD secrets (GitHub Actions, etc.), set environment-specific values in deployment workflows.