@syntax-syllogism/warden-core 0.2.0
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/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/lib/access/assignees.d.ts +27 -0
- package/lib/access/assignees.js +139 -0
- package/lib/access/assignees.js.map +1 -0
- package/lib/access/messages.d.ts +1 -0
- package/lib/access/messages.js +24 -0
- package/lib/access/messages.js.map +1 -0
- package/lib/access/metadata.d.ts +41 -0
- package/lib/access/metadata.js +168 -0
- package/lib/access/metadata.js.map +1 -0
- package/lib/access/muting.d.ts +7 -0
- package/lib/access/muting.js +22 -0
- package/lib/access/muting.js.map +1 -0
- package/lib/access/output.d.ts +12 -0
- package/lib/access/output.js +108 -0
- package/lib/access/output.js.map +1 -0
- package/lib/access/resolvers/field.d.ts +2 -0
- package/lib/access/resolvers/field.js +93 -0
- package/lib/access/resolvers/field.js.map +1 -0
- package/lib/access/resolvers/index.d.ts +2 -0
- package/lib/access/resolvers/index.js +22 -0
- package/lib/access/resolvers/index.js.map +1 -0
- package/lib/access/resolvers/object.d.ts +2 -0
- package/lib/access/resolvers/object.js +101 -0
- package/lib/access/resolvers/object.js.map +1 -0
- package/lib/access/resolvers/recordType.d.ts +2 -0
- package/lib/access/resolvers/recordType.js +181 -0
- package/lib/access/resolvers/recordType.js.map +1 -0
- package/lib/access/resolvers/setupEntity.d.ts +4 -0
- package/lib/access/resolvers/setupEntity.js +43 -0
- package/lib/access/resolvers/setupEntity.js.map +1 -0
- package/lib/access/resolvers/tab.d.ts +2 -0
- package/lib/access/resolvers/tab.js +36 -0
- package/lib/access/resolvers/tab.js.map +1 -0
- package/lib/access/reverse.d.ts +9 -0
- package/lib/access/reverse.js +429 -0
- package/lib/access/reverse.js.map +1 -0
- package/lib/access/soql.d.ts +6 -0
- package/lib/access/soql.js +31 -0
- package/lib/access/soql.js.map +1 -0
- package/lib/access/targetValidation.d.ts +7 -0
- package/lib/access/targetValidation.js +110 -0
- package/lib/access/targetValidation.js.map +1 -0
- package/lib/access/types.d.ts +88 -0
- package/lib/access/types.js +20 -0
- package/lib/access/types.js.map +1 -0
- package/lib/errors.d.ts +18 -0
- package/lib/errors.js +39 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +37 -0
- package/lib/index.js +33 -0
- package/lib/index.js.map +1 -0
- package/lib/lifecycle/assignmentState.d.ts +62 -0
- package/lib/lifecycle/assignmentState.js +89 -0
- package/lib/lifecycle/assignmentState.js.map +1 -0
- package/lib/lifecycle/conformance.d.ts +10 -0
- package/lib/lifecycle/conformance.js +59 -0
- package/lib/lifecycle/conformance.js.map +1 -0
- package/lib/lifecycle/diffOutput.d.ts +7 -0
- package/lib/lifecycle/diffOutput.js +94 -0
- package/lib/lifecycle/diffOutput.js.map +1 -0
- package/lib/lifecycle/dmlRunner.d.ts +30 -0
- package/lib/lifecycle/dmlRunner.js +45 -0
- package/lib/lifecycle/dmlRunner.js.map +1 -0
- package/lib/lifecycle/errors.d.ts +5 -0
- package/lib/lifecycle/errors.js +8 -0
- package/lib/lifecycle/errors.js.map +1 -0
- package/lib/lifecycle/freezeState.d.ts +28 -0
- package/lib/lifecycle/freezeState.js +88 -0
- package/lib/lifecycle/freezeState.js.map +1 -0
- package/lib/lifecycle/messages.d.ts +1 -0
- package/lib/lifecycle/messages.js +20 -0
- package/lib/lifecycle/messages.js.map +1 -0
- package/lib/lifecycle/output.d.ts +23 -0
- package/lib/lifecycle/output.js +148 -0
- package/lib/lifecycle/output.js.map +1 -0
- package/lib/lifecycle/snapshotState.d.ts +12 -0
- package/lib/lifecycle/snapshotState.js +353 -0
- package/lib/lifecycle/snapshotState.js.map +1 -0
- package/lib/lifecycle/stripApply.d.ts +3 -0
- package/lib/lifecycle/stripApply.js +17 -0
- package/lib/lifecycle/stripApply.js.map +1 -0
- package/lib/lifecycle/stripPlan.d.ts +75 -0
- package/lib/lifecycle/stripPlan.js +192 -0
- package/lib/lifecycle/stripPlan.js.map +1 -0
- package/lib/lifecycle/targeting.d.ts +32 -0
- package/lib/lifecycle/targeting.js +195 -0
- package/lib/lifecycle/targeting.js.map +1 -0
- package/lib/lifecycle/types.d.ts +88 -0
- package/lib/lifecycle/types.js +2 -0
- package/lib/lifecycle/types.js.map +1 -0
- package/lib/lifecycle/userDiff.d.ts +70 -0
- package/lib/lifecycle/userDiff.js +393 -0
- package/lib/lifecycle/userDiff.js.map +1 -0
- package/lib/matching/index.d.ts +53 -0
- package/lib/matching/index.js +181 -0
- package/lib/matching/index.js.map +1 -0
- package/lib/provisioning/assignmentPlan.d.ts +53 -0
- package/lib/provisioning/assignmentPlan.js +90 -0
- package/lib/provisioning/assignmentPlan.js.map +1 -0
- package/lib/provisioning/definitionReader.d.ts +38 -0
- package/lib/provisioning/definitionReader.js +77 -0
- package/lib/provisioning/definitionReader.js.map +1 -0
- package/lib/provisioning/errors.d.ts +9 -0
- package/lib/provisioning/errors.js +14 -0
- package/lib/provisioning/errors.js.map +1 -0
- package/lib/provisioning/licenseUsage.d.ts +15 -0
- package/lib/provisioning/licenseUsage.js +53 -0
- package/lib/provisioning/licenseUsage.js.map +1 -0
- package/lib/provisioning/messages.d.ts +4 -0
- package/lib/provisioning/messages.js +72 -0
- package/lib/provisioning/messages.js.map +1 -0
- package/lib/provisioning/planner.d.ts +63 -0
- package/lib/provisioning/planner.js +407 -0
- package/lib/provisioning/planner.js.map +1 -0
- package/lib/provisioning/provisionUserUseCase.d.ts +52 -0
- package/lib/provisioning/provisionUserUseCase.js +200 -0
- package/lib/provisioning/provisionUserUseCase.js.map +1 -0
- package/lib/provisioning/referenceResolution.d.ts +4 -0
- package/lib/provisioning/referenceResolution.js +173 -0
- package/lib/provisioning/referenceResolution.js.map +1 -0
- package/lib/provisioning/userPlan.d.ts +85 -0
- package/lib/provisioning/userPlan.js +162 -0
- package/lib/provisioning/userPlan.js.map +1 -0
- package/lib/provisioning/userSave.d.ts +24 -0
- package/lib/provisioning/userSave.js +119 -0
- package/lib/provisioning/userSave.js.map +1 -0
- package/lib/relatedRecords/apply.d.ts +18 -0
- package/lib/relatedRecords/apply.js +121 -0
- package/lib/relatedRecords/apply.js.map +1 -0
- package/lib/relatedRecords/catalog.d.ts +7 -0
- package/lib/relatedRecords/catalog.js +108 -0
- package/lib/relatedRecords/catalog.js.map +1 -0
- package/lib/relatedRecords/errors.d.ts +5 -0
- package/lib/relatedRecords/errors.js +8 -0
- package/lib/relatedRecords/errors.js.map +1 -0
- package/lib/relatedRecords/messages.d.ts +1 -0
- package/lib/relatedRecords/messages.js +2 -0
- package/lib/relatedRecords/messages.js.map +1 -0
- package/lib/relatedRecords/plan.d.ts +22 -0
- package/lib/relatedRecords/plan.js +271 -0
- package/lib/relatedRecords/plan.js.map +1 -0
- package/lib/relatedRecords/preflight.d.ts +29 -0
- package/lib/relatedRecords/preflight.js +164 -0
- package/lib/relatedRecords/preflight.js.map +1 -0
- package/lib/relatedRecords/sources.d.ts +25 -0
- package/lib/relatedRecords/sources.js +65 -0
- package/lib/relatedRecords/sources.js.map +1 -0
- package/lib/relatedRecords/types.d.ts +46 -0
- package/lib/relatedRecords/types.js +2 -0
- package/lib/relatedRecords/types.js.map +1 -0
- package/lib/shared/csv.d.ts +26 -0
- package/lib/shared/csv.js +237 -0
- package/lib/shared/csv.js.map +1 -0
- package/lib/shared/output.d.ts +25 -0
- package/lib/shared/output.js +74 -0
- package/lib/shared/output.js.map +1 -0
- package/lib/shared/prompt.d.ts +4 -0
- package/lib/shared/prompt.js +15 -0
- package/lib/shared/prompt.js.map +1 -0
- package/lib/shared/sfUtils.d.ts +19 -0
- package/lib/shared/sfUtils.js +42 -0
- package/lib/shared/sfUtils.js.map +1 -0
- package/lib/shared/userFields.d.ts +23 -0
- package/lib/shared/userFields.js +41 -0
- package/lib/shared/userFields.js.map +1 -0
- package/lib/spec/index.d.ts +10 -0
- package/lib/spec/index.js +6 -0
- package/lib/spec/index.js.map +1 -0
- package/lib/spec/parse.d.ts +32 -0
- package/lib/spec/parse.js +77 -0
- package/lib/spec/parse.js.map +1 -0
- package/lib/spec/personaDefinitions.d.ts +56 -0
- package/lib/spec/personaDefinitions.js +25 -0
- package/lib/spec/personaDefinitions.js.map +1 -0
- package/lib/spec/relatedCatalog.d.ts +61 -0
- package/lib/spec/relatedCatalog.js +32 -0
- package/lib/spec/relatedCatalog.js.map +1 -0
- package/lib/spec/snapshot.d.ts +42 -0
- package/lib/spec/snapshot.js +32 -0
- package/lib/spec/snapshot.js.map +1 -0
- package/lib/spec/usersDefinition.d.ts +19 -0
- package/lib/spec/usersDefinition.js +21 -0
- package/lib/spec/usersDefinition.js.map +1 -0
- package/package.json +78 -0
- package/schemas/persona-definitions.schema.json +108 -0
- package/schemas/related-catalog.schema.json +141 -0
- package/schemas/snapshot.schema.json +128 -0
- package/schemas/users-definition.schema.json +36 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0/).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.2.0] - 2026-09-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Warden domain logic extracted as a reusable core library for use across Warden CLI, VS Code extension, and SFDX package
|
|
15
|
+
- Zod file-format schemas and generated JSON Schema artifacts for validation and type-safety
|
|
16
|
+
|
|
17
|
+
## [0.1.0] - 2026-09-26
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Internal maintenance and tooling updates
|
|
22
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jake Richter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# warden-core
|
|
2
|
+
|
|
3
|
+
Shared Warden engine: the user-lifecycle library behind the
|
|
4
|
+
[`warden`](https://github.com/Syntax-Syllogism/warden) Salesforce CLI plugin,
|
|
5
|
+
the Warden VS Code extension, and the Warden SFDX package.
|
|
6
|
+
|
|
7
|
+
warden-core is a plain Node/TypeScript library with no CLI framework
|
|
8
|
+
dependency. Call its functions directly from a script, an editor extension, a
|
|
9
|
+
CI job, or any other Node codebase.
|
|
10
|
+
|
|
11
|
+
The [domain guides](docs/) describe the implemented workflows and data
|
|
12
|
+
contracts for [access auditing](docs/access.md),
|
|
13
|
+
[lifecycle operations](docs/lifecycle.md), [provisioning](docs/provisioning.md),
|
|
14
|
+
[related records](docs/related-records.md), and [user matching](docs/matching.md).
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @syntax-syllogism/warden-core
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Requires Node.js 22 or later.
|
|
23
|
+
|
|
24
|
+
## API
|
|
25
|
+
|
|
26
|
+
| Export | Purpose |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| `WardenError` | Base class for every error warden-core throws on purpose. Carries a stable `code`, optional structured `data`, and a default English `message`. |
|
|
29
|
+
| `isWardenError(error)` | Structural type guard for `WardenError`; safe across duplicate copies of this package. |
|
|
30
|
+
| `AccessError`, `UserAccessError` | Typed access-audit errors. |
|
|
31
|
+
| `LifecycleError` | Typed lifecycle-operation errors. |
|
|
32
|
+
| `ProvisioningError`, `DefinitionError` | Typed provisioning and definition-reading errors. |
|
|
33
|
+
| `RelatedRecordsError` | Typed related-record catalog errors. |
|
|
34
|
+
| File-format schemas and parsers | Zod schemas, JSON-text validators, and inferred types for persona, users, related-catalog, and snapshot files. |
|
|
35
|
+
| Lifecycle, access, provisioning, CSV, snapshot, and rendering functions | Framework-free domain operations used by Warden consumers. |
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { isWardenError } from '@syntax-syllogism/warden-core';
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
// call a warden-core function
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (isWardenError(error)) {
|
|
44
|
+
console.error(`${error.code}: ${error.message}`);
|
|
45
|
+
} else {
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Errors
|
|
52
|
+
|
|
53
|
+
Errors are reported as `WardenError` instances (or area-specific subclasses).
|
|
54
|
+
The `code` and the shape of `data` are part of the public API; the `message`
|
|
55
|
+
text is not and may be reworded in any release.
|
|
56
|
+
|
|
57
|
+
| Code | Thrown by |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `errorUnsupportedAccessType`, `errorInvalidTarget`, `errorFieldTargetMustBeQualified`, `errorObjectNotFound`, `errorFieldNotFound`, `errorApexClassNotFound`, `errorVisualforcePageNotFound`, `errorCustomPermissionNotFound`, `errorTabNotFound`, `errorRecordTypeTargetMustBeQualified`, `errorMasterRecordTypeUnsupported`, `errorRecordTypeNotFound`, `errorRecordTypeAmbiguous`, `errorRecordTypeInactive`, `errorRecordTypeMetadataReadFailed`, `errorAccessQueryFailed` | Access audit and target resolution. |
|
|
60
|
+
| `errorInvalidCsv`, `errorInvalidJson`, `errorInvalidPersonaDefinition`, `errorMissingUserFieldMap`, `errorPersonasWithoutDefinition` | Definition readers. |
|
|
61
|
+
| `errorInvalidJson`, `errorInvalidUserMatchField`, `errorInvalidUserValue`, `errorInvalidAgainstValue`, `errorInvalidAgainstMatchField`, `errorInvalidSnapshot`, `errorPromptDeclined` | Lifecycle targeting, snapshot validation, and operations. |
|
|
62
|
+
| `errorInvalidRelatedCatalog`, `errorRelationshipInvalidDefinition`, `errorRelationshipInvalidSobject`, `errorRelationshipMissingPhase`, `errorRelationshipInvalidPhase`, `errorPhaseBeforeUnsupported`, `errorLinkUserUnsupported`, `errorRelatedContextUnsupported`, `errorRelationshipInvalidMatch`, `errorRelationshipMatchFromUserId`, `errorRelationshipInvalidFields`, `errorRelationshipInvalidSource`, `errorRelationshipInvalidFrom`, `errorRelationshipUnknownUserField`, `errorRelationshipUnwritableField`, `errorRelationshipInvalidMode`, `errorRelationshipInvalidRecordType` | Related-record catalog validation. |
|
|
63
|
+
| `errorDuplicateExternalIdMatch`, `errorMissingRequiredFields`, `errorMissingSaveId`, `errorPromptDeclined`, `errorInvalidJson`, `errorInvalidPersonaDefinition`, `errorPersonasWithoutDefinition` | Provisioning execution and planning. |
|
|
64
|
+
| `schema-invalid`, `schema-version-unsupported` | Structural file-format validation. `data.issues` contains `{ path, message }` entries. |
|
|
65
|
+
|
|
66
|
+
## File formats and schemas
|
|
67
|
+
|
|
68
|
+
The four JSON file formats are defined by the exported Zod schemas and the
|
|
69
|
+
generated Draft 2020-12 schemas. See the detailed
|
|
70
|
+
[file-format schema guide](docs/spec-schemas.md)
|
|
71
|
+
for the full structural contract, parser results, versioning behavior, and
|
|
72
|
+
artifact-generation workflow.
|
|
73
|
+
|
|
74
|
+
- [persona definitions](schemas/persona-definitions.schema.json):
|
|
75
|
+
`{ personas: Record<string, Persona> }`.
|
|
76
|
+
- [users definitions](schemas/users-definition.schema.json):
|
|
77
|
+
`{ users: UserInput[] }`, where User fields are an open record and
|
|
78
|
+
`personas` is an optional string array.
|
|
79
|
+
- [related catalog](schemas/related-catalog.schema.json):
|
|
80
|
+
`{ relationships: Record<string, RelationshipDef> }`.
|
|
81
|
+
- [snapshot](schemas/snapshot.schema.json): the lifecycle snapshot with
|
|
82
|
+
`snapshotVersion: 1`.
|
|
83
|
+
|
|
84
|
+
## Versioning
|
|
85
|
+
|
|
86
|
+
warden-core follows [Semantic Versioning](https://semver.org/). Removing or
|
|
87
|
+
renaming an export, changing a signature or result shape, changing an error
|
|
88
|
+
`code`, or rejecting input that was previously valid is a major change.
|
|
89
|
+
Consumers should pin an exact version and upgrade deliberately.
|
|
90
|
+
|
|
91
|
+
## Development
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm install
|
|
95
|
+
npm run build
|
|
96
|
+
npm test # typecheck, lint, prettier, mocha + coverage
|
|
97
|
+
npm run pack:check
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Contributing
|
|
101
|
+
|
|
102
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) and the
|
|
103
|
+
[Code of Conduct](CODE_OF_CONDUCT.md).
|
|
104
|
+
|
|
105
|
+
## Security
|
|
106
|
+
|
|
107
|
+
See [SECURITY.md](SECURITY.md) for how to report vulnerabilities.
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
[MIT](LICENSE) © Jake Richter
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Connection } from '@salesforce/core';
|
|
2
|
+
import type { AccessTargetType, UserAccessResult, UserAccessRow, UserAccessStats } from './types.js';
|
|
3
|
+
export type PermissionSetParent = {
|
|
4
|
+
Id: string;
|
|
5
|
+
Name?: string;
|
|
6
|
+
DeveloperName?: string;
|
|
7
|
+
Label?: string;
|
|
8
|
+
MasterLabel?: string;
|
|
9
|
+
Type?: string;
|
|
10
|
+
IsOwnedByProfile?: boolean;
|
|
11
|
+
ProfileId?: string;
|
|
12
|
+
Profile?: {
|
|
13
|
+
Name?: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export type AccessBuildContext = {
|
|
17
|
+
assignmentType: 'PermissionSet' | 'PermissionSetGroup';
|
|
18
|
+
permissionSetId: string;
|
|
19
|
+
psgId?: string;
|
|
20
|
+
};
|
|
21
|
+
export type BuildAccess<TGrant, TAccess extends UserAccessRow['access']> = (grant: TGrant, context: AccessBuildContext) => TAccess | undefined;
|
|
22
|
+
export type AssigneeResolutionOptions = {
|
|
23
|
+
preparePsg?: (psgIds: string[]) => Promise<void>;
|
|
24
|
+
};
|
|
25
|
+
export declare const computeStats: (rows: UserAccessRow[]) => UserAccessStats;
|
|
26
|
+
export declare function resolveAssignees<TGrant, TAccess extends UserAccessRow['access']>(conn: Connection, targetType: AccessTargetType, targetName: string, grantByPermissionSetId: Map<string, TGrant>, permissionSetById: Map<string, PermissionSetParent>, buildAccess: BuildAccess<TGrant, TAccess>, options?: AssigneeResolutionOptions): Promise<UserAccessRow[]>;
|
|
27
|
+
export declare const resultFor: (targetType: AccessTargetType, targetName: string, rows: UserAccessRow[], warnings?: string[], extras?: Pick<UserAccessResult, "sobjectType" | "fieldApiName">) => UserAccessResult;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { queryAllInChunks, soqlIn } from './soql.js';
|
|
2
|
+
export const computeStats = (rows) => ({
|
|
3
|
+
totalActiveUsersWithAccess: new Set(rows.map((row) => row.userId)).size,
|
|
4
|
+
profileGrants: new Set(rows.filter((row) => row.assignmentType === 'Profile').map((row) => row.sourceId)).size,
|
|
5
|
+
permissionSetGrants: new Set(rows.filter((row) => row.assignmentType === 'PermissionSet').map((row) => row.sourceId))
|
|
6
|
+
.size,
|
|
7
|
+
permissionSetGroupGrants: new Set(rows.filter((row) => row.assignmentType === 'PermissionSetGroup').map((row) => row.sourceId)).size,
|
|
8
|
+
});
|
|
9
|
+
const keyFor = (row) => [row.userId, row.assignmentType, row.sourceId, row.viaPermissionSetId ?? ''].join('|');
|
|
10
|
+
// eslint-disable-next-line complexity
|
|
11
|
+
export async function resolveAssignees(conn, targetType, targetName, grantByPermissionSetId, permissionSetById, buildAccess, options = {}) {
|
|
12
|
+
const directPermissionSetIds = [...grantByPermissionSetId.keys()].filter((id) => permissionSetById.get(id)?.Type !== 'Group');
|
|
13
|
+
const componentPermissionSetIds = directPermissionSetIds.filter((id) => !permissionSetById.get(id)?.IsOwnedByProfile);
|
|
14
|
+
const psgComponentRows = await queryAllInChunks(conn, componentPermissionSetIds, (chunk) => [
|
|
15
|
+
'SELECT PermissionSetGroupId, PermissionSetGroup.DeveloperName, PermissionSetGroup.MasterLabel, PermissionSetId, PermissionSet.Name, PermissionSet.Type',
|
|
16
|
+
'FROM PermissionSetGroupComponent',
|
|
17
|
+
`WHERE PermissionSetId IN (${soqlIn(chunk)})`,
|
|
18
|
+
].join(' '));
|
|
19
|
+
const psgByPermissionSetId = new Map();
|
|
20
|
+
const psgNameById = new Map();
|
|
21
|
+
const psgApiNameById = new Map();
|
|
22
|
+
const psgLabelById = new Map();
|
|
23
|
+
for (const row of psgComponentRows) {
|
|
24
|
+
if (row.PermissionSetGroup?.DeveloperName) {
|
|
25
|
+
psgApiNameById.set(row.PermissionSetGroupId, row.PermissionSetGroup.DeveloperName);
|
|
26
|
+
}
|
|
27
|
+
if (row.PermissionSetGroup?.MasterLabel) {
|
|
28
|
+
psgLabelById.set(row.PermissionSetGroupId, row.PermissionSetGroup.MasterLabel);
|
|
29
|
+
}
|
|
30
|
+
const psgName = row.PermissionSetGroup?.MasterLabel ??
|
|
31
|
+
row.PermissionSetGroup?.DeveloperName ??
|
|
32
|
+
psgNameById.get(row.PermissionSetGroupId) ??
|
|
33
|
+
'(Unnamed Permission Set Group)';
|
|
34
|
+
psgNameById.set(row.PermissionSetGroupId, psgName);
|
|
35
|
+
const entries = psgByPermissionSetId.get(row.PermissionSetId) ?? [];
|
|
36
|
+
entries.push({ psgId: row.PermissionSetGroupId, psgName });
|
|
37
|
+
psgByPermissionSetId.set(row.PermissionSetId, entries);
|
|
38
|
+
}
|
|
39
|
+
const psgIds = [...new Set(psgComponentRows.map((row) => row.PermissionSetGroupId))];
|
|
40
|
+
await options.preparePsg?.(psgIds);
|
|
41
|
+
if (directPermissionSetIds.length === 0 && psgIds.length === 0)
|
|
42
|
+
return [];
|
|
43
|
+
const assignmentRowsById = new Map();
|
|
44
|
+
const assignmentSelect = [
|
|
45
|
+
'SELECT Id, AssigneeId, Assignee.Name, Assignee.Username, Assignee.IsActive, PermissionSetId, PermissionSet.Name, PermissionSet.IsOwnedByProfile, PermissionSet.ProfileId, PermissionSet.Profile.Name, PermissionSet.Type, PermissionSetGroupId, PermissionSetGroup.DeveloperName, PermissionSetGroup.MasterLabel',
|
|
46
|
+
'FROM PermissionSetAssignment',
|
|
47
|
+
'WHERE Assignee.IsActive = true',
|
|
48
|
+
];
|
|
49
|
+
const assignmentRowsByPermissionSet = await queryAllInChunks(conn, directPermissionSetIds, (chunk) => [...assignmentSelect, `AND PermissionSetId IN (${soqlIn(chunk)})`, 'ORDER BY Assignee.Name, Id'].join(' '));
|
|
50
|
+
for (const row of assignmentRowsByPermissionSet)
|
|
51
|
+
assignmentRowsById.set(row.Id, row);
|
|
52
|
+
const assignmentRowsByPsg = await queryAllInChunks(conn, psgIds, (chunk) => [...assignmentSelect, `AND PermissionSetGroupId IN (${soqlIn(chunk)})`, 'ORDER BY Assignee.Name, Id'].join(' '));
|
|
53
|
+
for (const row of assignmentRowsByPsg)
|
|
54
|
+
assignmentRowsById.set(row.Id, row);
|
|
55
|
+
const rowsByKey = new Map();
|
|
56
|
+
for (const row of assignmentRowsById.values()) {
|
|
57
|
+
if (row.Assignee?.IsActive === false)
|
|
58
|
+
continue;
|
|
59
|
+
const userName = row.Assignee?.Name ?? row.AssigneeId;
|
|
60
|
+
const username = row.Assignee?.Username ?? '';
|
|
61
|
+
if (row.PermissionSetId && grantByPermissionSetId.has(row.PermissionSetId)) {
|
|
62
|
+
const parent = permissionSetById.get(row.PermissionSetId) ?? row.PermissionSet;
|
|
63
|
+
if (parent?.Type !== 'Group') {
|
|
64
|
+
const context = { assignmentType: 'PermissionSet', permissionSetId: row.PermissionSetId };
|
|
65
|
+
const grant = grantByPermissionSetId.get(row.PermissionSetId);
|
|
66
|
+
const access = grant === undefined ? undefined : buildAccess(grant, context);
|
|
67
|
+
if (access !== undefined) {
|
|
68
|
+
const isProfile = parent?.IsOwnedByProfile === true;
|
|
69
|
+
const resolvedRow = {
|
|
70
|
+
userId: row.AssigneeId,
|
|
71
|
+
userName,
|
|
72
|
+
username,
|
|
73
|
+
targetType,
|
|
74
|
+
targetName,
|
|
75
|
+
assignmentType: isProfile ? 'Profile' : 'PermissionSet',
|
|
76
|
+
sourceId: isProfile ? parent?.ProfileId ?? row.PermissionSetId : row.PermissionSetId,
|
|
77
|
+
sourceName: isProfile
|
|
78
|
+
? parent?.Profile?.Name ?? parent?.Name ?? 'Profile'
|
|
79
|
+
: parent?.Name ?? row.PermissionSetId,
|
|
80
|
+
sourceApiName: parent?.DeveloperName ?? parent?.Name ?? row.PermissionSetId,
|
|
81
|
+
sourceLabel: parent?.MasterLabel ?? parent?.Label ?? parent?.Profile?.Name ?? parent?.Name ?? row.PermissionSetId,
|
|
82
|
+
access,
|
|
83
|
+
};
|
|
84
|
+
rowsByKey.set(keyFor(resolvedRow), resolvedRow);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (row.PermissionSetGroupId) {
|
|
89
|
+
const entries = [...psgByPermissionSetId.entries()]
|
|
90
|
+
.filter(([, psgEntries]) => psgEntries.some((entry) => entry.psgId === row.PermissionSetGroupId))
|
|
91
|
+
.flatMap(([permissionSetId, psgEntries]) => psgEntries.filter((entry) => entry.psgId === row.PermissionSetGroupId).map(() => permissionSetId));
|
|
92
|
+
for (const permissionSetId of entries) {
|
|
93
|
+
const grantingAccess = grantByPermissionSetId.get(permissionSetId);
|
|
94
|
+
if (grantingAccess === undefined)
|
|
95
|
+
continue;
|
|
96
|
+
const context = {
|
|
97
|
+
assignmentType: 'PermissionSetGroup',
|
|
98
|
+
permissionSetId,
|
|
99
|
+
psgId: row.PermissionSetGroupId,
|
|
100
|
+
};
|
|
101
|
+
const access = buildAccess(grantingAccess, context);
|
|
102
|
+
if (access === undefined)
|
|
103
|
+
continue;
|
|
104
|
+
const sourceName = row.PermissionSetGroup?.MasterLabel ??
|
|
105
|
+
row.PermissionSetGroup?.DeveloperName ??
|
|
106
|
+
psgNameById.get(row.PermissionSetGroupId) ??
|
|
107
|
+
row.PermissionSetGroupId;
|
|
108
|
+
const resolvedRow = {
|
|
109
|
+
userId: row.AssigneeId,
|
|
110
|
+
userName,
|
|
111
|
+
username,
|
|
112
|
+
targetType,
|
|
113
|
+
targetName,
|
|
114
|
+
assignmentType: 'PermissionSetGroup',
|
|
115
|
+
sourceId: row.PermissionSetGroupId,
|
|
116
|
+
sourceName,
|
|
117
|
+
sourceApiName: row.PermissionSetGroup?.DeveloperName ??
|
|
118
|
+
psgApiNameById.get(row.PermissionSetGroupId) ??
|
|
119
|
+
row.PermissionSetGroupId,
|
|
120
|
+
sourceLabel: row.PermissionSetGroup?.MasterLabel ?? psgLabelById.get(row.PermissionSetGroupId) ?? sourceName,
|
|
121
|
+
viaPermissionSetId: permissionSetId,
|
|
122
|
+
viaPermissionSetName: permissionSetById.get(permissionSetId)?.Name ?? permissionSetId,
|
|
123
|
+
access,
|
|
124
|
+
};
|
|
125
|
+
rowsByKey.set(keyFor(resolvedRow), resolvedRow);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return [...rowsByKey.values()];
|
|
130
|
+
}
|
|
131
|
+
export const resultFor = (targetType, targetName, rows, warnings = [], extras = {}) => ({
|
|
132
|
+
targetType,
|
|
133
|
+
targetName,
|
|
134
|
+
...extras,
|
|
135
|
+
rows,
|
|
136
|
+
stats: computeStats(rows),
|
|
137
|
+
warnings,
|
|
138
|
+
});
|
|
139
|
+
//# sourceMappingURL=assignees.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assignees.js","sourceRoot":"","sources":["../../src/access/assignees.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const accessMessage: (code: string, args?: string[]) => string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const TEXT = {
|
|
2
|
+
errorUnsupportedAccessType: 'Unsupported access type: %s.',
|
|
3
|
+
errorInvalidTarget: 'Invalid target value: %s.',
|
|
4
|
+
errorFieldTargetMustBeQualified: 'Field target must be qualified as ObjectApiName.FieldApiName: %s.',
|
|
5
|
+
errorObjectNotFound: 'Object not found: %s.',
|
|
6
|
+
errorFieldNotFound: 'Object %s does not have field %s.',
|
|
7
|
+
errorApexClassNotFound: 'Apex class not found: %s.',
|
|
8
|
+
errorVisualforcePageNotFound: 'Visualforce page not found: %s.',
|
|
9
|
+
errorCustomPermissionNotFound: 'Custom permission not found: %s.',
|
|
10
|
+
errorTabNotFound: 'Tab not found: %s.',
|
|
11
|
+
errorRecordTypeTargetMustBeQualified: 'Record type target must be qualified as SObject.DeveloperName for an active, non-master record type: %s.',
|
|
12
|
+
errorMasterRecordTypeUnsupported: 'The Master record type is not supported. Specify an active non-master record type as SObject.DeveloperName: %s.',
|
|
13
|
+
errorRecordTypeNotFound: 'Record type not found or ambiguous: %s.',
|
|
14
|
+
errorRecordTypeAmbiguous: 'Record type target matched multiple records and cannot be selected safely: %s.',
|
|
15
|
+
errorRecordTypeInactive: 'Record type is inactive: %s.',
|
|
16
|
+
errorRecordTypeMetadataReadFailed: 'Unable to read complete %s metadata for record-type access audit (%s). No partial audit result was returned. Record-type auditing reads every profile and permission set through the Metadata API, so the authenticated user needs org-wide metadata read access. Grant these system permissions: "API Enabled", "View Setup and Configuration", and either "Modify Metadata Through Metadata API Functions" or "Modify All Data". On large orgs a user missing "Modify All Data" (or the granular "Modify Metadata" permission) is silently served an incomplete metadata set rather than an error, which is the usual cause of this failure.',
|
|
17
|
+
errorAccessQueryFailed: 'Failed to resolve access for %s target %s.',
|
|
18
|
+
};
|
|
19
|
+
export const accessMessage = (code, args = []) => {
|
|
20
|
+
const template = TEXT[code] ?? code;
|
|
21
|
+
let index = 0;
|
|
22
|
+
return template.replaceAll('%s', () => args[index++] ?? '');
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/access/messages.ts"],"names":[],"mappings":"AAAA,MAAM,IAAI,GAA2B;IACnC,0BAA0B,EAAE,8BAA8B;IAC1D,kBAAkB,EAAE,2BAA2B;IAC/C,+BAA+B,EAAE,mEAAmE;IACpG,mBAAmB,EAAE,uBAAuB;IAC5C,kBAAkB,EAAE,mCAAmC;IACvD,sBAAsB,EAAE,2BAA2B;IACnD,4BAA4B,EAAE,iCAAiC;IAC/D,6BAA6B,EAAE,kCAAkC;IACjE,gBAAgB,EAAE,oBAAoB;IACtC,oCAAoC,EAClC,0GAA0G;IAC5G,gCAAgC,EAC9B,iHAAiH;IACnH,uBAAuB,EAAE,yCAAyC;IAClE,wBAAwB,EAAE,gFAAgF;IAC1G,uBAAuB,EAAE,8BAA8B;IACvD,iCAAiC,EAC/B,gnBAAgnB;IAClnB,sBAAsB,EAAE,4CAA4C;CACrE,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAE,OAAiB,EAAE,EAAU,EAAE;IACzE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IACpC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Connection } from '@salesforce/core';
|
|
2
|
+
export declare const METADATA_BATCH_SIZE = 10;
|
|
3
|
+
export declare const METADATA_CONCURRENCY = 2;
|
|
4
|
+
type MetadataComponent = {
|
|
5
|
+
fullName: string;
|
|
6
|
+
} & Record<string, unknown>;
|
|
7
|
+
export type MetadataType = 'Profile' | 'PermissionSet' | 'MutingPermissionSet';
|
|
8
|
+
export type MetadataReadResult<T> = {
|
|
9
|
+
metadata: Map<string, T>;
|
|
10
|
+
missing: string[];
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Warning shown when a record-type audit could not read every profile,
|
|
14
|
+
* permission set, or muting permission set because the running user lacks
|
|
15
|
+
* org-wide metadata read access. The audit continues with what it could read.
|
|
16
|
+
*/
|
|
17
|
+
export declare const partialMetadataWarning: (missingNames: string[]) => string;
|
|
18
|
+
/**
|
|
19
|
+
* Read components in bounded batches.
|
|
20
|
+
*
|
|
21
|
+
* Fails closed for genuinely broken responses (a rejected read, a non-object
|
|
22
|
+
* response, or a malformed, unexpected, or duplicated component) — those signal
|
|
23
|
+
* a misconfiguration the caller should not paper over. It fails *open* for a
|
|
24
|
+
* merely incomplete response: on large orgs the Metadata API silently omits
|
|
25
|
+
* components the running user cannot read rather than erroring, so the omitted
|
|
26
|
+
* fullNames are returned in `missing` for the caller to report as a partial
|
|
27
|
+
* result instead of aborting the whole audit.
|
|
28
|
+
*/
|
|
29
|
+
export declare function readMetadataInBatches<T>(conn: Connection, type: MetadataType, fullNames: string[], options?: {
|
|
30
|
+
batchSize?: number;
|
|
31
|
+
concurrency?: number;
|
|
32
|
+
}): Promise<MetadataReadResult<T>>;
|
|
33
|
+
export declare const profileMetadataNamesById: (conn: Connection) => Promise<Map<string, string>>;
|
|
34
|
+
export declare const mutingPermissionSetMetadataNamesById: (conn: Connection) => Promise<Map<string, string>>;
|
|
35
|
+
export type RecordTypeVisibility = {
|
|
36
|
+
recordType: string;
|
|
37
|
+
visible: boolean;
|
|
38
|
+
default?: boolean;
|
|
39
|
+
};
|
|
40
|
+
export declare const recordTypeVisibilities: (metadata: MetadataComponent, type: MetadataType) => RecordTypeVisibility[];
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { UserAccessError } from './types.js';
|
|
2
|
+
export const METADATA_BATCH_SIZE = 10;
|
|
3
|
+
export const METADATA_CONCURRENCY = 2;
|
|
4
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
5
|
+
const responseItems = (response) => {
|
|
6
|
+
if (response === null || response === undefined)
|
|
7
|
+
return [];
|
|
8
|
+
if (Array.isArray(response))
|
|
9
|
+
return response;
|
|
10
|
+
if (isRecord(response))
|
|
11
|
+
return [response];
|
|
12
|
+
throw new Error('Metadata API returned a non-object response.');
|
|
13
|
+
};
|
|
14
|
+
const metadataFailure = (type, names, cause) => new UserAccessError('errorRecordTypeMetadataReadFailed', [type, names.join(', ')], cause);
|
|
15
|
+
const validateBatch = (type, requestedNames, response) => {
|
|
16
|
+
let items;
|
|
17
|
+
try {
|
|
18
|
+
items = responseItems(response);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
throw metadataFailure(type, requestedNames, error);
|
|
22
|
+
}
|
|
23
|
+
const requested = new Set(requestedNames);
|
|
24
|
+
const seen = new Set();
|
|
25
|
+
const components = [];
|
|
26
|
+
for (const item of items) {
|
|
27
|
+
if (!isRecord(item)) {
|
|
28
|
+
throw metadataFailure(type, requestedNames, new Error('Metadata API returned a non-object component.'));
|
|
29
|
+
}
|
|
30
|
+
// Large orgs return an empty placeholder component (a well-formed object with
|
|
31
|
+
// no usable fullName) for a requested component the running user cannot read,
|
|
32
|
+
// instead of omitting it from the response array. Treat that as unreadable:
|
|
33
|
+
// skip it so it surfaces below as a partial `missing` result rather than
|
|
34
|
+
// failing the whole audit closed.
|
|
35
|
+
if (typeof item.fullName !== 'string' || !item.fullName) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (!requested.has(item.fullName)) {
|
|
39
|
+
throw metadataFailure(type, requestedNames, new Error(`Unexpected component ${item.fullName}.`));
|
|
40
|
+
}
|
|
41
|
+
if (seen.has(item.fullName)) {
|
|
42
|
+
throw metadataFailure(type, requestedNames, new Error(`Duplicate component ${item.fullName}.`));
|
|
43
|
+
}
|
|
44
|
+
seen.add(item.fullName);
|
|
45
|
+
components.push(item);
|
|
46
|
+
}
|
|
47
|
+
return components;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Warning shown when a record-type audit could not read every profile,
|
|
51
|
+
* permission set, or muting permission set because the running user lacks
|
|
52
|
+
* org-wide metadata read access. The audit continues with what it could read.
|
|
53
|
+
*/
|
|
54
|
+
export const partialMetadataWarning = (missingNames) => {
|
|
55
|
+
const examples = missingNames.slice(0, 5);
|
|
56
|
+
const suffix = missingNames.length > examples.length ? ', …' : '';
|
|
57
|
+
return (`Partial response: ${missingNames.length} metadata component(s) could not be read through the Metadata API ` +
|
|
58
|
+
`(${examples.join(', ')}${suffix}). Affected profiles and permission sets are omitted, and any muting they define ` +
|
|
59
|
+
'is not applied, so this audit may be incomplete. Grant the running user "API Enabled", ' +
|
|
60
|
+
'"View Setup and Configuration", and either "Modify Metadata Through Metadata API Functions" or "Modify All Data" ' +
|
|
61
|
+
'for a full accounting.');
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Read components in bounded batches.
|
|
65
|
+
*
|
|
66
|
+
* Fails closed for genuinely broken responses (a rejected read, a non-object
|
|
67
|
+
* response, or a malformed, unexpected, or duplicated component) — those signal
|
|
68
|
+
* a misconfiguration the caller should not paper over. It fails *open* for a
|
|
69
|
+
* merely incomplete response: on large orgs the Metadata API silently omits
|
|
70
|
+
* components the running user cannot read rather than erroring, so the omitted
|
|
71
|
+
* fullNames are returned in `missing` for the caller to report as a partial
|
|
72
|
+
* result instead of aborting the whole audit.
|
|
73
|
+
*/
|
|
74
|
+
export async function readMetadataInBatches(conn, type, fullNames, options = {}) {
|
|
75
|
+
const batchSize = options.batchSize ?? METADATA_BATCH_SIZE;
|
|
76
|
+
const concurrency = options.concurrency ?? METADATA_CONCURRENCY;
|
|
77
|
+
if (!Number.isInteger(batchSize) || batchSize <= 0)
|
|
78
|
+
throw new Error('Metadata batchSize must be greater than 0.');
|
|
79
|
+
if (!Number.isInteger(concurrency) || concurrency <= 0)
|
|
80
|
+
throw new Error('Metadata concurrency must be greater than 0.');
|
|
81
|
+
const uniqueNames = [...new Set(fullNames)];
|
|
82
|
+
if (uniqueNames.length === 0)
|
|
83
|
+
return { metadata: new Map(), missing: [] };
|
|
84
|
+
const batches = [];
|
|
85
|
+
for (let index = 0; index < uniqueNames.length; index += batchSize) {
|
|
86
|
+
batches.push(uniqueNames.slice(index, index + batchSize));
|
|
87
|
+
}
|
|
88
|
+
const batchResults = Array.from({ length: batches.length }, () => []);
|
|
89
|
+
let nextBatch = 0;
|
|
90
|
+
const worker = async () => {
|
|
91
|
+
while (nextBatch < batches.length) {
|
|
92
|
+
const batchIndex = nextBatch;
|
|
93
|
+
nextBatch += 1;
|
|
94
|
+
const batch = batches[batchIndex];
|
|
95
|
+
try {
|
|
96
|
+
// eslint-disable-next-line no-await-in-loop
|
|
97
|
+
const response = await conn.metadata.read(type, batch);
|
|
98
|
+
batchResults[batchIndex] = validateBatch(type, batch, response);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (error instanceof UserAccessError)
|
|
102
|
+
throw error;
|
|
103
|
+
throw metadataFailure(type, batch, error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, batches.length) }, () => worker()));
|
|
108
|
+
const result = new Map();
|
|
109
|
+
for (const components of batchResults) {
|
|
110
|
+
for (const component of components) {
|
|
111
|
+
if (result.has(component.fullName)) {
|
|
112
|
+
throw metadataFailure(type, [component.fullName], new Error(`Duplicate component ${component.fullName}.`));
|
|
113
|
+
}
|
|
114
|
+
result.set(component.fullName, component);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const missing = uniqueNames.filter((name) => !result.has(name));
|
|
118
|
+
return { metadata: result, missing };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Map each component's record Id (15- and 18-char) to the fullName the Metadata
|
|
122
|
+
* API expects, using a metadata listing.
|
|
123
|
+
*
|
|
124
|
+
* This solves two record-type audit problems that only appear against real orgs.
|
|
125
|
+
* Standard profiles expose a metadata name that differs from the SOQL Name/label
|
|
126
|
+
* (for example "Contract Manager" is "ContractManager" and "Standard User" is
|
|
127
|
+
* "Standard"), and non-auditable system profiles such as Automated Process are
|
|
128
|
+
* omitted from the listing entirely. Muting permission sets are a separate object
|
|
129
|
+
* whose Ids are not returned by a PermissionSet SOQL query and whose
|
|
130
|
+
* PermissionSetGroupComponent.PermissionSet relationship is null; the listing is
|
|
131
|
+
* the reliable Id to fullName source. Callers translate through this map and skip
|
|
132
|
+
* Ids it does not contain.
|
|
133
|
+
*/
|
|
134
|
+
const metadataNamesById = async (conn, type) => {
|
|
135
|
+
const response = await conn.metadata.list([{ type }], conn.getApiVersion());
|
|
136
|
+
const items = Array.isArray(response) ? response : response ? [response] : [];
|
|
137
|
+
const byId = new Map();
|
|
138
|
+
for (const item of items) {
|
|
139
|
+
if (typeof item?.id === 'string' && item.id && typeof item.fullName === 'string' && item.fullName) {
|
|
140
|
+
// Key on the canonical 15-char Id so lookups work whether the listing and
|
|
141
|
+
// the SOQL Id come back 15- or 18-char.
|
|
142
|
+
byId.set(item.id.substring(0, 15), item.fullName);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return byId;
|
|
146
|
+
};
|
|
147
|
+
export const profileMetadataNamesById = (conn) => metadataNamesById(conn, 'Profile');
|
|
148
|
+
export const mutingPermissionSetMetadataNamesById = (conn) => metadataNamesById(conn, 'MutingPermissionSet');
|
|
149
|
+
export const recordTypeVisibilities = (metadata, type) => {
|
|
150
|
+
const raw = metadata.recordTypeVisibilities;
|
|
151
|
+
const entries = raw === undefined || raw === null ? [] : Array.isArray(raw) ? raw : [raw];
|
|
152
|
+
const normalized = [];
|
|
153
|
+
for (const entry of entries) {
|
|
154
|
+
if (!isRecord(entry) || typeof entry.recordType !== 'string' || typeof entry.visible !== 'boolean') {
|
|
155
|
+
throw metadataFailure(type, [metadata.fullName], new Error('Malformed recordTypeVisibilities entry.'));
|
|
156
|
+
}
|
|
157
|
+
if (entry.default !== undefined && typeof entry.default !== 'boolean') {
|
|
158
|
+
throw metadataFailure(type, [metadata.fullName], new Error('Malformed record type default value.'));
|
|
159
|
+
}
|
|
160
|
+
normalized.push({
|
|
161
|
+
recordType: entry.recordType,
|
|
162
|
+
visible: entry.visible,
|
|
163
|
+
...(entry.default === undefined ? {} : { default: entry.default }),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return normalized;
|
|
167
|
+
};
|
|
168
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/access/metadata.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AACtC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAKtC,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEvE,MAAM,aAAa,GAAG,CAAC,QAAiB,EAAa,EAAE;IACrD,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAC3D,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC7C,IAAI,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;AAClE,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,IAAkB,EAAE,KAAe,EAAE,KAAc,EAAmB,EAAE,CAC/F,IAAI,eAAe,CAAC,mCAAmC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAE5F,MAAM,aAAa,GAAG,CAAC,IAAkB,EAAE,cAAwB,EAAE,QAAiB,EAAuB,EAAE;IAC7G,IAAI,KAAgB,CAAC;IACrB,IAAI,CAAC;QACH,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,UAAU,GAAwB,EAAE,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACpB,MAAM,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC,CAAC;QAC1G,CAAC;QACD,8EAA8E;QAC9E,8EAA8E;QAC9E,4EAA4E;QAC5E,yEAAyE;QACzE,kCAAkC;QAClC,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClC,MAAM,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,KAAK,CAAC,wBAAwB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QACnG,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,MAAM,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QAClG,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxB,UAAU,CAAC,IAAI,CAAC,IAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAIF;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,YAAsB,EAAU,EAAE;IACvE,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,OAAO,CACL,qBAAqB,YAAY,CAAC,MAAM,oEAAoE;QAC5G,IAAI,QAAQ,CAAC,IAAI,CACf,IAAI,CACL,GAAG,MAAM,mFAAmF;QAC7F,yFAAyF;QACzF,mHAAmH;QACnH,wBAAwB,CACzB,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,IAAgB,EAChB,IAAkB,EAClB,SAAmB,EACnB,UAAwD,EAAE;IAE1D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,mBAAmB,CAAC;IAC3D,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,oBAAoB,CAAC;IAChE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAClH,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,WAAW,IAAI,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAElE,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5C,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC1E,MAAM,OAAO,GAAe,EAAE,CAAC;IAC/B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,IAAI,SAAS,EAAE,CAAC;QACnE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,YAAY,GAA0B,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7F,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;QACvC,OAAO,SAAS,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAClC,MAAM,UAAU,GAAG,SAAS,CAAC;YAC7B,SAAS,IAAI,CAAC,CAAC;YACf,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;YAClC,IAAI,CAAC;gBACH,4CAA4C;gBAC5C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBACvD,YAAY,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;YAClE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,eAAe;oBAAE,MAAM,KAAK,CAAC;gBAClD,MAAM,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAEjG,MAAM,MAAM,GAAG,IAAI,GAAG,EAAa,CAAC;IACpC,KAAK,MAAM,UAAU,IAAI,YAAY,EAAE,CAAC;QACtC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,IAAI,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnC,MAAM,eAAe,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,IAAI,KAAK,CAAC,uBAAuB,SAAS,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YAC7G,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,EAAE,SAAc,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AACvC,CAAC;AAID;;;;;;;;;;;;;GAaG;AACH,MAAM,iBAAiB,GAAG,KAAK,EAC7B,IAAgB,EAChB,IAAuC,EACT,EAAE;IAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IAC5E,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,KAA0B,EAAE,CAAC;QAC9C,IAAI,OAAO,IAAI,EAAE,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,EAAE,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClG,0EAA0E;YAC1E,wCAAwC;YACxC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,IAAgB,EAAgC,EAAE,CACzF,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAErC,MAAM,CAAC,MAAM,oCAAoC,GAAG,CAAC,IAAgB,EAAgC,EAAE,CACrG,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;AAIjD,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,QAA2B,EAAE,IAAkB,EAA0B,EAAE;IAChH,MAAM,GAAG,GAAG,QAAQ,CAAC,sBAAsB,CAAC;IAC5C,MAAM,OAAO,GAAG,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1F,MAAM,UAAU,GAA2B,EAAE,CAAC;IAC9C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACnG,MAAM,eAAe,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC,CAAC;QACzG,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACtE,MAAM,eAAe,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC,CAAC;QACtG,CAAC;QACD,UAAU,CAAC,IAAI,CAAC;YACd,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;SACnE,CAAC,CAAC;IACL,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Connection } from '@salesforce/core';
|
|
2
|
+
export type PsgMutingState<TMask> = {
|
|
3
|
+
permissionSetIdsByGroup: Map<string, string[]>;
|
|
4
|
+
masksByPermissionSetId: Map<string, TMask>;
|
|
5
|
+
};
|
|
6
|
+
export declare function loadPsgMuting<TMask>(conn: Connection, psgIds: string[], loadMasks: (permissionSetIds: string[]) => Promise<Map<string, TMask>>): Promise<PsgMutingState<TMask>>;
|
|
7
|
+
export declare const combinePsgMuting: <TMask>(psgId: string | undefined, state: PsgMutingState<TMask>, emptyMask: TMask, combine: (left: TMask, right: TMask) => TMask) => TMask;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { queryAllInChunks, soqlIn } from './soql.js';
|
|
2
|
+
export async function loadPsgMuting(conn, psgIds, loadMasks) {
|
|
3
|
+
if (psgIds.length === 0) {
|
|
4
|
+
return { permissionSetIdsByGroup: new Map(), masksByPermissionSetId: new Map() };
|
|
5
|
+
}
|
|
6
|
+
const components = await queryAllInChunks(conn, psgIds, (chunk) => [
|
|
7
|
+
'SELECT PermissionSetGroupId, PermissionSetId',
|
|
8
|
+
'FROM PermissionSetGroupComponent',
|
|
9
|
+
`WHERE PermissionSetGroupId IN (${soqlIn(chunk)})`,
|
|
10
|
+
"AND PermissionSet.Type = 'Muting'",
|
|
11
|
+
].join(' '));
|
|
12
|
+
const permissionSetIdsByGroup = new Map();
|
|
13
|
+
for (const component of components) {
|
|
14
|
+
const ids = permissionSetIdsByGroup.get(component.PermissionSetGroupId) ?? [];
|
|
15
|
+
ids.push(component.PermissionSetId);
|
|
16
|
+
permissionSetIdsByGroup.set(component.PermissionSetGroupId, ids);
|
|
17
|
+
}
|
|
18
|
+
const permissionSetIds = [...new Set(components.map((component) => component.PermissionSetId))];
|
|
19
|
+
return { permissionSetIdsByGroup, masksByPermissionSetId: await loadMasks(permissionSetIds) };
|
|
20
|
+
}
|
|
21
|
+
export const combinePsgMuting = (psgId, state, emptyMask, combine) => (state.permissionSetIdsByGroup.get(psgId ?? '') ?? []).reduce((muted, permissionSetId) => combine(muted, state.masksByPermissionSetId.get(permissionSetId) ?? emptyMask), emptyMask);
|
|
22
|
+
//# sourceMappingURL=muting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"muting.js","sourceRoot":"","sources":["../../src/access/muting.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AASrD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAgB,EAChB,MAAgB,EAChB,SAAsE;IAEtE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,uBAAuB,EAAE,IAAI,GAAG,EAAE,EAAE,sBAAsB,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;IACnF,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAqB,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CACpF;QACE,8CAA8C;QAC9C,kCAAkC;QAClC,kCAAkC,MAAM,CAAC,KAAK,CAAC,GAAG;QAClD,mCAAmC;KACpC,CAAC,IAAI,CAAC,GAAG,CAAC,CACZ,CAAC;IACF,MAAM,uBAAuB,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC5D,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,uBAAuB,CAAC,GAAG,CAAC,SAAS,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;QAC9E,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACpC,uBAAuB,CAAC,GAAG,CAAC,SAAS,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,gBAAgB,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAChG,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC,gBAAgB,CAAC,EAAE,CAAC;AAChG,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,KAAyB,EACzB,KAA4B,EAC5B,SAAgB,EAChB,OAA6C,EACtC,EAAE,CACT,CAAC,KAAK,CAAC,uBAAuB,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAC3D,CAAC,KAAK,EAAE,eAAe,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,sBAAsB,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,SAAS,CAAC,EAC1G,SAAS,CACV,CAAC"}
|