sf-plugin-permission-sets 0.0.0-dev.4 → 0.0.0-dev.42

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 (123) hide show
  1. package/README.md +157 -93
  2. package/lib/adapters/connection-org-client.d.ts +17 -0
  3. package/lib/adapters/connection-org-client.d.ts.map +1 -0
  4. package/lib/adapters/connection-org-client.js +248 -0
  5. package/lib/adapters/connection-org-client.js.map +1 -0
  6. package/lib/adapters/index.d.ts +2 -0
  7. package/lib/adapters/index.d.ts.map +1 -0
  8. package/lib/adapters/index.js +2 -0
  9. package/lib/adapters/index.js.map +1 -0
  10. package/lib/commands/ps/apply.d.ts +39 -0
  11. package/lib/commands/ps/apply.d.ts.map +1 -0
  12. package/lib/commands/ps/apply.js +173 -0
  13. package/lib/commands/ps/apply.js.map +1 -0
  14. package/lib/commands/ps/check.d.ts +21 -0
  15. package/lib/commands/ps/check.d.ts.map +1 -0
  16. package/lib/commands/ps/check.js +55 -0
  17. package/lib/commands/ps/check.js.map +1 -0
  18. package/lib/commands/ps/export.d.ts +18 -0
  19. package/lib/commands/ps/export.d.ts.map +1 -0
  20. package/lib/commands/ps/export.js +35 -0
  21. package/lib/commands/ps/export.js.map +1 -0
  22. package/lib/commands/ps/plan.d.ts +61 -0
  23. package/lib/commands/ps/plan.d.ts.map +1 -0
  24. package/lib/commands/ps/plan.js +212 -0
  25. package/lib/commands/ps/plan.js.map +1 -0
  26. package/lib/commands/ps/validate.d.ts +21 -0
  27. package/lib/commands/ps/validate.d.ts.map +1 -0
  28. package/lib/commands/ps/validate.js +56 -0
  29. package/lib/commands/ps/validate.js.map +1 -0
  30. package/lib/core/diff.d.ts +10 -0
  31. package/lib/core/diff.d.ts.map +1 -0
  32. package/lib/core/diff.js +54 -0
  33. package/lib/core/diff.js.map +1 -0
  34. package/lib/core/finding.d.ts +41 -0
  35. package/lib/core/finding.d.ts.map +1 -0
  36. package/lib/core/finding.js +68 -0
  37. package/lib/core/finding.js.map +1 -0
  38. package/lib/core/index.d.ts +8 -0
  39. package/lib/core/index.d.ts.map +1 -0
  40. package/lib/core/index.js +7 -0
  41. package/lib/core/index.js.map +1 -0
  42. package/lib/core/load.d.ts +4 -0
  43. package/lib/core/load.d.ts.map +1 -0
  44. package/lib/core/load.js +60 -0
  45. package/lib/core/load.js.map +1 -0
  46. package/lib/core/model.d.ts +73 -0
  47. package/lib/core/model.d.ts.map +1 -0
  48. package/lib/core/model.js +2 -0
  49. package/lib/core/model.js.map +1 -0
  50. package/lib/core/normalize.d.ts +15 -0
  51. package/lib/core/normalize.d.ts.map +1 -0
  52. package/lib/core/normalize.js +62 -0
  53. package/lib/core/normalize.js.map +1 -0
  54. package/lib/core/parse.d.ts +10 -0
  55. package/lib/core/parse.d.ts.map +1 -0
  56. package/lib/core/parse.js +20 -0
  57. package/lib/core/parse.js.map +1 -0
  58. package/lib/core/report.d.ts +15 -0
  59. package/lib/core/report.d.ts.map +1 -0
  60. package/lib/core/report.js +114 -0
  61. package/lib/core/report.js.map +1 -0
  62. package/lib/core/resolve.d.ts +20 -0
  63. package/lib/core/resolve.d.ts.map +1 -0
  64. package/lib/core/resolve.js +88 -0
  65. package/lib/core/resolve.js.map +1 -0
  66. package/lib/core/schema.d.ts +23 -0
  67. package/lib/core/schema.d.ts.map +1 -0
  68. package/lib/core/schema.js +35 -0
  69. package/lib/core/schema.js.map +1 -0
  70. package/lib/core/serialize.d.ts +9 -0
  71. package/lib/core/serialize.d.ts.map +1 -0
  72. package/lib/core/serialize.js +34 -0
  73. package/lib/core/serialize.js.map +1 -0
  74. package/lib/index.d.ts +1 -0
  75. package/lib/index.d.ts.map +1 -0
  76. package/lib/services/adapters/index.d.ts +2 -0
  77. package/lib/services/adapters/index.d.ts.map +1 -0
  78. package/lib/services/adapters/index.js +2 -0
  79. package/lib/services/adapters/index.js.map +1 -0
  80. package/lib/services/adapters/org-client.d.ts +22 -0
  81. package/lib/services/adapters/org-client.d.ts.map +1 -0
  82. package/lib/services/adapters/org-client.js +2 -0
  83. package/lib/services/adapters/org-client.js.map +1 -0
  84. package/lib/services/apply.d.ts +39 -0
  85. package/lib/services/apply.d.ts.map +1 -0
  86. package/lib/services/apply.js +102 -0
  87. package/lib/services/apply.js.map +1 -0
  88. package/lib/services/check.d.ts +14 -0
  89. package/lib/services/check.d.ts.map +1 -0
  90. package/lib/services/check.js +18 -0
  91. package/lib/services/check.js.map +1 -0
  92. package/lib/services/export.d.ts +13 -0
  93. package/lib/services/export.d.ts.map +1 -0
  94. package/lib/services/export.js +23 -0
  95. package/lib/services/export.js.map +1 -0
  96. package/lib/services/index.d.ts +6 -0
  97. package/lib/services/index.d.ts.map +1 -0
  98. package/lib/services/index.js +6 -0
  99. package/lib/services/index.js.map +1 -0
  100. package/lib/services/plan.d.ts +29 -0
  101. package/lib/services/plan.d.ts.map +1 -0
  102. package/lib/services/plan.js +45 -0
  103. package/lib/services/plan.js.map +1 -0
  104. package/lib/services/resolution.d.ts +15 -0
  105. package/lib/services/resolution.d.ts.map +1 -0
  106. package/lib/services/resolution.js +37 -0
  107. package/lib/services/resolution.js.map +1 -0
  108. package/lib/services/validate.d.ts +19 -0
  109. package/lib/services/validate.d.ts.map +1 -0
  110. package/lib/services/validate.js +42 -0
  111. package/lib/services/validate.js.map +1 -0
  112. package/messages/ps.apply.md +85 -0
  113. package/messages/ps.check.md +33 -0
  114. package/messages/ps.export.md +25 -0
  115. package/messages/ps.plan.md +85 -0
  116. package/messages/ps.validate.md +29 -0
  117. package/package.json +112 -181
  118. package/lib/commands/ps/info.d.ts +0 -11
  119. package/lib/commands/ps/info.js +0 -17
  120. package/lib/commands/ps/info.js.map +0 -1
  121. package/messages/ps.info.md +0 -17
  122. package/oclif.lock +0 -7781
  123. package/oclif.manifest.json +0 -51
@@ -0,0 +1,22 @@
1
+ import { ActualAssignment, AssignmentOutcome, AssignmentUpdate, DesiredAssignment, Kind, OrgTarget, OrgUser, ResolvedAddition, TargetRef } from '../../core/index.js';
2
+ /**
3
+ * Port: the org operations a service needs, in domain terms. Declared here, by the
4
+ * consumer, so services depend on the abstraction and the adapter implements it.
5
+ */
6
+ export interface OrgClient {
7
+ /** The users that exist in the org, among the given usernames. */
8
+ findUsers(usernames: string[]): Promise<OrgUser[]>;
9
+ /** The targets (with ids) that exist in the org, among the given names of one kind. */
10
+ findTargets(kind: Kind, names: string[]): Promise<OrgTarget[]>;
11
+ /** Every assignable permission set, group, and license assignment held by active users. */
12
+ listAssignments(): Promise<DesiredAssignment[]>;
13
+ /** The current assignments of the given managed targets, with their record ids. */
14
+ currentAssignments(targets: TargetRef[]): Promise<ActualAssignment[]>;
15
+ /** Insert the given assignments, reporting per-record success or failure. */
16
+ addAssignments(additions: ResolvedAddition[]): Promise<AssignmentOutcome[]>;
17
+ /** Update the expiration of the given assignments, reporting per-record success or failure. */
18
+ updateAssignments(updates: AssignmentUpdate[]): Promise<AssignmentOutcome[]>;
19
+ /** Delete the given assignments by record id, reporting per-record success or failure. */
20
+ removeAssignments(removals: ActualAssignment[]): Promise<AssignmentOutcome[]>;
21
+ }
22
+ //# sourceMappingURL=org-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-client.d.ts","sourceRoot":"","sources":["../../../src/services/adapters/org-client.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,IAAI,EACJ,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,SAAS,EACZ,MAAM,qBAAqB,CAAC;AAE7B;;;GAGG;AACH,MAAM,WAAW,SAAS;IACtB,kEAAkE;IAClE,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,uFAAuF;IACvF,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAC/D,2FAA2F;IAC3F,eAAe,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAChD,mFAAmF;IACnF,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACtE,6EAA6E;IAC7E,cAAc,CAAC,SAAS,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAC5E,+FAA+F;IAC/F,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAC7E,0FAA0F;IAC1F,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;CACjF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=org-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-client.js","sourceRoot":"","sources":["../../../src/services/adapters/org-client.ts"],"names":[],"mappings":""}
@@ -0,0 +1,39 @@
1
+ import { AssignmentOutcome, Diff, Finding } from '../core/index.js';
2
+ import { OrgClient } from './adapters/index.js';
3
+ export type ApplyMode = 'additive' | 'destructive' | 'sync';
4
+ /** How the service asks its caller to approve a destructive batch before applying it. */
5
+ export type ConfirmDeletions = (count: number) => Promise<boolean>;
6
+ export type ApplyInput = {
7
+ mode: ApplyMode;
8
+ maxDeletes: number;
9
+ dryRun: boolean;
10
+ };
11
+ /** How a run ended, so the command can report and set the exit code. */
12
+ export type ApplyStatus = 'applied' | 'dry-run' | 'declined' | 'max-deletes-exceeded' | 'invalid';
13
+ export type ApplyResult = {
14
+ files: string[];
15
+ findings: Finding[];
16
+ diff: Diff;
17
+ /** What the chosen mode did not act on (surfaced as drift). */
18
+ drift: {
19
+ adds: number;
20
+ updates: number;
21
+ removes: number;
22
+ };
23
+ outcomes: AssignmentOutcome[];
24
+ status: ApplyStatus;
25
+ failed: boolean;
26
+ };
27
+ /**
28
+ * Online apply: load the files, resolve every reference to an org id, diff against
29
+ * the org's current state, then add and/or remove per the mode. Deletions are
30
+ * capped by maxDeletes and gated by an injected confirmation.
31
+ */
32
+ export declare class ApplyService {
33
+ private readonly org;
34
+ private readonly confirmDeletions;
35
+ constructor(org: OrgClient, confirmDeletions: ConfirmDeletions);
36
+ run(files: string[], input: ApplyInput): Promise<ApplyResult>;
37
+ private execute;
38
+ }
39
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/services/apply.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,iBAAiB,EAGjB,IAAI,EAEJ,OAAO,EAEV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,aAAa,GAAG,MAAM,CAAC;AAE5D,yFAAyF;AACzF,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEnE,MAAM,MAAM,UAAU,GAAG;IACrB,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,sBAAsB,GAAG,SAAS,CAAC;AAElG,MAAM,MAAM,WAAW,GAAG;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,IAAI,EAAE,IAAI,CAAC;IACX,+DAA+D;IAC/D,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;CACnB,CAAC;AAqCF;;;;GAIG;AACH,qBAAa,YAAY;IAEjB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBADhB,GAAG,EAAE,SAAS,EACd,gBAAgB,EAAE,gBAAgB;IAG1C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;YAqD5D,OAAO;CAwBxB"}
@@ -0,0 +1,102 @@
1
+ import { loadFiles, diffAssignments, countFindings, } from '../core/index.js';
2
+ import { ResolutionService, managedTargets } from './resolution.js';
3
+ const emptyDiff = { toAdd: [], toUpdate: [], toRemove: [], unchanged: [] };
4
+ /** An aborted-before-any-change result, carrying the findings that explain why. */
5
+ function invalidResult(files, findings) {
6
+ return {
7
+ files,
8
+ findings,
9
+ diff: emptyDiff,
10
+ drift: { adds: 0, updates: 0, removes: 0 },
11
+ outcomes: [],
12
+ status: 'invalid',
13
+ failed: true,
14
+ };
15
+ }
16
+ /** Scope a diff to what the mode acts on, plus the drift (what it deliberately leaves alone). */
17
+ function planForMode(diff, mode) {
18
+ const additions = mode === 'destructive' ? [] : diff.toAdd;
19
+ const updates = mode === 'destructive' ? [] : diff.toUpdate;
20
+ const removals = mode === 'additive' ? [] : diff.toRemove;
21
+ const drift = {
22
+ adds: mode === 'destructive' ? diff.toAdd.length : 0,
23
+ updates: mode === 'destructive' ? diff.toUpdate.length : 0,
24
+ removes: mode === 'additive' ? diff.toRemove.length : 0,
25
+ };
26
+ return { additions, updates, removals, drift };
27
+ }
28
+ /**
29
+ * Online apply: load the files, resolve every reference to an org id, diff against
30
+ * the org's current state, then add and/or remove per the mode. Deletions are
31
+ * capped by maxDeletes and gated by an injected confirmation.
32
+ */
33
+ export class ApplyService {
34
+ org;
35
+ confirmDeletions;
36
+ constructor(org, confirmDeletions) {
37
+ this.org = org;
38
+ this.confirmDeletions = confirmDeletions;
39
+ }
40
+ async run(files, input) {
41
+ const loaded = await loadFiles(files);
42
+ const loadCounts = countFindings(loaded.findings);
43
+ if (loadCounts.errors > 0) {
44
+ return invalidResult(loaded.files, loaded.findings);
45
+ }
46
+ const resolutionService = new ResolutionService(this.org);
47
+ const resolution = await resolutionService.run(loaded.assignments);
48
+ const findings = [
49
+ ...loaded.findings,
50
+ ...resolution.findings,
51
+ ];
52
+ const findingCounts = countFindings(findings);
53
+ if (findingCounts.errors > 0) {
54
+ return invalidResult(loaded.files, findings);
55
+ }
56
+ const actual = await this.org.currentAssignments(managedTargets(resolution));
57
+ const diff = diffAssignments(loaded.assignments, actual);
58
+ const { mode, maxDeletes, dryRun } = input;
59
+ const { additions, updates, removals, drift } = planForMode(diff, mode);
60
+ if (removals.length > maxDeletes) {
61
+ return {
62
+ files: loaded.files,
63
+ findings,
64
+ diff,
65
+ drift,
66
+ outcomes: [],
67
+ status: 'max-deletes-exceeded',
68
+ failed: true,
69
+ };
70
+ }
71
+ if (dryRun) {
72
+ return { files: loaded.files, findings, diff, drift, outcomes: [], status: 'dry-run', failed: false };
73
+ }
74
+ if (removals.length > 0) {
75
+ const confirmed = await this.confirmDeletions(removals.length);
76
+ if (!confirmed) {
77
+ return { files: loaded.files, findings, diff, drift, outcomes: [], status: 'declined', failed: false };
78
+ }
79
+ }
80
+ const outcomes = await this.execute(additions, updates, removals, resolution);
81
+ const failed = outcomes.some((outcome) => !outcome.success);
82
+ return { files: loaded.files, findings, diff, drift, outcomes, status: 'applied', failed };
83
+ }
84
+ async execute(additions, updates, removals, resolution) {
85
+ const resolved = additions.map((addition) => ({
86
+ ...addition,
87
+ assigneeId: resolution.userIds.get(addition.assignee.toLowerCase()) ?? '',
88
+ targetId: resolution.targetIds[addition.kind].get(addition.target.toLowerCase()) ?? '',
89
+ }));
90
+ const [added, updated, removed] = await Promise.all([
91
+ resolved.length > 0 ? this.org.addAssignments(resolved) : Promise.resolve([]),
92
+ updates.length > 0 ? this.org.updateAssignments(updates) : Promise.resolve([]),
93
+ removals.length > 0 ? this.org.removeAssignments(removals) : Promise.resolve([]),
94
+ ]);
95
+ return [
96
+ ...added,
97
+ ...updated,
98
+ ...removed,
99
+ ];
100
+ }
101
+ }
102
+ //# 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
+ /** Offline check: load the files, validate them, and summarize the findings. */
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
+ /** Offline check: load the files, validate them, and summarize the findings. */
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,13 @@
1
+ import { OrgClient } from './adapters/index.js';
2
+ export type ExportResult = {
3
+ outputFile: string;
4
+ users: number;
5
+ assignments: number;
6
+ };
7
+ /** Online export: read the org's current assignments and write them as a YAML file. */
8
+ export declare class ExportService {
9
+ private readonly org;
10
+ constructor(org: OrgClient);
11
+ run(outputFile: string): Promise<ExportResult>;
12
+ }
13
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/services/export.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,MAAM,YAAY,GAAG;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,uFAAuF;AACvF,qBAAa,aAAa;IACH,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAErC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;CAe9D"}
@@ -0,0 +1,23 @@
1
+ import { writeFile, mkdir } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { serializeAssignments } from '../core/index.js';
4
+ /** Online export: read the org's current assignments and write them as a YAML file. */
5
+ export class ExportService {
6
+ org;
7
+ constructor(org) {
8
+ this.org = org;
9
+ }
10
+ async run(outputFile) {
11
+ const assignments = await this.org.listAssignments();
12
+ const content = serializeAssignments(assignments);
13
+ await mkdir(dirname(outputFile), { recursive: true });
14
+ await writeFile(outputFile, content, 'utf8');
15
+ const assignees = new Set(assignments.map((assignment) => assignment.assignee));
16
+ return {
17
+ outputFile,
18
+ users: assignees.size,
19
+ assignments: assignments.length,
20
+ };
21
+ }
22
+ }
23
+ //# 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,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AASxD,uFAAuF;AACvF,MAAM,OAAO,aAAa;IACc;IAApC,YAAoC,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAE/C,KAAK,CAAC,GAAG,CAAC,UAAkB;QAC/B,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC;QACrD,MAAM,OAAO,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;QAElD,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,MAAM,SAAS,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAE7C,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;QAEhF,OAAO;YACH,UAAU;YACV,KAAK,EAAE,SAAS,CAAC,IAAI;YACrB,WAAW,EAAE,WAAW,CAAC,MAAM;SAClC,CAAC;IACN,CAAC;CACJ"}
@@ -0,0 +1,6 @@
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 { ValidateService } from './validate.js';
6
+ //# 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,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,6 @@
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 { ValidateService } from './validate.js';
6
+ //# 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,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { Diff, Finding } from '../core/index.js';
2
+ import { OrgClient } from './adapters/index.js';
3
+ export type PlanMode = 'additive' | 'destructive' | 'sync';
4
+ /** How a run ended, so the command can report and set the exit code. */
5
+ export type PlanStatus = 'planned' | 'invalid';
6
+ export type PlanResult = {
7
+ files: string[];
8
+ findings: Finding[];
9
+ diff: Diff;
10
+ /** What the chosen mode would not act on (surfaced as drift). */
11
+ drift: {
12
+ adds: number;
13
+ updates: number;
14
+ removes: number;
15
+ };
16
+ status: PlanStatus;
17
+ };
18
+ /**
19
+ * Read-only preview: load the files, resolve every reference to an org id, fetch the
20
+ * current state, and diff. The full diff (adds and would-be removes) is always returned
21
+ * regardless of mode; drift is whatever the chosen mode would not act on. Never changes
22
+ * the org. This is the apply pipeline stopping before any DML.
23
+ */
24
+ export declare class PlanService {
25
+ private readonly org;
26
+ constructor(org: OrgClient);
27
+ run(files: string[], mode: PlanMode): Promise<PlanResult>;
28
+ }
29
+ //# 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,EAAiB,MAAM,kBAAkB,CAAC;AAC5F,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,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,MAAM,EAAE,UAAU,CAAC;CACtB,CAAC;AASF;;;;;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,45 @@
1
+ import { loadFiles, diffAssignments, countFindings } from '../core/index.js';
2
+ import { ResolutionService, managedTargets } from './resolution.js';
3
+ const emptyDiff = { toAdd: [], toUpdate: [], toRemove: [], unchanged: [] };
4
+ /** An aborted-before-the-diff result, carrying the findings that explain why. */
5
+ function invalidResult(files, findings) {
6
+ return { files, findings, diff: emptyDiff, drift: { adds: 0, updates: 0, removes: 0 }, status: 'invalid' };
7
+ }
8
+ /**
9
+ * Read-only preview: load the files, resolve every reference to an org id, fetch the
10
+ * current state, and diff. The full diff (adds and would-be removes) is always returned
11
+ * regardless of mode; drift is whatever the chosen mode would not act on. Never changes
12
+ * the org. This is the apply pipeline stopping before any DML.
13
+ */
14
+ export class PlanService {
15
+ org;
16
+ constructor(org) {
17
+ this.org = org;
18
+ }
19
+ async run(files, mode) {
20
+ const loaded = await loadFiles(files);
21
+ const loadCounts = countFindings(loaded.findings);
22
+ if (loadCounts.errors > 0) {
23
+ return invalidResult(loaded.files, loaded.findings);
24
+ }
25
+ const resolutionService = new ResolutionService(this.org);
26
+ const resolution = await resolutionService.run(loaded.assignments);
27
+ const findings = [
28
+ ...loaded.findings,
29
+ ...resolution.findings,
30
+ ];
31
+ const findingCounts = countFindings(findings);
32
+ if (findingCounts.errors > 0) {
33
+ return invalidResult(loaded.files, findings);
34
+ }
35
+ const actual = await this.org.currentAssignments(managedTargets(resolution));
36
+ const diff = diffAssignments(loaded.assignments, actual);
37
+ const drift = {
38
+ adds: mode === 'destructive' ? diff.toAdd.length : 0,
39
+ updates: mode === 'destructive' ? diff.toUpdate.length : 0,
40
+ removes: mode === 'additive' ? diff.toRemove.length : 0,
41
+ };
42
+ return { files: loaded.files, findings, diff, drift, status: 'planned' };
43
+ }
44
+ }
45
+ //# 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,EAAiB,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE5F,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAgBpE,MAAM,SAAS,GAAS,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AAEjF,iFAAiF;AACjF,SAAS,aAAa,CAAC,KAAe,EAAE,QAAmB;IACvD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/G,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,MAAM,EAAE,SAAS,EAAE,CAAC;IAC7E,CAAC;CACJ"}
@@ -0,0 +1,15 @@
1
+ import { DesiredAssignment, Kind, 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
+ /** Look every declared reference up in the org, returning findings and the resolved id maps. */
10
+ export declare class ResolutionService {
11
+ private readonly org;
12
+ constructor(org: OrgClient);
13
+ run(assignments: DesiredAssignment[]): Promise<Resolution>;
14
+ }
15
+ //# 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,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,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;CAqC1E"}
@@ -0,0 +1,37 @@
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
+ /** Look every declared reference up in the org, returning findings and the resolved id maps. */
12
+ export class ResolutionService {
13
+ org;
14
+ constructor(org) {
15
+ this.org = org;
16
+ }
17
+ async run(assignments) {
18
+ const usernames = distinctAssignees(assignments);
19
+ const targetsByKind = kinds.map((kind) => ({ kind, targets: distinctTargets(assignments, kind) }));
20
+ const usersTask = usernames.length > 0 ? this.org.findUsers(usernames) : Promise.resolve([]);
21
+ const targetsTask = Promise.all(targetsByKind.map(({ kind, targets }) => (targets.length > 0 ? this.org.findTargets(kind, targets) : Promise.resolve([])).then((found) => ({ kind, targets, found }))));
22
+ const [foundUsers, perKind] = await Promise.all([
23
+ usersTask,
24
+ targetsTask,
25
+ ]);
26
+ const findings = [...evaluateUsers(usernames, foundUsers)];
27
+ for (const { kind, targets, found } of perKind) {
28
+ findings.push(...evaluateTargets(kind, targets, found.map((target) => target.name)));
29
+ }
30
+ const targetIds = {};
31
+ for (const { kind, found } of perKind) {
32
+ targetIds[kind] = indexTargetsById(found);
33
+ }
34
+ return { findings, userIds: indexUsersById(foundUsers), targetIds };
35
+ }
36
+ }
37
+ //# sourceMappingURL=resolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolution.js","sourceRoot":"","sources":["../../src/services/resolution.ts"],"names":[],"mappings":"AAAA,OAAO,EAOH,KAAK,EACL,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,eAAe,EACf,cAAc,EACd,gBAAgB,GACnB,MAAM,kBAAkB,CAAC;AAS1B,MAAM,UAAU,cAAc,CAAC,UAAsB;IACjD,MAAM,IAAI,GAAgB,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,KAAK,MAAM,EAAE,IAAI,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;YACnD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,gGAAgG;AAChG,MAAM,OAAO,iBAAiB;IACU;IAApC,YAAoC,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAE/C,KAAK,CAAC,GAAG,CAAC,WAAgC;QAC7C,MAAM,SAAS,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;QACjD,MAAM,aAAa,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAEnG,MAAM,SAAS,GACX,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/E,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAC3B,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CACpC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAc,EAAE,CAAC,CAAC,CAAC,IAAI,CAC9F,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CACxC,CACJ,CACJ,CAAC;QAEF,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC5C,SAAS;YACT,WAAW;SACd,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAc,CAAC,GAAG,aAAa,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;QACtE,KAAK,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;YAC7C,QAAQ,CAAC,IAAI,CACT,GAAG,eAAe,CACd,IAAI,EACJ,OAAO,EACP,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CACrC,CACJ,CAAC;QACN,CAAC;QAED,MAAM,SAAS,GAAG,EAAuC,CAAC;QAC1D,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;YACpC,SAAS,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACxE,CAAC;CACJ"}
@@ -0,0 +1,19 @@
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
+ /** Online validate: run the offline load, 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
+ }
19
+ //# 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,EAEV,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,2FAA2F;AAC3F,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;CA0BxB"}
@@ -0,0 +1,42 @@
1
+ import { loadFiles, kinds, distinctAssignees, distinctTargets, evaluateUsers, evaluateTargets, countFindings, } from '../core/index.js';
2
+ /** Online validate: run the offline load, 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 online = await this.resolve(loaded.assignments);
11
+ const findings = [
12
+ ...loaded.findings,
13
+ ...online,
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.org.findUsers(usernames).then((found) => evaluateUsers(usernames, found)));
31
+ }
32
+ for (const kind of kinds) {
33
+ const targets = distinctTargets(assignments, kind);
34
+ if (targets.length > 0) {
35
+ tasks.push(this.org.findTargets(kind, targets).then((found) => evaluateTargets(kind, targets, found.map((target) => target.name))));
36
+ }
37
+ }
38
+ const results = await Promise.all(tasks);
39
+ return results.flat();
40
+ }
41
+ }
42
+ //# 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,EAGf,aAAa,GAChB,MAAM,kBAAkB,CAAC;AAY1B,2FAA2F;AAC3F,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,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAEtD,MAAM,QAAQ,GAAG;YACb,GAAG,MAAM,CAAC,QAAQ;YAClB,GAAG,MAAM;SACZ,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,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/F,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,CACN,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAC/C,eAAe,CACX,IAAI,EACJ,OAAO,EACP,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CACrC,CACJ,CACJ,CAAC;YACN,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzC,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;CACJ"}
@@ -0,0 +1,85 @@
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.
12
+
13
+ # flags.mode.summary
14
+
15
+ Which half of the reconcile to run: additive adds missing assignments and updates expirations, destructive removes only, sync does both.
16
+
17
+ # flags.max-deletes.summary
18
+
19
+ Abort before any change if the run would remove more than this many assignments.
20
+
21
+ # flags.dry-run.summary
22
+
23
+ Resolve and diff, print the plan, and change nothing.
24
+
25
+ # flags.show-unchanged.summary
26
+
27
+ List assignments that already match, instead of only counting them.
28
+
29
+ # flags.no-prompt.summary
30
+
31
+ Skip the deletion confirmation prompt. Required to delete in JSON or other non-interactive runs.
32
+
33
+ # confirm.delete
34
+
35
+ This will remove %s assignment(s) from the org. Continue?
36
+
37
+ # summary.dryRun
38
+
39
+ Dry run: %s to add, %s to update, %s to remove. Nothing was changed.
40
+
41
+ # summary.applied
42
+
43
+ Applied: %s added, %s updated, %s removed.
44
+
45
+ # summary.declined
46
+
47
+ Aborted at the confirmation prompt. Nothing was changed.
48
+
49
+ # drift.note
50
+
51
+ %s change(s) the %s mode does not act on were skipped (drift). Run plan to see them.
52
+
53
+ # failure.line
54
+
55
+ failed to %s %s on %s: %s
56
+
57
+ # error.invalid
58
+
59
+ The files do not resolve cleanly against the org. Fix the errors above, then re-run.
60
+
61
+ # error.maxDeletes
62
+
63
+ Refusing to remove %s assignment(s): over the --max-deletes limit of %s. Raise the limit or narrow the change.
64
+
65
+ # error.promptInJson
66
+
67
+ Refusing to delete without confirmation in a non-interactive run. Re-run with --no-prompt.
68
+
69
+ # error.failed
70
+
71
+ Some changes failed. See the per-record errors above.
72
+
73
+ # examples
74
+
75
+ - Preview a full reconcile of the dev org without changing anything:
76
+
77
+ <%= config.bin %> <%= command.id %> --file "permissions/*.yml" --target-org dev --mode sync --dry-run
78
+
79
+ - Grant any missing assignments (additive, the default):
80
+
81
+ <%= config.bin %> <%= command.id %> --file "permissions/*.yml" --target-org dev
82
+
83
+ - Full reconcile of production in CI, without prompts:
84
+
85
+ <%= config.bin %> <%= command.id %> --file "permissions/*.yml" --target-org prod --mode sync --no-prompt