@salesforce/templates 66.17.0-beta.2 → 66.17.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 (159) hide show
  1. package/lib/generators/lightningAppGenerator.js +7 -7
  2. package/lib/generators/lightningAppGenerator.js.map +1 -1
  3. package/lib/generators/uiBundleGenerator.d.ts +1 -0
  4. package/lib/generators/uiBundleGenerator.js +12 -0
  5. package/lib/generators/uiBundleGenerator.js.map +1 -1
  6. package/lib/generators/visualforceComponentGenerator.js +7 -2
  7. package/lib/generators/visualforceComponentGenerator.js.map +1 -1
  8. package/lib/generators/visualforcePageGenerator.js +5 -0
  9. package/lib/generators/visualforcePageGenerator.js.map +1 -1
  10. package/lib/i18n/i18n.d.ts +2 -0
  11. package/lib/i18n/i18n.js +2 -0
  12. package/lib/i18n/i18n.js.map +1 -1
  13. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package-lock.json +1204 -998
  14. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package-lock.json +1210 -1004
  15. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package-lock.json +2166 -1676
  16. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package-lock.json +2172 -1682
  17. package/lib/templates/uiBundles/angularbasic/package-lock.json +1204 -998
  18. package/lib/templates/uiBundles/reactbasic/package-lock.json +2166 -1676
  19. package/lib/templates/uiBundles/vuebasic/.forceignore +15 -0
  20. package/lib/templates/uiBundles/vuebasic/.graphqlrc.yml +2 -0
  21. package/lib/templates/uiBundles/vuebasic/.prettierignore +9 -0
  22. package/lib/templates/uiBundles/vuebasic/.prettierrc +11 -0
  23. package/lib/templates/uiBundles/vuebasic/README.md +43 -0
  24. package/lib/templates/uiBundles/vuebasic/_uibundle.uibundle-meta.xml +7 -0
  25. package/lib/templates/uiBundles/vuebasic/codegen.yml +95 -0
  26. package/lib/templates/uiBundles/vuebasic/components.json +19 -0
  27. package/lib/templates/uiBundles/vuebasic/eslint.config.js +110 -0
  28. package/lib/templates/uiBundles/vuebasic/index.html +12 -0
  29. package/lib/templates/uiBundles/vuebasic/package-lock.json +15489 -0
  30. package/lib/templates/uiBundles/vuebasic/package.json +72 -0
  31. package/lib/templates/uiBundles/vuebasic/playwright.config.ts +24 -0
  32. package/lib/templates/uiBundles/vuebasic/scripts/get-graphql-schema.mjs +71 -0
  33. package/lib/templates/uiBundles/vuebasic/scripts/rewrite-e2e-assets.mjs +23 -0
  34. package/lib/templates/uiBundles/vuebasic/src/App.vue +5 -0
  35. package/lib/templates/uiBundles/vuebasic/src/api/graphqlClient.ts +44 -0
  36. package/lib/templates/uiBundles/vuebasic/src/appLayout.vue +80 -0
  37. package/lib/templates/uiBundles/vuebasic/src/components/alerts/status-alert.vue +59 -0
  38. package/lib/templates/uiBundles/vuebasic/src/components/layouts/card-layout.vue +30 -0
  39. package/lib/templates/uiBundles/vuebasic/src/components/ui/alert/Alert.vue +21 -0
  40. package/lib/templates/uiBundles/vuebasic/src/components/ui/alert/AlertAction.vue +17 -0
  41. package/lib/templates/uiBundles/vuebasic/src/components/ui/alert/AlertDescription.vue +22 -0
  42. package/lib/templates/uiBundles/vuebasic/src/components/ui/alert/AlertTitle.vue +22 -0
  43. package/lib/templates/uiBundles/vuebasic/src/components/ui/alert/index.ts +25 -0
  44. package/lib/templates/uiBundles/vuebasic/src/components/ui/avatar/Avatar.vue +23 -0
  45. package/lib/templates/uiBundles/vuebasic/src/components/ui/avatar/AvatarFallback.vue +31 -0
  46. package/lib/templates/uiBundles/vuebasic/src/components/ui/avatar/AvatarImage.vue +16 -0
  47. package/lib/templates/uiBundles/vuebasic/src/components/ui/avatar/index.ts +3 -0
  48. package/lib/templates/uiBundles/vuebasic/src/components/ui/button/Button.vue +33 -0
  49. package/lib/templates/uiBundles/vuebasic/src/components/ui/button/index.ts +43 -0
  50. package/lib/templates/uiBundles/vuebasic/src/components/ui/card/Card.vue +29 -0
  51. package/lib/templates/uiBundles/vuebasic/src/components/ui/card/CardAction.vue +22 -0
  52. package/lib/templates/uiBundles/vuebasic/src/components/ui/card/CardContent.vue +17 -0
  53. package/lib/templates/uiBundles/vuebasic/src/components/ui/card/CardDescription.vue +17 -0
  54. package/lib/templates/uiBundles/vuebasic/src/components/ui/card/CardFooter.vue +22 -0
  55. package/lib/templates/uiBundles/vuebasic/src/components/ui/card/CardHeader.vue +22 -0
  56. package/lib/templates/uiBundles/vuebasic/src/components/ui/card/CardTitle.vue +22 -0
  57. package/lib/templates/uiBundles/vuebasic/src/components/ui/card/index.ts +7 -0
  58. package/lib/templates/uiBundles/vuebasic/src/components/ui/checkbox/Checkbox.vue +43 -0
  59. package/lib/templates/uiBundles/vuebasic/src/components/ui/checkbox/index.ts +1 -0
  60. package/lib/templates/uiBundles/vuebasic/src/components/ui/collapsible/Collapsible.vue +19 -0
  61. package/lib/templates/uiBundles/vuebasic/src/components/ui/collapsible/CollapsibleContent.vue +12 -0
  62. package/lib/templates/uiBundles/vuebasic/src/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
  63. package/lib/templates/uiBundles/vuebasic/src/components/ui/collapsible/index.ts +3 -0
  64. package/lib/templates/uiBundles/vuebasic/src/components/ui/dialog/Dialog.vue +15 -0
  65. package/lib/templates/uiBundles/vuebasic/src/components/ui/dialog/DialogClose.vue +12 -0
  66. package/lib/templates/uiBundles/vuebasic/src/components/ui/dialog/DialogContent.vue +59 -0
  67. package/lib/templates/uiBundles/vuebasic/src/components/ui/dialog/DialogDescription.vue +31 -0
  68. package/lib/templates/uiBundles/vuebasic/src/components/ui/dialog/DialogFooter.vue +33 -0
  69. package/lib/templates/uiBundles/vuebasic/src/components/ui/dialog/DialogHeader.vue +17 -0
  70. package/lib/templates/uiBundles/vuebasic/src/components/ui/dialog/DialogOverlay.vue +31 -0
  71. package/lib/templates/uiBundles/vuebasic/src/components/ui/dialog/DialogPortal.vue +12 -0
  72. package/lib/templates/uiBundles/vuebasic/src/components/ui/dialog/DialogTitle.vue +26 -0
  73. package/lib/templates/uiBundles/vuebasic/src/components/ui/dialog/DialogTrigger.vue +12 -0
  74. package/lib/templates/uiBundles/vuebasic/src/components/ui/dialog/index.ts +10 -0
  75. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
  76. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +51 -0
  77. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuContent.vue +47 -0
  78. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
  79. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuItem.vue +44 -0
  80. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +30 -0
  81. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuPortal.vue +12 -0
  82. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
  83. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +52 -0
  84. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +26 -0
  85. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +19 -0
  86. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
  87. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +37 -0
  88. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +38 -0
  89. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
  90. package/lib/templates/uiBundles/vuebasic/src/components/ui/dropdown-menu/index.ts +15 -0
  91. package/lib/templates/uiBundles/vuebasic/src/components/ui/field/Field.vue +27 -0
  92. package/lib/templates/uiBundles/vuebasic/src/components/ui/field/FieldContent.vue +22 -0
  93. package/lib/templates/uiBundles/vuebasic/src/components/ui/field/FieldDescription.vue +24 -0
  94. package/lib/templates/uiBundles/vuebasic/src/components/ui/field/FieldError.vue +48 -0
  95. package/lib/templates/uiBundles/vuebasic/src/components/ui/field/FieldGroup.vue +22 -0
  96. package/lib/templates/uiBundles/vuebasic/src/components/ui/field/FieldLabel.vue +24 -0
  97. package/lib/templates/uiBundles/vuebasic/src/components/ui/field/FieldLegend.vue +29 -0
  98. package/lib/templates/uiBundles/vuebasic/src/components/ui/field/FieldSeparator.vue +31 -0
  99. package/lib/templates/uiBundles/vuebasic/src/components/ui/field/FieldSet.vue +22 -0
  100. package/lib/templates/uiBundles/vuebasic/src/components/ui/field/FieldTitle.vue +22 -0
  101. package/lib/templates/uiBundles/vuebasic/src/components/ui/field/index.ts +33 -0
  102. package/lib/templates/uiBundles/vuebasic/src/components/ui/index.ts +115 -0
  103. package/lib/templates/uiBundles/vuebasic/src/components/ui/input/Input.vue +30 -0
  104. package/lib/templates/uiBundles/vuebasic/src/components/ui/input/index.ts +1 -0
  105. package/lib/templates/uiBundles/vuebasic/src/components/ui/label/Label.vue +29 -0
  106. package/lib/templates/uiBundles/vuebasic/src/components/ui/label/index.ts +1 -0
  107. package/lib/templates/uiBundles/vuebasic/src/components/ui/pagination/Pagination.vue +17 -0
  108. package/lib/templates/uiBundles/vuebasic/src/components/ui/pagination/PaginationContent.vue +15 -0
  109. package/lib/templates/uiBundles/vuebasic/src/components/ui/pagination/PaginationEllipsis.vue +23 -0
  110. package/lib/templates/uiBundles/vuebasic/src/components/ui/pagination/PaginationItem.vue +7 -0
  111. package/lib/templates/uiBundles/vuebasic/src/components/ui/pagination/PaginationLink.vue +31 -0
  112. package/lib/templates/uiBundles/vuebasic/src/components/ui/pagination/PaginationNext.vue +29 -0
  113. package/lib/templates/uiBundles/vuebasic/src/components/ui/pagination/PaginationPrevious.vue +29 -0
  114. package/lib/templates/uiBundles/vuebasic/src/components/ui/pagination/index.ts +7 -0
  115. package/lib/templates/uiBundles/vuebasic/src/components/ui/popover/Popover.vue +15 -0
  116. package/lib/templates/uiBundles/vuebasic/src/components/ui/popover/PopoverAnchor.vue +12 -0
  117. package/lib/templates/uiBundles/vuebasic/src/components/ui/popover/PopoverContent.vue +44 -0
  118. package/lib/templates/uiBundles/vuebasic/src/components/ui/popover/PopoverDescription.vue +17 -0
  119. package/lib/templates/uiBundles/vuebasic/src/components/ui/popover/PopoverHeader.vue +17 -0
  120. package/lib/templates/uiBundles/vuebasic/src/components/ui/popover/PopoverTitle.vue +14 -0
  121. package/lib/templates/uiBundles/vuebasic/src/components/ui/popover/PopoverTrigger.vue +12 -0
  122. package/lib/templates/uiBundles/vuebasic/src/components/ui/popover/index.ts +7 -0
  123. package/lib/templates/uiBundles/vuebasic/src/components/ui/select/Select.vue +15 -0
  124. package/lib/templates/uiBundles/vuebasic/src/components/ui/select/SelectContent.vue +65 -0
  125. package/lib/templates/uiBundles/vuebasic/src/components/ui/select/SelectGroup.vue +26 -0
  126. package/lib/templates/uiBundles/vuebasic/src/components/ui/select/SelectItem.vue +41 -0
  127. package/lib/templates/uiBundles/vuebasic/src/components/ui/select/SelectLabel.vue +26 -0
  128. package/lib/templates/uiBundles/vuebasic/src/components/ui/select/SelectScrollDownButton.vue +32 -0
  129. package/lib/templates/uiBundles/vuebasic/src/components/ui/select/SelectScrollUpButton.vue +32 -0
  130. package/lib/templates/uiBundles/vuebasic/src/components/ui/select/SelectSeparator.vue +24 -0
  131. package/lib/templates/uiBundles/vuebasic/src/components/ui/select/SelectTrigger.vue +45 -0
  132. package/lib/templates/uiBundles/vuebasic/src/components/ui/select/SelectValue.vue +12 -0
  133. package/lib/templates/uiBundles/vuebasic/src/components/ui/select/index.ts +10 -0
  134. package/lib/templates/uiBundles/vuebasic/src/components/ui/separator/Separator.vue +33 -0
  135. package/lib/templates/uiBundles/vuebasic/src/components/ui/separator/index.ts +1 -0
  136. package/lib/templates/uiBundles/vuebasic/src/components/ui/skeleton/Skeleton.vue +15 -0
  137. package/lib/templates/uiBundles/vuebasic/src/components/ui/skeleton/index.ts +1 -0
  138. package/lib/templates/uiBundles/vuebasic/src/components/ui/sonner/Sonner.vue +29 -0
  139. package/lib/templates/uiBundles/vuebasic/src/components/ui/sonner/index.ts +3 -0
  140. package/lib/templates/uiBundles/vuebasic/src/components/ui/spinner/Spinner.vue +17 -0
  141. package/lib/templates/uiBundles/vuebasic/src/components/ui/spinner/index.ts +1 -0
  142. package/lib/templates/uiBundles/vuebasic/src/composables/useAsyncData.ts +69 -0
  143. package/lib/templates/uiBundles/vuebasic/src/lib/utils.ts +6 -0
  144. package/lib/templates/uiBundles/vuebasic/src/main.ts +6 -0
  145. package/lib/templates/uiBundles/vuebasic/src/pages/Home.vue +10 -0
  146. package/lib/templates/uiBundles/vuebasic/src/pages/NotFound.vue +18 -0
  147. package/lib/templates/uiBundles/vuebasic/src/router-utils.ts +32 -0
  148. package/lib/templates/uiBundles/vuebasic/src/router.ts +40 -0
  149. package/lib/templates/uiBundles/vuebasic/src/styles/global.css +134 -0
  150. package/lib/templates/uiBundles/vuebasic/src/test/setup.ts +1 -0
  151. package/lib/templates/uiBundles/vuebasic/tsconfig.json +45 -0
  152. package/lib/templates/uiBundles/vuebasic/tsconfig.node.json +13 -0
  153. package/lib/templates/uiBundles/vuebasic/ui-bundle.json +7 -0
  154. package/lib/templates/uiBundles/vuebasic/vite-env.d.ts +14 -0
  155. package/lib/templates/uiBundles/vuebasic/vite.config.ts +106 -0
  156. package/lib/templates/uiBundles/vuebasic/vitest-env.d.ts +2 -0
  157. package/lib/utils/constants.d.ts +1 -1
  158. package/lib/utils/constants.js +1 -1
  159. package/package.json +2 -1
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "base-vue-app",
3
+ "private": true,
4
+ "version": "1.59.0",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22"
8
+ },
9
+ "scripts": {
10
+ "dev": "vite",
11
+ "dev:design": "vite --mode design",
12
+ "build": "vue-tsc -b && vite build",
13
+ "build:e2e": "npm run build && node scripts/rewrite-e2e-assets.mjs",
14
+ "lint": "eslint .",
15
+ "preview": "vite preview",
16
+ "test": "vitest",
17
+ "graphql:codegen": "graphql-codegen",
18
+ "graphql:schema": "node scripts/get-graphql-schema.mjs"
19
+ },
20
+ "dependencies": {
21
+ "@lucide/vue": "^1.47.0",
22
+ "@salesforce/platform-sdk": "^12.11.0",
23
+ "@salesforce/ui-bundle": "^12.11.0",
24
+ "@tailwindcss/vite": "^4.1.17",
25
+ "class-variance-authority": "^0.7.1",
26
+ "clsx": "^2.1.1",
27
+ "date-fns": "^4.1.0",
28
+ "reka-ui": "^2.5.0",
29
+ "tailwind-merge": "^3.5.0",
30
+ "tailwindcss": "^4.1.17",
31
+ "tw-animate-css": "^1.4.0",
32
+ "vue": "^3.5.13",
33
+ "vue-router": "^4.5.0",
34
+ "vue-sonner": "^2.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@eslint/js": "^9.39.1",
38
+ "@graphql-codegen/cli": "^6.1.0",
39
+ "@graphql-codegen/typescript": "^5.0.6",
40
+ "@graphql-codegen/typescript-operations": "^5.0.6",
41
+ "@graphql-eslint/eslint-plugin": "^4.1.0",
42
+ "@graphql-tools/utils": "^11.0.0",
43
+ "@playwright/test": "^1.49.0",
44
+ "@salesforce/graphiti": "^12.11.0",
45
+ "@salesforce/vite-plugin-ui-bundle": "^12.11.0",
46
+ "@testing-library/jest-dom": "^6.6.3",
47
+ "@testing-library/user-event": "^14.5.2",
48
+ "@testing-library/vue": "^8.1.0",
49
+ "@types/node": "^24.10.1",
50
+ "@vitejs/plugin-vue": "^6.0.0",
51
+ "@vitest/ui": "^4.1.0",
52
+ "@vue/eslint-config-typescript": "^14.1.4",
53
+ "@vue/test-utils": "^2.4.6",
54
+ "eslint": "^9.39.1",
55
+ "eslint-plugin-vue": "^9.32.0",
56
+ "globals": "^16.5.0",
57
+ "graphql": "^16.11.0",
58
+ "graphql-codegen-typescript-operation-types": "^2.0.2",
59
+ "jsdom": "^25.0.1",
60
+ "serve": "^14.2.5",
61
+ "typescript": "~5.9.3",
62
+ "typescript-eslint": "^8.46.4",
63
+ "vite": "^7.2.4",
64
+ "vite-plugin-graphql-codegen": "^3.6.3",
65
+ "vitest": "^4.1.0",
66
+ "vue-tsc": "^2.2.0"
67
+ },
68
+ "overrides": {
69
+ "lodash": "^4.18.1",
70
+ "undici": "^7.28.0"
71
+ }
72
+ }
@@ -0,0 +1,24 @@
1
+ import { defineConfig, devices } from '@playwright/test';
2
+
3
+ const E2E_PORT = 5177;
4
+
5
+ export default defineConfig({
6
+ testDir: './e2e',
7
+ fullyParallel: true,
8
+ forbidOnly: !!process.env.CI,
9
+ retries: process.env.CI ? 2 : 0,
10
+ workers: process.env.CI ? 1 : undefined,
11
+ reporter: 'html',
12
+ use: {
13
+ baseURL: `http://localhost:${E2E_PORT}`,
14
+ trace: 'on-first-retry',
15
+ },
16
+ projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
17
+ webServer: {
18
+ // Serve built dist/ with static server so e2e works in CI without SF org (vite preview runs plugin and can fail)
19
+ command: `npx serve dist -l ${E2E_PORT}`,
20
+ url: `http://localhost:${E2E_PORT}`,
21
+ reuseExistingServer: !process.env.CI,
22
+ timeout: process.env.CI ? 120_000 : 60_000,
23
+ },
24
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Downloads the full GraphQL schema from a connected Salesforce org via introspection.
3
+ *
4
+ * Usage:
5
+ * npm run graphql:schema
6
+ * node scripts/get-graphql-schema.mjs [output-path]
7
+ *
8
+ * The default output path matches the schema location expected by codegen.yml
9
+ * and .graphqlrc.yml so that codegen and IDE tooling resolve it automatically.
10
+ */
11
+ import { writeFileSync } from 'node:fs';
12
+ import { resolve } from 'node:path';
13
+ import { getOrgInfo } from '@salesforce/ui-bundle/app';
14
+ import { buildClientSchema, getIntrospectionQuery, printSchema } from 'graphql';
15
+ import { pruneSchema } from '@graphql-tools/utils';
16
+
17
+ const DEFAULT_SCHEMA_PATH = '../../../../../schema.graphql';
18
+ const TARGET_ORG = process.env.SF_TARGET_ORG || undefined;
19
+
20
+ async function executeSalesforceGraphQLQuery(query, variables, operationName) {
21
+ const orgInfo = await getOrgInfo(TARGET_ORG);
22
+ if (!orgInfo) {
23
+ throw new Error(
24
+ 'Could not resolve a Salesforce org. Set SF_TARGET_ORG or a default org.'
25
+ );
26
+ }
27
+ const { rawInstanceUrl: instanceUrl, apiVersion, accessToken } = orgInfo;
28
+
29
+ const targetUrl = `${instanceUrl}/services/data/v${apiVersion}/graphql`;
30
+
31
+ console.log(`Executing introspection query against ${targetUrl}`);
32
+ const response = await fetch(targetUrl, {
33
+ method: 'POST',
34
+ headers: {
35
+ Authorization: `Bearer ${accessToken}`,
36
+ 'Content-Type': 'application/json',
37
+ 'X-Chatter-Entity-Encoding': 'false',
38
+ },
39
+ body: JSON.stringify({ query, variables, operationName }),
40
+ });
41
+
42
+ if (!response.ok) {
43
+ const errorText = await response.text();
44
+ throw new Error(
45
+ `Salesforce GraphQL request failed: ${response.status} ${response.statusText} - ${errorText}`
46
+ );
47
+ }
48
+
49
+ return response.json();
50
+ }
51
+
52
+ try {
53
+ const outputPath = resolve(process.argv[2] || DEFAULT_SCHEMA_PATH);
54
+
55
+ const introspectionResult = await executeSalesforceGraphQLQuery(
56
+ getIntrospectionQuery(),
57
+ {},
58
+ 'IntrospectionQuery'
59
+ );
60
+
61
+ const schema = buildClientSchema(introspectionResult.data);
62
+ const prunedSchema = pruneSchema(schema);
63
+ const sdl = printSchema(prunedSchema);
64
+
65
+ writeFileSync(outputPath, sdl);
66
+
67
+ console.log(`Schema saved to ${outputPath}`);
68
+ } catch (error) {
69
+ console.error('Error:', error.message);
70
+ process.exit(1);
71
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Prepares dist/ for e2e: root-relative asset paths + SPA fallback for serve.
3
+ */
4
+ import { readFileSync, writeFileSync } from 'node:fs';
5
+ import { join, dirname } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const distDir = join(__dirname, '..', 'dist');
10
+
11
+ // Rewrite index.html so asset paths are root-relative (/assets/...)
12
+ const indexPath = join(distDir, 'index.html');
13
+ let html = readFileSync(indexPath, 'utf8');
14
+ html = html.replace(/(src|href)="[^"]*\/assets\//g, '$1="/assets/');
15
+ writeFileSync(indexPath, html);
16
+
17
+ // SPA fallback so /about, /non-existent-route etc. serve index.html
18
+ writeFileSync(
19
+ join(distDir, 'serve.json'),
20
+ JSON.stringify({
21
+ rewrites: [{ source: '**', destination: '/index.html' }],
22
+ })
23
+ );
@@ -0,0 +1,5 @@
1
+ <script setup lang="ts"></script>
2
+
3
+ <template>
4
+ <RouterView />
5
+ </template>
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Thin GraphQL client: createDataSDK + sdk.graphql with centralized error
3
+ * handling. Mutations are routed to sdk.graphql.mutate and everything else to
4
+ * sdk.graphql.query (the SDK rejects an operation sent to the wrong method).
5
+ * Use with gql-tagged queries and generated operation types for type-safe calls.
6
+ */
7
+ import { createDataSDK } from '@salesforce/platform-sdk';
8
+
9
+ /**
10
+ * True when the operation's first definition is a `mutation`. Strips GraphQL
11
+ * comments first so a leading `# ...` line can't mask the keyword. Queries
12
+ * (named or anonymous `{ ... }` shorthand) and subscriptions fall through to
13
+ * query().
14
+ */
15
+ function isMutation(operation: string): boolean {
16
+ return /^\s*mutation\b/.test(operation.replace(/#[^\n\r]*/g, ''));
17
+ }
18
+
19
+ export async function executeGraphQL<TData, TVariables>(
20
+ operation: string,
21
+ variables?: TVariables
22
+ ): Promise<TData> {
23
+ const data = await createDataSDK();
24
+ const result = isMutation(operation)
25
+ ? await data.graphql!.mutate<TData, TVariables>({
26
+ mutation: operation,
27
+ variables: variables,
28
+ })
29
+ : await data.graphql!.query<TData, TVariables>({
30
+ query: operation,
31
+ variables: variables,
32
+ });
33
+
34
+ if (result.errors?.length) {
35
+ const msg = result.errors.map(e => e.message).join('; ');
36
+ throw new Error(`GraphQL Error: ${msg}`);
37
+ }
38
+
39
+ if (result.data == null) {
40
+ throw new Error('GraphQL response data is null');
41
+ }
42
+
43
+ return result.data;
44
+ }
@@ -0,0 +1,80 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue';
3
+ import { RouterLink, RouterView, useRoute } from 'vue-router';
4
+ import { getAllRoutes } from '@/router-utils';
5
+
6
+ const isOpen = ref(false);
7
+ const route = useRoute();
8
+
9
+ const toggleMenu = () => {
10
+ isOpen.value = !isOpen.value;
11
+ };
12
+
13
+ const isActive = (path: string) => route.path === path;
14
+
15
+ const navigationRoutes = computed<{ path: string; label: string }[]>(() =>
16
+ getAllRoutes()
17
+ .filter(
18
+ r =>
19
+ r.meta?.showInNavigation === true &&
20
+ r.fullPath !== undefined &&
21
+ r.meta?.label !== undefined
22
+ )
23
+ .map(r => ({
24
+ path: r.fullPath,
25
+ label: r.meta?.label as string,
26
+ }))
27
+ );
28
+ </script>
29
+
30
+ <template>
31
+ <nav class="bg-white border-b border-gray-200">
32
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
33
+ <div class="flex justify-between items-center h-16">
34
+ <RouterLink to="/" class="text-xl font-semibold text-gray-900">
35
+ Vue App
36
+ </RouterLink>
37
+ <button
38
+ type="button"
39
+ class="p-2 rounded-md text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
40
+ aria-label="Toggle menu"
41
+ @click="toggleMenu"
42
+ >
43
+ <div class="w-6 h-6 flex flex-col justify-center space-y-1.5">
44
+ <span
45
+ class="block h-0.5 w-6 bg-current transition-all"
46
+ :class="{ 'rotate-45 translate-y-2': isOpen }"
47
+ />
48
+ <span
49
+ class="block h-0.5 w-6 bg-current transition-all"
50
+ :class="{ 'opacity-0': isOpen }"
51
+ />
52
+ <span
53
+ class="block h-0.5 w-6 bg-current transition-all"
54
+ :class="{ '-rotate-45 -translate-y-2': isOpen }"
55
+ />
56
+ </div>
57
+ </button>
58
+ </div>
59
+ <div v-if="isOpen" class="pb-4">
60
+ <div class="flex flex-col space-y-2">
61
+ <RouterLink
62
+ v-for="item in navigationRoutes"
63
+ :key="item.path"
64
+ :to="item.path"
65
+ class="px-3 py-2 rounded-md text-sm font-medium transition-colors"
66
+ :class="
67
+ isActive(item.path)
68
+ ? 'bg-blue-100 text-blue-700'
69
+ : 'text-gray-700 hover:bg-gray-100'
70
+ "
71
+ @click="isOpen = false"
72
+ >
73
+ {{ item.label }}
74
+ </RouterLink>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </nav>
79
+ <RouterView />
80
+ </template>
@@ -0,0 +1,59 @@
1
+ <script setup lang="ts">
2
+ import { computed, useId } from 'vue';
3
+ import { cva } from 'class-variance-authority';
4
+ import { AlertCircleIcon, CheckCircle2Icon, InfoIcon } from '@lucide/vue';
5
+ import { Alert, AlertDescription } from '@/components/ui/alert';
6
+
7
+ const statusAlertVariants = cva('', {
8
+ variants: {
9
+ variant: {
10
+ error: '',
11
+ success: '',
12
+ info: 'text-blue-700 dark:text-blue-400 *:[svg]:text-current *:data-[slot=alert-description]:text-blue-700 dark:*:data-[slot=alert-description]:text-blue-400',
13
+ },
14
+ },
15
+ defaultVariants: {
16
+ variant: 'error',
17
+ },
18
+ });
19
+
20
+ /**
21
+ * Status alert component for displaying error, success, or info messages.
22
+ * Renders nothing when the default slot is empty.
23
+ */
24
+ const props = withDefaults(
25
+ defineProps<{
26
+ /** Alert variant type. @default "error" */
27
+ variant?: 'error' | 'success' | 'info';
28
+ }>(),
29
+ {
30
+ variant: 'error',
31
+ }
32
+ );
33
+
34
+ const descriptionId = useId();
35
+ const isError = computed(() => props.variant === 'error');
36
+ const icon = computed(
37
+ () =>
38
+ ({
39
+ error: AlertCircleIcon,
40
+ success: CheckCircle2Icon,
41
+ info: InfoIcon,
42
+ })[props.variant]
43
+ );
44
+ </script>
45
+
46
+ <template>
47
+ <Alert
48
+ v-if="$slots.default"
49
+ :variant="isError ? 'destructive' : 'default'"
50
+ :class="statusAlertVariants({ variant })"
51
+ :aria-describedby="descriptionId"
52
+ :role="isError ? 'alert' : 'status'"
53
+ >
54
+ <component :is="icon" aria-hidden="true" />
55
+ <AlertDescription :id="descriptionId">
56
+ <slot />
57
+ </AlertDescription>
58
+ </Alert>
59
+ </template>
@@ -0,0 +1,30 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ Card,
4
+ CardContent,
5
+ CardDescription,
6
+ CardHeader,
7
+ CardTitle,
8
+ } from '@/components/ui/card';
9
+
10
+ /**
11
+ * Card layout component for authentication pages.
12
+ * Provides a CardHeader with title and optional description, and CardContent.
13
+ */
14
+ defineProps<{
15
+ title: string;
16
+ description?: string;
17
+ }>();
18
+ </script>
19
+
20
+ <template>
21
+ <Card>
22
+ <CardHeader>
23
+ <CardTitle class="text-2xl">{{ title }}</CardTitle>
24
+ <CardDescription v-if="description">{{ description }}</CardDescription>
25
+ </CardHeader>
26
+ <CardContent>
27
+ <slot />
28
+ </CardContent>
29
+ </Card>
30
+ </template>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue';
3
+ import type { AlertVariants } from '.';
4
+ import { cn } from '@/lib/utils';
5
+ import { alertVariants } from '.';
6
+
7
+ const props = defineProps<{
8
+ class?: HTMLAttributes['class'];
9
+ variant?: AlertVariants['variant'];
10
+ }>();
11
+ </script>
12
+
13
+ <template>
14
+ <div
15
+ data-slot="alert"
16
+ role="alert"
17
+ :class="cn(alertVariants({ variant }), props.class)"
18
+ >
19
+ <slot />
20
+ </div>
21
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue';
3
+ import { cn } from '@/lib/utils';
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class'];
7
+ }>();
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ data-slot="alert-action"
13
+ :class="cn('absolute top-2 right-2', props.class)"
14
+ >
15
+ <slot />
16
+ </div>
17
+ </template>
@@ -0,0 +1,22 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue';
3
+ import { cn } from '@/lib/utils';
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class'];
7
+ }>();
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ data-slot="alert-description"
13
+ :class="
14
+ cn(
15
+ 'text-muted-foreground text-sm text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3',
16
+ props.class
17
+ )
18
+ "
19
+ >
20
+ <slot />
21
+ </div>
22
+ </template>
@@ -0,0 +1,22 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue';
3
+ import { cn } from '@/lib/utils';
4
+
5
+ const props = defineProps<{
6
+ class?: HTMLAttributes['class'];
7
+ }>();
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ data-slot="alert-title"
13
+ :class="
14
+ cn(
15
+ 'font-medium group-has-[>svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3',
16
+ props.class
17
+ )
18
+ "
19
+ >
20
+ <slot />
21
+ </div>
22
+ </template>
@@ -0,0 +1,25 @@
1
+ import type { VariantProps } from 'class-variance-authority';
2
+ import { cva } from 'class-variance-authority';
3
+
4
+ export { default as Alert } from './Alert.vue';
5
+ export { default as AlertTitle } from './AlertTitle.vue';
6
+ export { default as AlertDescription } from './AlertDescription.vue';
7
+ export { default as AlertAction } from './AlertAction.vue';
8
+
9
+ export const alertVariants = cva(
10
+ "grid gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 w-full relative group/alert",
11
+ {
12
+ variants: {
13
+ variant: {
14
+ default: 'bg-card text-card-foreground',
15
+ destructive:
16
+ 'text-red-700 dark:text-red-400 bg-card *:data-[slot=alert-description]:text-red-700 dark:*:data-[slot=alert-description]:text-red-400 *:[svg]:text-current',
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: 'default',
21
+ },
22
+ }
23
+ );
24
+
25
+ export type AlertVariants = VariantProps<typeof alertVariants>;
@@ -0,0 +1,23 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue';
3
+ import { AvatarRoot } from 'reka-ui';
4
+ import { cn } from '@/lib/utils';
5
+
6
+ const props = defineProps<{
7
+ class?: HTMLAttributes['class'];
8
+ }>();
9
+ </script>
10
+
11
+ <template>
12
+ <AvatarRoot
13
+ data-slot="avatar"
14
+ :class="
15
+ cn(
16
+ 'relative flex size-8 shrink-0 overflow-hidden rounded-full',
17
+ props.class
18
+ )
19
+ "
20
+ >
21
+ <slot />
22
+ </AvatarRoot>
23
+ </template>
@@ -0,0 +1,31 @@
1
+ <script setup lang="ts">
2
+ import type { AvatarFallbackProps } from 'reka-ui';
3
+ import type { HTMLAttributes } from 'vue';
4
+ import { computed } from 'vue';
5
+ import { AvatarFallback } from 'reka-ui';
6
+ import { cn } from '@/lib/utils';
7
+
8
+ const props = defineProps<
9
+ AvatarFallbackProps & { class?: HTMLAttributes['class'] }
10
+ >();
11
+
12
+ const delegatedProps = computed(() => {
13
+ const { class: _class, ...rest } = props;
14
+ return rest;
15
+ });
16
+ </script>
17
+
18
+ <template>
19
+ <AvatarFallback
20
+ data-slot="avatar-fallback"
21
+ v-bind="delegatedProps"
22
+ :class="
23
+ cn(
24
+ 'bg-muted flex size-full items-center justify-center rounded-full',
25
+ props.class
26
+ )
27
+ "
28
+ >
29
+ <slot />
30
+ </AvatarFallback>
31
+ </template>
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts">
2
+ import type { AvatarImageProps } from 'reka-ui';
3
+ import { AvatarImage } from 'reka-ui';
4
+
5
+ const props = defineProps<AvatarImageProps>();
6
+ </script>
7
+
8
+ <template>
9
+ <AvatarImage
10
+ data-slot="avatar-image"
11
+ v-bind="props"
12
+ class="aspect-square size-full"
13
+ >
14
+ <slot />
15
+ </AvatarImage>
16
+ </template>
@@ -0,0 +1,3 @@
1
+ export { default as Avatar } from './Avatar.vue';
2
+ export { default as AvatarFallback } from './AvatarFallback.vue';
3
+ export { default as AvatarImage } from './AvatarImage.vue';
@@ -0,0 +1,33 @@
1
+ <script setup lang="ts">
2
+ import type { PrimitiveProps } from 'reka-ui';
3
+ import type { HTMLAttributes } from 'vue';
4
+ import type { ButtonVariants } from '.';
5
+ import { Primitive } from 'reka-ui';
6
+ import { cn } from '@/lib/utils';
7
+ import { buttonVariants } from '.';
8
+
9
+ interface Props extends PrimitiveProps {
10
+ variant?: ButtonVariants['variant'];
11
+ size?: ButtonVariants['size'];
12
+ class?: HTMLAttributes['class'];
13
+ }
14
+
15
+ const props = withDefaults(defineProps<Props>(), {
16
+ as: 'button',
17
+ variant: 'default',
18
+ size: 'default',
19
+ });
20
+ </script>
21
+
22
+ <template>
23
+ <Primitive
24
+ data-slot="button"
25
+ :data-variant="variant"
26
+ :data-size="size"
27
+ :as="as"
28
+ :as-child="asChild"
29
+ :class="cn(buttonVariants({ variant, size }), props.class)"
30
+ >
31
+ <slot />
32
+ </Primitive>
33
+ </template>