From 00a1b1ef3f1736e1add6e8276d8f268b41c9a0c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 23:31:53 +0000 Subject: [PATCH 01/10] Initial plan From eb1f141b5df06cd42c7efab140fdaa1ab4482647 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 23:49:29 +0000 Subject: [PATCH 02/10] feat: add Profile/PermissionSet editor webview UI with React - Add reusable WebviewPanel base class for VSCode webview panels - Add ProfileEditorWebview backend with profile data loading and save - Add ProfileEditorCommand to open the editor from context menu or palette - Add React-based UI with virtualized object/field permission tables - Add filter bar with match highlighting for 500+ objects / 800+ fields - Add floating ActionBar for save/reset operations - Add AddPermissionDialog for adding new object/field permissions - Add setObjectPermissions() and setFieldPermissions() public API to SalesforceUserPermissions - Add license and description public getters to SalesforceUserPermissions - Add esbuild script for bundling React webview - Register editProfilePermissions command in constants.ts and commands.yaml - Add React, react-dom, react-window, esbuild as devDependencies" Co-authored-by: Codeneos <787686+Codeneos@users.noreply.github.com> --- .../src/salesforceUserPermissions.ts | 76 +++ packages/vscode-extension/build/webview.mts | 69 +++ packages/vscode-extension/commands.yaml | 10 + packages/vscode-extension/package.json | 31 +- .../src/commands/profiles/index.ts | 1 + .../commands/profiles/profileEditorCommand.ts | 72 +++ packages/vscode-extension/src/constants.ts | 1 + .../vscode-extension/src/lib/webview/index.ts | 3 + .../src/lib/webview/profileEditorWebview.ts | 175 ++++++ .../vscode-extension/src/lib/webview/types.ts | 71 +++ .../src/lib/webview/webviewPanel.ts | 168 ++++++ .../src/webviews/profileEditor/App.tsx | 395 +++++++++++++ .../profileEditor/components/ActionBar.tsx | 53 ++ .../components/AddPermissionDialog.tsx | 147 +++++ .../components/FieldPermissionsTable.tsx | 156 +++++ .../profileEditor/components/FilterBar.tsx | 52 ++ .../components/ObjectPermissionsTable.tsx | 155 +++++ .../components/ProfileHeader.tsx | 43 ++ .../profileEditor/hooks/useVsCodeApi.ts | 41 ++ .../src/webviews/profileEditor/index.tsx | 20 + .../src/webviews/profileEditor/styles.css | 555 ++++++++++++++++++ .../src/webviews/profileEditor/types.ts | 65 ++ pnpm-lock.yaml | 310 ++++++---- 23 files changed, 2555 insertions(+), 114 deletions(-) create mode 100644 packages/vscode-extension/build/webview.mts create mode 100644 packages/vscode-extension/src/commands/profiles/profileEditorCommand.ts create mode 100644 packages/vscode-extension/src/lib/webview/index.ts create mode 100644 packages/vscode-extension/src/lib/webview/profileEditorWebview.ts create mode 100644 packages/vscode-extension/src/lib/webview/types.ts create mode 100644 packages/vscode-extension/src/lib/webview/webviewPanel.ts create mode 100644 packages/vscode-extension/src/webviews/profileEditor/App.tsx create mode 100644 packages/vscode-extension/src/webviews/profileEditor/components/ActionBar.tsx create mode 100644 packages/vscode-extension/src/webviews/profileEditor/components/AddPermissionDialog.tsx create mode 100644 packages/vscode-extension/src/webviews/profileEditor/components/FieldPermissionsTable.tsx create mode 100644 packages/vscode-extension/src/webviews/profileEditor/components/FilterBar.tsx create mode 100644 packages/vscode-extension/src/webviews/profileEditor/components/ObjectPermissionsTable.tsx create mode 100644 packages/vscode-extension/src/webviews/profileEditor/components/ProfileHeader.tsx create mode 100644 packages/vscode-extension/src/webviews/profileEditor/hooks/useVsCodeApi.ts create mode 100644 packages/vscode-extension/src/webviews/profileEditor/index.tsx create mode 100644 packages/vscode-extension/src/webviews/profileEditor/styles.css create mode 100644 packages/vscode-extension/src/webviews/profileEditor/types.ts diff --git a/packages/salesforce/src/salesforceUserPermissions.ts b/packages/salesforce/src/salesforceUserPermissions.ts index 52f5fa625..83ab9a5f4 100644 --- a/packages/salesforce/src/salesforceUserPermissions.ts +++ b/packages/salesforce/src/salesforceUserPermissions.ts @@ -99,6 +99,21 @@ export class SalesforceUserPermissions { return this.metadata.fullName; } + /** + * The license associated with this profile or permission set. + * For profiles this is `userLicense`, for permission sets this is `license`. + */ + public get license() : string | undefined { + return (this.metadata as any).userLicense ?? (this.metadata as any).license; + } + + /** + * Optional human-readable description of this profile or permission set. + */ + public get description() : string | undefined { + return (this.metadata as any).description; + } + constructor( public readonly type: 'Profile' | 'PermissionSet', public readonly developerName: string, @@ -249,6 +264,67 @@ export class SalesforceUserPermissions { this.removeItem('fieldPermissions', name); } + /** + * Adds or updates object-level permissions for the specified Salesforce object. + * + * @param objectName - The API name of the SObject. + * @param permissions - Partial permission flags to set. Salesforce access rules are automatically enforced: + * - modifyAllRecords implies viewAllRecords, allowRead, allowCreate, allowEdit, allowDelete. + * - viewAllRecords implies allowRead. + * - allowCreate / allowEdit / allowDelete imply allowRead. + */ + public setObjectPermissions(objectName: string, permissions: { + allowRead?: boolean; + allowCreate?: boolean; + allowEdit?: boolean; + allowDelete?: boolean; + viewAllRecords?: boolean; + modifyAllRecords?: boolean; + }) { + const existing = this.objects.find(o => o.object === objectName) ?? {}; + const merged = { ...existing, ...permissions, object: objectName }; + + // Enforce Salesforce access rules + if (merged.modifyAllRecords) { + merged.viewAllRecords = true; + merged.allowRead = true; + merged.allowCreate = merged.allowCreate ?? true; + merged.allowEdit = merged.allowEdit ?? true; + merged.allowDelete = merged.allowDelete ?? true; + } + if (merged.viewAllRecords) { + merged.allowRead = true; + } + if (merged.allowCreate || merged.allowEdit || merged.allowDelete) { + merged.allowRead = true; + } + if (!merged.allowRead) { + merged.allowCreate = false; + merged.allowEdit = false; + merged.allowDelete = false; + merged.viewAllRecords = false; + merged.modifyAllRecords = false; + } + + this.update('objectPermissions', merged); + } + + /** + * Sets field-level security permissions for the specified field. + * Enforces the Salesforce rule that editable fields must also be readable. + * + * @param fieldName - Qualified field name in the format "ObjectName.FieldName". + * @param readable - Whether the field should be readable. + * @param editable - Whether the field should be editable. If true, readable is also set to true. + */ + public setFieldPermissions(fieldName: string, readable: boolean, editable: boolean) { + this.update('fieldPermissions', { + field: fieldName, + readable: editable ? true : readable, + editable + }); + } + /** * Checks if the user has access to a specific Apex page by its name. * diff --git a/packages/vscode-extension/build/webview.mts b/packages/vscode-extension/build/webview.mts new file mode 100644 index 000000000..8c6afc5bf --- /dev/null +++ b/packages/vscode-extension/build/webview.mts @@ -0,0 +1,69 @@ +/** + * Build script for React webview bundles. + * Runs independently of tsdown to produce browser-targeted bundles. + */ +import { build, type BuildOptions } from 'esbuild'; +import { join } from 'path'; + +const isProd = process.env.NODE_ENV === 'production' || !process.argv.includes('--watch'); +const watch = process.argv.includes('--watch'); + +const pkgDir = new URL('..', import.meta.url).pathname; + +const cssInjectPlugin = { + name: 'css-inject', + setup(pluginBuild: any) { + // Inject CSS into a + +
+ + + +