@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,106 @@
1
+ ---
2
+ name: generating-ui-bundle-metadata
3
+ description: "Scaffold new Salesforce UI bundles and configure their metadata — sf ui-bundle generate, UIBundle bundles (meta XML, ui-bundle.json with routing/headers/outputDir), and CSP Trusted Sites for external domains. Use whenever creating a new UI bundle, setting up UI bundle metadata structure, configuring routing or headers, setting outputDir, adding external domains that need CSP registration, or editing bundle configuration. Triggers on: create UI bundle, create ui-bundle, new app, sf ui-bundle generate, metadata, ui-bundle.json, CSP, trusted site, bundle configuration, meta XML, routing config, external domain, headers config, outputDir."
4
+ ---
5
+
6
+ # UI Bundle Metadata
7
+
8
+ ## Scaffolding a New UI Bundle
9
+
10
+ Use `sf ui-bundle generate` to create new apps — not create-react-app, Vite, or other generic scaffolds.
11
+
12
+ **UI bundle name (`-n`):** Alphanumerical only — no spaces, hyphens, underscores, or special characters. Example: `CoffeeBoutique` (not `Coffee Boutique`).
13
+
14
+ After generation:
15
+ 1. Replace all default boilerplate — "React App", "Vite + React", default `<title>`, placeholder text
16
+ 2. Populate the home page with real content (landing section, banners, hero, navigation)
17
+ 3. Update navigation and placeholders (see the `building-ui-bundle-frontend` skill)
18
+
19
+ Always install dependencies before running any scripts in the UI bundle directory.
20
+
21
+ ---
22
+
23
+ ## UIBundle Bundle
24
+
25
+ A UIBundle bundle lives under `uiBundles/<AppName>/` and must contain:
26
+
27
+ - `<AppName>.uibundle-meta.xml` — filename must exactly match the folder name
28
+ - A build output directory (default: `dist/`) with at least one file
29
+
30
+ ### Meta XML
31
+
32
+ Required fields: `masterLabel`, `version` (max 20 chars), `isActive` (boolean).
33
+ Optional: `description` (max 255 chars).
34
+
35
+ ### ui-bundle.json
36
+
37
+ Optional file. Allowed top-level keys: `outputDir`, `routing`, `headers`.
38
+
39
+ **Constraints:**
40
+ - Valid UTF-8 JSON, max 100 KB
41
+ - Root must be a non-empty object (never `{}`, arrays, or primitives)
42
+
43
+ **Path safety** (applies to `outputDir` and `routing.fallback`): Reject backslashes, leading `/` or `\`, `..` segments, null/control characters, globs (`*`, `?`, `**`), and `%`. All resolved paths must stay within the bundle.
44
+
45
+ #### outputDir
46
+ Non-empty string referencing a subdirectory (not `.` or `./`). Directory must exist and contain at least one file.
47
+
48
+ #### routing
49
+ If present, must be a non-empty object. Allowed keys: `rewrites`, `redirects`, `fallback`, `trailingSlash`, `fileBasedRouting`.
50
+
51
+ - **trailingSlash**: `"always"`, `"never"`, or `"auto"`
52
+ - **fileBasedRouting**: boolean
53
+ - **fallback**: non-empty string satisfying path safety; target file must exist
54
+ - **rewrites**: non-empty array of `{ route?, rewrite }` objects — e.g., `{ "route": "/app/:path*", "rewrite": "/index.html" }`
55
+ - **redirects**: non-empty array of `{ route?, redirect, statusCode? }` objects — statusCode must be 301, 302, 307, or 308
56
+
57
+ #### headers
58
+ Non-empty array of `{ source, headers: [{ key, value }] }` objects.
59
+
60
+ **Example:**
61
+ ```json
62
+ {
63
+ "routing": {
64
+ "rewrites": [{ "route": "/app/:path*", "rewrite": "/index.html" }],
65
+ "trailingSlash": "never"
66
+ },
67
+ "headers": [
68
+ {
69
+ "source": "/assets/**",
70
+ "headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
71
+ }
72
+ ]
73
+ }
74
+ ```
75
+
76
+ **Never suggest:** `{}` as root, empty `"routing": {}`, empty arrays, `[{}]`, `"outputDir": "."`, `"outputDir": "./"`.
77
+
78
+ ---
79
+
80
+ ## CSP Trusted Sites
81
+
82
+ Salesforce enforces Content Security Policy headers. Any external domain not registered as a CSP Trusted Site will be blocked (images won't load, API calls fail, fonts missing).
83
+
84
+ ### When to Create
85
+
86
+ Whenever the app references a new external domain: CDN images, external fonts, third-party APIs, map tiles, iframes, external stylesheets.
87
+
88
+ ### Steps
89
+
90
+ 1. **Identify external domains** — extract the origin (scheme + host) from each external URL in the code
91
+ 2. **Check existing registrations** — look in `force-app/main/default/cspTrustedSites/`
92
+ 3. **Map resource type to CSP directive:**
93
+
94
+ | Resource Type | Directive Field |
95
+ |--------------|----------------|
96
+ | Images | `isApplicableToImgSrc` |
97
+ | API calls (fetch, XHR) | `isApplicableToConnectSrc` |
98
+ | Fonts | `isApplicableToFontSrc` |
99
+ | Stylesheets | `isApplicableToStyleSrc` |
100
+ | Video / audio | `isApplicableToMediaSrc` |
101
+ | Iframes | `isApplicableToFrameSrc` |
102
+
103
+ Always also set `isApplicableToConnectSrc` to `true` for preflight/redirect handling.
104
+
105
+ 4. **Create the metadata file** — follow `implementation/csp-metadata-format.md` for the `.cspTrustedSite-meta.xml` format. Place in `force-app/main/default/cspTrustedSites/`.
106
+
@@ -1,10 +1,10 @@
1
1
  ---
2
- name: managing-webapp-agentforce-conversation-client
2
+ name: implementing-ui-bundle-agentforce-conversation-client
3
3
  description: "Adds or modifies AgentforceConversationClient in React apps (.tsx or .jsx files). Use when user says \"add chat widget\", \"embed agentforce\", \"add agent\", \"add chatbot\", \"integrate conversational AI\", or asks to change colors, dimensions, styling, or configure agentId, width, height, inline mode, or styleTokens for travel agent, HR agent, employee agent, or any Salesforce agent chat."
4
4
  metadata:
5
5
  author: ACC Components
6
6
  version: 1.0.0
7
- package: "@salesforce/webapp-template-feature-react-agentforce-conversation-client-experimental"
7
+ package: "@salesforce/ui-bundle-template-feature-react-agentforce-conversation-client"
8
8
  sdk-package: "@salesforce/agentforce-conversation-client"
9
9
  last-updated: 2025-03-18
10
10
  ---
@@ -58,13 +58,13 @@ Skip this step if:
58
58
  Use this import path by default in app code:
59
59
 
60
60
  ```tsx
61
- import { AgentforceConversationClient } from "@salesforce/webapp-template-feature-react-agentforce-conversation-client-experimental";
61
+ import { AgentforceConversationClient } from "@salesforce/ui-bundle-template-feature-react-agentforce-conversation-client";
62
62
  ```
63
63
 
64
64
  If the package is not installed, install it:
65
65
 
66
66
  ```bash
67
- npm install @salesforce/webapp-template-feature-react-agentforce-conversation-client-experimental
67
+ npm install @salesforce/ui-bundle-template-feature-react-agentforce-conversation-client
68
68
  ```
69
69
 
70
70
  Only use a local relative import (for example, `./components/AgentforceConversationClient`) when the user explicitly asks to use a patched/local component in that app.
@@ -79,7 +79,7 @@ Add to the target React component file using the canonical package import:
79
79
 
80
80
  ```tsx
81
81
  import { Outlet } from "react-router";
82
- import { AgentforceConversationClient } from "@salesforce/webapp-template-feature-react-agentforce-conversation-client-experimental";
82
+ import { AgentforceConversationClient } from "@salesforce/ui-bundle-template-feature-react-agentforce-conversation-client";
83
83
 
84
84
  export default function AgentChatHost() {
85
85
  return (
@@ -8,7 +8,7 @@ This document lists all invalid approaches and patterns to avoid when working wi
8
8
 
9
9
  - ✅ **DO edit**: Any React files that import and use `<AgentforceConversationClient />` (for example, shared shells, route components, or feature pages)
10
10
  - ❌ **DO NOT edit**: AgentforceConversationClient.tsx, AgentforceConversationClient.jsx, index.tsx, index.jsx, or any files inside:
11
- - `node_modules/@salesforce/webapp-template-feature-react-agentforce-conversation-client-experimental/src/`
11
+ - `node_modules/@salesforce/ui-bundle-template-feature-react-agentforce-conversation-client/src/`
12
12
  - `packages/template/feature/feature-react-agentforce-conversation-client/src/`
13
13
  - `src/components/AgentforceConversationClient.tsx` (patched templates)
14
14
  - Any path containing `/components/AgentforceConversationClient.`
@@ -127,7 +127,7 @@ import "./agent-styles.css";
127
127
 
128
128
  ### ❌ Wrong - Editing implementation file
129
129
 
130
- Reading or editing: `node_modules/@salesforce/webapp-template-feature-react-agentforce-conversation-client-experimental/src/AgentforceConversationClient.tsx`
130
+ Reading or editing: `node_modules/@salesforce/ui-bundle-template-feature-react-agentforce-conversation-client/src/AgentforceConversationClient.tsx`
131
131
 
132
132
  ### ✅ Correct - Editing usage file
133
133
 
@@ -109,7 +109,7 @@ export default function SupportPage() {
109
109
 
110
110
  ```tsx
111
111
  import { Outlet } from "react-router";
112
- import { AgentforceConversationClient } from "@salesforce/webapp-template-feature-react-agentforce-conversation-client-experimental";
112
+ import { AgentforceConversationClient } from "@salesforce/ui-bundle-template-feature-react-agentforce-conversation-client";
113
113
 
114
114
  export default function AgentChatHost() {
115
115
  return (
@@ -1,11 +1,11 @@
1
1
  ---
2
- name: implementing-webapp-file-upload
3
- description: "Add file upload functionality to React webapps with progress tracking and Salesforce ContentVersion integration. Use when the user wants to upload files, attach documents, handle file input, create file dropzones, track upload progress, or link files to Salesforce records. This feature provides programmatic APIs ONLY — no components or hooks are exported. Build your own custom UI using the upload() API. ALWAYS use this feature instead of building file upload from scratch with FormData or XHR."
2
+ name: implementing-ui-bundle-file-upload
3
+ description: "Add file upload functionality to React UI bundles with progress tracking and Salesforce ContentVersion integration. Use when the user wants to upload files, attach documents, handle file input, create file dropzones, track upload progress, or link files to Salesforce records. This feature provides programmatic APIs ONLY — no components or hooks are exported. Build your own custom UI using the upload() API. ALWAYS use this feature instead of building file upload from scratch with FormData or XHR."
4
4
  ---
5
5
 
6
6
  # File Upload API (workflow)
7
7
 
8
- When the user wants file upload functionality in a React webapp, follow this workflow. This feature provides **APIs only** — you must build the UI components yourself using the provided APIs.
8
+ When the user wants file upload functionality in a React UI bundle, follow this workflow. This feature provides **APIs only** — you must build the UI components yourself using the provided APIs.
9
9
 
10
10
  ## CRITICAL: This is an API-only package
11
11
 
@@ -26,12 +26,12 @@ The source code contains reference components for demonstration, but they are **
26
26
  ## 1. Install the package
27
27
 
28
28
  ```bash
29
- npm install @salesforce/webapp-template-feature-react-file-upload-experimental
29
+ npm install @salesforce/ui-bundle-template-feature-react-file-upload
30
30
  ```
31
31
 
32
32
  Dependencies are automatically installed:
33
33
 
34
- - `@salesforce/webapp-experimental` (API client)
34
+ - `@salesforce/ui-bundle` (API client)
35
35
  - `@salesforce/sdk-data` (data SDK)
36
36
 
37
37
  ## 2. Understand the three upload patterns
@@ -47,7 +47,7 @@ Upload files to Salesforce and get back `contentBodyId` for each file. No Conten
47
47
  - Deferred record linking scenarios
48
48
 
49
49
  ```tsx
50
- import { upload } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
50
+ import { upload } from "@salesforce/ui-bundle-template-feature-react-file-upload";
51
51
 
52
52
  const results = await upload({
53
53
  files: [file1, file2],
@@ -71,7 +71,7 @@ Upload files and immediately link them to an existing Salesforce record by creat
71
71
  - Direct upload-and-attach scenarios
72
72
 
73
73
  ```tsx
74
- import { upload } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
74
+ import { upload } from "@salesforce/ui-bundle-template-feature-react-file-upload";
75
75
 
76
76
  const results = await upload({
77
77
  files: [file1, file2],
@@ -99,7 +99,7 @@ Upload files without a record, then link them after the record is created.
99
99
  import {
100
100
  upload,
101
101
  createContentVersion,
102
- } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
102
+ } from "@salesforce/ui-bundle-template-feature-react-file-upload";
103
103
 
104
104
  // Step 1: Upload files (no recordId)
105
105
  const uploadResults = await upload({
@@ -128,7 +128,7 @@ The package provides the backend — you build the frontend. Here's a minimal ex
128
128
  import {
129
129
  upload,
130
130
  type FileUploadProgress,
131
- } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
131
+ } from "@salesforce/ui-bundle-template-feature-react-file-upload";
132
132
  import { useState } from "react";
133
133
 
134
134
  function CustomFileUpload({ recordId }: { recordId?: string }) {
@@ -212,7 +212,7 @@ If the user wants to upload files to their own profile or personal library:
212
212
  import {
213
213
  upload,
214
214
  getCurrentUserId,
215
- } from "@salesforce/webapp-template-feature-react-file-upload-experimental";
215
+ } from "@salesforce/ui-bundle-template-feature-react-file-upload";
216
216
 
217
217
  const userId = await getCurrentUserId();
218
218
  await upload({ files, recordId: userId });
@@ -366,7 +366,7 @@ The package includes a reference implementation in `src/features/fileupload/` wi
366
366
 
367
367
  **Upload fails with CORS error:**
368
368
 
369
- - Ensure the webapp is properly deployed to Salesforce or running on `localhost`
369
+ - Ensure the UI bundle is properly deployed to Salesforce or running on `localhost`
370
370
  - Check that the org allows the origin in CORS settings
371
371
 
372
372
  **No progress updates:**
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: searching-media
3
- description: "Searches for and retrieves existing visual media (images, logos, icons, photos, graphics, banners, thumbnails, hero images, backgrounds) from any 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 to generate NEW images with AI, design custom graphics from scratch, or edit existing images."
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
4
  compatibility: "Requires search_media_cms_channels and/or search_electronic_media MCP tools"
5
5
  metadata:
6
6
  version: "1.0"
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: using-webapp-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 web app 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."
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. Never use `fetch()` or `axios` directly.
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 webapps.
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 **webapp dir**: `npm run graphql:schema`
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
- # From project root — look up all relevant schema info for one or more entities
86
- bash .a4drules/skills/using-salesforce-data/graphql-search.sh Account
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 .a4drules/skills/using-salesforce-data/graphql-search.sh Account Contact Opportunity
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 webapp dir
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 .a4drules/skills/using-salesforce-data/graphql-search.sh <EntityName>
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
- ## Webapp Integration (React)
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 { value }
246
- Industry @optional { value }
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/webapplications/<app-name>/ ← webapp dir
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` | webapp dir | Script in webapp's package.json |
322
- | `npx eslint <file>` | webapp dir | Reads eslint.config.js |
323
- | `bash .a4drules/skills/using-salesforce-data/graphql-search.sh <Entity>` | project root | Schema lookup |
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 .a4drules/skills/using-salesforce-data/graphql-search.sh <EntityName>
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
+ ```