sf-plugin-permission-sets 0.0.0-dev.5 → 0.0.0-dev.51

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 (132) hide show
  1. package/LICENSE.md +28 -0
  2. package/README.md +259 -84
  3. package/lib/adapters/connection-org-client.d.ts +21 -0
  4. package/lib/adapters/connection-org-client.d.ts.map +1 -0
  5. package/lib/adapters/connection-org-client.js +279 -0
  6. package/lib/adapters/connection-org-client.js.map +1 -0
  7. package/lib/adapters/index.d.ts +2 -0
  8. package/lib/adapters/index.d.ts.map +1 -0
  9. package/lib/adapters/index.js +2 -0
  10. package/lib/adapters/index.js.map +1 -0
  11. package/lib/commands/ps/apply.d.ts +55 -0
  12. package/lib/commands/ps/apply.d.ts.map +1 -0
  13. package/lib/commands/ps/apply.js +249 -0
  14. package/lib/commands/ps/apply.js.map +1 -0
  15. package/lib/commands/ps/check.d.ts +21 -0
  16. package/lib/commands/ps/check.d.ts.map +1 -0
  17. package/lib/commands/ps/check.js +55 -0
  18. package/lib/commands/ps/check.js.map +1 -0
  19. package/lib/commands/ps/export.d.ts +26 -0
  20. package/lib/commands/ps/export.d.ts.map +1 -0
  21. package/lib/commands/ps/export.js +74 -0
  22. package/lib/commands/ps/export.js.map +1 -0
  23. package/lib/commands/ps/plan.d.ts +67 -0
  24. package/lib/commands/ps/plan.d.ts.map +1 -0
  25. package/lib/commands/ps/plan.js +246 -0
  26. package/lib/commands/ps/plan.js.map +1 -0
  27. package/lib/commands/ps/validate.d.ts +21 -0
  28. package/lib/commands/ps/validate.d.ts.map +1 -0
  29. package/lib/commands/ps/validate.js +56 -0
  30. package/lib/commands/ps/validate.js.map +1 -0
  31. package/lib/core/diff.d.ts +10 -0
  32. package/lib/core/diff.d.ts.map +1 -0
  33. package/lib/core/diff.js +54 -0
  34. package/lib/core/diff.js.map +1 -0
  35. package/lib/core/finding.d.ts +41 -0
  36. package/lib/core/finding.d.ts.map +1 -0
  37. package/lib/core/finding.js +68 -0
  38. package/lib/core/finding.js.map +1 -0
  39. package/lib/core/index.d.ts +11 -0
  40. package/lib/core/index.d.ts.map +1 -0
  41. package/lib/core/index.js +10 -0
  42. package/lib/core/index.js.map +1 -0
  43. package/lib/core/load.d.ts +4 -0
  44. package/lib/core/load.d.ts.map +1 -0
  45. package/lib/core/load.js +60 -0
  46. package/lib/core/load.js.map +1 -0
  47. package/lib/core/mode.d.ts +18 -0
  48. package/lib/core/mode.d.ts.map +1 -0
  49. package/lib/core/mode.js +16 -0
  50. package/lib/core/mode.js.map +1 -0
  51. package/lib/core/model.d.ts +78 -0
  52. package/lib/core/model.d.ts.map +1 -0
  53. package/lib/core/model.js +2 -0
  54. package/lib/core/model.js.map +1 -0
  55. package/lib/core/normalize.d.ts +17 -0
  56. package/lib/core/normalize.d.ts.map +1 -0
  57. package/lib/core/normalize.js +69 -0
  58. package/lib/core/normalize.js.map +1 -0
  59. package/lib/core/parse.d.ts +10 -0
  60. package/lib/core/parse.d.ts.map +1 -0
  61. package/lib/core/parse.js +20 -0
  62. package/lib/core/parse.js.map +1 -0
  63. package/lib/core/plan-file.d.ts +61 -0
  64. package/lib/core/plan-file.d.ts.map +1 -0
  65. package/lib/core/plan-file.js +74 -0
  66. package/lib/core/plan-file.js.map +1 -0
  67. package/lib/core/report.d.ts +15 -0
  68. package/lib/core/report.d.ts.map +1 -0
  69. package/lib/core/report.js +114 -0
  70. package/lib/core/report.js.map +1 -0
  71. package/lib/core/resolve.d.ts +20 -0
  72. package/lib/core/resolve.d.ts.map +1 -0
  73. package/lib/core/resolve.js +88 -0
  74. package/lib/core/resolve.js.map +1 -0
  75. package/lib/core/schema.d.ts +23 -0
  76. package/lib/core/schema.d.ts.map +1 -0
  77. package/lib/core/schema.js +35 -0
  78. package/lib/core/schema.js.map +1 -0
  79. package/lib/core/serialize.d.ts +9 -0
  80. package/lib/core/serialize.d.ts.map +1 -0
  81. package/lib/core/serialize.js +34 -0
  82. package/lib/core/serialize.js.map +1 -0
  83. package/lib/index.d.ts +1 -0
  84. package/lib/index.d.ts.map +1 -0
  85. package/lib/services/adapters/index.d.ts +2 -0
  86. package/lib/services/adapters/index.d.ts.map +1 -0
  87. package/lib/services/adapters/index.js +2 -0
  88. package/lib/services/adapters/index.js.map +1 -0
  89. package/lib/services/adapters/org-client.d.ts +22 -0
  90. package/lib/services/adapters/org-client.d.ts.map +1 -0
  91. package/lib/services/adapters/org-client.js +2 -0
  92. package/lib/services/adapters/org-client.js.map +1 -0
  93. package/lib/services/apply.d.ts +50 -0
  94. package/lib/services/apply.d.ts.map +1 -0
  95. package/lib/services/apply.js +111 -0
  96. package/lib/services/apply.js.map +1 -0
  97. package/lib/services/check.d.ts +14 -0
  98. package/lib/services/check.d.ts.map +1 -0
  99. package/lib/services/check.js +18 -0
  100. package/lib/services/check.js.map +1 -0
  101. package/lib/services/export.d.ts +19 -0
  102. package/lib/services/export.d.ts.map +1 -0
  103. package/lib/services/export.js +29 -0
  104. package/lib/services/export.js.map +1 -0
  105. package/lib/services/index.d.ts +7 -0
  106. package/lib/services/index.d.ts.map +1 -0
  107. package/lib/services/index.js +7 -0
  108. package/lib/services/index.js.map +1 -0
  109. package/lib/services/plan.d.ts +32 -0
  110. package/lib/services/plan.d.ts.map +1 -0
  111. package/lib/services/plan.js +57 -0
  112. package/lib/services/plan.js.map +1 -0
  113. package/lib/services/resolution.d.ts +17 -0
  114. package/lib/services/resolution.d.ts.map +1 -0
  115. package/lib/services/resolution.js +48 -0
  116. package/lib/services/resolution.js.map +1 -0
  117. package/lib/services/validate.d.ts +21 -0
  118. package/lib/services/validate.d.ts.map +1 -0
  119. package/lib/services/validate.js +50 -0
  120. package/lib/services/validate.js.map +1 -0
  121. package/messages/ps.apply.md +117 -0
  122. package/messages/ps.check.md +33 -0
  123. package/messages/ps.export.md +45 -0
  124. package/messages/ps.plan.md +93 -0
  125. package/messages/ps.validate.md +29 -0
  126. package/package.json +115 -180
  127. package/lib/commands/ps/info.d.ts +0 -11
  128. package/lib/commands/ps/info.js +0 -17
  129. package/lib/commands/ps/info.js.map +0 -1
  130. package/messages/ps.info.md +0 -17
  131. package/oclif.lock +0 -7781
  132. package/oclif.manifest.json +0 -51
@@ -0,0 +1,111 @@
1
+ import { loadFiles, diffAssignments, scopeToMode, countFindings, } from '../core/index.js';
2
+ import { ResolutionService, managedTargets, resolveAdditions } from './resolution.js';
3
+ const noDrift = { adds: 0, updates: 0, removes: 0 };
4
+ const emptyDiff = { toAdd: [], toUpdate: [], toRemove: [], unchanged: [] };
5
+ /** An aborted-before-any-change result, carrying the findings that explain why. */
6
+ function invalidResult(files, findings) {
7
+ return {
8
+ files,
9
+ findings,
10
+ diff: emptyDiff,
11
+ drift: { adds: 0, updates: 0, removes: 0 },
12
+ outcomes: [],
13
+ status: 'invalid',
14
+ failed: true,
15
+ };
16
+ }
17
+ /**
18
+ * Load the files, resolve every reference to an org id, diff against the org's
19
+ * current state, then add and/or remove per the mode. Deletions are capped by
20
+ * maxDeletes and gated by an injected confirmation.
21
+ */
22
+ export class ApplyService {
23
+ org;
24
+ confirmDeletions;
25
+ constructor(org, confirmDeletions) {
26
+ this.org = org;
27
+ this.confirmDeletions = confirmDeletions;
28
+ }
29
+ async run(files, input) {
30
+ const loaded = await loadFiles(files);
31
+ const loadCounts = countFindings(loaded.findings);
32
+ if (loadCounts.errors > 0) {
33
+ return invalidResult(loaded.files, loaded.findings);
34
+ }
35
+ const resolutionService = new ResolutionService(this.org);
36
+ const resolution = await resolutionService.run(loaded.assignments);
37
+ const findings = [
38
+ ...loaded.findings,
39
+ ...resolution.findings,
40
+ ];
41
+ const findingCounts = countFindings(findings);
42
+ if (findingCounts.errors > 0) {
43
+ return invalidResult(loaded.files, findings);
44
+ }
45
+ const actual = await this.org.currentAssignments(managedTargets(resolution));
46
+ const diff = diffAssignments(loaded.assignments, actual);
47
+ const { mode, maxDeletes, dryRun } = input;
48
+ const { additions, updates, removals, drift } = scopeToMode(diff, mode);
49
+ if (removals.length > maxDeletes) {
50
+ return {
51
+ files: loaded.files,
52
+ findings,
53
+ diff,
54
+ drift,
55
+ outcomes: [],
56
+ status: 'max-deletes-exceeded',
57
+ failed: true,
58
+ };
59
+ }
60
+ if (dryRun) {
61
+ return { files: loaded.files, findings, diff, drift, outcomes: [], status: 'dry-run', failed: false };
62
+ }
63
+ if (removals.length > 0) {
64
+ const confirmed = await this.confirmDeletions(removals.length);
65
+ if (!confirmed) {
66
+ return { files: loaded.files, findings, diff, drift, outcomes: [], status: 'declined', failed: false };
67
+ }
68
+ }
69
+ const outcomes = await this.executeResolved(resolveAdditions(additions, resolution), updates, removals);
70
+ const failed = outcomes.some((outcome) => !outcome.success);
71
+ return { files: loaded.files, findings, diff, drift, outcomes, status: 'applied', failed };
72
+ }
73
+ /**
74
+ * Apply a saved plan verbatim: no load, resolve, or diff. The plan is already
75
+ * mode-scoped and resolved, so there is no drift and no findings. maxDeletes and
76
+ * the deletion confirmation still guard the run.
77
+ */
78
+ async runPlan(plan, input) {
79
+ const diff = { toAdd: plan.add, toUpdate: plan.update, toRemove: plan.remove, unchanged: [] };
80
+ const { maxDeletes, dryRun } = input;
81
+ const base = { files: [], findings: [], diff, drift: noDrift, outcomes: [] };
82
+ if (plan.remove.length > maxDeletes) {
83
+ return { ...base, status: 'max-deletes-exceeded', failed: true };
84
+ }
85
+ if (dryRun) {
86
+ return { ...base, status: 'dry-run', failed: false };
87
+ }
88
+ if (plan.remove.length > 0) {
89
+ const confirmed = await this.confirmDeletions(plan.remove.length);
90
+ if (!confirmed) {
91
+ return { ...base, status: 'declined', failed: false };
92
+ }
93
+ }
94
+ const outcomes = await this.executeResolved(plan.add, plan.update, plan.remove);
95
+ const failed = outcomes.some((outcome) => !outcome.success);
96
+ return { ...base, outcomes, status: 'applied', failed };
97
+ }
98
+ async executeResolved(additions, updates, removals) {
99
+ const [added, updated, removed] = await Promise.all([
100
+ additions.length > 0 ? this.org.addAssignments(additions) : Promise.resolve([]),
101
+ updates.length > 0 ? this.org.updateAssignments(updates) : Promise.resolve([]),
102
+ removals.length > 0 ? this.org.removeAssignments(removals) : Promise.resolve([]),
103
+ ]);
104
+ return [
105
+ ...added,
106
+ ...updated,
107
+ ...removed,
108
+ ];
109
+ }
110
+ }
111
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/services/apply.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { DesiredAssignment, Finding } from '../core/index.js';
2
+ export type CheckResult = {
3
+ files: string[];
4
+ assignments: DesiredAssignment[];
5
+ findings: Finding[];
6
+ errors: number;
7
+ warnings: number;
8
+ failed: boolean;
9
+ };
10
+ /** Load the files, validate them, and summarize the findings. No org needed. */
11
+ export declare class CheckService {
12
+ run(files: string[], strict: boolean): Promise<CheckResult>;
13
+ }
14
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/services/check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,iBAAiB,EAAE,OAAO,EAAiB,MAAM,kBAAkB,CAAC;AAExF,MAAM,MAAM,WAAW,GAAG;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,gFAAgF;AAChF,qBAAa,YAAY;IACR,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC;CAc3E"}
@@ -0,0 +1,18 @@
1
+ import { loadFiles, countFindings } from '../core/index.js';
2
+ /** Load the files, validate them, and summarize the findings. No org needed. */
3
+ export class CheckService {
4
+ async run(files, strict) {
5
+ const loaded = await loadFiles(files);
6
+ const { errors, warnings } = countFindings(loaded.findings);
7
+ const failed = errors > 0 || (strict && warnings > 0);
8
+ return {
9
+ files: loaded.files,
10
+ assignments: loaded.assignments,
11
+ findings: loaded.findings,
12
+ errors,
13
+ warnings,
14
+ failed,
15
+ };
16
+ }
17
+ }
18
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/services/check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA8B,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAWxF,gFAAgF;AAChF,MAAM,OAAO,YAAY;IACd,KAAK,CAAC,GAAG,CAAC,KAAe,EAAE,MAAe;QAC7C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC;QAEtD,OAAO;YACH,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,MAAM;YACN,QAAQ;YACR,MAAM;SACT,CAAC;IACN,CAAC;CACJ"}
@@ -0,0 +1,19 @@
1
+ import { AssignmentFilter } from '../core/index.js';
2
+ import { OrgClient } from './adapters/index.js';
3
+ export type ExportResult = {
4
+ /** The path written, or null when the document was returned for stdout instead of a file. */
5
+ outputFile: string | null;
6
+ /** The serialized YAML document, exactly as written to the file. */
7
+ content: string;
8
+ users: number;
9
+ assignments: number;
10
+ /** Requested users (from the filter) that matched no assignments in scope. */
11
+ unmatchedUsers: string[];
12
+ };
13
+ /** Read the org's current assignments and serialize them as YAML. */
14
+ export declare class ExportService {
15
+ private readonly org;
16
+ constructor(org: OrgClient);
17
+ run(outputFile: string | undefined, filter?: AssignmentFilter): Promise<ExportResult>;
18
+ }
19
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/services/export.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAwB,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,MAAM,YAAY,GAAG;IACvB,6FAA6F;IAC7F,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,cAAc,EAAE,MAAM,EAAE,CAAC;CAC5B,CAAC;AAEF,qEAAqE;AACrE,qBAAa,aAAa;IACH,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAErC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;CAqBrG"}
@@ -0,0 +1,29 @@
1
+ import { writeFile, mkdir } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { serializeAssignments } from '../core/index.js';
4
+ /** Read the org's current assignments and serialize them as YAML. */
5
+ export class ExportService {
6
+ org;
7
+ constructor(org) {
8
+ this.org = org;
9
+ }
10
+ async run(outputFile, filter) {
11
+ const assignments = await this.org.listAssignments(filter);
12
+ const content = serializeAssignments(assignments);
13
+ if (outputFile) {
14
+ await mkdir(dirname(outputFile), { recursive: true });
15
+ await writeFile(outputFile, content, 'utf8');
16
+ }
17
+ const assignees = new Set(assignments.map((assignment) => assignment.assignee));
18
+ const requested = filter?.usernames ?? [];
19
+ const unmatchedUsers = requested.filter((username) => !assignees.has(username));
20
+ return {
21
+ outputFile: outputFile ?? null,
22
+ content,
23
+ users: assignees.size,
24
+ assignments: assignments.length,
25
+ unmatchedUsers,
26
+ };
27
+ }
28
+ }
29
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/services/export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAoB,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAc1E,qEAAqE;AACrE,MAAM,OAAO,aAAa;IACc;IAApC,YAAoC,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAE/C,KAAK,CAAC,GAAG,CAAC,UAA8B,EAAE,MAAyB;QACtE,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;QAElD,IAAI,UAAU,EAAE,CAAC;YACb,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACtD,MAAM,SAAS,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;QAChF,MAAM,SAAS,GAAG,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC;QAC1C,MAAM,cAAc,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;QAEhF,OAAO;YACH,UAAU,EAAE,UAAU,IAAI,IAAI;YAC9B,OAAO;YACP,KAAK,EAAE,SAAS,CAAC,IAAI;YACrB,WAAW,EAAE,WAAW,CAAC,MAAM;YAC/B,cAAc;SACjB,CAAC;IACN,CAAC;CACJ"}
@@ -0,0 +1,7 @@
1
+ export { ApplyService, ConfirmDeletions, ApplyResult } from './apply.js';
2
+ export { CheckService } from './check.js';
3
+ export { ExportService } from './export.js';
4
+ export { PlanService } from './plan.js';
5
+ export { Resolution, resolveAdditions } from './resolution.js';
6
+ export { ValidateService } from './validate.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { ApplyService } from './apply.js';
2
+ export { CheckService } from './check.js';
3
+ export { ExportService } from './export.js';
4
+ export { PlanService } from './plan.js';
5
+ export { resolveAdditions } from './resolution.js';
6
+ export { ValidateService } from './validate.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAiC,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAc,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { Diff, Finding } from '../core/index.js';
2
+ import { OrgClient } from './adapters/index.js';
3
+ import { Resolution } from './resolution.js';
4
+ export type PlanMode = 'additive' | 'destructive' | 'sync';
5
+ /** How a run ended, so the command can report and set the exit code. */
6
+ export type PlanStatus = 'planned' | 'invalid';
7
+ export type PlanResult = {
8
+ files: string[];
9
+ findings: Finding[];
10
+ diff: Diff;
11
+ /** What the chosen mode would not act on (surfaced as drift). */
12
+ drift: {
13
+ adds: number;
14
+ updates: number;
15
+ removes: number;
16
+ };
17
+ /** The resolved id maps, so callers can freeze a plan without re-resolving. */
18
+ resolution: Resolution;
19
+ status: PlanStatus;
20
+ };
21
+ /**
22
+ * Read-only preview: load the files, resolve every reference to an org id, fetch the
23
+ * current state, and diff. The full diff (adds and would-be removes) is always returned
24
+ * regardless of mode; drift is whatever the chosen mode would not act on. Never changes
25
+ * the org. This is the apply pipeline stopping before any DML.
26
+ */
27
+ export declare class PlanService {
28
+ private readonly org;
29
+ constructor(org: OrgClient);
30
+ run(files: string[], mode: PlanMode): Promise<PlanResult>;
31
+ }
32
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/services/plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,IAAI,EAAE,OAAO,EAAuB,MAAM,kBAAkB,CAAC;AAClG,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAqC,MAAM,iBAAiB,CAAC;AAEhF,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,aAAa,GAAG,MAAM,CAAC;AAE3D,wEAAwE;AACxE,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,IAAI,EAAE,IAAI,CAAC;IACX,iEAAiE;IACjE,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,+EAA+E;IAC/E,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,UAAU,CAAC;CACtB,CAAC;AAsBF;;;;;GAKG;AACH,qBAAa,WAAW;IACD,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAErC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;CA4BzE"}
@@ -0,0 +1,57 @@
1
+ import { loadFiles, diffAssignments, countFindings } from '../core/index.js';
2
+ import { ResolutionService, managedTargets } from './resolution.js';
3
+ const emptyDiff = { toAdd: [], toUpdate: [], toRemove: [], unchanged: [] };
4
+ const emptyResolution = {
5
+ findings: [],
6
+ userIds: new Map(),
7
+ targetIds: {},
8
+ };
9
+ /** An aborted-before-the-diff result, carrying the findings that explain why. */
10
+ function invalidResult(files, findings) {
11
+ return {
12
+ files,
13
+ findings,
14
+ diff: emptyDiff,
15
+ drift: { adds: 0, updates: 0, removes: 0 },
16
+ resolution: emptyResolution,
17
+ status: 'invalid',
18
+ };
19
+ }
20
+ /**
21
+ * Read-only preview: load the files, resolve every reference to an org id, fetch the
22
+ * current state, and diff. The full diff (adds and would-be removes) is always returned
23
+ * regardless of mode; drift is whatever the chosen mode would not act on. Never changes
24
+ * the org. This is the apply pipeline stopping before any DML.
25
+ */
26
+ export class PlanService {
27
+ org;
28
+ constructor(org) {
29
+ this.org = org;
30
+ }
31
+ async run(files, mode) {
32
+ const loaded = await loadFiles(files);
33
+ const loadCounts = countFindings(loaded.findings);
34
+ if (loadCounts.errors > 0) {
35
+ return invalidResult(loaded.files, loaded.findings);
36
+ }
37
+ const resolutionService = new ResolutionService(this.org);
38
+ const resolution = await resolutionService.run(loaded.assignments);
39
+ const findings = [
40
+ ...loaded.findings,
41
+ ...resolution.findings,
42
+ ];
43
+ const findingCounts = countFindings(findings);
44
+ if (findingCounts.errors > 0) {
45
+ return invalidResult(loaded.files, findings);
46
+ }
47
+ const actual = await this.org.currentAssignments(managedTargets(resolution));
48
+ const diff = diffAssignments(loaded.assignments, actual);
49
+ const drift = {
50
+ adds: mode === 'destructive' ? diff.toAdd.length : 0,
51
+ updates: mode === 'destructive' ? diff.toUpdate.length : 0,
52
+ removes: mode === 'additive' ? diff.toRemove.length : 0,
53
+ };
54
+ return { files: loaded.files, findings, diff, drift, resolution, status: 'planned' };
55
+ }
56
+ }
57
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/services/plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAuB,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAElG,OAAO,EAAc,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAkBhF,MAAM,SAAS,GAAS,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AAEjF,MAAM,eAAe,GAAe;IAChC,QAAQ,EAAE,EAAE;IACZ,OAAO,EAAE,IAAI,GAAG,EAAE;IAClB,SAAS,EAAE,EAAuC;CACrD,CAAC;AAEF,iFAAiF;AACjF,SAAS,aAAa,CAAC,KAAe,EAAE,QAAmB;IACvD,OAAO;QACH,KAAK;QACL,QAAQ;QACR,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;QAC1C,UAAU,EAAE,eAAe;QAC3B,MAAM,EAAE,SAAS;KACpB,CAAC;AACN,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,WAAW;IACgB;IAApC,YAAoC,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAE/C,KAAK,CAAC,GAAG,CAAC,KAAe,EAAE,IAAc;QAC5C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACnE,MAAM,QAAQ,GAAG;YACb,GAAG,MAAM,CAAC,QAAQ;YAClB,GAAG,UAAU,CAAC,QAAQ;SACzB,CAAC;QACF,MAAM,aAAa,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC9C,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,OAAO,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;QAC7E,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG;YACV,IAAI,EAAE,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACpD,OAAO,EAAE,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC1D,OAAO,EAAE,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;SAC1D,CAAC;QAEF,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACzF,CAAC;CACJ"}
@@ -0,0 +1,17 @@
1
+ import { DesiredAssignment, Kind, ResolvedAddition, TargetRef, Finding } from '../core/index.js';
2
+ import { OrgClient } from './adapters/index.js';
3
+ export type Resolution = {
4
+ findings: Finding[];
5
+ userIds: Map<string, string>;
6
+ targetIds: Record<Kind, Map<string, string>>;
7
+ };
8
+ export declare function managedTargets(resolution: Resolution): TargetRef[];
9
+ /** Attach the resolved assignee and target ids to each addition, so it can be inserted. */
10
+ export declare function resolveAdditions(additions: DesiredAssignment[], resolution: Resolution): ResolvedAddition[];
11
+ /** Look every declared reference up in the org, returning findings and the resolved id maps. */
12
+ export declare class ResolutionService {
13
+ private readonly org;
14
+ constructor(org: OrgClient);
15
+ run(assignments: DesiredAssignment[]): Promise<Resolution>;
16
+ }
17
+ //# sourceMappingURL=resolution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolution.d.ts","sourceRoot":"","sources":["../../src/services/resolution.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EACjB,IAAI,EAGJ,gBAAgB,EAChB,SAAS,EACT,OAAO,EAQV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,MAAM,UAAU,GAAG;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAChD,CAAC;AAEF,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,EAAE,CAQlE;AAED,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,iBAAiB,EAAE,EAAE,UAAU,EAAE,UAAU,GAAG,gBAAgB,EAAE,CAM3G;AAED,gGAAgG;AAChG,qBAAa,iBAAiB;IACP,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAErC,GAAG,CAAC,WAAW,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC;CAoC1E"}
@@ -0,0 +1,48 @@
1
+ import { kinds, distinctAssignees, distinctTargets, evaluateUsers, evaluateTargets, indexUsersById, indexTargetsById, } from '../core/index.js';
2
+ export function managedTargets(resolution) {
3
+ const refs = [];
4
+ for (const kind of kinds) {
5
+ for (const id of resolution.targetIds[kind].values()) {
6
+ refs.push({ kind, id });
7
+ }
8
+ }
9
+ return refs;
10
+ }
11
+ /** Attach the resolved assignee and target ids to each addition, so it can be inserted. */
12
+ export function resolveAdditions(additions, resolution) {
13
+ return additions.map((addition) => ({
14
+ ...addition,
15
+ assigneeId: resolution.userIds.get(addition.assignee.toLowerCase()) ?? '',
16
+ targetId: resolution.targetIds[addition.kind].get(addition.target.toLowerCase()) ?? '',
17
+ }));
18
+ }
19
+ /** Look every declared reference up in the org, returning findings and the resolved id maps. */
20
+ export class ResolutionService {
21
+ org;
22
+ constructor(org) {
23
+ this.org = org;
24
+ }
25
+ async run(assignments) {
26
+ const usernames = distinctAssignees(assignments);
27
+ const targetsByKind = kinds.map((kind) => ({ kind, targets: distinctTargets(assignments, kind) }));
28
+ const usersTask = usernames.length > 0 ? this.org.findUsers(usernames) : Promise.resolve([]);
29
+ const targetsTask = Promise.all(targetsByKind.map(async ({ kind, targets }) => {
30
+ const found = targets.length > 0 ? await this.org.findTargets(kind, targets) : [];
31
+ return { kind, targets, found };
32
+ }));
33
+ const [foundUsers, perKind] = await Promise.all([
34
+ usersTask,
35
+ targetsTask,
36
+ ]);
37
+ const findings = [...evaluateUsers(usernames, foundUsers)];
38
+ for (const { kind, targets, found } of perKind) {
39
+ findings.push(...evaluateTargets(kind, targets, found.map((target) => target.name)));
40
+ }
41
+ const targetIds = {};
42
+ for (const { kind, found } of perKind) {
43
+ targetIds[kind] = indexTargetsById(found);
44
+ }
45
+ return { findings, userIds: indexUsersById(foundUsers), targetIds };
46
+ }
47
+ }
48
+ //# sourceMappingURL=resolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolution.js","sourceRoot":"","sources":["../../src/services/resolution.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { DesiredAssignment, Finding } from '../core/index.js';
2
+ import { OrgClient } from './adapters/index.js';
3
+ export type ValidateResult = {
4
+ files: string[];
5
+ assignments: DesiredAssignment[];
6
+ findings: Finding[];
7
+ errors: number;
8
+ warnings: number;
9
+ failed: boolean;
10
+ };
11
+ /** Load the files, then resolve every reference against the org. */
12
+ export declare class ValidateService {
13
+ private readonly org;
14
+ constructor(org: OrgClient);
15
+ run(files: string[]): Promise<ValidateResult>;
16
+ /** Look every reference up in the org (in parallel) and evaluate the results. */
17
+ private resolve;
18
+ private evaluateUserRefs;
19
+ private evaluateTargetRefs;
20
+ }
21
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/services/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAOH,iBAAiB,EACjB,OAAO,EAGV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IACzB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,oEAAoE;AACpE,qBAAa,eAAe;IACL,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAErC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAoB1D,iFAAiF;YACnE,OAAO;YAmBP,gBAAgB;YAKhB,kBAAkB;CAQnC"}
@@ -0,0 +1,50 @@
1
+ import { loadFiles, kinds, distinctAssignees, distinctTargets, evaluateUsers, evaluateTargets, countFindings, } from '../core/index.js';
2
+ /** Load the files, then resolve every reference against the org. */
3
+ export class ValidateService {
4
+ org;
5
+ constructor(org) {
6
+ this.org = org;
7
+ }
8
+ async run(files) {
9
+ const loaded = await loadFiles(files);
10
+ const resolved = await this.resolve(loaded.assignments);
11
+ const findings = [
12
+ ...loaded.findings,
13
+ ...resolved,
14
+ ];
15
+ const { errors, warnings } = countFindings(findings);
16
+ return {
17
+ files: loaded.files,
18
+ assignments: loaded.assignments,
19
+ findings,
20
+ errors,
21
+ warnings,
22
+ failed: errors > 0,
23
+ };
24
+ }
25
+ /** Look every reference up in the org (in parallel) and evaluate the results. */
26
+ async resolve(assignments) {
27
+ const tasks = [];
28
+ const usernames = distinctAssignees(assignments);
29
+ if (usernames.length > 0) {
30
+ tasks.push(this.evaluateUserRefs(usernames));
31
+ }
32
+ for (const kind of kinds) {
33
+ const targets = distinctTargets(assignments, kind);
34
+ if (targets.length > 0) {
35
+ tasks.push(this.evaluateTargetRefs(kind, targets));
36
+ }
37
+ }
38
+ const results = await Promise.all(tasks);
39
+ return results.flat();
40
+ }
41
+ async evaluateUserRefs(usernames) {
42
+ const found = await this.org.findUsers(usernames);
43
+ return evaluateUsers(usernames, found);
44
+ }
45
+ async evaluateTargetRefs(kind, targets) {
46
+ const found = await this.org.findTargets(kind, targets);
47
+ return evaluateTargets(kind, targets, found.map((target) => target.name));
48
+ }
49
+ }
50
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/services/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,SAAS,EACT,KAAK,EACL,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,eAAe,EAIf,aAAa,GAChB,MAAM,kBAAkB,CAAC;AAY1B,oEAAoE;AACpE,MAAM,OAAO,eAAe;IACY;IAApC,YAAoC,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAE/C,KAAK,CAAC,GAAG,CAAC,KAAe;QAC5B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAExD,MAAM,QAAQ,GAAG;YACb,GAAG,MAAM,CAAC,QAAQ;YAClB,GAAG,QAAQ;SACd,CAAC;QACF,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QAErD,OAAO;YACH,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,QAAQ;YACR,MAAM;YACN,QAAQ;YACR,MAAM,EAAE,MAAM,GAAG,CAAC;SACrB,CAAC;IACN,CAAC;IAED,iFAAiF;IACzE,KAAK,CAAC,OAAO,CAAC,WAAgC;QAClD,MAAM,KAAK,GAA8B,EAAE,CAAC;QAE5C,MAAM,SAAS,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;QACjD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;QACjD,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,eAAe,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACnD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;YACvD,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzC,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,SAAmB;QAC9C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAClD,OAAO,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,IAAU,EAAE,OAAiB;QAC1D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACxD,OAAO,eAAe,CAClB,IAAI,EACJ,OAAO,EACP,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CACrC,CAAC;IACN,CAAC;CACJ"}
@@ -0,0 +1,117 @@
1
+ # summary
2
+
3
+ Reconcile a target org to match the permission set assignment files.
4
+
5
+ # description
6
+
7
+ Load the files, resolve every user and target against the org, diff the desired state against what the org currently has, then add, update expirations, and/or remove assignments per the mode. Additions, expiration updates, and removals run through the sObject Collections API with partial success, so one bad record does not roll back the rest. Deletions are capped by --max-deletes and confirmed before they run. Run validate and a --dry-run first.
8
+
9
+ # flags.file.summary
10
+
11
+ YAML file or glob to apply. Repeatable. Provide either this or --plan.
12
+
13
+ # flags.plan.summary
14
+
15
+ Apply a saved plan file from plan --out, running the reviewed change set verbatim. Provide either this or --file.
16
+
17
+ # flags.mode.summary
18
+
19
+ Which half of the reconcile to run: additive adds missing assignments and updates expirations, destructive removes only, sync does both. Cannot be combined with --plan, which carries its own mode.
20
+
21
+ # flags.max-deletes.summary
22
+
23
+ Abort before any change if the run would remove more than this many assignments.
24
+
25
+ # flags.dry-run.summary
26
+
27
+ Resolve and diff, print the plan, and change nothing.
28
+
29
+ # flags.show-unchanged.summary
30
+
31
+ List assignments that already match, instead of only counting them.
32
+
33
+ # flags.no-prompt.summary
34
+
35
+ Skip the deletion confirmation prompt. Required to delete in JSON or other non-interactive runs.
36
+
37
+ # info.applyingPlan
38
+
39
+ Applying saved plan (mode %s).
40
+
41
+ # confirm.delete
42
+
43
+ This will remove %s assignment(s) from the org. Continue?
44
+
45
+ # summary.dryRun
46
+
47
+ Dry run: %s to add, %s to update, %s to remove. Nothing was changed.
48
+
49
+ # summary.applied
50
+
51
+ Applied: %s added, %s updated, %s removed.
52
+
53
+ # summary.declined
54
+
55
+ Aborted at the confirmation prompt. Nothing was changed.
56
+
57
+ # drift.note
58
+
59
+ %s change(s) the %s mode does not act on were skipped (drift). Run plan to see them.
60
+
61
+ # failure.line
62
+
63
+ failed to %s %s on %s: %s
64
+
65
+ # error.sourceConflict
66
+
67
+ Pass either --file or --plan, not both. --plan runs a saved plan; --file reads and diffs the YAML now.
68
+
69
+ # error.sourceMissing
70
+
71
+ Provide a source to apply: --file <glob> to read the YAML now, or --plan <file> to run a saved plan.
72
+
73
+ # error.modeWithPlan
74
+
75
+ --mode cannot be combined with --plan. A saved plan already carries the mode it was computed for. Re-run plan with the mode you want to get a new plan.
76
+
77
+ # error.planRead
78
+
79
+ Could not read the plan file: %s
80
+
81
+ # error.planInvalid
82
+
83
+ The plan file %s is not a valid plan: %s. Regenerate it with plan --out.
84
+
85
+ # error.planOrg
86
+
87
+ This plan was built for org %s but the target org is %s. Regenerate the plan against the target org.
88
+
89
+ # error.invalid
90
+
91
+ The files do not resolve cleanly against the org. Fix the errors above, then re-run.
92
+
93
+ # error.maxDeletes
94
+
95
+ Refusing to remove %s assignment(s): over the --max-deletes limit of %s. Raise the limit or narrow the change.
96
+
97
+ # error.promptInJson
98
+
99
+ Refusing to delete without confirmation in a non-interactive run. Re-run with --no-prompt.
100
+
101
+ # error.failed
102
+
103
+ Some changes failed. See the per-record errors above.
104
+
105
+ # examples
106
+
107
+ - Preview a full reconcile of the dev org without changing anything:
108
+
109
+ <%= config.bin %> <%= command.id %> --file "permissions/*.yml" --target-org dev --mode sync --dry-run
110
+
111
+ - Grant any missing assignments (additive, the default):
112
+
113
+ <%= config.bin %> <%= command.id %> --file "permissions/*.yml" --target-org dev
114
+
115
+ - Full reconcile of production in CI, without prompts:
116
+
117
+ <%= config.bin %> <%= command.id %> --file "permissions/*.yml" --target-org prod --mode sync --no-prompt