@salesforce/afv-skills 1.5.0 → 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.
- package/README.md +16 -415
- package/package.json +5 -3
- package/skills/building-ui-bundle-app/SKILL.md +325 -0
- package/skills/building-ui-bundle-frontend/SKILL.md +122 -0
- package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/component.md +1 -1
- package/skills/creating-b2b-commerce-store/SKILL.md +169 -0
- package/skills/creating-b2b-commerce-store/references/store-vs-storefront.md +169 -0
- package/skills/deploying-ui-bundle/SKILL.md +77 -0
- package/skills/generating-apex/CREDITS.md +30 -0
- package/skills/generating-apex/SKILL.md +399 -0
- package/skills/generating-apex/assets/abstract.cls +132 -0
- package/skills/generating-apex/assets/batch.cls +125 -0
- package/skills/generating-apex/assets/domain.cls +102 -0
- package/skills/generating-apex/assets/dto.cls +108 -0
- package/skills/generating-apex/assets/exception.cls +51 -0
- package/skills/generating-apex/assets/interface.cls +25 -0
- package/skills/generating-apex/assets/invocable.cls +115 -0
- package/skills/generating-apex/assets/queueable.cls +92 -0
- package/skills/generating-apex/assets/rest-resource.cls +300 -0
- package/skills/generating-apex/assets/schedulable.cls +75 -0
- package/skills/generating-apex/assets/selector.cls +92 -0
- package/skills/generating-apex/assets/service.cls +69 -0
- package/skills/generating-apex/assets/trigger.cls +45 -0
- package/skills/generating-apex/assets/utility.cls +97 -0
- package/skills/generating-apex/references/AccountDeduplicationBatch.cls +148 -0
- package/skills/generating-apex/references/AccountSelector.cls +193 -0
- package/skills/generating-apex/references/AccountService.cls +201 -0
- package/skills/generating-apex-test/CREDITS.md +30 -0
- package/skills/generating-apex-test/SKILL.md +199 -0
- package/skills/generating-apex-test/assets/test-class-template.cls +93 -0
- package/skills/generating-apex-test/assets/test-data-factory-template.cls +111 -0
- package/skills/generating-apex-test/references/assertion-patterns.md +108 -0
- package/skills/generating-apex-test/references/async-testing.md +193 -0
- package/skills/generating-apex-test/references/mocking-patterns.md +220 -0
- package/skills/generating-apex-test/references/test-data-factory.md +75 -0
- package/skills/generating-experience-react-site/SKILL.md +20 -9
- package/skills/generating-experience-react-site/docs/configure-metadata-digital-experience.md +1 -1
- package/skills/generating-flexipage/SKILL.md +58 -60
- package/skills/generating-ui-bundle-features/SKILL.md +45 -0
- package/skills/generating-ui-bundle-metadata/SKILL.md +106 -0
- package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/SKILL.md +5 -5
- package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/constraints.md +2 -2
- package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/examples.md +1 -1
- package/skills/{implementing-webapp-file-upload → implementing-ui-bundle-file-upload}/SKILL.md +11 -11
- package/skills/searching-media/SKILL.md +342 -0
- package/skills/{using-webapp-salesforce-data → using-ui-bundle-salesforce-data}/SKILL.md +52 -25
- package/skills/using-ui-bundle-salesforce-data/references/mutation-query-generation.md +140 -0
- package/skills/using-ui-bundle-salesforce-data/references/query-testing.md +78 -0
- package/skills/using-ui-bundle-salesforce-data/references/read-query-generation.md +307 -0
- package/skills/using-ui-bundle-salesforce-data/references/schema-introspection.md +53 -0
- package/skills/using-ui-bundle-salesforce-data/references/ui-bundle-integration.md +221 -0
- package/skills/{using-webapp-salesforce-data → using-ui-bundle-salesforce-data/scripts}/graphql-search.sh +75 -23
- package/skills/building-webapp-data-visualization/SKILL.md +0 -72
- package/skills/building-webapp-data-visualization/implementation/bar-line-chart.md +0 -316
- package/skills/building-webapp-data-visualization/implementation/dashboard-layout.md +0 -189
- package/skills/building-webapp-data-visualization/implementation/donut-chart.md +0 -181
- package/skills/building-webapp-data-visualization/implementation/stat-card.md +0 -150
- package/skills/building-webapp-react-components/SKILL.md +0 -96
- package/skills/configuring-webapp-csp-trusted-sites/SKILL.md +0 -90
- package/skills/configuring-webapp-metadata/SKILL.md +0 -158
- package/skills/creating-webapp/SKILL.md +0 -140
- package/skills/deploying-webapp-to-salesforce/SKILL.md +0 -226
- package/skills/installing-webapp-features/SKILL.md +0 -210
- /package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/header-footer.md +0 -0
- /package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/page.md +0 -0
- /package/skills/{configuring-webapp-csp-trusted-sites/implementation/metadata-format.md → generating-ui-bundle-metadata/implementation/csp-metadata-format.md} +0 -0
- /package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/style-tokens.md +0 -0
- /package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/troubleshooting.md +0 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: searching-media
|
|
3
|
+
description: "Searches for and retrieves existing visual media (images, logos, icons, photos, graphics, banners, thumbnails, hero images, backgrounds) from sources such as Salesforce CMS, Data 360 or any other source. Use this skill ANY TIME a user request involves finding, searching, getting, fetching, retrieving, grabbing, looking up, or locating media. Takes PRIORITY and activates FIRST when ANY media search/retrieval is mentioned, regardless of what else happens with the media afterward. Triggers for requests like \"search for logo\", \"find hero image\", \"get company logo\", \"locate icons\", \"fetch background image\", \"retrieve product photos\". Handles the search and source selection workflow. Does not apply when the request is about brand search, to generate NEW images with AI, design custom graphics from scratch, or edit existing images."
|
|
4
|
+
compatibility: "Requires search_media_cms_channels and/or search_electronic_media MCP tools"
|
|
5
|
+
metadata:
|
|
6
|
+
version: "1.0"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Media Search
|
|
10
|
+
|
|
11
|
+
Universal routing skill for searching and retrieving existing images and media.
|
|
12
|
+
|
|
13
|
+
## Scope
|
|
14
|
+
|
|
15
|
+
**This skill is for SEARCHING FOR existing media, not CREATING new media.**
|
|
16
|
+
|
|
17
|
+
**Use this skill when the user wants to:**
|
|
18
|
+
- Search for images in Salesforce CMS, Data Cloud
|
|
19
|
+
- Find existing visual assets to use in their app
|
|
20
|
+
- Retrieve media from connected sources
|
|
21
|
+
- Browse available images for their project
|
|
22
|
+
- Locate specific photos or graphics
|
|
23
|
+
|
|
24
|
+
**DO NOT use this skill when the user wants to:**
|
|
25
|
+
- Generate new images with AI (use image generation tools)
|
|
26
|
+
- Create graphics or designs from scratch
|
|
27
|
+
- Edit or modify existing images
|
|
28
|
+
- Build custom visuals or diagrams
|
|
29
|
+
|
|
30
|
+
## Before You Search
|
|
31
|
+
|
|
32
|
+
**CRITICAL: This is a routing skill, not a direct search skill.**
|
|
33
|
+
|
|
34
|
+
When a user requests to find an image:
|
|
35
|
+
|
|
36
|
+
**Your first response MUST be plain text only — zero tool calls.** You MUST follow this sequence:
|
|
37
|
+
|
|
38
|
+
1. **First response MUST be text only:** A numbered list of search sources for the user. No tool calls of any kind.
|
|
39
|
+
2. **Wait for user to reply** with their selected option number
|
|
40
|
+
3. **Only then** call the appropriate search tool (this is the FIRST tool call in the entire interaction)
|
|
41
|
+
|
|
42
|
+
**Example of what NOT to do:**
|
|
43
|
+
- ❌ Calling ANY tool before the user picks a source (MCP tools, file reads, descriptor checks, etc.)
|
|
44
|
+
- ❌ "Checking which MCP tools are available" — do not probe or discover tools via tool calls
|
|
45
|
+
- ❌ Immediately calling `search_electronic_media` or `search_media_cms_channels`
|
|
46
|
+
- ❌ Reading MCP tool descriptors or schemas to see what's available
|
|
47
|
+
- ❌ Deciding which search source to use without asking
|
|
48
|
+
|
|
49
|
+
**Example of what TO do:**
|
|
50
|
+
- ✅ Respond with ONLY text — a numbered list of search sources
|
|
51
|
+
- ✅ Ask: "Which option would you like to use?"
|
|
52
|
+
- ✅ Wait for user to reply with their choice
|
|
53
|
+
- ✅ Then (and only then) call the tool they selected
|
|
54
|
+
|
|
55
|
+
**Your first response when this skill triggers MUST be a text-only message presenting search sources. No tool calls. No exceptions.**
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## Workflow Overview
|
|
59
|
+
|
|
60
|
+
**The user MUST choose the search source. You CANNOT skip this step.**
|
|
61
|
+
|
|
62
|
+
Copy this checklist and track your progress:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Media Search Progress:
|
|
66
|
+
- [ ] Step 1: Check your own tool list for available search tools (no tool calls — just inspect what's in your context)
|
|
67
|
+
- [ ] Step 2: Present only the available options to the user as a numbered list (plain text, no tool calls)
|
|
68
|
+
- [ ] Step 3: Wait for the user to reply with their selection
|
|
69
|
+
- [ ] Step 4: Execute the selected search method (this is the first tool call)
|
|
70
|
+
- [ ] Step 5: Present all results to user for selection
|
|
71
|
+
- [ ] Step 6: Apply selected image to code
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
If you call any tool before step 4, you are not following this skill correctly.
|
|
75
|
+
|
|
76
|
+
## Presenting Search Sources (First Response)
|
|
77
|
+
|
|
78
|
+
**DO NOT call any tool, read any MCP descriptor, or make any external request to determine available tools.**
|
|
79
|
+
|
|
80
|
+
Your tools are already loaded into your context. Look at the tool names you already have access to — this is introspection, not a tool call.
|
|
81
|
+
|
|
82
|
+
**Step 1: Check your own tool list (no tool calls)**
|
|
83
|
+
|
|
84
|
+
Look at the tools already in your context and check for these names:
|
|
85
|
+
- `search_media_cms_channels` → If present, include **"Search using keywords"**
|
|
86
|
+
- `search_electronic_media` → If present, include **"Search using Data 360 hybrid search"**
|
|
87
|
+
- Always include **"Other"** as the last option
|
|
88
|
+
|
|
89
|
+
**Step 2: Build your response**
|
|
90
|
+
|
|
91
|
+
Include ONLY the sources whose tools you actually have. Number them sequentially.
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
I can help you find that image. Where would you like to search?
|
|
95
|
+
|
|
96
|
+
[NUMBER]. [SEARCH SOURCE NAME] — [Brief description]
|
|
97
|
+
...
|
|
98
|
+
[NUMBER]. Other — Provide your own URL or path
|
|
99
|
+
|
|
100
|
+
Which option would you like to use?
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Step 3: Stop and wait**
|
|
104
|
+
|
|
105
|
+
After presenting the list, STOP. Do not call any tool. Do not proceed. Wait for the user to reply with their choice.
|
|
106
|
+
|
|
107
|
+
### Examples
|
|
108
|
+
|
|
109
|
+
**Both tools available:**
|
|
110
|
+
```
|
|
111
|
+
I can help you find that image. Where would you like to search?
|
|
112
|
+
|
|
113
|
+
1. Search using Data 360 hybrid search — Semantic search across Salesforce CMS and connected DAMs
|
|
114
|
+
2. Search using keywords — Search Salesforce CMS by keywords and taxonomies
|
|
115
|
+
3. Other — Provide your own URL or path
|
|
116
|
+
|
|
117
|
+
Which option would you like to use?
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Only `search_media_cms_channels` available:**
|
|
121
|
+
```
|
|
122
|
+
I can help you find that image. Where would you like to search?
|
|
123
|
+
|
|
124
|
+
1. Search using keywords — Search Salesforce CMS by keywords and taxonomies
|
|
125
|
+
2. Other — Provide your own URL or path
|
|
126
|
+
|
|
127
|
+
Which option would you like to use?
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Only `search_electronic_media` available:**
|
|
131
|
+
```
|
|
132
|
+
I can help you find that image. Where would you like to search?
|
|
133
|
+
|
|
134
|
+
1. Search using Data 360 hybrid search — Semantic search across Salesforce CMS and connected DAMs
|
|
135
|
+
2. Other — Provide your own URL or path
|
|
136
|
+
|
|
137
|
+
Which option would you like to use?
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Neither tool available:**
|
|
141
|
+
```
|
|
142
|
+
No automated media search sources are currently configured. Please provide a direct URL or asset library path.
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Wait for the user to select** before proceeding.
|
|
146
|
+
|
|
147
|
+
## Executing the Selected Search Method
|
|
148
|
+
|
|
149
|
+
**⚠️ ONLY reach this step if the user has explicitly selected an option from your numbered list.**
|
|
150
|
+
|
|
151
|
+
If you haven't shown options yet, go back to the "Presenting Search Sources" section first.
|
|
152
|
+
|
|
153
|
+
After the user selects an option, execute the corresponding search method below.
|
|
154
|
+
|
|
155
|
+
### Search using keywords
|
|
156
|
+
|
|
157
|
+
**Tool:** `search_media_cms_channels`
|
|
158
|
+
|
|
159
|
+
**Process:**
|
|
160
|
+
|
|
161
|
+
1. **Analyze the query** — Understand what the user is searching for (subject, attributes, domain)
|
|
162
|
+
|
|
163
|
+
2. **Extract keywords** — Concrete nouns that would appear in image metadata
|
|
164
|
+
- Use domain-specific synonyms
|
|
165
|
+
- Maximum 10 terms
|
|
166
|
+
- Examples:
|
|
167
|
+
- "luxury apartments" → apartment, villa, penthouse, residence, condo
|
|
168
|
+
- "company logo" → logo, emblem, corporate logo
|
|
169
|
+
- "bright room" → _(empty if no concrete nouns)_
|
|
170
|
+
|
|
171
|
+
3. **Extract taxonomies** — Descriptive qualities, styles, moods, categories
|
|
172
|
+
- Only adjectives and attributes
|
|
173
|
+
- Examples:
|
|
174
|
+
- "luxury apartment with river view" → Luxury, Premium, Waterfront, Riverside, Panoramic
|
|
175
|
+
- "bright spacious room" → Bright, Spacious, Open, Airy, Light
|
|
176
|
+
- "car" → _(empty if no descriptive terms)_
|
|
177
|
+
|
|
178
|
+
4. **Determine locale** — Use format `en_US`, `es_MX`, `fr_FR` (default: `en_US`)
|
|
179
|
+
|
|
180
|
+
5. **Build the JSON payload** — Construct this exact structure:
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"inputs": [{
|
|
185
|
+
"searchKeyword": "keyword1 OR keyword2 OR keyword3",
|
|
186
|
+
"taxonomyExpression": "{\"OR\": [\"Taxonomy1\", \"Taxonomy2\"]}",
|
|
187
|
+
"searchLanguage": "en_US",
|
|
188
|
+
"channelIds": "",
|
|
189
|
+
"channelType": "PublicUnauthenticated",
|
|
190
|
+
"contentTypeFqn": "sfdc_cms__image",
|
|
191
|
+
"pageOffset": 0,
|
|
192
|
+
"searchLimit": 5
|
|
193
|
+
}]
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Field rules:**
|
|
198
|
+
- `searchKeyword`: Join keywords with ` OR ` (space-OR-space). Use empty string if no keywords.
|
|
199
|
+
- `taxonomyExpression`: Stringify JSON object `{"OR": ["term1", "term2"]}`. Use `"{}"` if no taxonomies.
|
|
200
|
+
- `searchLanguage`: Locale with underscore (e.g., `en_US`)
|
|
201
|
+
- `channelIds`: Always empty string
|
|
202
|
+
- `channelType`: Always `"PublicUnauthenticated"`
|
|
203
|
+
- `contentTypeFqn`: Always `"sfdc_cms__image"`
|
|
204
|
+
- `pageOffset`: Start at `0`, increment by `searchLimit` for pagination
|
|
205
|
+
- `searchLimit`: Default `5`, adjust if user requests more
|
|
206
|
+
|
|
207
|
+
**Examples:**
|
|
208
|
+
|
|
209
|
+
Query: "luxury apartment with river view"
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"inputs": [{
|
|
213
|
+
"searchKeyword": "apartment OR villa OR penthouse OR residence",
|
|
214
|
+
"taxonomyExpression": "{\"OR\": [\"Luxury\", \"Premium\", \"Waterfront\", \"Riverside\"]}",
|
|
215
|
+
"searchLanguage": "en_US",
|
|
216
|
+
"channelIds": "",
|
|
217
|
+
"channelType": "PublicUnauthenticated",
|
|
218
|
+
"contentTypeFqn": "sfdc_cms__image",
|
|
219
|
+
"pageOffset": 0,
|
|
220
|
+
"searchLimit": 5
|
|
221
|
+
}]
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Query: "bright spacious room" (no concrete nouns)
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"inputs": [{
|
|
229
|
+
"searchKeyword": "",
|
|
230
|
+
"taxonomyExpression": "{\"OR\": [\"Bright\", \"Spacious\", \"Open\", \"Airy\"]}",
|
|
231
|
+
"searchLanguage": "en_US",
|
|
232
|
+
"channelIds": "",
|
|
233
|
+
"channelType": "PublicUnauthenticated",
|
|
234
|
+
"contentTypeFqn": "sfdc_cms__image",
|
|
235
|
+
"pageOffset": 0,
|
|
236
|
+
"searchLimit": 5
|
|
237
|
+
}]
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Query: "car images" (no descriptive terms)
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"inputs": [{
|
|
245
|
+
"searchKeyword": "car OR automobile OR vehicle OR auto",
|
|
246
|
+
"taxonomyExpression": "{}",
|
|
247
|
+
"searchLanguage": "en_US",
|
|
248
|
+
"channelIds": "",
|
|
249
|
+
"channelType": "PublicUnauthenticated",
|
|
250
|
+
"contentTypeFqn": "sfdc_cms__image",
|
|
251
|
+
"pageOffset": 0,
|
|
252
|
+
"searchLimit": 5
|
|
253
|
+
}]
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
6. **Call the tool** with the exact JSON payload
|
|
258
|
+
|
|
259
|
+
### Search using Data 360 hybrid search
|
|
260
|
+
|
|
261
|
+
**Tool:** `search_electronic_media`
|
|
262
|
+
|
|
263
|
+
**Process:**
|
|
264
|
+
|
|
265
|
+
1. Use the user's query **as-is** — no keyword extraction or transformation needed
|
|
266
|
+
2. Call `search_electronic_media`
|
|
267
|
+
3. Pass the query to the tool's `searchQuery` parameter
|
|
268
|
+
|
|
269
|
+
**Example:**
|
|
270
|
+
- User query: "modern luxury apartment with natural lighting"
|
|
271
|
+
- Tool call: `search_electronic_media(searchQuery="modern luxury apartment with natural lighting")`
|
|
272
|
+
|
|
273
|
+
### Other (User-Provided URL)
|
|
274
|
+
|
|
275
|
+
Ask the user to provide:
|
|
276
|
+
- Direct URL to the image
|
|
277
|
+
- Asset library path
|
|
278
|
+
- Specific system/location to check
|
|
279
|
+
|
|
280
|
+
## Presenting Search Results
|
|
281
|
+
|
|
282
|
+
Parse the tool response and present **ALL** results as numbered options. Show the image title only — do not display the URL. When the user selects an option, use the URL internally to apply the image.
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
I found 4 images. Which one would you like to use?
|
|
286
|
+
|
|
287
|
+
1. Luxury Apartment Exterior
|
|
288
|
+
Source: Salesforce CMS
|
|
289
|
+
|
|
290
|
+
2. Modern High-Rise Building
|
|
291
|
+
Source: Salesforce CMS
|
|
292
|
+
|
|
293
|
+
3. Waterfront Residence
|
|
294
|
+
Source: Salesforce CMS
|
|
295
|
+
|
|
296
|
+
4. Premium Condominium
|
|
297
|
+
Source: Salesforce CMS
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Never auto-select an image.** Always wait for user choice.
|
|
301
|
+
|
|
302
|
+
## Applying the Selected Image
|
|
303
|
+
|
|
304
|
+
After the user chooses:
|
|
305
|
+
|
|
306
|
+
1. Confirm the selection with image name and URL
|
|
307
|
+
2. Use the complete URL returned by the tool, including all query parameters. CMS and DAM URLs rely on query parameters for authentication, resizing, and CDN routing — dropping them breaks the image. For example, a URL like `https://cms.example.com/media/img.jpg?oid=00D&refid=0EM&v=2` must be used in full.
|
|
308
|
+
3. Apply the URL to the user's code/component
|
|
309
|
+
4. Show what was changed (file path and line number)
|
|
310
|
+
|
|
311
|
+
## Error Handling
|
|
312
|
+
|
|
313
|
+
| Error | Response |
|
|
314
|
+
|---|---|
|
|
315
|
+
| Tool unavailable | "The [source name] tool is unavailable. Would you like to try a different source?" |
|
|
316
|
+
| Tool returns error | Show error message, offer retry with different terms or alternative source |
|
|
317
|
+
| No results found | "No results found. Try broader keywords, removing descriptive terms, or a different source." |
|
|
318
|
+
| Invalid user selection | Re-display options and ask again |
|
|
319
|
+
|
|
320
|
+
**Never silently fail.** Always inform the user and offer alternatives.
|
|
321
|
+
|
|
322
|
+
## Search Behavior Notes
|
|
323
|
+
|
|
324
|
+
**Search using keywords:**
|
|
325
|
+
- Both keyword and taxonomy → results match keyword OR (keyword + taxonomy)
|
|
326
|
+
- Empty keyword → search by taxonomy only
|
|
327
|
+
- Empty taxonomy → search by keyword only
|
|
328
|
+
- Use `pageOffset` for pagination (increment by `searchLimit`)
|
|
329
|
+
|
|
330
|
+
**Search using Data 360 hybrid search:**
|
|
331
|
+
- Handles natural language queries
|
|
332
|
+
- Semantic similarity matching
|
|
333
|
+
- Searches across multiple connected systems
|
|
334
|
+
|
|
335
|
+
## Key Principles
|
|
336
|
+
|
|
337
|
+
1. **First response is always text-only** — Present search sources without calling any tool
|
|
338
|
+
2. **Only show configured sources** — Check your own tool list (introspection, not tool calls) and only present sources whose tools you have
|
|
339
|
+
3. **Wait for user selection** — Never auto-select a source or image
|
|
340
|
+
4. **Show all results** — Let the user choose the best match
|
|
341
|
+
5. **Confirm before applying** — Verify the selection before modifying code
|
|
342
|
+
6. **Handle errors gracefully** — Provide clear feedback and alternatives
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: using-
|
|
3
|
-
description: "Salesforce data access for reading, writing, and querying records via REST, GraphQL, Apex, or Platform SDK. Use when the user wants to fetch, search, filter, sort, display, create, update, delete, or attach files to Salesforce records (standard objects like Accounts, Contacts, Opportunities, Cases, Quotes, or any custom object) in a
|
|
2
|
+
name: using-ui-bundle-salesforce-data
|
|
3
|
+
description: "Salesforce data access for reading, writing, and querying records via REST, GraphQL, Apex, or Platform SDK. Use when the user wants to fetch, search, filter, sort, display, create, update, delete, or attach files to Salesforce records (standard objects like Accounts, Contacts, Opportunities, Cases, Quotes, or any custom object) in a UI bundle or UI component (React, Angular, Vue, etc.); call Chatter, Connect, or Apex REST APIs; or invoke AuraEnabled Apex methods from an external app. Does not apply to authentication/OAuth setup, schema changes (adding fields, relationships), Bulk/Tooling/Metadata API usage, declarative automation (Flows, Process Builder), general LWC/Apex coding guidance without a specific data operation, or Salesforce admin/configuration tasks."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Salesforce Data Access
|
|
@@ -17,7 +17,7 @@ Use this skill when the user wants to:
|
|
|
17
17
|
|
|
18
18
|
## Data SDK Requirement
|
|
19
19
|
|
|
20
|
-
> **All Salesforce data access MUST use the Data SDK** (`@salesforce/sdk-data`). The SDK handles authentication, CSRF, and base URL resolution.
|
|
20
|
+
> **All Salesforce data access MUST use the Data SDK** (`@salesforce/sdk-data`). The SDK handles authentication, CSRF, and base URL resolution.
|
|
21
21
|
|
|
22
22
|
```typescript
|
|
23
23
|
import { createDataSDK, gql } from "@salesforce/sdk-data";
|
|
@@ -48,7 +48,7 @@ const res = await sdk.fetch?.("/services/apexrest/my-resource");
|
|
|
48
48
|
**Not supported:**
|
|
49
49
|
|
|
50
50
|
- **Enterprise REST query endpoint** (`/services/data/v*/query` with SOQL) — blocked at the proxy level. Use GraphQL for record reads; use Apex REST if server-side SOQL aggregates are required.
|
|
51
|
-
- **Aura-enabled Apex** (`@AuraEnabled`) — an LWC/Aura pattern with no invocation path from React
|
|
51
|
+
- **Aura-enabled Apex** (`@AuraEnabled`) — an LWC/Aura pattern with no invocation path from React UI bundles.
|
|
52
52
|
- **Chatter API** (`/chatter/users/me`) — use `uiapi { currentUser { ... } }` in a GraphQL query instead.
|
|
53
53
|
- **Any other Salesforce REST endpoint** not listed in the supported table above.
|
|
54
54
|
|
|
@@ -67,6 +67,24 @@ const res = await sdk.fetch?.("/services/apexrest/my-resource");
|
|
|
67
67
|
|
|
68
68
|
---
|
|
69
69
|
|
|
70
|
+
## GraphQL Non-Negotiable Rules
|
|
71
|
+
|
|
72
|
+
These rules exist because Salesforce GraphQL has platform-specific behaviors that differ from standard GraphQL. Violations cause silent runtime failures.
|
|
73
|
+
|
|
74
|
+
1. **Schema is the single source of truth** — Every entity name, field name, and type must be confirmed via the schema search script before use in a query. Never guess — Salesforce field names are case-sensitive, relationships may be polymorphic, and custom objects use suffixes (`__c`, `__e`). See [Schema Introspection](references/schema-introspection.md) for entity identification and iterative lookup procedures.
|
|
75
|
+
|
|
76
|
+
2. **`@optional` on all record fields** (read queries) — Salesforce field-level security (FLS) causes queries to fail entirely if the user lacks access to even one field. The `@optional` directive (v65+) tells the server to omit inaccessible fields instead of failing. Apply it to every scalar field, parent relationship, and child relationship. Consuming code must use optional chaining (`?.`) and nullish coalescing (`??`).
|
|
77
|
+
|
|
78
|
+
3. **Correct mutation syntax** — Mutations wrap under `uiapi(input: { allOrNone: true/false })`, not bare `uiapi { ... }`. Always set `allOrNone` explicitly. Output fields cannot include child relationships or navigated reference fields. See [Mutation Query Generation](references/mutation-query-generation.md).
|
|
79
|
+
|
|
80
|
+
4. **Explicit pagination** — Always include `first:` in every query. If omitted, the server silently defaults to 10 records. Include `pageInfo { hasNextPage endCursor }` for any query that may need pagination.
|
|
81
|
+
|
|
82
|
+
5. **SOQL-derived execution limits** — Max 10 subqueries per request, max 5 levels of child-to-parent traversal, max 1 level of parent-to-child (no grandchildren), max 2,000 records per subquery. If a query would exceed these, split into multiple requests.
|
|
83
|
+
|
|
84
|
+
6. **HTTP 200 does not mean success** — Salesforce returns HTTP 200 even when operations fail. Always parse the `errors` array in the response body.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
70
88
|
## GraphQL Workflow
|
|
71
89
|
|
|
72
90
|
### Step 1: Acquire Schema
|
|
@@ -74,7 +92,7 @@ const res = await sdk.fetch?.("/services/apexrest/my-resource");
|
|
|
74
92
|
The `schema.graphql` file (265K+ lines) is the source of truth. **Never open or parse it directly.**
|
|
75
93
|
|
|
76
94
|
1. Check if `schema.graphql` exists at the SFDX project root
|
|
77
|
-
2. If missing, run from the **
|
|
95
|
+
2. If missing, run from the **UI bundle dir**: `npm run graphql:schema`
|
|
78
96
|
3. Custom objects appear only after metadata is deployed
|
|
79
97
|
|
|
80
98
|
### Step 2: Look Up Entity Schema
|
|
@@ -82,11 +100,11 @@ The `schema.graphql` file (265K+ lines) is the source of truth. **Never open or
|
|
|
82
100
|
Map user intent to PascalCase names ("accounts" → `Account`), then **run the search script from the project root**:
|
|
83
101
|
|
|
84
102
|
```bash
|
|
85
|
-
#
|
|
86
|
-
bash
|
|
103
|
+
# Look up all relevant schema info for one or more entities
|
|
104
|
+
bash scripts/graphql-search.sh Account
|
|
87
105
|
|
|
88
106
|
# Multiple entities at once
|
|
89
|
-
bash
|
|
107
|
+
bash scripts/graphql-search.sh Account Contact Opportunity
|
|
90
108
|
```
|
|
91
109
|
|
|
92
110
|
The script outputs five sections per entity:
|
|
@@ -96,11 +114,11 @@ The script outputs five sections per entity:
|
|
|
96
114
|
4. **Create input** — fields accepted by create mutations
|
|
97
115
|
5. **Update input** — fields accepted by update mutations
|
|
98
116
|
|
|
99
|
-
Use this output to determine exact field names before writing any query or mutation. **Maximum 2 script runs.** If the entity still can't be found, ask the user — the object may not be deployed.
|
|
117
|
+
Use this output to determine exact field names before writing any query or mutation. **Maximum 2 script runs.** If the entity still can't be found, ask the user — the object may not be deployed. For entity identification procedures (`_Record` suffix, `__c` conventions) and iterative introspection cycles, see [Schema Introspection](references/schema-introspection.md).
|
|
100
118
|
|
|
101
119
|
### Step 3: Generate Query
|
|
102
120
|
|
|
103
|
-
Use the templates below. Every field name **must** be verified from the script output in Step 2.
|
|
121
|
+
Use the templates below. Every field name **must** be verified from the script output in Step 2. For detailed generation rules, filtering, pagination, ordering, semi-joins, and field value wrappers, see [Read Query Generation](references/read-query-generation.md). For mutation chaining, input/output constraints, and transactional semantics, see [Mutation Query Generation](references/mutation-query-generation.md).
|
|
104
122
|
|
|
105
123
|
#### Read Query Template
|
|
106
124
|
|
|
@@ -138,7 +156,7 @@ const name = node.Name?.value ?? "";
|
|
|
138
156
|
|
|
139
157
|
```graphql
|
|
140
158
|
mutation CreateAccount($input: AccountCreateInput!) {
|
|
141
|
-
uiapi {
|
|
159
|
+
uiapi(input: { allOrNone: true }) {
|
|
142
160
|
AccountCreate(input: $input) {
|
|
143
161
|
Record { Id Name { value } }
|
|
144
162
|
}
|
|
@@ -215,21 +233,26 @@ const fields = response?.data?.uiapi?.objectInfos?.[0]?.fields ?? [];
|
|
|
215
233
|
|
|
216
234
|
### Step 4: Validate & Test
|
|
217
235
|
|
|
218
|
-
1. **Lint**: `npx eslint <file>` from
|
|
236
|
+
1. **Lint**: `npx eslint <file>` from UI bundle dir
|
|
219
237
|
2. **Test**: Ask user before testing. For mutations, request input values — never fabricate data.
|
|
220
238
|
|
|
221
239
|
**If ESLint reports a GraphQL error** (e.g. `Cannot query field`, `Unknown type`, `Unknown argument`), the field or type name is wrong. Re-run the schema search script to find the correct name — do not guess:
|
|
222
240
|
|
|
223
241
|
```bash
|
|
224
242
|
# From project root — re-check the entity that caused the error
|
|
225
|
-
bash
|
|
243
|
+
bash scripts/graphql-search.sh <EntityName>
|
|
226
244
|
```
|
|
227
245
|
|
|
228
|
-
Then fix the query using the exact names from the script output.
|
|
246
|
+
Then fix the query using the exact names from the script output. For detailed error categories, status handling, and retry strategy, see [Query Testing](references/query-testing.md).
|
|
229
247
|
|
|
230
248
|
---
|
|
231
249
|
|
|
232
|
-
##
|
|
250
|
+
## UI Bundle Integration (React)
|
|
251
|
+
|
|
252
|
+
Two integration patterns are available:
|
|
253
|
+
|
|
254
|
+
- **Pattern 1 — External `.graphql` file** (recommended for complex queries): Create a `.graphql` file, run `npm run graphql:codegen`, import with `?raw` suffix
|
|
255
|
+
- **Pattern 2 — Inline `gql` tag** (for simple queries): Use the `gql` template tag from `@salesforce/sdk-data`. **Must use `gql`** — plain template strings bypass ESLint schema validation.
|
|
233
256
|
|
|
234
257
|
```typescript
|
|
235
258
|
import { createDataSDK, gql } from "@salesforce/sdk-data";
|
|
@@ -242,8 +265,9 @@ const GET_ACCOUNTS = gql`
|
|
|
242
265
|
edges {
|
|
243
266
|
node {
|
|
244
267
|
Id
|
|
245
|
-
Name @optional {
|
|
246
|
-
|
|
268
|
+
Name @optional {
|
|
269
|
+
value
|
|
270
|
+
}
|
|
247
271
|
}
|
|
248
272
|
}
|
|
249
273
|
}
|
|
@@ -254,14 +278,14 @@ const GET_ACCOUNTS = gql`
|
|
|
254
278
|
|
|
255
279
|
const sdk = await createDataSDK();
|
|
256
280
|
const response = await sdk.graphql?.(GET_ACCOUNTS);
|
|
257
|
-
|
|
258
281
|
if (response?.errors?.length) {
|
|
259
282
|
throw new Error(response.errors.map(e => e.message).join("; "));
|
|
260
283
|
}
|
|
261
|
-
|
|
262
284
|
const accounts = response?.data?.uiapi?.query?.Account?.edges?.map(e => e.node) ?? [];
|
|
263
285
|
```
|
|
264
286
|
|
|
287
|
+
For detailed patterns (external .graphql files, codegen, error handling strategies, quality checklists), see [UI Bundle Integration](references/ui-bundle-integration.md).
|
|
288
|
+
|
|
265
289
|
---
|
|
266
290
|
|
|
267
291
|
## REST API Patterns
|
|
@@ -311,16 +335,16 @@ const response = await sdk.graphql?.(GET_CURRENT_USER);
|
|
|
311
335
|
<project-root>/ ← SFDX project root
|
|
312
336
|
├── schema.graphql ← grep target (lives here)
|
|
313
337
|
├── sfdx-project.json
|
|
314
|
-
└── force-app/main/default/
|
|
338
|
+
└── force-app/main/default/uiBundles/<app-name>/ ← UI bundle dir
|
|
315
339
|
├── package.json ← npm scripts
|
|
316
340
|
└── src/
|
|
317
341
|
```
|
|
318
342
|
|
|
319
343
|
| Command | Run From | Why |
|
|
320
344
|
|---------|----------|-----|
|
|
321
|
-
| `npm run graphql:schema` |
|
|
322
|
-
| `npx eslint <file>` |
|
|
323
|
-
| `bash
|
|
345
|
+
| `npm run graphql:schema` | UI bundle dir | Script in UI bundle's package.json |
|
|
346
|
+
| `npx eslint <file>` | UI bundle dir | Reads eslint.config.js |
|
|
347
|
+
| `bash scripts/graphql-search.sh <Entity>` | project root | Schema lookup |
|
|
324
348
|
| `sf api request rest` | project root | Needs sfdx-project.json |
|
|
325
349
|
|
|
326
350
|
---
|
|
@@ -332,7 +356,7 @@ const response = await sdk.graphql?.(GET_CURRENT_USER);
|
|
|
332
356
|
Run the search script to get all relevant schema info in one step:
|
|
333
357
|
|
|
334
358
|
```bash
|
|
335
|
-
bash
|
|
359
|
+
bash scripts/graphql-search.sh <EntityName>
|
|
336
360
|
```
|
|
337
361
|
|
|
338
362
|
| Script Output Section | Used For |
|
|
@@ -358,6 +382,9 @@ bash .a4drules/skills/using-salesforce-data/graphql-search.sh <EntityName>
|
|
|
358
382
|
### Checklist
|
|
359
383
|
|
|
360
384
|
- [ ] All field names verified via search script (Step 2)
|
|
361
|
-
- [ ] `@optional` applied to record fields (reads)
|
|
385
|
+
- [ ] `@optional` applied to all record fields (reads)
|
|
386
|
+
- [ ] Mutations use `uiapi(input: { allOrNone: ... })` wrapper
|
|
387
|
+
- [ ] `first:` specified in every query
|
|
362
388
|
- [ ] Optional chaining in consuming code
|
|
389
|
+
- [ ] `errors` array checked in response handling
|
|
363
390
|
- [ ] Lint passes: `npx eslint <file>`
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Mutation Query Generation
|
|
2
|
+
|
|
3
|
+
## Mutation Types
|
|
4
|
+
|
|
5
|
+
The GraphQL engine supports three mutation operations:
|
|
6
|
+
|
|
7
|
+
- **Create** — Insert a new record
|
|
8
|
+
- **Update** — Modify an existing record (Id-based)
|
|
9
|
+
- **Delete** — Remove an existing record (Id-based)
|
|
10
|
+
|
|
11
|
+
Mutations are GA in API v66+. They live under `mutation { uiapi { ... } }` and only support UI API-available objects.
|
|
12
|
+
|
|
13
|
+
## Generation Rules
|
|
14
|
+
|
|
15
|
+
1. **Input fields validation** — Validate that input fields satisfy the constraints for the operation type
|
|
16
|
+
2. **Output fields validation** — Validate that output fields satisfy the constraints for the operation type
|
|
17
|
+
3. **Type consistency** — Variables used as query arguments and their related fields must share the same GraphQL type. Verify types via the schema search script — do NOT assume types
|
|
18
|
+
4. **Input arguments** — `input` is the default argument name unless otherwise specified
|
|
19
|
+
5. **Output field** — For `Create` and `Update`, the output field is always named `Record` (type: EntityName)
|
|
20
|
+
6. **Field name validation** — Every field name in the generated mutation **MUST** match a field confirmed via the schema search script. Do NOT guess or assume field names exist
|
|
21
|
+
7. **Raw input values** — Numeric values must be raw numbers without commas, currency symbols, or locale formatting (e.g., `80000` not `"80,000"` or `"$80,000"`). Compound fields (like addresses) require constituent fields (e.g., `BillingCity`, `BillingStreet`) — do not attempt to set the compound wrapper itself.
|
|
22
|
+
|
|
23
|
+
## Transactional Semantics: `allOrNone`
|
|
24
|
+
|
|
25
|
+
The `uiapi` mutation input accepts an `allOrNone` argument that controls rollback behavior:
|
|
26
|
+
|
|
27
|
+
- **`allOrNone: true` (default)** — If any operation fails, all operations in the request are rolled back. Use when operations must succeed or fail together.
|
|
28
|
+
- **`allOrNone: false`** — Independent operations can succeed individually. However, dependent operations (those using `@{alias}` references) still roll back together with their dependencies.
|
|
29
|
+
|
|
30
|
+
Always set `allOrNone` explicitly to make transactional intent clear.
|
|
31
|
+
|
|
32
|
+
## Mutation Schema Patterns
|
|
33
|
+
|
|
34
|
+
Replace `EntityName` with the actual entity name (e.g., Account, Case). `Delete` operations use generic `Record` types.
|
|
35
|
+
|
|
36
|
+
```graphql
|
|
37
|
+
input EntityNameCreateRepresentation {
|
|
38
|
+
# Subset of EntityName fields
|
|
39
|
+
}
|
|
40
|
+
input EntityNameCreateInput { EntityName: EntityNameCreateRepresentation! }
|
|
41
|
+
type EntityNameCreatePayload { Record: EntityName! }
|
|
42
|
+
|
|
43
|
+
input EntityNameUpdateRepresentation {
|
|
44
|
+
# Subset of EntityName fields
|
|
45
|
+
}
|
|
46
|
+
input EntityNameUpdateInput { Id: IdOrRef! EntityName: EntityNameUpdateRepresentation! }
|
|
47
|
+
type EntityNameUpdatePayload { Record: EntityName! }
|
|
48
|
+
|
|
49
|
+
input RecordDeleteInput { Id: IdOrRef! }
|
|
50
|
+
type RecordDeletePayload { Id: ID }
|
|
51
|
+
|
|
52
|
+
type UIAPIMutations {
|
|
53
|
+
EntityNameCreate(input: EntityNameCreateInput!): EntityNameCreatePayload
|
|
54
|
+
EntityNameDelete(input: RecordDeleteInput!): RecordDeletePayload
|
|
55
|
+
EntityNameUpdate(input: EntityNameUpdateInput!): EntityNameUpdatePayload
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Input Field Constraints
|
|
60
|
+
|
|
61
|
+
### Create
|
|
62
|
+
|
|
63
|
+
- **Must** include all required fields (unless `defaultedOnCreate` is `true` and not explicitly requested)
|
|
64
|
+
- **Must** only include `createable` fields
|
|
65
|
+
- Child relationships cannot be set — exclude them
|
|
66
|
+
- Reference fields (`REFERENCE` type) can only be assigned IDs through their `ApiName` name
|
|
67
|
+
- **No nested child creates** — Creating a record with child relationships in a single create operation is not supported. To create a parent and child together, use separate operations with `IdOrRef` chaining (see [Mutation Chaining](#mutation-chaining)).
|
|
68
|
+
|
|
69
|
+
### Update
|
|
70
|
+
|
|
71
|
+
- **Must** include the `Id` of the entity to update
|
|
72
|
+
- **Must** only include `updateable` fields
|
|
73
|
+
- Child relationships cannot be set — exclude them
|
|
74
|
+
- Reference fields (`REFERENCE` type) can only be assigned IDs through their `ApiName` name
|
|
75
|
+
|
|
76
|
+
### Delete
|
|
77
|
+
|
|
78
|
+
- **Must** include the `Id` of the entity to delete
|
|
79
|
+
|
|
80
|
+
## Output Field Constraints
|
|
81
|
+
|
|
82
|
+
### Create and Update
|
|
83
|
+
|
|
84
|
+
- **Must** exclude all child relationships (child relationships cannot be queried in mutations)
|
|
85
|
+
- **Must** exclude all `REFERENCE` fields unless accessed through their `ApiName` member (no navigation to referenced entity, no sub fields)
|
|
86
|
+
- Inaccessible fields are reported in the `errors` attribute of the returned payload
|
|
87
|
+
|
|
88
|
+
### Delete
|
|
89
|
+
|
|
90
|
+
- **Must** only include the `Id` field
|
|
91
|
+
|
|
92
|
+
## Mutation Chaining
|
|
93
|
+
|
|
94
|
+
Chain related mutations in a single request using references to `Id` values from previous mutations. This is the required approach for creating parent-child records together, since nested child creates are not supported.
|
|
95
|
+
|
|
96
|
+
1. **Ordering** — Mutation `B` can reference mutation `A` only if `A` comes first in the query
|
|
97
|
+
2. **Notation** — Use `SomeId: "@{A}"` in mutation `B` to set a field to the `Id` produced by mutation `A`
|
|
98
|
+
3. **IDs only** — `@{A}` is always interpreted as the `Id` from mutation `A`
|
|
99
|
+
4. **Restrictions** — `A` must be a `Create` or `Delete` mutation (chaining from `Update` will fail)
|
|
100
|
+
|
|
101
|
+
### Chaining Example
|
|
102
|
+
|
|
103
|
+
```graphql
|
|
104
|
+
mutation CreateAccountAndContact {
|
|
105
|
+
uiapi(input: { allOrNone: true }) {
|
|
106
|
+
AccountCreate(input: { Account: { Name: "Acme" } }) {
|
|
107
|
+
Record { Id }
|
|
108
|
+
}
|
|
109
|
+
ContactCreate(input: { Contact: { LastName: "Smith", AccountId: "@{AccountCreate}" } }) {
|
|
110
|
+
Record { Id }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Mutation Query Template
|
|
117
|
+
|
|
118
|
+
```graphql
|
|
119
|
+
mutation mutateEntityName(
|
|
120
|
+
# arguments
|
|
121
|
+
) {
|
|
122
|
+
uiapi(input: { allOrNone: true }) {
|
|
123
|
+
EntityNameOperation(input: {
|
|
124
|
+
# For Create and Update only:
|
|
125
|
+
EntityName: {
|
|
126
|
+
# Input fields — use raw values, no formatting
|
|
127
|
+
}
|
|
128
|
+
# For Update and Delete only:
|
|
129
|
+
Id: ... # id here
|
|
130
|
+
}) {
|
|
131
|
+
# For Create and Update only:
|
|
132
|
+
Record {
|
|
133
|
+
# Output fields
|
|
134
|
+
}
|
|
135
|
+
# For Delete only:
|
|
136
|
+
Id
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|