@salesforce/afv-skills 1.5.1 → 1.5.2

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 (68) hide show
  1. package/README.md +16 -415
  2. package/package.json +5 -3
  3. package/skills/building-ui-bundle-app/SKILL.md +325 -0
  4. package/skills/building-ui-bundle-frontend/SKILL.md +122 -0
  5. package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/component.md +1 -1
  6. package/skills/creating-b2b-commerce-store/SKILL.md +169 -0
  7. package/skills/creating-b2b-commerce-store/references/store-vs-storefront.md +169 -0
  8. package/skills/deploying-ui-bundle/SKILL.md +77 -0
  9. package/skills/generating-apex/CREDITS.md +30 -0
  10. package/skills/generating-apex/SKILL.md +335 -189
  11. package/skills/generating-apex/assets/abstract.cls +12 -8
  12. package/skills/generating-apex/assets/batch.cls +7 -7
  13. package/skills/generating-apex/assets/domain.cls +5 -5
  14. package/skills/generating-apex/assets/dto.cls +11 -11
  15. package/skills/generating-apex/assets/exception.cls +1 -1
  16. package/skills/generating-apex/assets/interface.cls +2 -2
  17. package/skills/generating-apex/assets/invocable.cls +115 -0
  18. package/skills/generating-apex/assets/queueable.cls +6 -6
  19. package/skills/generating-apex/assets/rest-resource.cls +300 -0
  20. package/skills/generating-apex/assets/schedulable.cls +7 -7
  21. package/skills/generating-apex/assets/selector.cls +7 -7
  22. package/skills/generating-apex/assets/service.cls +4 -4
  23. package/skills/generating-apex/assets/trigger.cls +45 -0
  24. package/skills/generating-apex/assets/utility.cls +5 -5
  25. package/skills/generating-apex/references/AccountDeduplicationBatch.cls +7 -7
  26. package/skills/generating-apex/references/AccountSelector.cls +10 -10
  27. package/skills/generating-apex/references/AccountService.cls +9 -9
  28. package/skills/generating-apex-test/CREDITS.md +30 -0
  29. package/skills/generating-apex-test/SKILL.md +165 -74
  30. package/skills/generating-apex-test/assets/test-class-template.cls +23 -54
  31. package/skills/generating-apex-test/assets/test-data-factory-template.cls +0 -1
  32. package/skills/generating-apex-test/references/assertion-patterns.md +38 -95
  33. package/skills/generating-apex-test/references/async-testing.md +59 -142
  34. package/skills/generating-apex-test/references/mocking-patterns.md +77 -76
  35. package/skills/generating-apex-test/references/test-data-factory.md +29 -130
  36. package/skills/generating-experience-react-site/SKILL.md +9 -9
  37. package/skills/generating-experience-react-site/docs/configure-metadata-digital-experience.md +1 -1
  38. package/skills/generating-flexipage/SKILL.md +28 -12
  39. package/skills/generating-ui-bundle-features/SKILL.md +45 -0
  40. package/skills/generating-ui-bundle-metadata/SKILL.md +106 -0
  41. package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/SKILL.md +5 -5
  42. package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/constraints.md +2 -2
  43. package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/examples.md +1 -1
  44. package/skills/{implementing-webapp-file-upload → implementing-ui-bundle-file-upload}/SKILL.md +11 -11
  45. package/skills/searching-media/SKILL.md +1 -1
  46. package/skills/{using-webapp-salesforce-data → using-ui-bundle-salesforce-data}/SKILL.md +52 -25
  47. package/skills/using-ui-bundle-salesforce-data/references/mutation-query-generation.md +140 -0
  48. package/skills/using-ui-bundle-salesforce-data/references/query-testing.md +78 -0
  49. package/skills/using-ui-bundle-salesforce-data/references/read-query-generation.md +307 -0
  50. package/skills/using-ui-bundle-salesforce-data/references/schema-introspection.md +53 -0
  51. package/skills/using-ui-bundle-salesforce-data/references/ui-bundle-integration.md +221 -0
  52. package/skills/{using-webapp-salesforce-data → using-ui-bundle-salesforce-data/scripts}/graphql-search.sh +75 -23
  53. package/skills/building-webapp-data-visualization/SKILL.md +0 -72
  54. package/skills/building-webapp-data-visualization/implementation/bar-line-chart.md +0 -316
  55. package/skills/building-webapp-data-visualization/implementation/dashboard-layout.md +0 -189
  56. package/skills/building-webapp-data-visualization/implementation/donut-chart.md +0 -181
  57. package/skills/building-webapp-data-visualization/implementation/stat-card.md +0 -150
  58. package/skills/building-webapp-react-components/SKILL.md +0 -96
  59. package/skills/configuring-webapp-csp-trusted-sites/SKILL.md +0 -90
  60. package/skills/configuring-webapp-metadata/SKILL.md +0 -158
  61. package/skills/creating-webapp/SKILL.md +0 -138
  62. package/skills/deploying-webapp-to-salesforce/SKILL.md +0 -226
  63. package/skills/installing-webapp-features/SKILL.md +0 -210
  64. /package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/header-footer.md +0 -0
  65. /package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/page.md +0 -0
  66. /package/skills/{configuring-webapp-csp-trusted-sites/implementation/metadata-format.md → generating-ui-bundle-metadata/implementation/csp-metadata-format.md} +0 -0
  67. /package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/style-tokens.md +0 -0
  68. /package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/troubleshooting.md +0 -0
@@ -0,0 +1,300 @@
1
+ /**
2
+ * REST API endpoint for {SObject} operations.
3
+ * Exposes CRUD operations via @RestResource at /services/apexrest/{urlPath}/v1/*.
4
+ * Uses versioned URL mapping for future-proof API evolution.
5
+ * All queries use WITH USER_MODE for CRUD/FLS enforcement.
6
+ *
7
+ * Authentication: OAuth 2.0 via Connected App
8
+ * Base URL: /services/apexrest/{urlPath}/v1/
9
+ */
10
+ @RestResource(urlMapping='/{urlPath}/v1/*')
11
+ global with sharing class {ClassName} {
12
+
13
+ // ─── Constants ────────────────────────────────────────────────────────
14
+ private static final Integer DEFAULT_PAGE_SIZE = 20;
15
+ private static final Integer MAX_PAGE_SIZE = 200;
16
+ private static final String ERROR_MISSING_ID = 'Record Id is required in the URL path.';
17
+ private static final String ERROR_INVALID_ID = 'Invalid Salesforce Id format.';
18
+ private static final String ERROR_MISSING_BODY = 'Request body is required.';
19
+ private static final String ERROR_NOT_FOUND = '{SObject} record not found.';
20
+ private static final String ERROR_INSUFFICIENT_ACCESS = 'Insufficient access to perform this operation.';
21
+
22
+ // ─── GET — Retrieve ───────────────────────────────────────────────────
23
+
24
+ /**
25
+ * Retrieves a single {SObject} by Id (URL path) or a paginated list (query params).
26
+ * Single: GET /services/apexrest/{urlPath}/v1/{recordId}
27
+ * List: GET /services/apexrest/{urlPath}/v1?pageSize=20&offset=0
28
+ * @return ApiResponse containing the requested data
29
+ */
30
+ @HttpGet
31
+ global static ApiResponse doGet() {
32
+ RestRequest req = RestContext.request;
33
+ RestResponse res = RestContext.response;
34
+
35
+ try {
36
+ String recordId = extractIdFromUri(req.requestURI);
37
+
38
+ if (String.isNotBlank(recordId)) {
39
+ return getSingleRecord(recordId, res);
40
+ }
41
+ return getRecordList(req, res);
42
+
43
+ } catch (Exception e) {
44
+ return handleError(res, 500, e.getMessage());
45
+ }
46
+ }
47
+
48
+ // ─── POST — Create ───────────────────────────────────────────────────
49
+
50
+ /**
51
+ * Creates a new {SObject} record from the JSON request body.
52
+ * POST /services/apexrest/{urlPath}/v1/
53
+ * @return ApiResponse with the created record Id
54
+ */
55
+ @HttpPost
56
+ global static ApiResponse doPost() {
57
+ RestRequest req = RestContext.request;
58
+ RestResponse res = RestContext.response;
59
+
60
+ try {
61
+ if (req.requestBody == null || String.isBlank(req.requestBody.toString())) {
62
+ return handleError(res, 400, ERROR_MISSING_BODY);
63
+ }
64
+
65
+ {ClassName}Request payload = ({ClassName}Request) JSON.deserialize(
66
+ req.requestBody.toString(),
67
+ {ClassName}Request.class
68
+ );
69
+
70
+ // TODO: Map request payload to SObject fields
71
+ {SObject} record = new {SObject}(
72
+ Name = payload.name
73
+ );
74
+
75
+ Database.SaveResult saveResult = Database.insert(record, true);
76
+ if (saveResult.isSuccess()) {
77
+ res.statusCode = 201;
78
+ return new ApiResponse(true, 'Record created successfully.', record.Id);
79
+ }
80
+
81
+ return handleError(res, 422, saveResult.getErrors()[0].getMessage());
82
+
83
+ } catch (JSONException e) {
84
+ return handleError(res, 400, 'Malformed JSON: ' + e.getMessage());
85
+ } catch (DmlException e) {
86
+ return handleError(res, 422, 'Validation failed: ' + e.getDmlMessage(0));
87
+ } catch (Exception e) {
88
+ return handleError(res, 500, e.getMessage());
89
+ }
90
+ }
91
+
92
+ // ─── PATCH — Partial Update ───────────────────────────────────────────
93
+
94
+ /**
95
+ * Partially updates an existing {SObject} record.
96
+ * PATCH /services/apexrest/{urlPath}/v1/{recordId}
97
+ * @return ApiResponse confirming the update
98
+ */
99
+ @HttpPatch
100
+ global static ApiResponse doPatch() {
101
+ RestRequest req = RestContext.request;
102
+ RestResponse res = RestContext.response;
103
+
104
+ try {
105
+ String recordId = extractIdFromUri(req.requestURI);
106
+ if (String.isBlank(recordId)) {
107
+ return handleError(res, 400, ERROR_MISSING_ID);
108
+ }
109
+ if (!isValidSalesforceId(recordId)) {
110
+ return handleError(res, 400, ERROR_INVALID_ID);
111
+ }
112
+ if (req.requestBody == null || String.isBlank(req.requestBody.toString())) {
113
+ return handleError(res, 400, ERROR_MISSING_BODY);
114
+ }
115
+
116
+ List<{SObject}> existing = [
117
+ SELECT Id FROM {SObject} WHERE Id = :recordId WITH USER_MODE LIMIT 1
118
+ ];
119
+ if (existing.isEmpty()) {
120
+ return handleError(res, 404, ERROR_NOT_FOUND);
121
+ }
122
+
123
+ Map<String, Object> fieldUpdates = (Map<String, Object>) JSON.deserializeUntyped(
124
+ req.requestBody.toString()
125
+ );
126
+
127
+ {SObject} record = existing[0];
128
+ // TODO: Apply allowed field updates from the payload to the record
129
+ // for (String fieldName : fieldUpdates.keySet()) {
130
+ // record.put(fieldName, fieldUpdates.get(fieldName));
131
+ // }
132
+
133
+ Database.SaveResult saveResult = Database.update(record, true);
134
+ if (saveResult.isSuccess()) {
135
+ res.statusCode = 200;
136
+ return new ApiResponse(true, 'Record updated successfully.', record.Id);
137
+ }
138
+
139
+ return handleError(res, 422, saveResult.getErrors()[0].getMessage());
140
+
141
+ } catch (JSONException e) {
142
+ return handleError(res, 400, 'Malformed JSON: ' + e.getMessage());
143
+ } catch (DmlException e) {
144
+ return handleError(res, 422, 'Validation failed: ' + e.getDmlMessage(0));
145
+ } catch (Exception e) {
146
+ return handleError(res, 500, e.getMessage());
147
+ }
148
+ }
149
+
150
+ // ─── DELETE — Remove ──────────────────────────────────────────────────
151
+
152
+ /**
153
+ * Deletes a {SObject} record by Id.
154
+ * DELETE /services/apexrest/{urlPath}/v1/{recordId}
155
+ * @return ApiResponse confirming the deletion
156
+ */
157
+ @HttpDelete
158
+ global static ApiResponse doDelete() {
159
+ RestRequest req = RestContext.request;
160
+ RestResponse res = RestContext.response;
161
+
162
+ try {
163
+ String recordId = extractIdFromUri(req.requestURI);
164
+ if (String.isBlank(recordId)) {
165
+ return handleError(res, 400, ERROR_MISSING_ID);
166
+ }
167
+ if (!isValidSalesforceId(recordId)) {
168
+ return handleError(res, 400, ERROR_INVALID_ID);
169
+ }
170
+
171
+ List<{SObject}> existing = [
172
+ SELECT Id FROM {SObject} WHERE Id = :recordId WITH USER_MODE LIMIT 1
173
+ ];
174
+ if (existing.isEmpty()) {
175
+ return handleError(res, 404, ERROR_NOT_FOUND);
176
+ }
177
+
178
+ Database.DeleteResult deleteResult = Database.delete(existing[0], true);
179
+ if (deleteResult.isSuccess()) {
180
+ res.statusCode = 200;
181
+ return new ApiResponse(true, 'Record deleted successfully.', recordId);
182
+ }
183
+
184
+ return handleError(res, 422, deleteResult.getErrors()[0].getMessage());
185
+
186
+ } catch (DmlException e) {
187
+ return handleError(res, 422, e.getDmlMessage(0));
188
+ } catch (Exception e) {
189
+ return handleError(res, 500, e.getMessage());
190
+ }
191
+ }
192
+
193
+ // ─── Private Helpers ──────────────────────────────────────────────────
194
+
195
+ private static ApiResponse getSingleRecord(String recordId, RestResponse res) {
196
+ if (!isValidSalesforceId(recordId)) {
197
+ return handleError(res, 400, ERROR_INVALID_ID);
198
+ }
199
+
200
+ List<{SObject}> records = [
201
+ SELECT Id, Name, CreatedDate, LastModifiedDate
202
+ FROM {SObject}
203
+ WHERE Id = :recordId
204
+ WITH USER_MODE
205
+ LIMIT 1
206
+ ];
207
+
208
+ if (records.isEmpty()) {
209
+ return handleError(res, 404, ERROR_NOT_FOUND);
210
+ }
211
+
212
+ res.statusCode = 200;
213
+ ApiResponse response = new ApiResponse(true, 'Record retrieved successfully.', recordId);
214
+ response.data = records[0];
215
+ return response;
216
+ }
217
+
218
+ private static ApiResponse getRecordList(RestRequest req, RestResponse res) {
219
+ Integer pageSize = getIntParam(req, 'pageSize', DEFAULT_PAGE_SIZE);
220
+ Integer offset = getIntParam(req, 'offset', 0);
221
+
222
+ pageSize = Math.min(pageSize, MAX_PAGE_SIZE);
223
+
224
+ List<{SObject}> records = [
225
+ SELECT Id, Name, CreatedDate, LastModifiedDate
226
+ FROM {SObject}
227
+ WITH USER_MODE
228
+ ORDER BY Name ASC
229
+ LIMIT :pageSize
230
+ OFFSET :offset
231
+ ];
232
+
233
+ res.statusCode = 200;
234
+ ApiResponse response = new ApiResponse(true, 'Records retrieved successfully.', null);
235
+ response.records = records;
236
+ response.pageSize = pageSize;
237
+ response.offset = offset;
238
+ return response;
239
+ }
240
+
241
+ private static String extractIdFromUri(String uri) {
242
+ String lastSegment = uri.substringAfterLast('/');
243
+ if (String.isBlank(lastSegment) || lastSegment == 'v1') {
244
+ return null;
245
+ }
246
+ return lastSegment;
247
+ }
248
+
249
+ private static Boolean isValidSalesforceId(String idValue) {
250
+ return Pattern.matches('[a-zA-Z0-9]{15,18}', idValue);
251
+ }
252
+
253
+ private static Integer getIntParam(RestRequest req, String paramName, Integer defaultValue) {
254
+ String paramValue = req.params.get(paramName);
255
+ if (String.isBlank(paramValue)) {
256
+ return defaultValue;
257
+ }
258
+ try {
259
+ return Integer.valueOf(paramValue);
260
+ } catch (TypeException e) {
261
+ return defaultValue;
262
+ }
263
+ }
264
+
265
+ private static ApiResponse handleError(RestResponse res, Integer statusCode, String message) {
266
+ res.statusCode = statusCode;
267
+ return new ApiResponse(false, message, null);
268
+ }
269
+
270
+ // ─── Request / Response DTOs ──────────────────────────────────────────
271
+
272
+ /**
273
+ * Inbound request payload for POST operations.
274
+ * Extend with additional fields as needed.
275
+ */
276
+ global class {ClassName}Request {
277
+ global String name;
278
+ // TODO: Add fields matching the expected JSON request body
279
+ }
280
+
281
+ /**
282
+ * Standardized API response envelope.
283
+ * All endpoints return this structure for consistent client parsing.
284
+ */
285
+ global class ApiResponse {
286
+ global Boolean success;
287
+ global String message;
288
+ global String recordId;
289
+ global SObject data;
290
+ global List<SObject> records;
291
+ global Integer pageSize;
292
+ global Integer offset;
293
+
294
+ global ApiResponse(Boolean success, String message, String recordId) {
295
+ this.success = success;
296
+ this.message = message;
297
+ this.recordId = recordId;
298
+ }
299
+ }
300
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @description Schedulable Apex class for {describe the scheduled operation}.
2
+ * Schedulable Apex class for {describe the scheduled operation}.
3
3
  * Delegates heavy processing to a Batch or Queueable job.
4
4
  * Keep execute() lightweight — it should only launch other jobs.
5
5
  * @author Generated by Apex Class Writer Skill
@@ -20,19 +20,19 @@ public with sharing class {ClassName} implements Schedulable {
20
20
  // ─── CRON Expressions ────────────────────────────────────────────────
21
21
  // Seconds Minutes Hours Day_of_month Month Day_of_week Optional_year
22
22
 
23
- /** @description Runs daily at 2:00 AM */
23
+ /** Runs daily at 2:00 AM */
24
24
  public static final String CRON_DAILY_2AM = '0 0 2 * * ?';
25
25
 
26
- /** @description Runs every weekday at 6:00 AM */
26
+ /** Runs every weekday at 6:00 AM */
27
27
  public static final String CRON_WEEKDAYS_6AM = '0 0 6 ? * MON-FRI';
28
28
 
29
- /** @description Runs hourly at the top of the hour */
29
+ /** Runs hourly at the top of the hour */
30
30
  public static final String CRON_HOURLY = '0 0 * * * ?';
31
31
 
32
32
  // ─── Schedulable Interface ───────────────────────────────────────────
33
33
 
34
34
  /**
35
- * @description Entry point for the scheduled execution.
35
+ * Entry point for the scheduled execution.
36
36
  * Delegates to a Batch or Queueable for the actual work.
37
37
  * @param sc The schedulable context
38
38
  */
@@ -49,7 +49,7 @@ public with sharing class {ClassName} implements Schedulable {
49
49
  // ─── Convenience Scheduling Methods ──────────────────────────────────
50
50
 
51
51
  /**
52
- * @description Schedules this job to run daily at 2 AM
52
+ * Schedules this job to run daily at 2 AM
53
53
  * @return The scheduled job Id
54
54
  */
55
55
  public static String scheduleDaily() {
@@ -61,7 +61,7 @@ public with sharing class {ClassName} implements Schedulable {
61
61
  }
62
62
 
63
63
  /**
64
- * @description Aborts this scheduled job by name
64
+ * Aborts this scheduled job by name
65
65
  * @param jobName The name used when scheduling
66
66
  */
67
67
  public static void abort(String jobName) {
@@ -1,15 +1,15 @@
1
1
  /**
2
- * @description Selector class for {SObject} queries.
2
+ * Selector class for {SObject} queries.
3
3
  * Encapsulates all SOQL for {SObject} records.
4
4
  * All methods return bulkified results (Lists or Maps).
5
5
  * @author Generated by Apex Class Writer Skill
6
6
  */
7
- public with sharing class {SObject}Selector {
7
+ public inherited sharing class {SObject}Selector {
8
8
 
9
9
  // ─── Field Lists ─────────────────────────────────────────────────────
10
10
 
11
11
  /**
12
- * @description Returns the default set of fields to query for {SObject}.
12
+ * Returns the default set of fields to query for {SObject}.
13
13
  * Centralizes field references to keep queries DRY.
14
14
  * @return Comma-separated field list as a String
15
15
  */
@@ -29,7 +29,7 @@ public with sharing class {SObject}Selector {
29
29
  // ─── Query Methods ───────────────────────────────────────────────────
30
30
 
31
31
  /**
32
- * @description Selects {SObject} records by their Ids
32
+ * Selects {SObject} records by their Ids
33
33
  * @param recordIds Set of {SObject} Ids to query
34
34
  * @return List of {SObject} records matching the provided Ids
35
35
  * @example
@@ -49,7 +49,7 @@ public with sharing class {SObject}Selector {
49
49
  }
50
50
 
51
51
  /**
52
- * @description Selects {SObject} records as a Map keyed by Id
52
+ * Selects {SObject} records as a Map keyed by Id
53
53
  * @param recordIds Set of {SObject} Ids to query
54
54
  * @return Map of Id to {SObject}
55
55
  */
@@ -58,7 +58,7 @@ public with sharing class {SObject}Selector {
58
58
  }
59
59
 
60
60
  /**
61
- * @description Selects {SObject} records by a specific field value
61
+ * Selects {SObject} records by a specific field value
62
62
  * @param fieldName API name of the field to filter on
63
63
  * @param values Set of values to match
64
64
  * @return List of matching {SObject} records
@@ -86,7 +86,7 @@ public with sharing class {SObject}Selector {
86
86
  // ─── Exception ───────────────────────────────────────────────────────
87
87
 
88
88
  /**
89
- * @description Custom exception for query errors
89
+ * Custom exception for query errors
90
90
  */
91
91
  public class QueryException extends Exception {}
92
92
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @description Service class for {SObject} business logic.
2
+ * Service class for {SObject} business logic.
3
3
  * Follows separation of concerns: delegates queries to {SObject}Selector
4
4
  * and SObject manipulation to {SObject}Domain where applicable.
5
5
  * @author Generated by Apex Class Writer Skill
@@ -12,7 +12,7 @@ public with sharing class {SObject}Service {
12
12
  // ─── Public API ──────────────────────────────────────────────────────
13
13
 
14
14
  /**
15
- * @description {Describe the primary operation}
15
+ * {Describe the primary operation}
16
16
  * @param recordIds Set of {SObject} Ids to process
17
17
  * @return List of processed {SObject} records
18
18
  * @throws {SObject}ServiceException if processing fails
@@ -47,7 +47,7 @@ public with sharing class {SObject}Service {
47
47
  // ─── Convenience Overloads ───────────────────────────────────────────
48
48
 
49
49
  /**
50
- * @description Single-record convenience overload
50
+ * Single-record convenience overload
51
51
  * @param recordId The {SObject} Id to process
52
52
  * @return The processed {SObject} record
53
53
  */
@@ -63,7 +63,7 @@ public with sharing class {SObject}Service {
63
63
  // ─── Exception ───────────────────────────────────────────────────────
64
64
 
65
65
  /**
66
- * @description Custom exception for {SObject}Service errors
66
+ * Custom exception for {SObject}Service errors
67
67
  */
68
68
  public class {SObject}ServiceException extends Exception {}
69
69
  }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @author Generated by Apex Class Writer Skill
3
+ */
4
+ trigger {SObject}Trigger on {SObject} (
5
+ before insert,
6
+ before update,
7
+ before delete,
8
+ after insert,
9
+ after update,
10
+ after delete,
11
+ after undelete
12
+ ) {
13
+ // ─── Option A: Trigger Actions Framework (TAF) ───────────────────────
14
+ // Delegates to Trigger_Action__mdt-registered action classes.
15
+ // Each action class handles one concern in one context.
16
+ //
17
+ // new MetadataTriggerHandler().run();
18
+
19
+ // ─── Option B: Custom Handler Pattern ────────────────────────────────
20
+ // Delegates to a single handler class that routes by context.
21
+ //
22
+ // {SObject}TriggerHandler handler = new {SObject}TriggerHandler();
23
+ //
24
+ // if (Trigger.isBefore) {
25
+ // if (Trigger.isInsert) {
26
+ // handler.beforeInsert(Trigger.new);
27
+ // } else if (Trigger.isUpdate) {
28
+ // handler.beforeUpdate(Trigger.new, Trigger.oldMap);
29
+ // } else if (Trigger.isDelete) {
30
+ // handler.beforeDelete(Trigger.old, Trigger.oldMap);
31
+ // }
32
+ // } else if (Trigger.isAfter) {
33
+ // if (Trigger.isInsert) {
34
+ // handler.afterInsert(Trigger.new, Trigger.newMap);
35
+ // } else if (Trigger.isUpdate) {
36
+ // handler.afterUpdate(Trigger.new, Trigger.oldMap);
37
+ // } else if (Trigger.isDelete) {
38
+ // handler.afterDelete(Trigger.old, Trigger.oldMap);
39
+ // } else if (Trigger.isUndelete) {
40
+ // handler.afterUndelete(Trigger.new, Trigger.newMap);
41
+ // }
42
+ // }
43
+
44
+ // TODO: Uncomment one option above and remove the other
45
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @description Utility class for {describe the category of utilities: String, Date, Collection, etc.}.
2
+ * Utility class for {describe the category of utilities: String, Date, Collection, etc.}.
3
3
  * All methods are static and side-effect-free (no SOQL, no DML).
4
4
  * Private constructor prevents instantiation.
5
5
  * @author Generated by Apex Class Writer Skill
@@ -9,7 +9,7 @@ public with sharing class {ClassName} {
9
9
  // ─── Private Constructor ─────────────────────────────────────────────
10
10
 
11
11
  /**
12
- * @description Prevents instantiation — use static methods only
12
+ * Prevents instantiation — use static methods only
13
13
  */
14
14
  @TestVisible
15
15
  private {ClassName}() {
@@ -21,7 +21,7 @@ public with sharing class {ClassName} {
21
21
  // TODO: Add utility methods below. Examples:
22
22
 
23
23
  /**
24
- * @description Safely converts a String to an Integer, returning a default if parsing fails
24
+ * Safely converts a String to an Integer, returning a default if parsing fails
25
25
  * @param value The String to parse
26
26
  * @param defaultValue The fallback value if parsing fails
27
27
  * @return The parsed Integer or the default value
@@ -41,7 +41,7 @@ public with sharing class {ClassName} {
41
41
  }
42
42
 
43
43
  /**
44
- * @description Chunks a list into smaller sublists of the specified size.
44
+ * Chunks a list into smaller sublists of the specified size.
45
45
  * Useful for processing records in governor-limit-safe batches.
46
46
  * @param items The list to chunk
47
47
  * @param chunkSize The maximum size of each chunk
@@ -72,7 +72,7 @@ public with sharing class {ClassName} {
72
72
  }
73
73
 
74
74
  /**
75
- * @description Extracts a Set of non-null field values from a list of SObjects
75
+ * Extracts a Set of non-null field values from a list of SObjects
76
76
  * @param records The SObject records to extract from
77
77
  * @param fieldName The API name of the field to extract
78
78
  * @return A Set of non-null String values
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @description Batch Apex class for identifying and flagging duplicate Account records.
2
+ * Batch Apex class for identifying and flagging duplicate Account records.
3
3
  * Compares Accounts by Name and BillingPostalCode to find potential duplicates.
4
4
  * Flags duplicates by setting the Is_Potential_Duplicate__c checkbox.
5
5
  * Implements Database.Stateful to track results across batch chunks.
@@ -26,7 +26,7 @@ public with sharing class AccountDeduplicationBatch implements Database.Batchabl
26
26
  // ─── Batchable Interface ─────────────────────────────────────────────
27
27
 
28
28
  /**
29
- * @description Queries all active Accounts that haven't already been flagged
29
+ * Queries all active Accounts that haven't already been flagged
30
30
  * @param bc The batch context
31
31
  * @return QueryLocator scoped to unflagged active Accounts
32
32
  */
@@ -41,7 +41,7 @@ public with sharing class AccountDeduplicationBatch implements Database.Batchabl
41
41
  }
42
42
 
43
43
  /**
44
- * @description Processes each batch by building a duplicate key and checking for matches.
44
+ * Processes each batch by building a duplicate key and checking for matches.
45
45
  * Uses a composite key of normalized Name + BillingPostalCode.
46
46
  * @param bc The batch context
47
47
  * @param scope List of Account records in the current batch
@@ -82,7 +82,7 @@ public with sharing class AccountDeduplicationBatch implements Database.Batchabl
82
82
  }
83
83
 
84
84
  /**
85
- * @description Logs a summary of the deduplication batch run
85
+ * Logs a summary of the deduplication batch run
86
86
  * @param bc The batch context
87
87
  */
88
88
  public void finish(Database.BatchableContext bc) {
@@ -101,7 +101,7 @@ public with sharing class AccountDeduplicationBatch implements Database.Batchabl
101
101
  // ─── Private Helpers ─────────────────────────────────────────────────
102
102
 
103
103
  /**
104
- * @description Builds a normalized composite key for duplicate detection
104
+ * Builds a normalized composite key for duplicate detection
105
105
  * @param acct The Account record
106
106
  * @return Normalized key string, or null if insufficient data
107
107
  */
@@ -117,7 +117,7 @@ public with sharing class AccountDeduplicationBatch implements Database.Batchabl
117
117
  }
118
118
 
119
119
  /**
120
- * @description Processes DML results, tracking successes and failures
120
+ * Processes DML results, tracking successes and failures
121
121
  * @param results List of Database.SaveResult from update operation
122
122
  */
123
123
  private void processResults(List<Database.SaveResult> results) {
@@ -139,7 +139,7 @@ public with sharing class AccountDeduplicationBatch implements Database.Batchabl
139
139
  // ─── Static Helpers ──────────────────────────────────────────────────
140
140
 
141
141
  /**
142
- * @description Convenience method to execute with default batch size
142
+ * Convenience method to execute with default batch size
143
143
  * @return The batch job Id
144
144
  */
145
145
  public static Id run() {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @description Selector class for Account queries.
2
+ * Selector class for Account queries.
3
3
  * Encapsulates all SOQL for Account records.
4
4
  * All methods return bulkified results (Lists or Maps).
5
5
  * @author Generated by Apex Class Writer Skill
@@ -9,7 +9,7 @@ public with sharing class AccountSelector {
9
9
  // ─── Field Lists ─────────────────────────────────────────────────────
10
10
 
11
11
  /**
12
- * @description Returns the default set of fields to query for Account.
12
+ * Returns the default set of fields to query for Account.
13
13
  * Centralizes field references to keep queries DRY.
14
14
  * @return Comma-separated field list as a String
15
15
  */
@@ -34,7 +34,7 @@ public with sharing class AccountSelector {
34
34
  }
35
35
 
36
36
  /**
37
- * @description Returns fields needed for billing/territory operations
37
+ * Returns fields needed for billing/territory operations
38
38
  * @return Comma-separated field list as a String
39
39
  */
40
40
  private static String getBillingFields() {
@@ -56,7 +56,7 @@ public with sharing class AccountSelector {
56
56
  // ─── Query Methods ───────────────────────────────────────────────────
57
57
 
58
58
  /**
59
- * @description Selects Account records by their Ids
59
+ * Selects Account records by their Ids
60
60
  * @param recordIds Set of Account Ids to query
61
61
  * @return List of Account records matching the provided Ids
62
62
  * @example
@@ -76,7 +76,7 @@ public with sharing class AccountSelector {
76
76
  }
77
77
 
78
78
  /**
79
- * @description Selects Account records as a Map keyed by Id
79
+ * Selects Account records as a Map keyed by Id
80
80
  * @param recordIds Set of Account Ids to query
81
81
  * @return Map of Id to Account
82
82
  */
@@ -85,7 +85,7 @@ public with sharing class AccountSelector {
85
85
  }
86
86
 
87
87
  /**
88
- * @description Selects Accounts with billing address fields for territory assignment
88
+ * Selects Accounts with billing address fields for territory assignment
89
89
  * @param recordIds Set of Account Ids to query
90
90
  * @return List of Account records with billing address fields populated
91
91
  */
@@ -102,7 +102,7 @@ public with sharing class AccountSelector {
102
102
  }
103
103
 
104
104
  /**
105
- * @description Selects Accounts by Account Type
105
+ * Selects Accounts by Account Type
106
106
  * @param accountTypes Set of Account Type values to filter by
107
107
  * @return List of matching Account records
108
108
  * @example
@@ -124,7 +124,7 @@ public with sharing class AccountSelector {
124
124
  }
125
125
 
126
126
  /**
127
- * @description Selects Accounts by Industry with a minimum annual revenue
127
+ * Selects Accounts by Industry with a minimum annual revenue
128
128
  * @param industries Set of Industry values to filter by
129
129
  * @param minRevenue Minimum AnnualRevenue threshold
130
130
  * @return List of matching Account records ordered by revenue descending
@@ -151,7 +151,7 @@ public with sharing class AccountSelector {
151
151
  }
152
152
 
153
153
  /**
154
- * @description Selects Accounts with their related Contacts (subquery)
154
+ * Selects Accounts with their related Contacts (subquery)
155
155
  * @param recordIds Set of Account Ids to query
156
156
  * @return List of Account records with nested Contacts
157
157
  */
@@ -173,7 +173,7 @@ public with sharing class AccountSelector {
173
173
  // ─── Aggregate Queries ───────────────────────────────────────────────
174
174
 
175
175
  /**
176
- * @description Returns a count of Accounts grouped by Industry
176
+ * Returns a count of Accounts grouped by Industry
177
177
  * @return List of AggregateResult with Industry and record count
178
178
  * @example
179
179
  * List<AggregateResult> results = AccountSelector.countByIndustry();