@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
@@ -0,0 +1,610 @@
1
+ ---
2
+ description: Adding App Builder Data (ABDB) collection schema to Adobe I/O Runtime actions using @adobe-commerce/aio-toolkit
3
+ globs: '**/{actions,lib}/**/*.{ts,js}'
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Using AbdbCollection
8
+
9
+ ## Trigger Conditions
10
+
11
+ This rule applies when the user requests to add MongoDB-backed database storage to an action using any of these phrases:
12
+
13
+ - "Use App Builder Data"
14
+ - "Use ABDB"
15
+ - "MongoDB-backed storage"
16
+ - "Define collection schema"
17
+ - "Database in App Builder"
18
+ - "Store data in MongoDB"
19
+ - "Create a collection for [entity]"
20
+ - "Persist data with ABDB"
21
+ - "App Builder Data Services"
22
+ - "Custom database query"
23
+ - "Define database schema for [entity]"
24
+
25
+ **Important**: This rule covers **collection schema creation** and direct `run()` usage for custom queries. For full CRUD operations (insert, find, update, delete) via `AbdbRepository`, use the "Using AbdbRepository" rule after this one.
26
+
27
+ **Integration with Other Action Creation Rules:**
28
+
29
+ When using action creation rules (RuntimeAction, WebhookAction, EventConsumerAction, GraphQlAction, OpenwhiskAction) and the developer mentions persisting data in a managed database:
30
+
31
+ - **Automatically trigger this rule** after the action is created
32
+ - Examples: "save records to the database", "query MongoDB", "use App Builder Data", "persist with ABDB"
33
+ - This rule handles creating the collection class, wiring IMS credentials, configuring the YAML, and integrating `run()` into the action
34
+
35
+ **If you need to create a new action first:**
36
+
37
+ - Use the appropriate action creation command first
38
+ - Then this rule will add the ABDB collection to that action
39
+
40
+ ---
41
+
42
+ ## Step 1: Verify Prerequisites
43
+
44
+ Before proceeding, verify:
45
+
46
+ 1. **Toolkit installed**: Check `@adobe-commerce/aio-toolkit` in `package.json` dependencies
47
+ - If NOT installed: `npm install @adobe-commerce/aio-toolkit`
48
+
49
+ 2. **Peer dependency — aio-lib-db**: Check `@adobe/aio-lib-db` in `package.json` dependencies
50
+ - **Required**: `AbdbCollection` uses this internally to connect to the App Builder Data database
51
+ - If NOT installed: `npm install @adobe/aio-lib-db`
52
+ - Minimum version: `>= 1.0.0`
53
+
54
+ 3. **AIO SDK installed**: Check `@adobe/aio-sdk` in `package.json` dependencies
55
+ - Required for `Core.AuthClient.generateAccessToken(params)` to obtain an IMS access token
56
+ - If NOT installed: `npm install @adobe/aio-sdk`
57
+
58
+ 4. **Detect Language (TypeScript vs JavaScript)**:
59
+
60
+ **Check for TypeScript indicators (check ALL of these)**:
61
+ 1. **Package Dependencies**: Look for `typescript` in `package.json` dependencies or devDependencies
62
+ 2. **Config File**: Check if `tsconfig.json` exists in project root
63
+ 3. **TypeScript Loaders**: Look for `ts-loader`, `ts-node`, or `@types/*` packages in `package.json`
64
+ 4. **Existing Files**: Check for `.ts` or `.tsx` files in key directories (`actions/`, `src/`, `lib/`)
65
+
66
+ **Detection Logic**:
67
+ - **If ANY of the following are true → TypeScript project**:
68
+ - `typescript` package installed AND `tsconfig.json` exists
69
+ - Multiple TypeScript indicators (2 or more from above list)
70
+ - Existing `.ts` files found in `actions/` or `lib/` directories
71
+ - **Otherwise → JavaScript project**
72
+
73
+ 5. **Detect Project Structure**:
74
+ - Check `app.config.yaml` for `application:` section (root actions)
75
+ - Check `app.config.yaml` for `extensions:` section (extension point actions)
76
+ - If extensions exist, parse the `$include` path for the extension directory
77
+
78
+ 6. **Check Existing Actions**: List all existing actions and their file paths
79
+ - From root `actions/` directory
80
+ - From `[extension-path]/actions/` directory
81
+ - If NO actions exist: Stop and recommend creating an action first
82
+
83
+ 7. **Check Existing Collections**: Look for `lib/database/collection/` directory
84
+ - List any existing collection classes already defined
85
+ - Inform the user if a collection for the same entity already exists
86
+
87
+ 8. Only proceed to Step 2 after confirming all prerequisites
88
+
89
+ ---
90
+
91
+ ## Step 2: Ask Required Questions
92
+
93
+ Before generating any code, ask the user the following questions:
94
+
95
+ **Important**: Do not make assumptions — keep asking until all details are clear.
96
+
97
+ 1. **Entity Name**: What entity will this collection store?
98
+ - Examples: `Order`, `User`, `QueueMessage`, `Product`, `Session`
99
+ - Used to name the collection class (e.g. `OrdersCollection`) and derive the collection name
100
+
101
+ 2. **Collection Name**: What is the MongoDB collection name?
102
+ - Rules: **only alphanumeric characters and underscores** (`[a-zA-Z0-9_]`) — no hyphens, no spaces
103
+ - Convention: snake_case plural — e.g. `orders`, `queue_messages`, `user_sessions`
104
+ - **Default suggestion**: derive from entity name, e.g. `Order` → `orders`
105
+
106
+ 3. **Columns / Schema**: What fields does this collection need?
107
+ - For each column, ask:
108
+ - **Name**: snake_case, alphanumeric + underscore only
109
+ - **Type**: one of `STRING`, `NUMBER`, `BOOLEAN`, `DATETIME`
110
+ - **Required?**: is this field mandatory in every record?
111
+ - **Description** (optional): human-readable label
112
+ - Minimum: at least one column
113
+ - Tip: guide the user with examples — e.g. `status` (STRING, required), `total` (NUMBER, required), `created_at` (DATETIME, optional)
114
+
115
+ 4. **Data Region**: Which region should the database connect to?
116
+ - Options: `amer` (default), `emea`, `apac`
117
+ - **Default**: `amer` — suggest this unless the user knows they need another region
118
+
119
+ 5. **Target Action(s)**: Which existing action(s) will use this collection?
120
+ - List all discovered actions for the user to choose from
121
+ - The action will use `collection.run()` directly for custom queries
122
+
123
+ 6. **Query / Operation**: What database operation(s) will the action perform?
124
+ - Examples: find all, find by filter, insert one, update, delete, count
125
+ - This determines what goes inside the `run()` callback
126
+ - Note: for standard CRUD (insert/find/update/delete), suggest "Using AbdbRepository" rule after this setup
127
+
128
+ 7. **Validation Strategy**: Should the action validate incoming params before writing?
129
+ - **`validate()`** — fail fast on the first bad field (good for single inserts or tight control)
130
+ - **`validateAll()`** — collect all errors at once (better UX at API boundaries)
131
+ - **None** — skip validation (read-only or trusted data only)
132
+
133
+ ---
134
+
135
+ ## Step 3: Confirm Implementation Details
136
+
137
+ After receiving answers to ALL questions, present a comprehensive summary and **wait for user confirmation** before proceeding to Step 4:
138
+
139
+ ---
140
+
141
+ ### 📋 AbdbCollection Integration Summary
142
+
143
+ **Entity:** `[EntityName]`
144
+ **Collection Class:** `[EntityName]Collection`
145
+ **Collection Name (DB):** `[collection_name]`
146
+ **Language:** [TypeScript/JavaScript] (auto-detected)
147
+ **Region:** `[amer/emea/apac]`
148
+
149
+ **Schema:**
150
+
151
+ | Column | Type | Required | Description |
152
+ |---|---|---|---|
153
+ | `[column_name]` | `[TYPE]` | Yes/No | `[description or —]` |
154
+
155
+ **Target Action(s):**
156
+ - `[action-name]` → `[path/to/action/index.js]`
157
+ - **Operation**: `[find all / find by filter / insert / update / delete / custom]`
158
+ - **Validation**: `validate()` / `validateAll()` / None
159
+
160
+ ---
161
+
162
+ ### ✅ What Will Be Changed
163
+
164
+ **1. New Collection Class**
165
+ - ➕ `lib/database/collection/[entity-name]/index.[js/ts]`
166
+ - Extends `AbdbCollection` with fixed collection name `[collection_name]`
167
+ - Defines all columns via `addColumn()`
168
+ - Reusable across all actions in the project
169
+
170
+ **2. Action Implementation**
171
+ - ✏️ `[action-path]/index.[js/ts]`
172
+ - Import `[EntityName]Collection` from `lib/database/collection/[entity-name]`
173
+ - Import `Core` from `@adobe/aio-sdk`
174
+ - Generate IMS access token via `Core.AuthClient.generateAccessToken(params)`
175
+ - Call `collection.run(callback, accessToken, region)` for the database operation
176
+ - Add validation with `validate()` / `validateAll()` if selected
177
+
178
+ **3. Action Configuration**
179
+ - ✏️ `app.config.yaml` or `ext.config.yaml` (or `actions.config.yaml` if packaged)
180
+ - Add `include-ims-credentials: true` annotation
181
+ - Add database auto-provision block (if not already present)
182
+
183
+ **4. No Additional `.env` Variables Required**
184
+ - `AbdbCollection` uses standard IMS credentials already injected via `include-ims-credentials: true`
185
+ - No new secrets are needed
186
+
187
+ ---
188
+
189
+ **Should I proceed with this implementation?**
190
+
191
+ ---
192
+
193
+ ## Step 4: Generate Implementation
194
+
195
+ ### Step 4.1: Create the Collection Class
196
+
197
+ Create a dedicated collection class under `lib/database/collection/`. Always extend `AbdbCollection` — never instantiate it directly with ad hoc column definitions in action code.
198
+
199
+ #### TypeScript
200
+
201
+ ```typescript
202
+ // lib/database/collection/[entity-name]/index.ts
203
+
204
+ import { AbdbCollection, AbdbColumnType } from '@adobe-commerce/aio-toolkit';
205
+
206
+ export class [EntityName]Collection extends AbdbCollection {
207
+ constructor() {
208
+ super('[collection_name]', collection => {
209
+ collection
210
+ .addColumn('[column_1]', AbdbColumnType.[TYPE], '[description]', true) // required
211
+ .addColumn('[column_2]', AbdbColumnType.[TYPE], '[description]', false) // optional
212
+ .addColumn('[column_3]', AbdbColumnType.[TYPE], { isRequired: true }); // options-object form
213
+ });
214
+ }
215
+ }
216
+ ```
217
+
218
+ #### JavaScript
219
+
220
+ ```javascript
221
+ // lib/database/collection/[entity-name]/index.js
222
+
223
+ const { AbdbCollection, AbdbColumnType } = require('@adobe-commerce/aio-toolkit');
224
+
225
+ class [EntityName]Collection extends AbdbCollection {
226
+ constructor() {
227
+ super('[collection_name]', collection => {
228
+ collection
229
+ .addColumn('[column_1]', AbdbColumnType.[TYPE], '[description]', true)
230
+ .addColumn('[column_2]', AbdbColumnType.[TYPE], '[description]', false);
231
+ });
232
+ }
233
+ }
234
+
235
+ module.exports = { [EntityName]Collection };
236
+ ```
237
+
238
+ > **Column naming rules**: Only `[a-zA-Z0-9_]` characters are allowed — use snake_case. Hyphens and spaces will throw during construction.
239
+
240
+ ---
241
+
242
+ ### Step 4.2: Integrate into the Target Action
243
+
244
+ Add the collection to the existing action. Generate an IMS token, then call `run()` with the database logic.
245
+
246
+ #### Find All Records — TypeScript
247
+
248
+ ```typescript
249
+ import { HttpMethod, RuntimeAction, RuntimeActionResponse } from '@adobe-commerce/aio-toolkit';
250
+ import { Core } from '@adobe/aio-sdk';
251
+ import { [EntityName]Collection } from '../../lib/database/collection/[entity-name]';
252
+
253
+ const name = '[action-name]';
254
+
255
+ export const main = RuntimeAction.execute(
256
+ name,
257
+ [HttpMethod.POST],
258
+ [],
259
+ ['Authorization'],
260
+ async (params, ctx) => {
261
+ const { logger } = ctx;
262
+
263
+ const tokenResponse = await Core.AuthClient.generateAccessToken(params);
264
+ const accessToken = tokenResponse && tokenResponse.access_token;
265
+
266
+ const collection = new [EntityName]Collection();
267
+
268
+ logger.info({ message: `${name}-fetching` });
269
+
270
+ const results = await collection.run(async (col) => {
271
+ return await col.find({}).toArray();
272
+ }, accessToken, '[amer/emea/apac]');
273
+
274
+ logger.info({ message: `${name}-fetched`, count: results.length });
275
+
276
+ return RuntimeActionResponse.success(results);
277
+ }
278
+ );
279
+ ```
280
+
281
+ #### Find with Filter — TypeScript
282
+
283
+ ```typescript
284
+ const results = await collection.run(async (col) => {
285
+ return await col
286
+ .find({ status: 'active' })
287
+ .sort({ _created_at: -1 })
288
+ .limit(50)
289
+ .toArray();
290
+ }, accessToken, '[region]');
291
+ ```
292
+
293
+ #### Insert with Validation — TypeScript
294
+
295
+ ```typescript
296
+ // Option A: validate() — fail fast on first error
297
+ collection.validateAll(params); // use validateAll to surface all problems
298
+ const errors = collection.validateAll(params);
299
+ if (errors.length > 0) {
300
+ return RuntimeActionResponse.error(HttpStatus.BAD_REQUEST, errors.join(', '));
301
+ }
302
+
303
+ const result = await collection.run(async (col) => {
304
+ return await col.insertOne({
305
+ [column_1]: params.[column_1],
306
+ [column_2]: params.[column_2],
307
+ });
308
+ }, accessToken, '[region]');
309
+ ```
310
+
311
+ #### JavaScript (equivalent patterns with `require`)
312
+
313
+ ```javascript
314
+ const { HttpMethod, RuntimeAction, RuntimeActionResponse, HttpStatus } = require('@adobe-commerce/aio-toolkit');
315
+ const { Core } = require('@adobe/aio-sdk');
316
+ const { [EntityName]Collection } = require('../../lib/database/collection/[entity-name]');
317
+
318
+ const name = '[action-name]';
319
+
320
+ exports.main = RuntimeAction.execute(
321
+ name,
322
+ [HttpMethod.POST],
323
+ [],
324
+ ['Authorization'],
325
+ async (params, ctx) => {
326
+ const { logger } = ctx;
327
+
328
+ const tokenResponse = await Core.AuthClient.generateAccessToken(params);
329
+ const accessToken = tokenResponse && tokenResponse.access_token;
330
+
331
+ const collection = new [EntityName]Collection();
332
+
333
+ const results = await collection.run(async (col) => {
334
+ return await col.find({}).toArray();
335
+ }, accessToken, 'amer');
336
+
337
+ return RuntimeActionResponse.success(results);
338
+ }
339
+ );
340
+ ```
341
+
342
+ ---
343
+
344
+ ### Step 4.3: Update Action Configuration
345
+
346
+ #### `app.config.yaml` / `ext.config.yaml`
347
+
348
+ ```yaml
349
+ [action-name]:
350
+ function: [action-path]/index.[js/ts]
351
+ web: 'yes'
352
+ runtime: nodejs:22
353
+ annotations:
354
+ require-adobe-auth: true
355
+ final: true
356
+ include-ims-credentials: true # REQUIRED: injects IMS credentials for token generation
357
+ inputs:
358
+ LOG_LEVEL: debug
359
+ ```
360
+
361
+ > **Why `include-ims-credentials: true`?** `AbdbCollection.run()` requires an IMS access token obtained via `Core.AuthClient.generateAccessToken(params)`. Without this annotation, `generateAccessToken` will fail and `accessToken` will be blank.
362
+
363
+ #### Database Provisioning (add once per project)
364
+
365
+ Add the database auto-provision block to `app.config.yaml` if not already present:
366
+
367
+ ```yaml
368
+ application:
369
+ runtimeManifest:
370
+ database:
371
+ auto-provision: true
372
+ region: [amer/emea/apac] # match the region used in collection.run()
373
+ ```
374
+
375
+ Or provision manually for local development:
376
+
377
+ ```bash
378
+ aio app db provision --region amer
379
+ ```
380
+
381
+ > **App Builder Data Services API must be enabled** in your Adobe Developer Console project workspace before provisioning.
382
+
383
+ ---
384
+
385
+ ## Step 5: Recommendations
386
+
387
+ ### A. Always Use `validateAll()` at API Boundaries
388
+
389
+ Prefer `validateAll()` over `validate()` when the action receives data from an external caller — it surfaces all schema errors at once rather than forcing the caller to fix one field at a time:
390
+
391
+ ```typescript
392
+ const errors = collection.validateAll(params);
393
+ if (errors.length > 0) {
394
+ return RuntimeActionResponse.error(HttpStatus.BAD_REQUEST, errors.join(', '));
395
+ }
396
+ ```
397
+
398
+ Use `validate()` (fail-fast) only when you need to short-circuit immediately and the first error is sufficient context.
399
+
400
+ ### B. Collection and Column Naming Rules
401
+
402
+ Both collection names and column names must match `[a-zA-Z0-9_]`:
403
+
404
+ | Input | Valid? | Note |
405
+ |---|---|---|
406
+ | `queue_messages` | ✅ | snake_case — preferred |
407
+ | `orders` | ✅ | simple lowercase |
408
+ | `QueueMessages` | ✅ | PascalCase — allowed but not conventional |
409
+ | `queue-messages` | ❌ | hyphens are not allowed |
410
+ | `order items` | ❌ | spaces are not allowed |
411
+ | `order.items` | ❌ | dots are not allowed |
412
+
413
+ The collection name throws at construction time — catch naming errors during development, not at runtime.
414
+
415
+ ### C. Missing Value Rules — `0` and `false` Are Not Missing
416
+
417
+ For **required** columns, a value is considered **missing** (and will fail) only if it is `undefined`, `null`, or an empty/whitespace-only string:
418
+
419
+ | Value | Required column | Optional column |
420
+ |---|---|---|
421
+ | `undefined` | ❌ Fails — `"field" is required` | ✅ Passes |
422
+ | `null` | ❌ Fails | ✅ Passes |
423
+ | `''` / `' '` | ❌ Fails | ✅ Passes |
424
+ | `0` | ✅ Passes (not missing) | ✅ Passes |
425
+ | `false` | ✅ Passes (not missing) | ✅ Passes |
426
+
427
+ > **Common mistake**: Assuming `0` or `false` will fail a required check — they won't. Only truly absent/empty values fail.
428
+
429
+ ### D. Column Type Accepted Values
430
+
431
+ | Type | Accepted values |
432
+ |---|---|
433
+ | `STRING` | Primitive `string` only |
434
+ | `NUMBER` | Finite `number`, or a string that parses to a finite number via `Number()` (e.g. `'3.14'`) |
435
+ | `BOOLEAN` | Primitive `boolean`, or trimmed string `'true'` / `'false'` (case-insensitive) |
436
+ | `DATETIME` | Valid `Date` instance; finite `number` (ms timestamp); ISO 8601 string (e.g. `'2024-01-15'`, `'2024-01-15T10:30:00.000Z'`) |
437
+
438
+ ### E. Connection Lifecycle — `run()` Always Closes
439
+
440
+ `run()` opens a database connection, executes the callback, then **always closes the connection** in a `finally` block — whether the callback succeeds or throws. You never need to manually close the client.
441
+
442
+ ### F. Error Wrapping in `run()`
443
+
444
+ Errors from the database are wrapped and rethrown with a prefix:
445
+ - `DbError` → `AbdbCollection: database error: <message>`
446
+ - Any other error → `AbdbCollection: unexpected error: <message>`
447
+
448
+ Wrap `run()` in a try/catch at the action level to handle these uniformly:
449
+
450
+ ```typescript
451
+ try {
452
+ const results = await collection.run(async (col) => col.find({}).toArray(), accessToken);
453
+ return RuntimeActionResponse.success(results);
454
+ } catch (error) {
455
+ logger.error({ message: `${name}-db-error`, error: error.message });
456
+ return RuntimeActionResponse.error(HttpStatus.INTERNAL_ERROR, error.message);
457
+ }
458
+ ```
459
+
460
+ ### G. Prefer `AbdbRepository` Over `run()` Directly
461
+
462
+ **Recommendation: always prefer `AbdbRepository`** over calling `run()` directly in action files. `AbdbRepository` provides all standard CRUD operations built-in and exposes `getCollection().run()` for custom queries inside a repository subclass — keeping all database access centralized in one place.
463
+
464
+ | | `run()` in action file | `AbdbRepository` (recommended) |
465
+ |---|---|---|
466
+ | Use case | Avoid — only when explicitly bypassing the repository | All database operations — insert, find, update, delete, count, pagination |
467
+ | Boilerplate | Raw MongoDB query logic | Built-in methods cover all common patterns |
468
+ | Custom queries | Direct in action (discouraged) | Override in repository subclass via `getCollection().run()` |
469
+ | Recommendation | Rare edge cases only | **Default choice** — use "Using AbdbRepository" rule |
470
+
471
+ > **When you need a custom query**: extend `AbdbRepository` and add a method that calls `this.getCollection().run()` — do not call `collection.run()` directly in the action file. This keeps all DB access in the repository layer.
472
+
473
+ ---
474
+
475
+ ## Key Components from @adobe-commerce/aio-toolkit
476
+
477
+ ### AbdbCollection Constructor
478
+
479
+ ```typescript
480
+ new AbdbCollection(
481
+ name: string, // collection name — [a-zA-Z0-9_] only
482
+ callback?: (collection: AbdbCollection) => void // optional fluent setup function
483
+ )
484
+ ```
485
+
486
+ ### AbdbCollection.addColumn()
487
+
488
+ ```typescript
489
+ // Positional form
490
+ addColumn(name: string, type: AbdbColumnType, description?: string, isRequired?: boolean): this
491
+
492
+ // Options object form
493
+ addColumn(name: string, type: AbdbColumnType, options?: { description?: string; isRequired?: boolean }): this
494
+ ```
495
+
496
+ Returns `this` — chainable.
497
+
498
+ ### AbdbCollection.run()
499
+
500
+ ```typescript
501
+ async run<T>(
502
+ callback: (collection: DbCollection, client: DbClient) => Promise<T>,
503
+ token: string, // IMS access token — from Core.AuthClient.generateAccessToken(params)
504
+ region?: string // default: 'amer' — options: 'amer', 'emea', 'apac'
505
+ ): Promise<T>
506
+ ```
507
+
508
+ Connection is always closed in `finally` — never call `client.close()` manually.
509
+
510
+ **`_id` filters inside `run()`:** MongoDB stores document identifiers as `ObjectId` instances, not plain strings. If your callback filters by `_id`, you must wrap the string with `new ObjectId()` from `bson`:
511
+
512
+ ```typescript
513
+ import { ObjectId } from 'bson';
514
+
515
+ const result = await collection.run(async (col) => {
516
+ return await col.findOne({ _id: new ObjectId(params.id) });
517
+ }, accessToken);
518
+ ```
519
+
520
+ > `bson` is a transitive dependency of `@adobe/aio-lib-db` — it is already present in your project and does not need to be installed separately. Plain string comparisons against `_id` will return no results.
521
+
522
+ ### AbdbCollection.validate() / validateAll()
523
+
524
+ ```typescript
525
+ validate(record: Record<string, unknown>): void // throws on first error
526
+ validateAll(record: Record<string, unknown>): string[] // returns all errors ([] if valid)
527
+ ```
528
+
529
+ ### AbdbColumnType Enum
530
+
531
+ ```typescript
532
+ enum AbdbColumnType {
533
+ STRING = 'STRING',
534
+ NUMBER = 'NUMBER',
535
+ BOOLEAN = 'BOOLEAN',
536
+ DATETIME = 'DATETIME',
537
+ }
538
+ ```
539
+
540
+ ### Exports
541
+
542
+ ```typescript
543
+ import {
544
+ AbdbCollection,
545
+ AbdbColumn,
546
+ AbdbColumnType,
547
+ } from '@adobe-commerce/aio-toolkit';
548
+
549
+ // Types (TypeScript only)
550
+ import type {
551
+ AbdbCollectionCallback,
552
+ AbdbRunCallback,
553
+ AddColumnOptions,
554
+ AbdbColumnOptions,
555
+ AbdbColumnJson,
556
+ } from '@adobe-commerce/aio-toolkit';
557
+ ```
558
+
559
+ ---
560
+
561
+ ## Important Notes
562
+
563
+ 1. **Peer dependency is mandatory**: `@adobe/aio-lib-db >= 1.0.0` must be installed — `AbdbCollection` will not function without it
564
+ 2. **`include-ims-credentials: true` is required**: `Core.AuthClient.generateAccessToken(params)` needs injected IMS credentials to generate a token for `run()`
565
+ 3. **Always extend `AbdbCollection`**: Never use `AbdbCollection` directly in action code — always create a dedicated subclass per entity in `lib/database/collection/`
566
+ 4. **Collection and column names are validated at construction**: Naming errors throw immediately — not at query time
567
+ 5. **`run()` always closes the connection**: No manual cleanup needed — the `finally` block handles it regardless of success or failure
568
+ 6. **Database must be provisioned first**: Use `aio app db provision --region amer` or `auto-provision: true` in `app.config.yaml` before the action can connect
569
+ 7. **App Builder Data Services API must be enabled**: Enabled in Adobe Developer Console → Your project → Workspace → Services
570
+ 8. **`0` and `false` are not missing**: Only `undefined`, `null`, and empty/whitespace strings are treated as missing for required column validation
571
+ 9. **`validate()` throws; `validateAll()` returns errors**: Use `validateAll()` at API boundaries for better error messages; use `validate()` for internal guard checks
572
+ 10. **Prefer `AbdbRepository` over direct `run()`**: Do not call `collection.run()` in action files — use `AbdbRepository` for all standard operations and `getCollection().run()` inside a repository subclass for custom queries
573
+ 11. **`_id` filters require `ObjectId`**: Wrap string IDs with `new ObjectId(id)` from `bson` when filtering by `_id` inside a `run()` callback — plain strings will return no results
574
+
575
+ ## Integration with Action Creation Rules
576
+
577
+ **This rule should be referenced in other action creation rules when database storage is mentioned:**
578
+
579
+ ### Trigger Patterns in Other Rules
580
+
581
+ When developers mention these phrases during action creation in the "Business Logic" question:
582
+
583
+ - "Store data in App Builder Data"
584
+ - "Use ABDB / MongoDB"
585
+ - "Persist records to the database"
586
+ - "Define a collection schema"
587
+ - "Query the database"
588
+ - Any phrase containing "ABDB", "App Builder Data", "aio-lib-db", or "MongoDB"
589
+
590
+ **Action to take:**
591
+
592
+ 1. Complete the action creation first
593
+ 2. Then inform the user: "I see you need App Builder Data storage. Let me set up the AbdbCollection."
594
+ 3. Trigger/reference the "Using AbdbCollection" rule
595
+ 4. This rule handles the peer dependency, collection class, token generation, `run()` integration, and YAML config
596
+
597
+ **Example Flow:**
598
+
599
+ ```
600
+ User: "Create a runtime action that saves queue messages to the App Builder database"
601
+ AI: ✓ Detected TypeScript project
602
+ AI: Creating runtime action... [generates index.ts]
603
+ AI: I see you need App Builder Data storage. Let me set up the AbdbCollection.
604
+ AI: [Creates lib/database/collection/queue-messages/index.ts]
605
+ AI: The collection schema is ready. For full CRUD (insert, find, update, delete), use the "Using AbdbRepository" rule next.
606
+ ```
607
+
608
+ ## Related Rules
609
+
610
+ - **Using AbdbRepository**: Add full CRUD operations (insert, find, update, delete, count, pagination) on top of this collection using `AbdbRepository`