@salesforce/webapp-template-feature-graphql-experimental 1.35.1 → 1.36.0

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 (95) hide show
  1. package/README.md +52 -65
  2. package/package.json +6 -43
  3. package/rules/graphql-data-access-rule.md +5 -5
  4. package/skills/graphql-data-access/SKILL.md +17 -40
  5. package/skills/graphql-data-access/docs/explore-schema.md +3 -3
  6. package/dist/.a4drules/build-validation.md +0 -81
  7. package/dist/.a4drules/code-quality.md +0 -150
  8. package/dist/.a4drules/graphql/tools/knowledge/lds-explore-graphql-schema.md +0 -227
  9. package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-mutationquery.md +0 -212
  10. package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-readquery.md +0 -185
  11. package/dist/.a4drules/graphql/tools/knowledge/lds-guide-graphql.md +0 -205
  12. package/dist/.a4drules/graphql/tools/schemas/shared.graphqls +0 -1150
  13. package/dist/.a4drules/graphql.md +0 -408
  14. package/dist/.a4drules/images.md +0 -13
  15. package/dist/.a4drules/react.md +0 -361
  16. package/dist/.a4drules/react_image_processing.md +0 -45
  17. package/dist/.a4drules/skills/install-feature/SKILL.md +0 -67
  18. package/dist/.a4drules/skills/install-feature/scripts/copy-feature-assets.sh +0 -36
  19. package/dist/.a4drules/typescript.md +0 -224
  20. package/dist/.forceignore +0 -15
  21. package/dist/.husky/pre-commit +0 -4
  22. package/dist/.prettierignore +0 -11
  23. package/dist/.prettierrc +0 -17
  24. package/dist/CHANGELOG.md +0 -487
  25. package/dist/README.md +0 -18
  26. package/dist/config/project-scratch-def.json +0 -13
  27. package/dist/force-app/main/default/webapplications/feature-graphql/.graphqlrc.yml +0 -6
  28. package/dist/force-app/main/default/webapplications/feature-graphql/.prettierignore +0 -9
  29. package/dist/force-app/main/default/webapplications/feature-graphql/.prettierrc +0 -11
  30. package/dist/force-app/main/default/webapplications/feature-graphql/build/vite.config.d.ts +0 -2
  31. package/dist/force-app/main/default/webapplications/feature-graphql/build/vite.config.js +0 -76
  32. package/dist/force-app/main/default/webapplications/feature-graphql/e2e/app.spec.ts +0 -24
  33. package/dist/force-app/main/default/webapplications/feature-graphql/eslint.config.js +0 -113
  34. package/dist/force-app/main/default/webapplications/feature-graphql/feature-graphql.webapplication-meta.xml +0 -7
  35. package/dist/force-app/main/default/webapplications/feature-graphql/index.html +0 -13
  36. package/dist/force-app/main/default/webapplications/feature-graphql/package-lock.json +0 -11134
  37. package/dist/force-app/main/default/webapplications/feature-graphql/package.json +0 -52
  38. package/dist/force-app/main/default/webapplications/feature-graphql/playwright.config.ts +0 -24
  39. package/dist/force-app/main/default/webapplications/feature-graphql/scripts/rewrite-e2e-assets.mjs +0 -23
  40. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/graphql-operations-types.ts +0 -118
  41. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/graphql.test.ts +0 -91
  42. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/graphql.ts +0 -35
  43. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/utils/accounts.ts +0 -35
  44. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/utils/query/highRevenueAccountsQuery.graphql +0 -29
  45. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/utils/user.test.ts +0 -129
  46. package/dist/force-app/main/default/webapplications/feature-graphql/src/api/utils/user.ts +0 -53
  47. package/dist/force-app/main/default/webapplications/feature-graphql/src/app.tsx +0 -22
  48. package/dist/force-app/main/default/webapplications/feature-graphql/src/appLayout.tsx +0 -11
  49. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/icons/book.svg +0 -3
  50. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/icons/copy.svg +0 -4
  51. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/icons/rocket.svg +0 -3
  52. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/icons/star.svg +0 -3
  53. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/images/codey-1.png +0 -0
  54. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/images/codey-2.png +0 -0
  55. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/images/codey-3.png +0 -0
  56. package/dist/force-app/main/default/webapplications/feature-graphql/src/assets/images/vibe-codey.svg +0 -194
  57. package/dist/force-app/main/default/webapplications/feature-graphql/src/components/AccountsTable.tsx +0 -121
  58. package/dist/force-app/main/default/webapplications/feature-graphql/src/index.ts +0 -7
  59. package/dist/force-app/main/default/webapplications/feature-graphql/src/navigationMenu.tsx +0 -81
  60. package/dist/force-app/main/default/webapplications/feature-graphql/src/pages/About.tsx +0 -12
  61. package/dist/force-app/main/default/webapplications/feature-graphql/src/pages/AccountsPage.tsx +0 -19
  62. package/dist/force-app/main/default/webapplications/feature-graphql/src/pages/Home.tsx +0 -12
  63. package/dist/force-app/main/default/webapplications/feature-graphql/src/pages/NotFound.tsx +0 -18
  64. package/dist/force-app/main/default/webapplications/feature-graphql/src/pages/new.tsx +0 -10
  65. package/dist/force-app/main/default/webapplications/feature-graphql/src/router-utils.tsx +0 -34
  66. package/dist/force-app/main/default/webapplications/feature-graphql/src/routes.tsx +0 -48
  67. package/dist/force-app/main/default/webapplications/feature-graphql/src/styles/global.css +0 -13
  68. package/dist/force-app/main/default/webapplications/feature-graphql/tsconfig.json +0 -36
  69. package/dist/force-app/main/default/webapplications/feature-graphql/tsconfig.node.json +0 -13
  70. package/dist/force-app/main/default/webapplications/feature-graphql/vite-env.d.ts +0 -1
  71. package/dist/force-app/main/default/webapplications/feature-graphql/vite.config.ts +0 -69
  72. package/dist/force-app/main/default/webapplications/feature-graphql/vitest-env.d.ts +0 -2
  73. package/dist/force-app/main/default/webapplications/feature-graphql/vitest.config.ts +0 -11
  74. package/dist/force-app/main/default/webapplications/feature-graphql/vitest.setup.ts +0 -1
  75. package/dist/force-app/main/default/webapplications/feature-graphql/webapplication.json +0 -7
  76. package/dist/jest.config.js +0 -6
  77. package/dist/package.json +0 -37
  78. package/dist/scripts/apex/hello.apex +0 -10
  79. package/dist/scripts/soql/account.soql +0 -6
  80. package/dist/sfdx-project.json +0 -12
  81. package/skills/graphql-data-access/scripts/codegen.js +0 -15
  82. package/skills/graphql-data-access/scripts/get-graphql-schema.mjs +0 -59
  83. package/src/force-app/main/default/webapplications/feature-graphql/.graphqlrc.yml +0 -6
  84. package/src/force-app/main/default/webapplications/feature-graphql/src/api/graphql-operations-types.ts +0 -118
  85. package/src/force-app/main/default/webapplications/feature-graphql/src/api/graphql.test.ts +0 -91
  86. package/src/force-app/main/default/webapplications/feature-graphql/src/api/graphql.ts +0 -35
  87. package/src/force-app/main/default/webapplications/feature-graphql/src/api/utils/accounts.ts +0 -35
  88. package/src/force-app/main/default/webapplications/feature-graphql/src/api/utils/query/highRevenueAccountsQuery.graphql +0 -29
  89. package/src/force-app/main/default/webapplications/feature-graphql/src/api/utils/user.test.ts +0 -129
  90. package/src/force-app/main/default/webapplications/feature-graphql/src/api/utils/user.ts +0 -53
  91. package/src/force-app/main/default/webapplications/feature-graphql/src/components/AccountsTable.tsx +0 -121
  92. package/src/force-app/main/default/webapplications/feature-graphql/src/index.ts +0 -7
  93. package/src/force-app/main/default/webapplications/feature-graphql/src/pages/AccountsPage.tsx +0 -19
  94. package/src/force-app/main/default/webapplications/feature-graphql/src/routes.tsx +0 -20
  95. package/src/force-app/main/default/webapplications/feature-graphql/vite.config.ts +0 -69
@@ -1,29 +0,0 @@
1
- query GetHighRevenueAccounts($minRevenue: Currency) {
2
- uiapi {
3
- query {
4
- Account(
5
- where: { AnnualRevenue: { gt: $minRevenue } }
6
- orderBy: { AnnualRevenue: { order: DESC } }
7
- first: 50
8
- ) {
9
- edges {
10
- node {
11
- Id
12
- Name {
13
- value
14
- }
15
- AnnualRevenue {
16
- value
17
- }
18
- Industry {
19
- value
20
- }
21
- Website {
22
- value
23
- }
24
- }
25
- }
26
- }
27
- }
28
- }
29
- }
@@ -1,129 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { getCurrentUser } from "./user";
3
- import { createDataSDK } from "@salesforce/sdk-data";
4
-
5
- vi.mock("@salesforce/sdk-data", () => ({
6
- createDataSDK: vi.fn(),
7
- }));
8
-
9
- const mockFetch = vi.fn();
10
- const mockSDK = { fetch: mockFetch };
11
-
12
- // Mock console.error to avoid noise in test output
13
- const mockConsoleError = vi.spyOn(console, "error").mockImplementation(() => {});
14
-
15
- describe("User API Utils", () => {
16
- beforeEach(() => {
17
- vi.clearAllMocks();
18
- vi.mocked(createDataSDK).mockResolvedValue(mockSDK);
19
- });
20
-
21
- afterEach(() => {
22
- mockConsoleError.mockClear();
23
- });
24
-
25
- describe("getCurrentUser", () => {
26
- const mockChatterUser = {
27
- id: "005000000000001",
28
- name: "John Doe",
29
- email: "john@example.com",
30
- username: "john@example.com",
31
- };
32
-
33
- it("successfully fetches current user", async () => {
34
- mockFetch.mockResolvedValue({
35
- ok: true,
36
- json: () => Promise.resolve(mockChatterUser),
37
- });
38
-
39
- const result = await getCurrentUser();
40
-
41
- expect(mockFetch).toHaveBeenCalledWith("/services/data/v65.0/chatter/users/me");
42
- expect(result).toEqual({
43
- id: "005000000000001",
44
- name: "John Doe",
45
- });
46
- });
47
-
48
- it('falls back to "User" when name is empty', async () => {
49
- mockFetch.mockResolvedValue({
50
- ok: true,
51
- json: () =>
52
- Promise.resolve({
53
- id: "005000000000003",
54
- name: "",
55
- }),
56
- });
57
-
58
- const result = await getCurrentUser();
59
-
60
- expect(result).toEqual({
61
- id: "005000000000003",
62
- name: "User",
63
- });
64
- });
65
-
66
- it('falls back to "User" when name is null', async () => {
67
- mockFetch.mockResolvedValue({
68
- ok: true,
69
- json: () =>
70
- Promise.resolve({
71
- id: "005000000000004",
72
- name: null,
73
- }),
74
- });
75
-
76
- const result = await getCurrentUser();
77
-
78
- expect(result).toEqual({
79
- id: "005000000000004",
80
- name: "User",
81
- });
82
- });
83
-
84
- it('falls back to "User" when name is undefined', async () => {
85
- mockFetch.mockResolvedValue({
86
- ok: true,
87
- json: () =>
88
- Promise.resolve({
89
- id: "005000000000005",
90
- }),
91
- });
92
-
93
- const result = await getCurrentUser();
94
-
95
- expect(result).toEqual({
96
- id: "005000000000005",
97
- name: "User",
98
- });
99
- });
100
-
101
- it("throws error when request fails", async () => {
102
- mockFetch.mockResolvedValue({
103
- ok: false,
104
- status: 500,
105
- });
106
-
107
- await expect(getCurrentUser()).rejects.toThrow("HTTP 500");
108
- expect(mockConsoleError).toHaveBeenCalled();
109
- });
110
-
111
- it("throws error when no user data is found", async () => {
112
- mockFetch.mockResolvedValue({
113
- ok: true,
114
- json: () => Promise.resolve({}),
115
- });
116
-
117
- await expect(getCurrentUser()).rejects.toThrow("No user data found in Chatter API response");
118
- expect(mockConsoleError).toHaveBeenCalled();
119
- });
120
-
121
- it("throws error when fetch rejects", async () => {
122
- const apiError = new Error("Network error");
123
- mockFetch.mockRejectedValue(apiError);
124
-
125
- await expect(getCurrentUser()).rejects.toThrow("Network error");
126
- expect(mockConsoleError).toHaveBeenCalledWith("Error fetching user data:", apiError);
127
- });
128
- });
129
- });
@@ -1,53 +0,0 @@
1
- import { createDataSDK, type DataSDK } from "@salesforce/sdk-data";
2
-
3
- interface User {
4
- id: string;
5
- name: string;
6
- }
7
-
8
- interface ChatterUser {
9
- id: string;
10
- name: string;
11
- email?: string;
12
- username?: string;
13
- }
14
-
15
- // Lazy-initialized SDK instance
16
- let sdkInstance: DataSDK | null = null;
17
-
18
- async function getSDK(): Promise<DataSDK> {
19
- if (!sdkInstance) {
20
- sdkInstance = await createDataSDK();
21
- }
22
- return sdkInstance;
23
- }
24
-
25
- /**
26
- * Fetch current user information from Salesforce
27
- * Uses Chatter API to get current user details
28
- * @returns User info or null if no session
29
- */
30
- export async function getCurrentUser(): Promise<User | null> {
31
- try {
32
- const sdk = await getSDK();
33
- const response = await sdk.fetch!("/services/data/v65.0/chatter/users/me");
34
-
35
- if (!response.ok) {
36
- throw new Error(`HTTP ${response.status}`);
37
- }
38
-
39
- const userData: ChatterUser = await response.json();
40
-
41
- if (!userData || !userData.id) {
42
- throw new Error("No user data found in Chatter API response");
43
- }
44
-
45
- return {
46
- id: userData.id,
47
- name: userData.name || "User",
48
- };
49
- } catch (error) {
50
- console.error("Error fetching user data:", error);
51
- throw error;
52
- }
53
- }
@@ -1,121 +0,0 @@
1
- import { useState, useEffect } from "react";
2
- import { getHighRevenueAccounts } from "../api/utils/accounts";
3
-
4
- interface AccountRow {
5
- id: string;
6
- name: string;
7
- industry: string;
8
- annualRevenue: string;
9
- website: string;
10
- }
11
-
12
- /**
13
- * AccountsTable – Example component that demonstrates GraphQL data fetching
14
- * from Salesforce using the executeGraphQL utility.
15
- *
16
- * Uses Pattern 1 (external .graphql file) via getHighRevenueAccounts().
17
- */
18
- export function AccountsTable() {
19
- const [accounts, setAccounts] = useState<AccountRow[]>([]);
20
- const [loading, setLoading] = useState(true);
21
- const [error, setError] = useState<string | null>(null);
22
-
23
- useEffect(() => {
24
- async function fetchAccounts() {
25
- try {
26
- const data = await getHighRevenueAccounts({ minRevenue: 100000 });
27
- const rows: AccountRow[] = (data ?? [])
28
- .filter((node): node is NonNullable<typeof node> => node != null)
29
- .map((node) => ({
30
- id: node.Id,
31
- name: node.Name?.value ?? "-",
32
- industry: node.Industry?.value ?? "-",
33
- annualRevenue: node.AnnualRevenue?.value
34
- ? new Intl.NumberFormat("en-US", {
35
- style: "currency",
36
- currency: "USD",
37
- maximumFractionDigits: 0,
38
- }).format(Number(node.AnnualRevenue.value))
39
- : "-",
40
- website: node.Website?.value ?? "-",
41
- }));
42
- setAccounts(rows);
43
- } catch (err) {
44
- setError(err instanceof Error ? err.message : "Failed to fetch accounts");
45
- } finally {
46
- setLoading(false);
47
- }
48
- }
49
- void fetchAccounts();
50
- }, []);
51
-
52
- if (loading) {
53
- return (
54
- <div className="flex items-center justify-center p-8 text-sm text-gray-500">
55
- Loading accounts…
56
- </div>
57
- );
58
- }
59
-
60
- if (error) {
61
- return (
62
- <div className="rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-700">
63
- {error}
64
- </div>
65
- );
66
- }
67
-
68
- if (accounts.length === 0) {
69
- return (
70
- <div className="flex items-center justify-center p-8 text-sm text-gray-500 italic">
71
- No accounts found
72
- </div>
73
- );
74
- }
75
-
76
- return (
77
- <div className="w-full overflow-auto rounded-lg border border-gray-200">
78
- <table className="w-full border-collapse text-sm">
79
- <thead>
80
- <tr className="bg-indigo-50">
81
- <th className="border-b border-indigo-200 px-4 py-2.5 text-left font-medium text-gray-700">
82
- Account Name
83
- </th>
84
- <th className="border-b border-indigo-200 px-4 py-2.5 text-left font-medium text-gray-700">
85
- Industry
86
- </th>
87
- <th className="border-b border-indigo-200 px-4 py-2.5 text-right font-medium text-gray-700">
88
- Annual Revenue
89
- </th>
90
- <th className="border-b border-indigo-200 px-4 py-2.5 text-left font-medium text-gray-700">
91
- Website
92
- </th>
93
- </tr>
94
- </thead>
95
- <tbody>
96
- {accounts.map((account) => (
97
- <tr key={account.id} className="border-b border-gray-100 hover:bg-gray-50">
98
- <td className="px-4 py-2.5 font-medium text-gray-900">{account.name}</td>
99
- <td className="px-4 py-2.5 text-gray-600">{account.industry}</td>
100
- <td className="px-4 py-2.5 text-right text-gray-600">{account.annualRevenue}</td>
101
- <td className="px-4 py-2.5 text-gray-600">
102
- {account.website !== "-" ? (
103
- <a
104
- href={account.website}
105
- target="_blank"
106
- rel="noopener noreferrer"
107
- className="text-indigo-600 hover:underline"
108
- >
109
- {account.website}
110
- </a>
111
- ) : (
112
- "-"
113
- )}
114
- </td>
115
- </tr>
116
- ))}
117
- </tbody>
118
- </table>
119
- </div>
120
- );
121
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * feature-graphql – Salesforce GraphQL data access utilities
3
- */
4
-
5
- export { executeGraphQL, gql } from "./api/graphql";
6
- export type { NodeOfConnection } from "./api/graphql";
7
- export { AccountsTable } from "./components/AccountsTable";
@@ -1,19 +0,0 @@
1
- import { AccountsTable } from "../components/AccountsTable";
2
-
3
- /**
4
- * AccountsPage – Demo page showing the AccountsTable component
5
- * that fetches Salesforce data via GraphQL.
6
- */
7
- export default function AccountsPage() {
8
- return (
9
- <div className="mx-auto max-w-5xl space-y-6 p-6">
10
- <div>
11
- <h1 className="text-2xl font-bold text-gray-900">Accounts</h1>
12
- <p className="mt-1 text-sm text-gray-500">
13
- High-revenue accounts fetched from Salesforce via GraphQL.
14
- </p>
15
- </div>
16
- <AccountsTable />
17
- </div>
18
- );
19
- }
@@ -1,20 +0,0 @@
1
- import type { RouteObject } from "react-router";
2
- import AppLayout from "./__inherit__appLayout";
3
- import AccountsPage from "./pages/AccountsPage";
4
-
5
- export const routes: RouteObject[] = [
6
- {
7
- path: "/",
8
- element: <AppLayout />,
9
- children: [
10
- {
11
- path: "accounts",
12
- element: <AccountsPage />,
13
- handle: {
14
- showInNavigation: true,
15
- label: "Accounts",
16
- },
17
- },
18
- ],
19
- },
20
- ];
@@ -1,69 +0,0 @@
1
- import { defineConfig } from "vite";
2
- import react from "@vitejs/plugin-react";
3
- import path from "path";
4
- import { resolve } from "path";
5
- import tailwindcss from "@tailwindcss/vite";
6
- import salesforce from "@salesforce/vite-plugin-webapp-experimental";
7
-
8
- export default defineConfig(({ mode }) => {
9
- return {
10
- plugins: [tailwindcss(), react(), salesforce()],
11
-
12
- build: {
13
- outDir: resolve(__dirname, "dist"),
14
- assetsDir: "assets",
15
- sourcemap: false,
16
- },
17
-
18
- resolve: {
19
- dedupe: ["react", "react-dom"],
20
- alias: {
21
- react: path.resolve(__dirname, "node_modules/react"),
22
- "react-dom": path.resolve(__dirname, "node_modules/react-dom"),
23
- "@": path.resolve(__dirname, "./src"),
24
- "@api": path.resolve(__dirname, "./src/api"),
25
- "@components": path.resolve(__dirname, "./src/components"),
26
- "@utils": path.resolve(__dirname, "./src/utils"),
27
- "@styles": path.resolve(__dirname, "./src/styles"),
28
- "@assets": path.resolve(__dirname, "./src/assets"),
29
- },
30
- },
31
-
32
- test: {
33
- root: resolve(__dirname),
34
- environment: "jsdom",
35
- setupFiles: ["./src/test/setup.ts"],
36
- include: [
37
- "src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}",
38
- "src/**/__tests__/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}",
39
- ],
40
- coverage: {
41
- provider: "v8",
42
- reporter: ["text", "html", "clover", "json"],
43
- exclude: [
44
- "node_modules/",
45
- "src/test/",
46
- "src/**/*.d.ts",
47
- "src/main.tsx",
48
- "src/vite-env.d.ts",
49
- "src/components/**/index.ts",
50
- "**/*.config.ts",
51
- "build/",
52
- "dist/",
53
- "coverage/",
54
- "eslint.config.js",
55
- ],
56
- thresholds: {
57
- global: {
58
- branches: 85,
59
- functions: 85,
60
- lines: 85,
61
- statements: 85,
62
- },
63
- },
64
- },
65
- testTimeout: 10000,
66
- globals: true,
67
- },
68
- };
69
- });