sf-plugin-permission-sets 0.0.0-dev.11 → 0.0.0-dev.13
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.
- package/README.md +28 -3
- package/lib/commands/ps/check.d.ts +18 -0
- package/lib/commands/ps/check.js +45 -0
- package/lib/commands/ps/check.js.map +1 -0
- package/lib/commands/ps/info.js.map +1 -1
- package/lib/core/load.d.ts +8 -0
- package/lib/core/load.js +52 -0
- package/lib/core/load.js.map +1 -0
- package/lib/core/model.d.ts +19 -0
- package/lib/core/model.js +2 -0
- package/lib/core/model.js.map +1 -0
- package/lib/core/normalize.d.ts +10 -0
- package/lib/core/normalize.js +45 -0
- package/lib/core/normalize.js.map +1 -0
- package/lib/core/parse.d.ts +9 -0
- package/lib/core/parse.js +25 -0
- package/lib/core/parse.js.map +1 -0
- package/lib/core/report.d.ts +8 -0
- package/lib/core/report.js +15 -0
- package/lib/core/report.js.map +1 -0
- package/lib/core/schema.d.ts +20 -0
- package/lib/core/schema.js +26 -0
- package/lib/core/schema.js.map +1 -0
- package/lib/services/check.d.ts +15 -0
- package/lib/services/check.js +17 -0
- package/lib/services/check.js.map +1 -0
- package/messages/ps.check.md +33 -0
- package/oclif.lock +570 -1
- package/oclif.manifest.json +63 -1
- package/package.json +12 -24
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@ Stop clicking through Setup to grant access. Commit a YAML file, open a PR, let
|
|
|
21
21
|
- [Commands](#commands)
|
|
22
22
|
- [Inspiration & equivalents](#inspiration--equivalents)
|
|
23
23
|
- [Versioning](#versioning)
|
|
24
|
+
- [Architecture](#architecture)
|
|
24
25
|
|
|
25
26
|
---
|
|
26
27
|
|
|
@@ -196,11 +197,12 @@ Fully offline: runs in any CI job or pre-commit hook without org credentials.
|
|
|
196
197
|
|
|
197
198
|
```
|
|
198
199
|
USAGE
|
|
199
|
-
$ sf ps check -f <glob>... [--strict] [--json]
|
|
200
|
+
$ sf ps check -f <glob>... [--strict] [--watch] [--json]
|
|
200
201
|
|
|
201
202
|
FLAGS
|
|
202
203
|
-f, --file=<glob>... (required) YAML file(s) to read. Repeatable, globs are expanded by the plugin.
|
|
203
204
|
--strict Treat warnings as errors.
|
|
205
|
+
-w, --watch Re-run on every change to a matched file. Stays in the foreground until you stop it (Ctrl-C).
|
|
204
206
|
|
|
205
207
|
CHECKS
|
|
206
208
|
- valid YAML & schema (unknown keys rejected)
|
|
@@ -210,15 +212,18 @@ CHECKS
|
|
|
210
212
|
- internal referential integrity
|
|
211
213
|
```
|
|
212
214
|
|
|
215
|
+
`--watch` is for the local edit loop: leave it running while you tweak the YAML and the findings refresh on every save. It re-expands the globs each run, so files you add or delete are picked up too. It's a foreground, interactive mode, so it can't be combined with `--json` and isn't meant for CI, where a single gated `check` run is what you want.
|
|
216
|
+
|
|
213
217
|
### `sf ps validate`
|
|
214
218
|
|
|
215
219
|
```
|
|
216
220
|
USAGE
|
|
217
|
-
$ sf ps validate -o <org> -f <glob>... [--json]
|
|
221
|
+
$ sf ps validate -o <org> -f <glob>... [--watch] [--json]
|
|
218
222
|
|
|
219
223
|
FLAGS
|
|
220
224
|
-o, --target-org=<org> (required) Org to resolve against.
|
|
221
225
|
-f, --file=<glob>... (required) YAML file(s) to read. Repeatable, globs expanded by the plugin.
|
|
226
|
+
-w, --watch Re-run on every change to a matched file. Read-only, but each run queries the org, so point it at a scratch or dev org.
|
|
222
227
|
|
|
223
228
|
Runs all offline checks, then verifies that every user (active), permission set,
|
|
224
229
|
group, and license referenced actually exists and resolves uniquely.
|
|
@@ -228,13 +233,14 @@ group, and license referenced actually exists and resolves uniquely.
|
|
|
228
233
|
|
|
229
234
|
```
|
|
230
235
|
USAGE
|
|
231
|
-
$ sf ps plan -o <org> -f <glob>... [--mode <value>] [--fail-on-drift] [--json]
|
|
236
|
+
$ sf ps plan -o <org> -f <glob>... [--mode <value>] [--fail-on-drift] [--watch] [--json]
|
|
232
237
|
|
|
233
238
|
FLAGS
|
|
234
239
|
-o, --target-org=<org> (required)
|
|
235
240
|
-f, --file=<glob>... (required) YAML file(s) to read. Repeatable, globs expanded by the plugin.
|
|
236
241
|
--mode=<value> additive | destructive | sync [default: additive]
|
|
237
242
|
--fail-on-drift Exit non-zero if any change is pending (for CI gates).
|
|
243
|
+
-w, --watch Re-diff against the org on every change to a matched file. Read-only (never applies), but each run queries the org.
|
|
238
244
|
```
|
|
239
245
|
|
|
240
246
|
Example output:
|
|
@@ -335,6 +341,25 @@ gh release create v0.2.0 --target main --title v0.2.0 --notes "Add ps export"
|
|
|
335
341
|
|
|
336
342
|
The `next` tag is selected whenever the version contains a hyphen, not by GitHub's prerelease checkbox.
|
|
337
343
|
|
|
344
|
+
## Architecture
|
|
345
|
+
|
|
346
|
+
The plugin is layered so every command reuses the same core. Commands stay thin, services hold the orchestration, and core holds the reusable primitives.
|
|
347
|
+
|
|
348
|
+
- **Commands** (`src/commands/ps/`): oclif only. Parse flags, call a service, render output, set the exit code.
|
|
349
|
+
- **Services** (`src/services/`): one per command (`check` today, then `validate`, `plan`, `apply`, `export`). Each turns the core into a command's behavior.
|
|
350
|
+
- **Core** (`src/core/`): the reusable building blocks.
|
|
351
|
+
|
|
352
|
+
| Core module | Responsibility |
|
|
353
|
+
| --- | --- |
|
|
354
|
+
| `model` | Shared domain types (assignment, finding, result). |
|
|
355
|
+
| `schema` | The zod contract for a file, plus validation. |
|
|
356
|
+
| `parse` | File text to an object, with YAML and duplicate-key errors. |
|
|
357
|
+
| `normalize` | A validated file to canonical `(assignee, kind, target)` tuples, plus structural findings. |
|
|
358
|
+
| `load` | Expand globs, run parse then validate then normalize per file, and merge by union. |
|
|
359
|
+
| `report` | Format and count findings. |
|
|
360
|
+
|
|
361
|
+
Commands are slices of one pipeline. `check` runs the offline **load** stage only. The online commands add **resolve** (names to ids), **fetch** (current org state), **diff** (desired vs actual), and **apply** (DML) on top, reusing load, report, model, and schema unchanged.
|
|
362
|
+
|
|
338
363
|
## License
|
|
339
364
|
|
|
340
365
|
BSD-3-Clause © Isaac Ferreira
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SfCommand } from '@salesforce/sf-plugins-core';
|
|
2
|
+
import { Finding } from '../../core/model.js';
|
|
3
|
+
export type PsCheckResult = {
|
|
4
|
+
files: number;
|
|
5
|
+
users: number;
|
|
6
|
+
assignments: number;
|
|
7
|
+
findings: Finding[];
|
|
8
|
+
};
|
|
9
|
+
export default class Check extends SfCommand<PsCheckResult> {
|
|
10
|
+
static readonly summary: string;
|
|
11
|
+
static readonly description: string;
|
|
12
|
+
static readonly examples: string[];
|
|
13
|
+
static readonly flags: {
|
|
14
|
+
file: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
strict: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<PsCheckResult>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
|
|
2
|
+
import { Messages } from '@salesforce/core';
|
|
3
|
+
import { check } from '../../services/check.js';
|
|
4
|
+
import { formatFindings } from '../../core/report.js';
|
|
5
|
+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
6
|
+
const messages = Messages.loadMessages('sf-plugin-permission-sets', 'ps.check');
|
|
7
|
+
export default class Check extends SfCommand {
|
|
8
|
+
static summary = messages.getMessage('summary');
|
|
9
|
+
static description = messages.getMessage('description');
|
|
10
|
+
static examples = messages.getMessages('examples');
|
|
11
|
+
static flags = {
|
|
12
|
+
file: Flags.string({
|
|
13
|
+
char: 'f',
|
|
14
|
+
summary: messages.getMessage('flags.file.summary'),
|
|
15
|
+
required: true,
|
|
16
|
+
multiple: true,
|
|
17
|
+
}),
|
|
18
|
+
strict: Flags.boolean({
|
|
19
|
+
summary: messages.getMessage('flags.strict.summary'),
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
async run() {
|
|
23
|
+
const { flags } = await this.parse(Check);
|
|
24
|
+
const result = await check({ files: flags.file, strict: flags.strict });
|
|
25
|
+
for (const line of formatFindings(result.findings)) {
|
|
26
|
+
this.log(line);
|
|
27
|
+
}
|
|
28
|
+
const assignees = new Set(result.assignments.map((a) => a.assignee));
|
|
29
|
+
this.log('');
|
|
30
|
+
this.log(messages.getMessage('summary.counts', [String(result.errors), String(result.warnings)]));
|
|
31
|
+
if (result.failed) {
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
if (!this.jsonEnabled()) {
|
|
34
|
+
this.error(messages.getMessage('error.failed'), { exit: 1 });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
files: result.files.length,
|
|
39
|
+
users: assignees.size,
|
|
40
|
+
assignments: result.assignments.length,
|
|
41
|
+
findings: result.findings,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/commands/ps/check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,2BAA2B,EAAE,UAAU,CAAC,CAAC;AAShF,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,SAAwB;IAChD,MAAM,CAAU,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,CAAU,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACjE,MAAM,CAAU,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAE5D,MAAM,CAAU,KAAK,GAAG;QAC3B,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC;YACf,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,oBAAoB,CAAC;YAClD,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,IAAI;SACjB,CAAC;QACF,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC;YAClB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,sBAAsB,CAAC;SACvD,CAAC;KACL,CAAC;IAEK,KAAK,CAAC,GAAG;QACZ,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAExE,KAAK,MAAM,IAAI,IAAI,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QACrE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,gBAAgB,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAElG,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACtB,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;YACjE,CAAC;QACL,CAAC;QAED,OAAO;YACH,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM;YAC1B,KAAK,EAAE,SAAS,CAAC,IAAI;YACrB,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM;YACtC,QAAQ,EAAE,MAAM,CAAC,QAAQ;SAC5B,CAAC;IACN,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"info.js","sourceRoot":"","sources":["../../../src/commands/ps/info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,2BAA2B,EAAE,SAAS,CAAC,CAAC;AAO/E,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,SAAuB;
|
|
1
|
+
{"version":3,"file":"info.js","sourceRoot":"","sources":["../../../src/commands/ps/info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,2BAA2B,EAAE,SAAS,CAAC,CAAC;AAO/E,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,SAAuB;IAC9C,MAAM,CAAU,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,CAAU,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACjE,MAAM,CAAU,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAE5D,KAAK,CAAC,GAAG;QACZ,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvB,MAAM,IAAI,GAAG,2BAA2B,CAAC;QACzC,MAAM,WAAW,GAAG,qEAAqE,CAAC;QAC1F,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;QACnE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IACjC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { DesiredAssignment, Finding, LoadResult } from './model.js';
|
|
2
|
+
/** Process one file's text through parse, validate, and normalize. Pure, no disk. */
|
|
3
|
+
export declare function checkContent(text: string, file: string): {
|
|
4
|
+
assignments: DesiredAssignment[];
|
|
5
|
+
findings: Finding[];
|
|
6
|
+
};
|
|
7
|
+
/** Expand the globs, read every matched file, and merge into one model by union. */
|
|
8
|
+
export declare function loadFiles(patterns: string[]): Promise<LoadResult>;
|
package/lib/core/load.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { globby } from 'globby';
|
|
3
|
+
import { parseFile } from './parse.js';
|
|
4
|
+
import { validateFile } from './schema.js';
|
|
5
|
+
import { normalize } from './normalize.js';
|
|
6
|
+
/** Process one file's text through parse, validate, and normalize. Pure, no disk. */
|
|
7
|
+
export function checkContent(text, file) {
|
|
8
|
+
const parsed = parseFile(text, file);
|
|
9
|
+
if (!parsed.data) {
|
|
10
|
+
return { assignments: [], findings: parsed.findings };
|
|
11
|
+
}
|
|
12
|
+
const validated = validateFile(parsed.data, file);
|
|
13
|
+
if (!validated.data) {
|
|
14
|
+
return { assignments: [], findings: [...parsed.findings, ...validated.findings] };
|
|
15
|
+
}
|
|
16
|
+
const normalized = normalize(validated.data, file);
|
|
17
|
+
return {
|
|
18
|
+
assignments: normalized.assignments,
|
|
19
|
+
findings: [...parsed.findings, ...validated.findings, ...normalized.findings],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Expand the globs, read every matched file, and merge into one model by union. */
|
|
23
|
+
export async function loadFiles(patterns) {
|
|
24
|
+
const files = await globby(patterns);
|
|
25
|
+
if (files.length === 0) {
|
|
26
|
+
return {
|
|
27
|
+
files,
|
|
28
|
+
assignments: [],
|
|
29
|
+
findings: [{ level: 'error', code: 'NO_FILES', message: `no files matched: ${patterns.join(', ')}` }],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const findings = [];
|
|
33
|
+
const collected = [];
|
|
34
|
+
for (const file of files) {
|
|
35
|
+
// eslint-disable-next-line no-await-in-loop
|
|
36
|
+
const text = await readFile(file, 'utf8');
|
|
37
|
+
const res = checkContent(text, file);
|
|
38
|
+
findings.push(...res.findings);
|
|
39
|
+
collected.push(...res.assignments);
|
|
40
|
+
}
|
|
41
|
+
const seen = new Set();
|
|
42
|
+
const assignments = [];
|
|
43
|
+
for (const a of collected) {
|
|
44
|
+
const dedupeKey = `${a.assignee} ${a.kind} ${a.target}`;
|
|
45
|
+
if (seen.has(dedupeKey))
|
|
46
|
+
continue;
|
|
47
|
+
seen.add(dedupeKey);
|
|
48
|
+
assignments.push(a);
|
|
49
|
+
}
|
|
50
|
+
return { files, assignments, findings };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=load.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/core/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,IAAY;IACnD,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACf,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC1D,CAAC;IAED,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;IACtF,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACnD,OAAO;QACH,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,SAAS,CAAC,QAAQ,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC;KAChF,CAAC;AACN,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAkB;IAC9C,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO;YACH,KAAK;YACL,WAAW,EAAE,EAAE;YACf,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,qBAAqB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;SACxG,CAAC;IACN,CAAC;IAED,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAwB,EAAE,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,4CAA4C;QAC5C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACrC,QAAQ,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC/B,SAAS,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,WAAW,GAAwB,EAAE,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QACxB,MAAM,SAAS,GAAG,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QACxD,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,SAAS;QAClC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACpB,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type Kind = 'permissionSet' | 'permissionSetGroup' | 'permissionSetLicense';
|
|
2
|
+
export type DesiredAssignment = {
|
|
3
|
+
assignee: string;
|
|
4
|
+
kind: Kind;
|
|
5
|
+
target: string;
|
|
6
|
+
};
|
|
7
|
+
export type FindingLevel = 'error' | 'warning';
|
|
8
|
+
export type Finding = {
|
|
9
|
+
level: FindingLevel;
|
|
10
|
+
code: string;
|
|
11
|
+
message: string;
|
|
12
|
+
file?: string;
|
|
13
|
+
line?: number;
|
|
14
|
+
};
|
|
15
|
+
export type LoadResult = {
|
|
16
|
+
files: string[];
|
|
17
|
+
assignments: DesiredAssignment[];
|
|
18
|
+
findings: Finding[];
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/core/model.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FileShape } from './schema.js';
|
|
2
|
+
import { DesiredAssignment, Finding } from './model.js';
|
|
3
|
+
/**
|
|
4
|
+
* Turn a validated file into canonical (assignee, kind, target) tuples, and
|
|
5
|
+
* emit the structural findings: duplicate targets, empty lists, empty users.
|
|
6
|
+
*/
|
|
7
|
+
export declare function normalize(data: FileShape, file: string): {
|
|
8
|
+
assignments: DesiredAssignment[];
|
|
9
|
+
findings: Finding[];
|
|
10
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const KIND_KEYS = [
|
|
2
|
+
['permissionSet', 'permissionSets'],
|
|
3
|
+
['permissionSetGroup', 'permissionSetGroups'],
|
|
4
|
+
['permissionSetLicense', 'permissionSetLicenses'],
|
|
5
|
+
];
|
|
6
|
+
/**
|
|
7
|
+
* Turn a validated file into canonical (assignee, kind, target) tuples, and
|
|
8
|
+
* emit the structural findings: duplicate targets, empty lists, empty users.
|
|
9
|
+
*/
|
|
10
|
+
export function normalize(data, file) {
|
|
11
|
+
const assignments = [];
|
|
12
|
+
const findings = [];
|
|
13
|
+
for (const [username, entry] of Object.entries(data.users)) {
|
|
14
|
+
let scopeCount = 0;
|
|
15
|
+
for (const [kind, key] of KIND_KEYS) {
|
|
16
|
+
const list = entry[key];
|
|
17
|
+
if (list === undefined)
|
|
18
|
+
continue;
|
|
19
|
+
if (list.length === 0) {
|
|
20
|
+
findings.push({ level: 'warning', code: 'EMPTY_LIST', message: `${username}: ${key} is empty`, file });
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
scopeCount += 1;
|
|
24
|
+
const seen = new Set();
|
|
25
|
+
for (const target of list) {
|
|
26
|
+
if (seen.has(target)) {
|
|
27
|
+
findings.push({
|
|
28
|
+
level: 'warning',
|
|
29
|
+
code: 'DUP_TARGET',
|
|
30
|
+
message: `${username}: ${target} is listed twice under ${key}`,
|
|
31
|
+
file,
|
|
32
|
+
});
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
seen.add(target);
|
|
36
|
+
assignments.push({ assignee: username, kind, target });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (scopeCount === 0) {
|
|
40
|
+
findings.push({ level: 'warning', code: 'EMPTY_USER', message: `${username}: no scopes declared`, file });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { assignments, findings };
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/core/normalize.ts"],"names":[],"mappings":"AAKA,MAAM,SAAS,GAA4B;IACvC,CAAC,eAAe,EAAE,gBAAgB,CAAC;IACnC,CAAC,oBAAoB,EAAE,qBAAqB,CAAC;IAC7C,CAAC,sBAAsB,EAAE,uBAAuB,CAAC;CACpD,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAe,EAAE,IAAY;IACnD,MAAM,WAAW,GAAwB,EAAE,CAAC;IAC5C,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,SAAS,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,IAAI,KAAK,SAAS;gBAAE,SAAS;YACjC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,QAAQ,KAAK,GAAG,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;gBACvG,SAAS;YACb,CAAC;YAED,UAAU,IAAI,CAAC,CAAC;YAChB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/B,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,CAAC;gBACxB,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;oBACnB,QAAQ,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,SAAS;wBAChB,IAAI,EAAE,YAAY;wBAClB,OAAO,EAAE,GAAG,QAAQ,KAAK,MAAM,0BAA0B,GAAG,EAAE;wBAC9D,IAAI;qBACP,CAAC,CAAC;oBACH,SAAS;gBACb,CAAC;gBACD,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACjB,WAAW,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YAC3D,CAAC;QACL,CAAC;QAED,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,QAAQ,sBAAsB,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9G,CAAC;IACL,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Finding } from './model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Read one file's text into a plain object. Reports invalid YAML and duplicate
|
|
4
|
+
* keys (uniqueKeys), and treats an empty document as a warning.
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseFile(text: string, file: string): {
|
|
7
|
+
data?: unknown;
|
|
8
|
+
findings: Finding[];
|
|
9
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { parseDocument } from 'yaml';
|
|
2
|
+
/**
|
|
3
|
+
* Read one file's text into a plain object. Reports invalid YAML and duplicate
|
|
4
|
+
* keys (uniqueKeys), and treats an empty document as a warning.
|
|
5
|
+
*/
|
|
6
|
+
export function parseFile(text, file) {
|
|
7
|
+
const doc = parseDocument(text, { uniqueKeys: true });
|
|
8
|
+
if (doc.errors.length > 0) {
|
|
9
|
+
return {
|
|
10
|
+
findings: doc.errors.map((err) => ({
|
|
11
|
+
level: 'error',
|
|
12
|
+
code: 'YAML',
|
|
13
|
+
message: err.message,
|
|
14
|
+
file,
|
|
15
|
+
line: err.linePos?.[0]?.line,
|
|
16
|
+
})),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const data = doc.toJS();
|
|
20
|
+
if (data === null || data === undefined) {
|
|
21
|
+
return { findings: [{ level: 'warning', code: 'EMPTY_FILE', message: 'file is empty', file }] };
|
|
22
|
+
}
|
|
23
|
+
return { data, findings: [] };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/core/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAGrC;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAY;IAChD,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IAEtD,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACH,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBAC/B,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,IAAI;gBACJ,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI;aAC/B,CAAC,CAAC;SACN,CAAC;IACN,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAa,CAAC;IACnC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACpG,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Finding } from './model.js';
|
|
2
|
+
/** Render findings as human-readable lines. Shared by check, validate, and plan. */
|
|
3
|
+
export declare function formatFindings(findings: Finding[]): string[];
|
|
4
|
+
/** Count findings by level. */
|
|
5
|
+
export declare function countFindings(findings: Finding[]): {
|
|
6
|
+
errors: number;
|
|
7
|
+
warnings: number;
|
|
8
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Render findings as human-readable lines. Shared by check, validate, and plan. */
|
|
2
|
+
export function formatFindings(findings) {
|
|
3
|
+
return findings.map((finding) => {
|
|
4
|
+
const where = finding.file ? `${finding.file}${finding.line ? `:${finding.line}` : ''} ` : '';
|
|
5
|
+
return `${finding.level}: ${where}${finding.message}`;
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
/** Count findings by level. */
|
|
9
|
+
export function countFindings(findings) {
|
|
10
|
+
return {
|
|
11
|
+
errors: findings.filter((f) => f.level === 'error').length,
|
|
12
|
+
warnings: findings.filter((f) => f.level === 'warning').length,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/core/report.ts"],"names":[],"mappings":"AAEA,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,QAAmB;IAC9C,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9F,OAAO,GAAG,OAAO,CAAC,KAAK,KAAK,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC,CAAC,CAAC;AACP,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,aAAa,CAAC,QAAmB;IAC7C,OAAO;QACH,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,MAAM;QAC1D,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,MAAM;KACjE,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Finding } from './model.js';
|
|
3
|
+
export declare const userEntrySchema: z.ZodObject<{
|
|
4
|
+
permissionSets: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
5
|
+
permissionSetGroups: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
6
|
+
permissionSetLicenses: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7
|
+
}, z.core.$strict>;
|
|
8
|
+
export declare const fileSchema: z.ZodObject<{
|
|
9
|
+
users: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
10
|
+
permissionSets: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
11
|
+
permissionSetGroups: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
12
|
+
permissionSetLicenses: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13
|
+
}, z.core.$strict>>;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
export type FileShape = z.infer<typeof fileSchema>;
|
|
16
|
+
/** Validate a parsed object against the file contract, turning issues into findings. */
|
|
17
|
+
export declare function validateFile(data: unknown, file: string): {
|
|
18
|
+
data?: FileShape;
|
|
19
|
+
findings: Finding[];
|
|
20
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const scopeList = z.array(z.string().min(1)).optional();
|
|
3
|
+
export const userEntrySchema = z.strictObject({
|
|
4
|
+
permissionSets: scopeList,
|
|
5
|
+
permissionSetGroups: scopeList,
|
|
6
|
+
permissionSetLicenses: scopeList,
|
|
7
|
+
});
|
|
8
|
+
export const fileSchema = z.strictObject({
|
|
9
|
+
users: z.record(z.string().min(1), userEntrySchema),
|
|
10
|
+
});
|
|
11
|
+
/** Validate a parsed object against the file contract, turning issues into findings. */
|
|
12
|
+
export function validateFile(data, file) {
|
|
13
|
+
const parsed = fileSchema.safeParse(data);
|
|
14
|
+
if (parsed.success) {
|
|
15
|
+
return { data: parsed.data, findings: [] };
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
findings: parsed.error.issues.map((issue) => ({
|
|
19
|
+
level: 'error',
|
|
20
|
+
code: 'SCHEMA',
|
|
21
|
+
message: `${issue.path.join('.') || '(root)'}: ${issue.message}`,
|
|
22
|
+
file,
|
|
23
|
+
})),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AAExD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,YAAY,CAAC;IAC1C,cAAc,EAAE,SAAS;IACzB,mBAAmB,EAAE,SAAS;IAC9B,qBAAqB,EAAE,SAAS;CACnC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,YAAY,CAAC;IACrC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC;CACtD,CAAC,CAAC;AAIH,wFAAwF;AACxF,MAAM,UAAU,YAAY,CAAC,IAAa,EAAE,IAAY;IACpD,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACjB,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC/C,CAAC;IACD,OAAO;QACH,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC1C,KAAK,EAAE,OAAO;YACd,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE;YAChE,IAAI;SACP,CAAC,CAAC;KACN,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DesiredAssignment, Finding } from '../core/model.js';
|
|
2
|
+
export type CheckOptions = {
|
|
3
|
+
files: string[];
|
|
4
|
+
strict: boolean;
|
|
5
|
+
};
|
|
6
|
+
export type CheckResult = {
|
|
7
|
+
files: string[];
|
|
8
|
+
assignments: DesiredAssignment[];
|
|
9
|
+
findings: Finding[];
|
|
10
|
+
errors: number;
|
|
11
|
+
warnings: number;
|
|
12
|
+
failed: boolean;
|
|
13
|
+
};
|
|
14
|
+
/** Offline check: load the files, validate them, and summarize the findings. */
|
|
15
|
+
export declare function check(options: CheckOptions): Promise<CheckResult>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { loadFiles } from '../core/load.js';
|
|
2
|
+
import { countFindings } from '../core/report.js';
|
|
3
|
+
/** Offline check: load the files, validate them, and summarize the findings. */
|
|
4
|
+
export async function check(options) {
|
|
5
|
+
const loaded = await loadFiles(options.files);
|
|
6
|
+
const { errors, warnings } = countFindings(loaded.findings);
|
|
7
|
+
const failed = errors > 0 || (options.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
|
+
//# 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,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAiBlD,gFAAgF;AAChF,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,OAAqB;IAC7C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC;IAE9D,OAAO;QACH,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM;QACN,QAAQ;QACR,MAAM;KACT,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# summary
|
|
2
|
+
|
|
3
|
+
Statically check permission set assignment files, with no org connection.
|
|
4
|
+
|
|
5
|
+
# description
|
|
6
|
+
|
|
7
|
+
Parse and validate the YAML files offline. Reports invalid YAML, schema violations, duplicate keys, duplicate targets, and empty entries. Run it in a pre-commit hook or in CI before validate, plan, or apply.
|
|
8
|
+
|
|
9
|
+
# flags.file.summary
|
|
10
|
+
|
|
11
|
+
YAML file or glob to check. Repeatable.
|
|
12
|
+
|
|
13
|
+
# flags.strict.summary
|
|
14
|
+
|
|
15
|
+
Treat warnings as errors.
|
|
16
|
+
|
|
17
|
+
# summary.counts
|
|
18
|
+
|
|
19
|
+
%s errors, %s warnings.
|
|
20
|
+
|
|
21
|
+
# error.failed
|
|
22
|
+
|
|
23
|
+
Check found problems. See the output above.
|
|
24
|
+
|
|
25
|
+
# examples
|
|
26
|
+
|
|
27
|
+
- Check every file under permissions:
|
|
28
|
+
|
|
29
|
+
<%= config.bin %> <%= command.id %> --file "permissions/*.yml"
|
|
30
|
+
|
|
31
|
+
- Check specific files:
|
|
32
|
+
|
|
33
|
+
<%= config.bin %> <%= command.id %> --file permissions/sales.yml --file permissions/service.yml
|