@simplysf/simply-permissions 1.2.7 → 1.2.9

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.
@@ -43,6 +43,10 @@ export type PermissionSetTemplateData = {
43
43
  /**
44
44
  * Render a complete PermissionSet metadata XML document from template data.
45
45
  *
46
+ * Element text content is set via `.txt()`, which xmlbuilder2 escapes for us — there's no
47
+ * hand-rolled escaping (and no risk of it drifting out of sync with what's actually unsafe in
48
+ * XML text content).
49
+ *
46
50
  * @param data - The permission set's label, description, and permission entries.
47
51
  * @returns The rendered `<PermissionSet>` XML document, ready to write to a `.permissionset-meta.xml` file.
48
52
  */
@@ -13,75 +13,101 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- /**
17
- * @param value - The raw string to escape.
18
- * @returns `value` with XML special characters (`& < > " '`) replaced by their entity references.
19
- */
20
- function escapeXml(value) {
21
- return value
22
- .replace(/&/g, '&amp;')
23
- .replace(/</g, '&lt;')
24
- .replace(/>/g, '&gt;')
25
- .replace(/"/g, '&quot;')
26
- .replace(/'/g, '&apos;');
27
- }
16
+ import { create } from 'xmlbuilder2';
28
17
  /**
29
18
  * Render a complete PermissionSet metadata XML document from template data.
30
19
  *
20
+ * Element text content is set via `.txt()`, which xmlbuilder2 escapes for us — there's no
21
+ * hand-rolled escaping (and no risk of it drifting out of sync with what's actually unsafe in
22
+ * XML text content).
23
+ *
31
24
  * @param data - The permission set's label, description, and permission entries.
32
25
  * @returns The rendered `<PermissionSet>` XML document, ready to write to a `.permissionset-meta.xml` file.
33
26
  */
34
27
  export function buildPermissionSetXml(data) {
35
- const objectPermissionsXml = data.objectPermissions
36
- .map((permission) => ` <objectPermissions>
37
- <allowCreate>${permission.allowCreate}</allowCreate>
38
- <allowDelete>${permission.allowDelete}</allowDelete>
39
- <allowEdit>${permission.allowEdit}</allowEdit>
40
- <allowRead>${permission.allowRead}</allowRead>
41
- <object>${escapeXml(permission.object)}</object>
42
- <modifyAllRecords>${permission.modifyAllRecords}</modifyAllRecords>
43
- <viewAllRecords>${permission.viewAllRecords}</viewAllRecords>
44
- <viewAllFields>${permission.viewAllFields}</viewAllFields>
45
- </objectPermissions>`)
46
- .join('\n');
47
- const fieldPermissionsXml = data.fieldPermissions
48
- .map((permission) => ` <fieldPermissions>
49
- <field>${escapeXml(permission.field)}</field>
50
- <readable>${permission.readable}</readable>
51
- <editable>${permission.editable}</editable>
52
- </fieldPermissions>`)
53
- .join('\n');
54
- const tabSettingsXml = data.tabSettings
55
- .map((setting) => ` <tabSettings>
56
- <tab>${escapeXml(setting.tab)}</tab>
57
- <visibility>${setting.visible ? 'Visible' : 'Hidden'}</visibility>
58
- </tabSettings>`)
59
- .join('\n');
60
- const recordTypeVisibilitiesXml = data.recordTypeVisibilities
61
- .map((visibility) => ` <recordTypeVisibilities>
62
- <recordType>${escapeXml(visibility.recordType)}</recordType>
63
- <visible>${visibility.visible}</visible>
64
- </recordTypeVisibilities>`)
65
- .join('\n');
66
- const userPermissionsXml = data.userPermissions
67
- .map((permission) => ` <userPermissions>
68
- <name>${escapeXml(permission.name)}</name>
69
- <enabled>${permission.enabled}</enabled>
70
- </userPermissions>`)
71
- .join('\n');
72
- const sections = [
73
- ` <label>${escapeXml(data.label)}</label>`,
74
- data.description ? ` <description>${escapeXml(data.description)}</description>` : '',
75
- objectPermissionsXml,
76
- fieldPermissionsXml,
77
- tabSettingsXml,
78
- recordTypeVisibilitiesXml,
79
- userPermissionsXml,
80
- ].filter(Boolean);
81
- return `<?xml version="1.0" encoding="UTF-8"?>
82
- <PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
83
- ${sections.join('\n')}
84
- </PermissionSet>
85
- `;
28
+ const root = create({ version: '1.0', encoding: 'UTF-8' }).ele('PermissionSet', {
29
+ xmlns: 'http://soap.sforce.com/2006/04/metadata',
30
+ });
31
+ root.ele('label').txt(data.label).up();
32
+ if (data.description) {
33
+ root.ele('description').txt(data.description).up();
34
+ }
35
+ for (const permission of data.objectPermissions) {
36
+ root
37
+ .ele('objectPermissions')
38
+ .ele('allowCreate')
39
+ .txt(String(permission.allowCreate))
40
+ .up()
41
+ .ele('allowDelete')
42
+ .txt(String(permission.allowDelete))
43
+ .up()
44
+ .ele('allowEdit')
45
+ .txt(String(permission.allowEdit))
46
+ .up()
47
+ .ele('allowRead')
48
+ .txt(String(permission.allowRead))
49
+ .up()
50
+ .ele('object')
51
+ .txt(permission.object)
52
+ .up()
53
+ .ele('modifyAllRecords')
54
+ .txt(String(permission.modifyAllRecords))
55
+ .up()
56
+ .ele('viewAllRecords')
57
+ .txt(String(permission.viewAllRecords))
58
+ .up()
59
+ .ele('viewAllFields')
60
+ .txt(String(permission.viewAllFields))
61
+ .up()
62
+ .up();
63
+ }
64
+ for (const permission of data.fieldPermissions) {
65
+ root
66
+ .ele('fieldPermissions')
67
+ .ele('field')
68
+ .txt(permission.field)
69
+ .up()
70
+ .ele('readable')
71
+ .txt(String(permission.readable))
72
+ .up()
73
+ .ele('editable')
74
+ .txt(String(permission.editable))
75
+ .up()
76
+ .up();
77
+ }
78
+ for (const setting of data.tabSettings) {
79
+ root
80
+ .ele('tabSettings')
81
+ .ele('tab')
82
+ .txt(setting.tab)
83
+ .up()
84
+ .ele('visibility')
85
+ .txt(setting.visible ? 'Visible' : 'Hidden')
86
+ .up()
87
+ .up();
88
+ }
89
+ for (const visibility of data.recordTypeVisibilities) {
90
+ root
91
+ .ele('recordTypeVisibilities')
92
+ .ele('recordType')
93
+ .txt(visibility.recordType)
94
+ .up()
95
+ .ele('visible')
96
+ .txt(String(visibility.visible))
97
+ .up()
98
+ .up();
99
+ }
100
+ for (const permission of data.userPermissions) {
101
+ root
102
+ .ele('userPermissions')
103
+ .ele('name')
104
+ .txt(permission.name)
105
+ .up()
106
+ .ele('enabled')
107
+ .txt(String(permission.enabled))
108
+ .up()
109
+ .up();
110
+ }
111
+ return root.end({ prettyPrint: true });
86
112
  }
87
113
  //# sourceMappingURL=permissionSetXmlTemplate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"permissionSetXmlTemplate.js","sourceRoot":"","sources":["../../src/common/permissionSetXmlTemplate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAkDH;;;GAGG;AACH,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAA+B;IACnE,MAAM,oBAAoB,GAAG,IAAI,CAAC,iBAAiB;SAChD,GAAG,CACF,CAAC,UAAU,EAAE,EAAE,CAAC;mBACH,UAAU,CAAC,WAAW;mBACtB,UAAU,CAAC,WAAW;iBACxB,UAAU,CAAC,SAAS;iBACpB,UAAU,CAAC,SAAS;cACvB,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC;wBAClB,UAAU,CAAC,gBAAgB;sBAC7B,UAAU,CAAC,cAAc;qBAC1B,UAAU,CAAC,aAAa;uBACtB,CAClB;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,mBAAmB,GAAG,IAAI,CAAC,gBAAgB;SAC9C,GAAG,CACF,CAAC,UAAU,EAAE,EAAE,CAAC;aACT,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC;gBACxB,UAAU,CAAC,QAAQ;gBACnB,UAAU,CAAC,QAAQ;sBACb,CACjB;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW;SACpC,GAAG,CACF,CAAC,OAAO,EAAE,EAAE,CAAC;WACR,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC;kBACf,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ;iBACvC,CACZ;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,yBAAyB,GAAG,IAAI,CAAC,sBAAsB;SAC1D,GAAG,CACF,CAAC,UAAU,EAAE,EAAE,CAAC;kBACJ,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC;eACnC,UAAU,CAAC,OAAO;4BACL,CACvB;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,kBAAkB,GAAG,IAAI,CAAC,eAAe;SAC5C,GAAG,CACF,CAAC,UAAU,EAAE,EAAE,CAAC;YACV,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC;eACvB,UAAU,CAAC,OAAO;qBACZ,CAChB;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,QAAQ,GAAG;QACf,YAAY,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU;QAC3C,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,kBAAkB,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE;QACrF,oBAAoB;QACpB,mBAAmB;QACnB,cAAc;QACd,yBAAyB;QACzB,kBAAkB;KACnB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAElB,OAAO;;EAEP,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;;CAEpB,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"permissionSetXmlTemplate.js","sourceRoot":"","sources":["../../src/common/permissionSetXmlTemplate.ts"],"names":[],"mappings":"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"}
@@ -13,91 +13,50 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- /**
17
- * @param value - The raw string to escape.
18
- * @returns `value` with HTML special characters (`& < > "`) replaced by their entity references.
19
- */
20
- function escapeHtml(value) {
21
- return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
22
- }
23
- /** @returns An HTML `<table>` of object permissions, or a placeholder `<p>` if `perms` is empty. */
24
- function renderObjectPermsTable(perms) {
25
- if (perms.length === 0) {
26
- return '<p>No object permissions.</p>';
27
- }
28
- const rows = perms
29
- .map((permission) => `<tr><td>${escapeHtml(permission.SobjectType)}</td><td>${permission.PermissionsRead}</td><td>${permission.PermissionsCreate}</td><td>${permission.PermissionsEdit}</td><td>${permission.PermissionsDelete}</td><td>${permission.PermissionsViewAllRecords}</td><td>${permission.PermissionsModifyAllRecords}</td></tr>`)
30
- .join('');
31
- return `<table><thead><tr><th>Object</th><th>Read</th><th>Create</th><th>Edit</th><th>Delete</th><th>View All</th><th>Modify All</th></tr></thead><tbody>${rows}</tbody></table>`;
32
- }
33
- /** @returns An HTML `<table>` of field permissions, or a placeholder `<p>` if `perms` is empty. */
34
- function renderFieldPermsTable(perms) {
35
- if (perms.length === 0) {
36
- return '<p>No field permissions.</p>';
37
- }
38
- const rows = perms
39
- .map((permission) => `<tr><td>${escapeHtml(permission.SobjectType)}</td><td>${escapeHtml(permission.Field.split('.')[1] ?? permission.Field)}</td><td>${permission.PermissionsRead}</td><td>${permission.PermissionsEdit}</td></tr>`)
40
- .join('');
41
- return `<table><thead><tr><th>Object</th><th>Field</th><th>Read</th><th>Edit</th></tr></thead><tbody>${rows}</tbody></table>`;
42
- }
43
- /** @returns A collapsible `<details>` section summarizing one permission set's permissions. */
44
- function renderPermissionSet(permissionSet) {
45
- return `
46
- <details>
47
- <summary>${escapeHtml(permissionSet.Label)} (${escapeHtml(permissionSet.Name)})</summary>
48
- <div class="content">
49
- ${permissionSet.Description ? `<p>${escapeHtml(permissionSet.Description)}</p>` : ''}
50
- <h4>Object Permissions</h4>
51
- ${renderObjectPermsTable(permissionSet.objectPerms)}
52
- <h4>Field Permissions</h4>
53
- ${renderFieldPermsTable(permissionSet.fieldPerms)}
54
- </div>
55
- </details>`;
56
- }
57
- /** @returns A collapsible `<details>` section summarizing one permission set group's permissions. */
58
- function renderPermissionSetGroup(permissionSetGroup) {
59
- return `
60
- <details>
61
- <summary>${escapeHtml(permissionSetGroup.MasterLabel)} (${escapeHtml(permissionSetGroup.DeveloperName)}) [Group]</summary>
62
- <div class="content">
63
- ${permissionSetGroup.Description ? `<p>${escapeHtml(permissionSetGroup.Description)}</p>` : ''}
64
- <p><strong>Components:</strong> ${permissionSetGroup.components.map(escapeHtml).join(', ') || 'None'}</p>
65
- <h4>Object Permissions</h4>
66
- ${renderObjectPermsTable(permissionSetGroup.objectPerms)}
67
- <h4>Field Permissions</h4>
68
- ${renderFieldPermsTable(permissionSetGroup.fieldPerms)}
69
- </div>
70
- </details>`;
71
- }
72
- /**
73
- * Render a complete, self-contained HTML report of permission sets and permission set groups,
74
- * grouped by package, with each permission set/group collapsible for browsing.
75
- *
76
- * @param options.username - The org username the report was generated against.
77
- * @param options.reportDate - The report generation date/time, displayed as-is.
78
- * @param options.groupedData - The permission sets/groups to render, grouped by package.
79
- * @returns The rendered HTML document.
80
- */
81
- export function buildPermissionsReportHtml(options) {
82
- const { username, reportDate, groupedData } = options;
83
- const sortedPackages = [...groupedData.keys()].sort();
84
- const sections = sortedPackages
85
- .map((pkg) => {
86
- const data = groupedData.get(pkg);
87
- if (!data) {
88
- return '';
89
- }
90
- return `
91
- <div class="package-section" data-package="${escapeHtml(pkg)}">
92
- <h2>Package: ${escapeHtml(pkg)}</h2>
93
- <h3>Permission Sets (${data.permissionSets.length})</h3>
94
- ${data.permissionSets.map(renderPermissionSet).join('')}
95
- <h3>Permission Set Groups (${data.permissionSetGroups.length})</h3>
96
- ${data.permissionSetGroups.map(renderPermissionSetGroup).join('')}
97
- </div>`;
98
- })
99
- .join('');
100
- return `<!DOCTYPE html>
16
+ import Handlebars from 'handlebars';
17
+ // Handlebars auto-escapes every `{{expression}}` (unlike `{{{expression}}}`, which is left raw),
18
+ // so none of the templates below need a hand-rolled escapeHtml() call.
19
+ const handlebars = Handlebars.create();
20
+ // `Field` is the fully-qualified `Object.Field` name; the report only displays the field half.
21
+ handlebars.registerHelper('fieldName', (field) => field.split('.')[1] ?? field);
22
+ const objectPermsTableSource = `
23
+ {{~#if perms}}
24
+ <table><thead><tr><th>Object</th><th>Read</th><th>Create</th><th>Edit</th><th>Delete</th><th>View All</th><th>Modify All</th></tr></thead><tbody>{{#each perms}}<tr><td>{{SobjectType}}</td><td>{{PermissionsRead}}</td><td>{{PermissionsCreate}}</td><td>{{PermissionsEdit}}</td><td>{{PermissionsDelete}}</td><td>{{PermissionsViewAllRecords}}</td><td>{{PermissionsModifyAllRecords}}</td></tr>{{/each}}</tbody></table>
25
+ {{~else~}}
26
+ <p>No object permissions.</p>
27
+ {{~/if~}}
28
+ `;
29
+ const fieldPermsTableSource = `
30
+ {{~#if perms}}
31
+ <table><thead><tr><th>Object</th><th>Field</th><th>Read</th><th>Edit</th></tr></thead><tbody>{{#each perms}}<tr><td>{{SobjectType}}</td><td>{{fieldName Field}}</td><td>{{PermissionsRead}}</td><td>{{PermissionsEdit}}</td></tr>{{/each}}</tbody></table>
32
+ {{~else~}}
33
+ <p>No field permissions.</p>
34
+ {{~/if~}}
35
+ `;
36
+ const permissionSetSource = `
37
+ <details>
38
+ <summary>{{Label}} ({{Name}})</summary>
39
+ <div class="content">
40
+ {{#if Description}}<p>{{Description}}</p>{{/if}}
41
+ <h4>Object Permissions</h4>
42
+ {{> objectPermsTable perms=objectPerms}}
43
+ <h4>Field Permissions</h4>
44
+ {{> fieldPermsTable perms=fieldPerms}}
45
+ </div>
46
+ </details>`;
47
+ const permissionSetGroupSource = `
48
+ <details>
49
+ <summary>{{MasterLabel}} ({{DeveloperName}}) [Group]</summary>
50
+ <div class="content">
51
+ {{#if Description}}<p>{{Description}}</p>{{/if}}
52
+ <p><strong>Components:</strong> {{componentsDisplay}}</p>
53
+ <h4>Object Permissions</h4>
54
+ {{> objectPermsTable perms=objectPerms}}
55
+ <h4>Field Permissions</h4>
56
+ {{> fieldPermsTable perms=fieldPerms}}
57
+ </div>
58
+ </details>`;
59
+ const reportSource = `<!DOCTYPE html>
101
60
  <html lang="en">
102
61
  <head>
103
62
  <meta charset="UTF-8">
@@ -118,9 +77,45 @@ export function buildPermissionsReportHtml(options) {
118
77
  </head>
119
78
  <body>
120
79
  <h1>Permissions Report</h1>
121
- <p>Org: <strong>${escapeHtml(username)}</strong> | Generated: ${escapeHtml(reportDate)}</p>
122
- ${sections}
80
+ <p>Org: <strong>{{username}}</strong> | Generated: {{reportDate}}</p>
81
+ {{#each packages}}
82
+ <div class="package-section" data-package="{{name}}">
83
+ <h2>Package: {{name}}</h2>
84
+ <h3>Permission Sets ({{permissionSets.length}})</h3>
85
+ {{#each permissionSets}}{{> permissionSet}}{{/each}}
86
+ <h3>Permission Set Groups ({{permissionSetGroups.length}})</h3>
87
+ {{#each permissionSetGroups}}{{> permissionSetGroup}}{{/each}}
88
+ </div>
89
+ {{/each}}
123
90
  </body>
124
91
  </html>`;
92
+ handlebars.registerPartial('objectPermsTable', objectPermsTableSource);
93
+ handlebars.registerPartial('fieldPermsTable', fieldPermsTableSource);
94
+ handlebars.registerPartial('permissionSet', permissionSetSource);
95
+ handlebars.registerPartial('permissionSetGroup', permissionSetGroupSource);
96
+ const renderReport = handlebars.compile(reportSource);
97
+ /**
98
+ * Render a complete, self-contained HTML report of permission sets and permission set groups,
99
+ * grouped by package, with each permission set/group collapsible for browsing.
100
+ *
101
+ * @param options.username - The org username the report was generated against.
102
+ * @param options.reportDate - The report generation date/time, displayed as-is.
103
+ * @param options.groupedData - The permission sets/groups to render, grouped by package.
104
+ * @returns The rendered HTML document.
105
+ */
106
+ export function buildPermissionsReportHtml(options) {
107
+ const { username, reportDate, groupedData } = options;
108
+ const packages = [...groupedData.keys()].sort().map((name) => {
109
+ const data = groupedData.get(name);
110
+ return {
111
+ name,
112
+ permissionSets: data?.permissionSets ?? [],
113
+ permissionSetGroups: (data?.permissionSetGroups ?? []).map((group) => ({
114
+ ...group,
115
+ componentsDisplay: group.components.length > 0 ? group.components.join(', ') : 'None',
116
+ })),
117
+ };
118
+ });
119
+ return renderReport({ username, reportDate, packages });
125
120
  }
126
121
  //# sourceMappingURL=permissionsReportTemplate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"permissionsReportTemplate.js","sourceRoot":"","sources":["../../src/common/permissionsReportTemplate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permissionsReportTemplate.js","sourceRoot":"","sources":["../../src/common/permissionsReportTemplate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,UAAU,MAAM,YAAY,CAAC;AAgDpC,iGAAiG;AACjG,uEAAuE;AACvE,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;AAEvC,+FAA+F;AAC/F,UAAU,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC;AAEhG,MAAM,sBAAsB,GAAG;;;;;;CAM9B,CAAC;AAEF,MAAM,qBAAqB,GAAG;;;;;;CAM7B,CAAC;AAEF,MAAM,mBAAmB,GAAG;;;;;;;;;;WAUjB,CAAC;AAEZ,MAAM,wBAAwB,GAAG;;;;;;;;;;;WAWtB,CAAC;AAEZ,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAgCb,CAAC;AAET,UAAU,CAAC,eAAe,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;AACvE,UAAU,CAAC,eAAe,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,CAAC;AACrE,UAAU,CAAC,eAAe,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAC;AACjE,UAAU,CAAC,eAAe,CAAC,oBAAoB,EAAE,wBAAwB,CAAC,CAAC;AAE3E,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;AAStD;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAI1C;IACC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAEtD,MAAM,QAAQ,GAAqB,CAAC,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAC7E,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEnC,OAAO;YACL,IAAI;YACJ,cAAc,EAAE,IAAI,EAAE,cAAc,IAAI,EAAE;YAC1C,mBAAmB,EAAE,CAAC,IAAI,EAAE,mBAAmB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACrE,GAAG,KAAK;gBACR,iBAAiB,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM;aACtF,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC;AAC1D,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@simplysf/simply-permissions",
3
3
  "description": "Utilities for working with permissions",
4
- "version": "1.2.7",
4
+ "version": "1.2.9",
5
5
  "author": "@ClayChipps",
6
6
  "homepage": "https://github.com/SimplySF/simply",
7
7
  "bugs": "https://github.com/SimplySF/simply/issues",
@@ -61,7 +61,9 @@
61
61
  "@salesforce/kit": "^3.2.6",
62
62
  "@salesforce/sf-plugins-core": "^12.2.16",
63
63
  "@salesforce/source-deploy-retrieve": "^12.37.2",
64
- "@simplysf/simply-core": "^1.1.3",
64
+ "@simplysf/simply-core": "^1.1.5",
65
+ "handlebars": "^4.7.9",
66
+ "xmlbuilder2": "^4.0.3",
65
67
  "zod": "^4.1.12"
66
68
  },
67
69
  "devDependencies": {
@@ -208,5 +210,5 @@
208
210
  "output": []
209
211
  }
210
212
  },
211
- "gitHead": "c8f2c684566e7cdb98a1ee0186eadbc932cebb61"
213
+ "gitHead": "99fde0b9705ed0bdd1b30a973a9e1dae92c724c8"
212
214
  }