@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.
@@ -0,0 +1,331 @@
1
+ ---
2
+ description: Object-Oriented Programming Best Practices for Actions, Lib, and Scripts
3
+ globs: '**/{actions,lib,scripts}/**/*.{ts,js}'
4
+ alwaysApply: true
5
+ ---
6
+
7
+ # Object-Oriented Programming Best Practices
8
+
9
+ ## Overview
10
+
11
+ This rule enforces essential OOP principles in `actions/`, `lib/`, and `scripts/` directories for maintainable, reusable code.
12
+
13
+ ## Core Principles
14
+
15
+ ### 1. Use Classes for Stateful Components
16
+
17
+ **When to use classes:**
18
+ - Components with multiple methods and shared state
19
+ - Services that need initialization
20
+ - Reusable utilities with configuration
21
+ - Client builders and singletons
22
+
23
+ ```javascript
24
+ // ✅ Good - Class with state and behavior
25
+ class ProductService {
26
+ constructor(client, logger) {
27
+ this.client = client;
28
+ this.logger = logger;
29
+ }
30
+
31
+ async getProduct(sku) {
32
+ this.logger.info(`Fetching product: ${sku}`);
33
+ return await this.client.get(`products/${sku}`);
34
+ }
35
+
36
+ async updateProduct(sku, data) {
37
+ this.logger.info(`Updating product: ${sku}`);
38
+ return await this.client.put(`products/${sku}`, {}, data);
39
+ }
40
+ }
41
+ ```
42
+
43
+ ### 2. Always Initialize Properties in Constructor
44
+
45
+ **Initialize all properties that your class will use:**
46
+
47
+ ```javascript
48
+ // ✅ Good - All properties initialized
49
+ class OrderProcessor {
50
+ constructor(config, logger) {
51
+ this.config = config;
52
+ this.logger = logger;
53
+ this.client = null;
54
+ this.cache = new Map();
55
+ this.retryCount = 3;
56
+ }
57
+ }
58
+
59
+ // ❌ Bad - Properties not initialized
60
+ class OrderProcessor {
61
+ constructor(config) {
62
+ this.config = config;
63
+ // this.client, this.logger, etc. are undefined!
64
+ }
65
+ }
66
+ ```
67
+
68
+ ### 3. Use Private Fields and Methods
69
+
70
+ **Keep implementation details private:**
71
+
72
+ ```javascript
73
+ // JavaScript (Node.js 12+)
74
+ class UserManager {
75
+ #apiKey; // Private field
76
+
77
+ constructor(apiKey) {
78
+ this.#apiKey = apiKey;
79
+ }
80
+
81
+ #validateKey() { // Private method
82
+ return this.#apiKey.length > 0;
83
+ }
84
+
85
+ async authenticate() {
86
+ if (!this.#validateKey()) {
87
+ throw new Error('Invalid API key');
88
+ }
89
+ // Use this.#apiKey
90
+ }
91
+ }
92
+
93
+ // TypeScript
94
+ class UserManager {
95
+ private apiKey: string;
96
+
97
+ constructor(apiKey: string) {
98
+ this.apiKey = apiKey;
99
+ }
100
+
101
+ private validateKey(): boolean {
102
+ return this.apiKey.length > 0;
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### 4. Use Dependency Injection
108
+
109
+ **Inject dependencies instead of hardcoding them:**
110
+
111
+ ```javascript
112
+ // ✅ Good - Dependencies injected
113
+ class OrderService {
114
+ constructor(paymentProcessor, logger, emailService) {
115
+ this.paymentProcessor = paymentProcessor;
116
+ this.logger = logger;
117
+ this.emailService = emailService;
118
+ }
119
+
120
+ async processOrder(order) {
121
+ await this.paymentProcessor.charge(order.total);
122
+ await this.emailService.send(order.confirmationEmail);
123
+ this.logger.info('Order processed');
124
+ }
125
+ }
126
+
127
+ // ❌ Bad - Dependencies hardcoded
128
+ class OrderService {
129
+ constructor() {
130
+ this.paymentProcessor = new PaymentProcessor(); // Hardcoded!
131
+ this.emailService = new EmailService(); // Hardcoded!
132
+ }
133
+ }
134
+ ```
135
+
136
+ ### 5. Prefer Composition Over Inheritance
137
+
138
+ **Use composition (has-a) over inheritance (is-a) when possible:**
139
+
140
+ ```javascript
141
+ // ✅ Good - Composition
142
+ class OrderService {
143
+ constructor(paymentProcessor, inventoryManager) {
144
+ this.paymentProcessor = paymentProcessor;
145
+ this.inventoryManager = inventoryManager;
146
+ }
147
+
148
+ async processOrder(order) {
149
+ await this.inventoryManager.reserve(order.items);
150
+ await this.paymentProcessor.charge(order.total);
151
+ }
152
+ }
153
+
154
+ // ❌ Bad - Unnecessary inheritance
155
+ class OrderService extends PaymentProcessor {
156
+ // OrderService is NOT a PaymentProcessor
157
+ }
158
+ ```
159
+
160
+ ## Common Patterns
161
+
162
+ ### Singleton Pattern (for expensive resources)
163
+
164
+ ```javascript
165
+ class AdobeCommerceClientBuilder {
166
+ constructor(baseUrl, consumerKey, consumerSecret, accessToken, accessTokenSecret) {
167
+ this.baseUrl = baseUrl;
168
+ this.consumerKey = consumerKey;
169
+ this.consumerSecret = consumerSecret;
170
+ this.accessToken = accessToken;
171
+ this.accessTokenSecret = accessTokenSecret;
172
+ this.client = null; // Cached instance
173
+ }
174
+
175
+ getClient() {
176
+ if (this.client) return this.client; // Return cached
177
+
178
+ const connection = new Oauth1aConnection(
179
+ this.consumerKey,
180
+ this.consumerSecret,
181
+ this.accessToken,
182
+ this.accessTokenSecret
183
+ );
184
+ this.client = new AdobeCommerceClient(this.baseUrl, connection);
185
+ return this.client;
186
+ }
187
+ }
188
+ ```
189
+
190
+ ### Resolver Pattern (for GraphQL actions)
191
+
192
+ ```javascript
193
+ class GetUserResolver {
194
+ constructor(ctx) {
195
+ this.ctx = ctx;
196
+ }
197
+
198
+ async execute() {
199
+ return async (args) => {
200
+ const { logger } = this.ctx;
201
+ logger.info({ message: 'getUser-execution', args });
202
+
203
+ try {
204
+ // Business logic here
205
+ return result;
206
+ } catch (error) {
207
+ logger.error({ message: 'getUser-error', error: error.message });
208
+ throw error;
209
+ }
210
+ };
211
+ }
212
+ }
213
+ ```
214
+
215
+ ## Anti-Patterns to Avoid
216
+
217
+ ### ❌ God Classes (doing too much)
218
+
219
+ ```javascript
220
+ // ❌ Bad
221
+ class ApplicationManager {
222
+ createUser() { }
223
+ deleteUser() { }
224
+ createProduct() { }
225
+ processOrder() { }
226
+ sendEmail() { }
227
+ generateReport() { }
228
+ // ... 50 more methods
229
+ }
230
+
231
+ // ✅ Good - Separate concerns
232
+ class UserService { }
233
+ class ProductService { }
234
+ class OrderService { }
235
+ class EmailService { }
236
+ ```
237
+
238
+ ### ❌ Anemic Models (data only, no behavior)
239
+
240
+ ```javascript
241
+ // ❌ Bad
242
+ class Order {
243
+ constructor(id, items, total) {
244
+ this.id = id;
245
+ this.items = items;
246
+ this.total = total;
247
+ }
248
+ }
249
+ // All logic in separate service
250
+
251
+ // ✅ Good - Rich model with behavior
252
+ class Order {
253
+ constructor(id, items) {
254
+ this.id = id;
255
+ this.items = items;
256
+ this.total = this.calculateTotal();
257
+ }
258
+
259
+ calculateTotal() {
260
+ return this.items.reduce((sum, item) => sum + item.price, 0);
261
+ }
262
+
263
+ addItem(item) {
264
+ this.items.push(item);
265
+ this.total = this.calculateTotal();
266
+ }
267
+ }
268
+ ```
269
+
270
+ ### ❌ Tight Coupling (hardcoded dependencies)
271
+
272
+ ```javascript
273
+ // ❌ Bad
274
+ class OrderService {
275
+ constructor() {
276
+ this.emailService = new EmailService(); // Hardcoded!
277
+ }
278
+ }
279
+
280
+ // ✅ Good - Loose coupling
281
+ class OrderService {
282
+ constructor(emailService) {
283
+ this.emailService = emailService; // Injected
284
+ }
285
+ }
286
+ ```
287
+
288
+ ## Key Takeaways
289
+
290
+ 1. **Use classes for stateful, reusable components**
291
+ 2. **Initialize all properties in constructor**
292
+ 3. **Use private fields for encapsulation**
293
+ 4. **Inject dependencies, don't hardcode them**
294
+ 5. **Prefer composition over inheritance**
295
+ 6. **Keep classes focused (Single Responsibility)**
296
+ 7. **Avoid God classes and anemic models**
297
+
298
+ ## Examples from @adobe-commerce/aio-toolkit
299
+
300
+ Follow the toolkit's class-based patterns:
301
+
302
+ ```javascript
303
+ // GraphQL Resolver
304
+ class HelloWorld {
305
+ constructor(ctx) {
306
+ this.ctx = ctx;
307
+ }
308
+
309
+ async execute() {
310
+ return async (args) => {
311
+ const { logger } = this.ctx;
312
+ // Implementation
313
+ };
314
+ }
315
+ }
316
+
317
+ // Client Builder with Singleton
318
+ class AdobeCommerceClientBuilder {
319
+ constructor(baseUrl, credentials) {
320
+ this.baseUrl = baseUrl;
321
+ this.credentials = credentials;
322
+ this.client = null;
323
+ }
324
+
325
+ getClient() {
326
+ if (this.client) return this.client;
327
+ this.client = new AdobeCommerceClient(this.baseUrl, this.credentials);
328
+ return this.client;
329
+ }
330
+ }
331
+ ```
@@ -0,0 +1,354 @@
1
+ ---
2
+ description: Setting up New Relic Telemetry for Adobe I/O Runtime Actions
3
+ globs: '**/{app.config.yaml,ext.config.yaml,actions.config.yaml},.env'
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Setting up New Relic Telemetry
8
+
9
+ ## Trigger Conditions
10
+
11
+ This rule applies when the user requests to add New Relic telemetry to their actions using any of these phrases:
12
+
13
+ - "Add New Relic telemetry"
14
+ - "Enable New Relic monitoring"
15
+ - "Setup telemetry for [action-name]"
16
+ - "Configure New Relic for my actions"
17
+ - "Add telemetry to [action-type] action"
18
+ - "Enable observability with New Relic"
19
+ - "Setup New Relic OTLP"
20
+ - "Add telemetry monitoring"
21
+
22
+ ### Supported Action Types
23
+
24
+ New Relic telemetry can be added to:
25
+
26
+ - RuntimeAction
27
+ - WebhookAction
28
+ - EventConsumerAction
29
+ - GraphQlAction
30
+ - OpenwhiskAction
31
+
32
+ **Note**: This rule should be used AFTER an action has been created. It provides recommendations for enabling telemetry on existing actions.
33
+
34
+ ## Overview
35
+
36
+ The `@adobe-commerce/aio-toolkit` library includes built-in New Relic telemetry support using OpenTelemetry Protocol (OTLP). When enabled, the toolkit automatically:
37
+
38
+ 1. **Instruments actions** with OpenTelemetry
39
+ 2. **Exports traces** - Captures action execution spans and timing
40
+ 3. **Exports metrics** - Sends performance metrics
41
+ 4. **Exports logs** - Forwards structured logs with JSON attribute extraction
42
+ 5. **Tags data** - Adds environment, service name, and custom resource attributes
43
+ 6. **Detects success/failure** - Automatically detects action outcomes
44
+
45
+ **No code changes required** - Telemetry is configured entirely through action inputs and environment variables.
46
+
47
+ ## Step 1: Verify Prerequisites and Existing Configuration
48
+
49
+ Before proceeding, verify and analyze the current configuration:
50
+
51
+ 1. **Action exists** - Check that the action is already created
52
+ 2. **Toolkit is installed** - Confirm `@adobe-commerce/aio-toolkit` is in `package.json`
53
+ 3. **Check existing telemetry configuration**:
54
+ - Check if `.env` file exists in project root directory (workspace root)
55
+ - If project root `.env` exists, check for these variables:
56
+ - `ENVIRONMENT` - Note the value if present (dev, staging, production)
57
+ - `NEW_RELIC_SERVICE_NAME` - Note the value if present
58
+ - `NEW_RELIC_LICENSE_KEY` - Note if present (don't show the value)
59
+ - `NEW_RELIC_URL` - Note if custom endpoint is configured
60
+ - Check existing actions in `app.config.yaml` for telemetry inputs to detect default environment
61
+ - Look for `ENVIRONMENT` input in any action
62
+ - Common values: `dev`, `staging`, `production`
63
+ 4. Only proceed to Step 2 after analyzing the current configuration
64
+
65
+ ## Step 2: Ask Required Questions
66
+
67
+ Before configuring telemetry, ask the user the following questions:
68
+
69
+ **Important**:
70
+
71
+ - **Smart Configuration Detection**: Based on Step 1 analysis, skip questions for values already configured in project root `.env` or detected from existing actions
72
+ - If `ENVIRONMENT` exists in project root `.env`: Skip Question 2, use existing value
73
+ - If `NEW_RELIC_SERVICE_NAME` exists in project root `.env`: Skip Question 3, use existing value
74
+ - If `NEW_RELIC_LICENSE_KEY` exists in project root `.env`: Skip Question 4, confirm it's already set
75
+ - If any question is unclear or you need more information to proceed, continue asking follow-up questions until you have complete clarity. Do not make assumptions - keep the conversation going until all details are clear.
76
+ - Once all questions are clearly answered, move to Step 3 to get user confirmation before making any configuration changes.
77
+
78
+ 1. **Which actions need telemetry?**
79
+ - Provide comma-separated action names
80
+ - Example: `runtime-action, webhook-action, product-consumer`
81
+ - Or say "all actions" to add telemetry to every action in the project
82
+ 2. **Environment name** (for filtering and tagging in New Relic)?
83
+ - **ONLY ask if `ENVIRONMENT` is NOT in project root `.env`** (from Step 1)
84
+ - **If already configured**: Skip this question and use the existing value
85
+ - Options: `dev`, `staging`, `production`, `test`, or custom name
86
+ - **If detected from existing actions in Step 1**: Show detected value and ask to confirm or change
87
+ - Example: "I detected `staging` from your existing actions. Use this environment or specify a different one?"
88
+ - **If not detected**: Ask what environment to use (no default assumption)
89
+ - Note: This appears as a tag in New Relic for filtering data
90
+
91
+ 3. **Service name** (for New Relic identification)?
92
+ - **ONLY ask if `NEW_RELIC_SERVICE_NAME` is NOT in project root `.env`** (from Step 1)
93
+ - **If already configured**: Skip this question and use the existing value
94
+ - Provide a descriptive name for your service
95
+ - Example: `my-commerce-app`, `customer-portal`, `order-processing`
96
+ - Note: This appears as the service name in New Relic dashboards
97
+
98
+ 4. **Do you have a New Relic license key?**
99
+ - **ONLY ask if `NEW_RELIC_LICENSE_KEY` is NOT in project root `.env`** (from Step 1)
100
+ - **If already configured**: Skip this question and confirm the key is set
101
+ - Options: Yes or No
102
+
103
+ **If No, provide instructions**:
104
+ - Log in to [New Relic](https://one.newrelic.com/)
105
+ - Navigate to **Account settings** > **API keys**
106
+ - Find or create an **Ingest - License** key type
107
+ - Copy the key value (it starts with `NRAK-`)
108
+ - You'll add this to your project root `.env` file
109
+
110
+ ## Step 3: Confirm Telemetry Configuration
111
+
112
+ After receiving answers to ALL questions, present a comprehensive summary and **wait for user confirmation** before proceeding to Step 4:
113
+
114
+ ---
115
+
116
+ ### 📊 New Relic Telemetry Configuration Summary
117
+
118
+ **Actions to Configure:**
119
+
120
+ - [List of action names]
121
+
122
+ **Existing Configuration** (detected from project root `.env` and actions):
123
+ [If any values were detected in Step 1, list them here:]
124
+
125
+ - ✅ **Environment:** `[existing-environment]` (already configured in project root `.env`)
126
+ - ✅ **Service Name:** `[existing-service-name]` (already configured in project root `.env`)
127
+ - ✅ **License Key:** Configured in project root `.env` (starts with `NRAK-...`)
128
+ [Only if NEW_RELIC_URL was detected:]
129
+ - ✅ **Custom Endpoint:** `[existing-endpoint]` (already configured in project root `.env`)
130
+
131
+ **New Configuration** (to be added):
132
+ [Only show values that are NEW or changed:]
133
+
134
+ - **Environment:** [environment name] [Only if NEW - not in project root .env]
135
+ - **Service Name:** [service name] [Only if NEW - not in project root .env]
136
+ - **License Key:** [To be added] [Only if NEW - not in project root .env]
137
+ - **OTLP Endpoint:** Default US endpoint (https://otlp.nr-data.net) [Only if no custom endpoint detected]
138
+
139
+ **What Will Be Added:**
140
+
141
+ For each action, the following inputs will be added to the action configuration:
142
+
143
+ ```yaml
144
+ inputs:
145
+ LOG_LEVEL: debug
146
+ ENVIRONMENT: $ENVIRONMENT
147
+ ENABLE_TELEMETRY: true
148
+ NEW_RELIC_TELEMETRY: true
149
+ NEW_RELIC_SERVICE_NAME: $NEW_RELIC_SERVICE_NAME
150
+ NEW_RELIC_LICENSE_KEY: $NEW_RELIC_LICENSE_KEY
151
+ [NEW_RELIC_URL: $NEW_RELIC_URL] # Only if custom endpoint was detected in project root .env
152
+ ```
153
+
154
+ **Environment Variables** (project root `.env` file):
155
+
156
+ [If all variables already exist:]
157
+ ✅ All required environment variables are already configured in project root `.env`
158
+
159
+ [If some need to be added:]
160
+ You'll need to add these to your project root `.env` file:
161
+
162
+ ```bash
163
+ [Only show variables that DON'T already exist:]
164
+ ENVIRONMENT=[environment] # NEW (e.g., staging, production)
165
+ NEW_RELIC_SERVICE_NAME=[service-name] # NEW
166
+ NEW_RELIC_LICENSE_KEY=[your-license-key] # NEW
167
+ NEW_RELIC_URL=[custom-endpoint] # NEW (optional)
168
+ ```
169
+
170
+ **Files to Modify:**
171
+
172
+ - ✏️ `app.config.yaml` or `[extension-path]/ext.config.yaml` (for each action)
173
+ [Only if new variables:]
174
+ - 📄/✏️ `.env` (project root) - Create or update with new environment variables
175
+
176
+ ---
177
+
178
+ **Should I proceed with adding New Relic telemetry to these actions?**
179
+
180
+ ## Step 4: Add Telemetry Configuration
181
+
182
+ Once confirmed, update the action configurations and provide environment variable setup instructions.
183
+
184
+ ### A. Update Action Configurations
185
+
186
+ For each action identified, add telemetry inputs to the action configuration.
187
+
188
+ #### Standard Actions (in `app.config.yaml`)
189
+
190
+ Locate the action under `application.runtimeManifest.packages.[package-name].actions.[action-name]` and add/update the `inputs` section:
191
+
192
+ ```yaml
193
+ [action-name]:
194
+ function: actions/[action-name]/index.[js/ts]
195
+ web: 'yes' # or 'no' for event consumers
196
+ runtime: nodejs:22
197
+ inputs:
198
+ LOG_LEVEL: debug
199
+ ENVIRONMENT: $ENVIRONMENT
200
+ ENABLE_TELEMETRY: true
201
+ NEW_RELIC_TELEMETRY: true
202
+ NEW_RELIC_SERVICE_NAME: $NEW_RELIC_SERVICE_NAME
203
+ NEW_RELIC_LICENSE_KEY: $NEW_RELIC_LICENSE_KEY
204
+ # Only add if custom endpoint specified:
205
+ # NEW_RELIC_URL: $NEW_RELIC_URL
206
+ annotations:
207
+ require-adobe-auth: [true/false]
208
+ final: true
209
+ ```
210
+
211
+ #### Packaged Actions (in `actions/[package]/actions.config.yaml`)
212
+
213
+ For actions defined in separate config files:
214
+
215
+ ```yaml
216
+ [action-name]:
217
+ function: [action-name]/index.[js/ts]
218
+ web: 'yes' # or 'no' for event consumers
219
+ runtime: nodejs:22
220
+ inputs:
221
+ LOG_LEVEL: debug
222
+ ENVIRONMENT: $ENVIRONMENT
223
+ ENABLE_TELEMETRY: true
224
+ NEW_RELIC_TELEMETRY: true
225
+ NEW_RELIC_SERVICE_NAME: $NEW_RELIC_SERVICE_NAME
226
+ NEW_RELIC_LICENSE_KEY: $NEW_RELIC_LICENSE_KEY
227
+ # Only add if custom endpoint specified:
228
+ # NEW_RELIC_URL: $NEW_RELIC_URL
229
+ annotations:
230
+ require-adobe-auth: [true/false]
231
+ final: true
232
+ ```
233
+
234
+ #### Extension Point Actions (in `[extension-path]/ext.config.yaml`)
235
+
236
+ For actions in extension points, follow the same pattern but update the extension's config file:
237
+
238
+ ```yaml
239
+ runtimeManifest:
240
+ packages:
241
+ [package-name]:
242
+ license: Apache-2.0
243
+ actions:
244
+ [action-name]:
245
+ function: actions/[action-name]/index.js
246
+ web: 'yes'
247
+ runtime: nodejs:22
248
+ inputs:
249
+ LOG_LEVEL: debug
250
+ ENVIRONMENT: $ENVIRONMENT
251
+ ENABLE_TELEMETRY: true
252
+ NEW_RELIC_TELEMETRY: true
253
+ NEW_RELIC_SERVICE_NAME: $NEW_RELIC_SERVICE_NAME
254
+ NEW_RELIC_LICENSE_KEY: $NEW_RELIC_LICENSE_KEY
255
+ # Only add if custom endpoint specified:
256
+ # NEW_RELIC_URL: $NEW_RELIC_URL
257
+ annotations:
258
+ require-adobe-auth: [true/false]
259
+ final: true
260
+ ```
261
+
262
+ ### B. Environment Variables Setup
263
+
264
+ **If all variables exist**: ✅ `ENVIRONMENT`, `NEW_RELIC_SERVICE_NAME`, `NEW_RELIC_LICENSE_KEY` (and optional `NEW_RELIC_URL`) already in project root `.env` - no changes needed.
265
+
266
+ **If variables need adding**:
267
+
268
+ 1. Check/create project root `.env` file (workspace root)
269
+ 2. Add missing variables (only show variables that need to be added)
270
+ 3. Get license key from New Relic → Account settings → API keys → Ingest - License (starts with `NRAK-`)
271
+ 4. Never commit `.env` - ensure it's in `.gitignore`
272
+
273
+ ```bash
274
+ # Add to project root .env
275
+ ENVIRONMENT=[environment]
276
+ NEW_RELIC_SERVICE_NAME=[service-name]
277
+ NEW_RELIC_LICENSE_KEY=[license-key]
278
+ NEW_RELIC_URL=[custom-endpoint] # Optional: EU only
279
+ ```
280
+
281
+ ## Step 5: Testing and Verification
282
+
283
+ **Deploy**: Run `aio app deploy`
284
+
285
+ **Test**: Invoke action via `aio runtime action invoke [action-name] --result` or API/webhook call
286
+
287
+ **Verify in New Relic** (1-2 minutes delay): Login → APM & Services → Find service → View Traces/Logs/Metrics
288
+
289
+ **Troubleshooting**: Check `NEW_RELIC_LICENSE_KEY` and `NEW_RELIC_SERVICE_NAME` in project root `.env`, run `aio app logs` for errors, verify `ENABLE_TELEMETRY: true` in action inputs, confirm correct New Relic account/region.
290
+
291
+ **Common Errors**: Missing license key/service name in project root `.env`, invalid configuration, wrong data center (EU requires `NEW_RELIC_URL`).
292
+
293
+ ## Additional Recommendations
294
+
295
+ **Dashboards & Monitoring**: Create custom dashboards for success/failure rates, execution duration, request volume, and error trends.
296
+
297
+ **Alerting**: Set up alerts for high error rates, slow execution, failed invocations, or missing telemetry data.
298
+
299
+ **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
+
301
+ **CI/CD Integration**: Store `NEW_RELIC_LICENSE_KEY` in CI/CD secrets (GitHub Actions, etc.), set environment-specific values in deployment workflows.
302
+
303
+ **Custom OTLP Endpoint**: For EU data center, add `NEW_RELIC_URL=https://otlp.eu01.nr-data.net` to project root `.env` and reference as `NEW_RELIC_URL: $NEW_RELIC_URL` in action inputs. US uses default `https://otlp.nr-data.net`.
304
+
305
+ **Cost & Performance**: Telemetry adds ~1-5ms latency (asynchronous, non-blocking). Monitor data ingest volume, consider sampling for high-volume actions, review billing regularly.
306
+
307
+ ## Key Features
308
+
309
+ **Automatic Instrumentation**: Captures traces (execution spans, timing, success/failure, HTTP codes), structured logs (`logger.info/error/warn`, JSON attributes, trace correlation), metrics (duration, invocation counts, error rates), and resource attributes (`service.name`, `environment`, `service.version`).
310
+
311
+ **Multi-Provider Support**: Priority order: New Relic → Grafana → None. Explicit configuration validation ensures failures when required config is missing (e.g., `NEW_RELIC_LICENSE_KEY`) rather than silent fallback.
312
+
313
+ ## Important Notes
314
+
315
+ 1. **No Code Changes**: Telemetry configured via action inputs only
316
+ 2. **Auto-Export**: Toolkit handles OpenTelemetry setup, export, error handling
317
+ 3. **Security**: Never commit license keys - use project root `.env` + `.gitignore`
318
+ 4. **Environment Tags**: Set `ENVIRONMENT` in project root `.env` to filter by environment
319
+ 5. **Service Names**: Use descriptive names for identification; same name for related actions or different names for separate services
320
+ 6. **Data Center**: US (default), EU requires `NEW_RELIC_URL` in project root `.env`
321
+ 7. **Providers**: Supports New Relic, Grafana; this rule focuses on New Relic
322
+
323
+ ## Example Configuration
324
+
325
+ **Action inputs (app.config.yaml or ext.config.yaml)**:
326
+
327
+ ```yaml
328
+ [action-name]:
329
+ inputs:
330
+ LOG_LEVEL: debug
331
+ ENVIRONMENT: $ENVIRONMENT
332
+ ENABLE_TELEMETRY: true
333
+ NEW_RELIC_TELEMETRY: true
334
+ NEW_RELIC_SERVICE_NAME: $NEW_RELIC_SERVICE_NAME
335
+ NEW_RELIC_LICENSE_KEY: $NEW_RELIC_LICENSE_KEY
336
+ NEW_RELIC_URL: $NEW_RELIC_URL # Optional: for EU data center
337
+ ```
338
+
339
+ **Project root .env**:
340
+
341
+ ```bash
342
+ ENVIRONMENT=production # or dev, staging
343
+ NEW_RELIC_SERVICE_NAME=my-commerce-app
344
+ NEW_RELIC_LICENSE_KEY=NRAK-XXXXXXXXXXXXXXXXXXXXX
345
+ NEW_RELIC_URL=https://otlp.eu01.nr-data.net # Optional: EU only
346
+ ```
347
+
348
+ ## Reference Links
349
+
350
+ - [New Relic Account Setup](https://one.newrelic.com/)
351
+ - [New Relic API Keys](https://one.newrelic.com/api-keys)
352
+ - [New Relic OTLP Endpoint](https://docs.newrelic.com/docs/more-integrations/open-source-telemetry-integrations/opentelemetry/opentelemetry-setup/)
353
+ - [OpenTelemetry Documentation](https://opentelemetry.io/docs/)
354
+ - [@adobe-commerce/aio-toolkit NPM](https://www.npmjs.com/package/@adobe-commerce/aio-toolkit)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe-commerce/aio-toolkit",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "A comprehensive TypeScript toolkit for Adobe App Builder applications providing standardized Adobe Commerce integrations, I/O Events orchestration, file storage utilities, authentication helpers, and robust backend development tools with 100% test coverage.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -14,10 +14,14 @@
14
14
  },
15
15
  "files": [
16
16
  "dist/**/*",
17
+ "files/**/*",
17
18
  "README.md",
18
19
  "CHANGELOG.md",
19
20
  "LICENSE"
20
21
  ],
22
+ "bin": {
23
+ "aio-toolkit-cursor-context": "dist/aio-toolkit-cursor-context/bin/cli.js"
24
+ },
21
25
  "scripts": {
22
26
  "build": "tsup",
23
27
  "build:watch": "tsup --watch",
@@ -93,6 +97,7 @@
93
97
  "typescript": "^5.9.2"
94
98
  },
95
99
  "peerDependencies": {
100
+ "@adobe-commerce/commerce-extensibility-tools": "^1.6.0",
96
101
  "typescript": ">=4.9.0"
97
102
  },
98
103
  "engines": {