@peernova/cuneiform-sf 1.0.2 → 1.0.4-beta.8
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/LICENSE +81 -30
- package/README.md +59 -95
- package/lib/adapters/connection-facade.d.ts +458 -0
- package/lib/adapters/connection-facade.js +379 -0
- package/lib/adapters/connection-facade.js.map +1 -0
- package/lib/adapters/errors.d.ts +547 -0
- package/lib/adapters/errors.js +937 -0
- package/lib/adapters/errors.js.map +1 -0
- package/lib/adapters/index.d.ts +33 -0
- package/lib/adapters/index.js +50 -0
- package/lib/adapters/index.js.map +1 -0
- package/lib/adapters/lifecycle.d.ts +119 -0
- package/lib/adapters/lifecycle.js +94 -0
- package/lib/adapters/lifecycle.js.map +1 -0
- package/lib/adapters/rest/cache.d.ts +69 -0
- package/lib/adapters/rest/cache.js +133 -0
- package/lib/adapters/rest/cache.js.map +1 -0
- package/lib/adapters/rest/index.d.ts +11 -0
- package/lib/adapters/rest/index.js +18 -0
- package/lib/adapters/rest/index.js.map +1 -0
- package/lib/adapters/rest/profiling-rest-client.d.ts +137 -0
- package/lib/adapters/rest/profiling-rest-client.js +115 -0
- package/lib/adapters/rest/profiling-rest-client.js.map +1 -0
- package/lib/adapters/rest/rest-api-adapter.d.ts +389 -0
- package/lib/adapters/rest/rest-api-adapter.js +747 -0
- package/lib/adapters/rest/rest-api-adapter.js.map +1 -0
- package/lib/adapters/rest/types.d.ts +34 -0
- package/lib/adapters/rest/types.js +9 -0
- package/lib/adapters/rest/types.js.map +1 -0
- package/lib/adapters/retry.d.ts +91 -0
- package/lib/adapters/retry.js +215 -0
- package/lib/adapters/retry.js.map +1 -0
- package/lib/adapters/soql/cuneiform-query-builder.d.ts +391 -0
- package/lib/adapters/soql/cuneiform-query-builder.js +559 -0
- package/lib/adapters/soql/cuneiform-query-builder.js.map +1 -0
- package/lib/adapters/soql/index.d.ts +13 -0
- package/lib/adapters/soql/index.js +21 -0
- package/lib/adapters/soql/index.js.map +1 -0
- package/lib/adapters/soql/soql-query-adapter.d.ts +141 -0
- package/lib/adapters/soql/soql-query-adapter.js +259 -0
- package/lib/adapters/soql/soql-query-adapter.js.map +1 -0
- package/lib/adapters/soql/types.d.ts +37 -0
- package/lib/adapters/soql/types.js +19 -0
- package/lib/adapters/soql/types.js.map +1 -0
- package/lib/adapters/testing/index.d.ts +37 -0
- package/lib/adapters/testing/index.js +20 -0
- package/lib/adapters/testing/index.js.map +1 -0
- package/lib/adapters/testing/mock-connection.d.ts +77 -0
- package/lib/adapters/testing/mock-connection.js +207 -0
- package/lib/adapters/testing/mock-connection.js.map +1 -0
- package/lib/adapters/testing/mock-logger.d.ts +29 -0
- package/lib/adapters/testing/mock-logger.js +57 -0
- package/lib/adapters/testing/mock-logger.js.map +1 -0
- package/lib/adapters/testing/mock-mcp-adapters.d.ts +32 -0
- package/lib/adapters/testing/mock-mcp-adapters.js +52 -0
- package/lib/adapters/testing/mock-mcp-adapters.js.map +1 -0
- package/lib/adapters/testing/mock-oclif-config.d.ts +22 -0
- package/lib/adapters/testing/mock-oclif-config.js +90 -0
- package/lib/adapters/testing/mock-oclif-config.js.map +1 -0
- package/lib/adapters/testing/mock-rest-adapter.d.ts +26 -0
- package/lib/adapters/testing/mock-rest-adapter.js +243 -0
- package/lib/adapters/testing/mock-rest-adapter.js.map +1 -0
- package/lib/adapters/testing/mock-salesforce-connection.d.ts +40 -0
- package/lib/adapters/testing/mock-salesforce-connection.js +61 -0
- package/lib/adapters/testing/mock-salesforce-connection.js.map +1 -0
- package/lib/adapters/testing/mock-soql-adapter.d.ts +30 -0
- package/lib/adapters/testing/mock-soql-adapter.js +120 -0
- package/lib/adapters/testing/mock-soql-adapter.js.map +1 -0
- package/lib/adapters/testing/mock-tooling-adapter.d.ts +24 -0
- package/lib/adapters/testing/mock-tooling-adapter.js +163 -0
- package/lib/adapters/testing/mock-tooling-adapter.js.map +1 -0
- package/lib/adapters/testing/stub-connection.d.ts +93 -0
- package/lib/adapters/testing/stub-connection.js +97 -0
- package/lib/adapters/testing/stub-connection.js.map +1 -0
- package/lib/adapters/testing/stub-rest-adapter.d.ts +52 -0
- package/lib/adapters/testing/stub-rest-adapter.js +58 -0
- package/lib/adapters/testing/stub-rest-adapter.js.map +1 -0
- package/lib/adapters/testing/stub-soql-adapter.d.ts +56 -0
- package/lib/adapters/testing/stub-soql-adapter.js +50 -0
- package/lib/adapters/testing/stub-soql-adapter.js.map +1 -0
- package/lib/adapters/testing/types.d.ts +71 -0
- package/lib/adapters/testing/types.js +9 -0
- package/lib/adapters/testing/types.js.map +1 -0
- package/lib/adapters/tooling/index.d.ts +10 -0
- package/lib/adapters/tooling/index.js +17 -0
- package/lib/adapters/tooling/index.js.map +1 -0
- package/lib/adapters/tooling/tooling-api-adapter.d.ts +157 -0
- package/lib/adapters/tooling/tooling-api-adapter.js +339 -0
- package/lib/adapters/tooling/tooling-api-adapter.js.map +1 -0
- package/lib/adapters/tooling/types.d.ts +81 -0
- package/lib/adapters/tooling/types.js +9 -0
- package/lib/adapters/tooling/types.js.map +1 -0
- package/lib/adapters/types.d.ts +112 -0
- package/lib/adapters/types.js +169 -0
- package/lib/adapters/types.js.map +1 -0
- package/lib/base/cuneiform-command.d.ts +152 -0
- package/lib/base/cuneiform-command.js +243 -0
- package/lib/base/cuneiform-command.js.map +1 -0
- package/lib/commands/cuneiform/compatibility/check.d.ts +43 -0
- package/lib/commands/cuneiform/compatibility/check.js +114 -0
- package/lib/commands/cuneiform/compatibility/check.js.map +1 -0
- package/lib/commands/cuneiform/definition/create.d.ts +119 -0
- package/lib/commands/cuneiform/definition/create.js +693 -0
- package/lib/commands/cuneiform/definition/create.js.map +1 -0
- package/lib/commands/cuneiform/definition/export.d.ts +57 -0
- package/lib/commands/cuneiform/definition/export.js +133 -0
- package/lib/commands/cuneiform/definition/export.js.map +1 -0
- package/lib/commands/cuneiform/definition/get.d.ts +86 -0
- package/lib/commands/cuneiform/definition/get.js +270 -0
- package/lib/commands/cuneiform/definition/get.js.map +1 -0
- package/lib/commands/cuneiform/definition/import.d.ts +54 -0
- package/lib/commands/cuneiform/definition/import.js +118 -0
- package/lib/commands/cuneiform/definition/import.js.map +1 -0
- package/lib/commands/cuneiform/definition/list.d.ts +110 -0
- package/lib/commands/cuneiform/definition/list.js +344 -0
- package/lib/commands/cuneiform/definition/list.js.map +1 -0
- package/lib/commands/cuneiform/definition/purge.d.ts +105 -0
- package/lib/commands/cuneiform/definition/purge.js +533 -0
- package/lib/commands/cuneiform/definition/purge.js.map +1 -0
- package/lib/commands/cuneiform/definition/update.d.ts +58 -0
- package/lib/commands/cuneiform/definition/update.js +206 -0
- package/lib/commands/cuneiform/definition/update.js.map +1 -0
- package/lib/commands/cuneiform/mcp/serve.d.ts +56 -0
- package/lib/commands/cuneiform/mcp/serve.js +109 -0
- package/lib/commands/cuneiform/mcp/serve.js.map +1 -0
- package/lib/commands/cuneiform/object/describe.d.ts +61 -0
- package/lib/commands/cuneiform/object/describe.js +461 -0
- package/lib/commands/cuneiform/object/describe.js.map +1 -0
- package/lib/commands/cuneiform/object/list.d.ts +111 -0
- package/lib/commands/cuneiform/object/list.js +239 -0
- package/lib/commands/cuneiform/object/list.js.map +1 -0
- package/lib/commands/cuneiform/org/details.d.ts +99 -0
- package/lib/commands/cuneiform/org/details.js +521 -0
- package/lib/commands/cuneiform/org/details.js.map +1 -0
- package/lib/commands/cuneiform/org/reset.d.ts +46 -0
- package/lib/commands/cuneiform/org/reset.js +135 -0
- package/lib/commands/cuneiform/org/reset.js.map +1 -0
- package/lib/commands/cuneiform/profile/request/cancel.d.ts +59 -0
- package/lib/commands/cuneiform/profile/request/cancel.js +202 -0
- package/lib/commands/cuneiform/profile/request/cancel.js.map +1 -0
- package/lib/commands/cuneiform/profile/request/delete.d.ts +59 -0
- package/lib/commands/cuneiform/profile/request/delete.js +223 -0
- package/lib/commands/cuneiform/profile/request/delete.js.map +1 -0
- package/lib/commands/cuneiform/profile/request/list.d.ts +35 -0
- package/lib/commands/cuneiform/profile/request/list.js +102 -0
- package/lib/commands/cuneiform/profile/request/list.js.map +1 -0
- package/lib/commands/cuneiform/profile.d.ts +90 -0
- package/lib/commands/cuneiform/profile.js +322 -0
- package/lib/commands/cuneiform/profile.js.map +1 -0
- package/lib/commands/cuneiform/summary/purge.d.ts +77 -0
- package/lib/commands/cuneiform/summary/purge.js +429 -0
- package/lib/commands/cuneiform/summary/purge.js.map +1 -0
- package/lib/commands/cuneiform/summary/reprofile.d.ts +60 -0
- package/lib/commands/cuneiform/summary/reprofile.js +236 -0
- package/lib/commands/cuneiform/summary/reprofile.js.map +1 -0
- package/lib/commands/cuneiform/summary/stop.d.ts +59 -0
- package/lib/commands/cuneiform/summary/stop.js +234 -0
- package/lib/commands/cuneiform/summary/stop.js.map +1 -0
- package/lib/commands/cuneiform/user/details.d.ts +73 -0
- package/lib/commands/cuneiform/user/details.js +391 -0
- package/lib/commands/cuneiform/user/details.js.map +1 -0
- package/lib/constants/index.d.ts +8 -0
- package/lib/constants/index.js +16 -0
- package/lib/constants/index.js.map +1 -0
- package/lib/constants/namespace-constants.d.ts +91 -0
- package/lib/constants/namespace-constants.js +211 -0
- package/lib/constants/namespace-constants.js.map +1 -0
- package/lib/debug/command-debug-proxy.d.ts +101 -0
- package/lib/debug/command-debug-proxy.js +171 -0
- package/lib/debug/command-debug-proxy.js.map +1 -0
- package/lib/debug/debug-logger.d.ts +85 -0
- package/lib/debug/debug-logger.js +133 -0
- package/lib/debug/debug-logger.js.map +1 -0
- package/lib/debug/index.d.ts +12 -0
- package/lib/debug/index.js +20 -0
- package/lib/debug/index.js.map +1 -0
- package/lib/debug/service-debug-proxy.d.ts +30 -0
- package/lib/debug/service-debug-proxy.js +102 -0
- package/lib/debug/service-debug-proxy.js.map +1 -0
- package/lib/hooks/prerun.d.ts +25 -0
- package/lib/hooks/prerun.js +47 -0
- package/lib/hooks/prerun.js.map +1 -0
- package/lib/mcp/config/mcp-config.d.ts +55 -0
- package/lib/mcp/config/mcp-config.js +51 -0
- package/lib/mcp/config/mcp-config.js.map +1 -0
- package/lib/mcp/config/pagination.d.ts +96 -0
- package/lib/mcp/config/pagination.js +108 -0
- package/lib/mcp/config/pagination.js.map +1 -0
- package/lib/mcp/config/system-prompts.d.ts +18 -0
- package/lib/mcp/config/system-prompts.js +92 -0
- package/lib/mcp/config/system-prompts.js.map +1 -0
- package/lib/mcp/errors.d.ts +23 -0
- package/lib/mcp/errors.js +27 -0
- package/lib/mcp/errors.js.map +1 -0
- package/lib/mcp/schemas/input-schemas.d.ts +327 -0
- package/lib/mcp/schemas/input-schemas.js +302 -0
- package/lib/mcp/schemas/input-schemas.js.map +1 -0
- package/lib/mcp/server.d.ts +40 -0
- package/lib/mcp/server.js +316 -0
- package/lib/mcp/server.js.map +1 -0
- package/lib/mcp/tools/contactpoint-tools.d.ts +14 -0
- package/lib/mcp/tools/contactpoint-tools.js +34 -0
- package/lib/mcp/tools/contactpoint-tools.js.map +1 -0
- package/lib/mcp/tools/definition-io-tools.d.ts +19 -0
- package/lib/mcp/tools/definition-io-tools.js +152 -0
- package/lib/mcp/tools/definition-io-tools.js.map +1 -0
- package/lib/mcp/tools/definition-tools.d.ts +51 -0
- package/lib/mcp/tools/definition-tools.js +199 -0
- package/lib/mcp/tools/definition-tools.js.map +1 -0
- package/lib/mcp/tools/index.d.ts +37 -0
- package/lib/mcp/tools/index.js +88 -0
- package/lib/mcp/tools/index.js.map +1 -0
- package/lib/mcp/tools/object-tools.d.ts +22 -0
- package/lib/mcp/tools/object-tools.js +306 -0
- package/lib/mcp/tools/object-tools.js.map +1 -0
- package/lib/mcp/tools/org-tools.d.ts +14 -0
- package/lib/mcp/tools/org-tools.js +177 -0
- package/lib/mcp/tools/org-tools.js.map +1 -0
- package/lib/mcp/tools/profile-tools.d.ts +59 -0
- package/lib/mcp/tools/profile-tools.js +213 -0
- package/lib/mcp/tools/profile-tools.js.map +1 -0
- package/lib/mcp/tools/summary-tools.d.ts +14 -0
- package/lib/mcp/tools/summary-tools.js +38 -0
- package/lib/mcp/tools/summary-tools.js.map +1 -0
- package/lib/mcp/tools/tool-factory.d.ts +63 -0
- package/lib/mcp/tools/tool-factory.js +146 -0
- package/lib/mcp/tools/tool-factory.js.map +1 -0
- package/lib/mcp/tools/user-tools.d.ts +25 -0
- package/lib/mcp/tools/user-tools.js +167 -0
- package/lib/mcp/tools/user-tools.js.map +1 -0
- package/lib/models/date-literal.d.ts +211 -0
- package/lib/models/date-literal.js +615 -0
- package/lib/models/date-literal.js.map +1 -0
- package/lib/models/object-describe-types.d.ts +173 -0
- package/lib/models/object-describe-types.js +9 -0
- package/lib/models/object-describe-types.js.map +1 -0
- package/lib/models/profile-request-types.d.ts +118 -0
- package/lib/models/profile-request-types.js +23 -0
- package/lib/models/profile-request-types.js.map +1 -0
- package/lib/models/profiling-execution-types.d.ts +154 -0
- package/lib/models/profiling-execution-types.js +14 -0
- package/lib/models/profiling-execution-types.js.map +1 -0
- package/lib/models/service-result.d.ts +114 -0
- package/lib/models/service-result.js +81 -0
- package/lib/models/service-result.js.map +1 -0
- package/lib/models/sfdmu-types.d.ts +53 -0
- package/lib/models/sfdmu-types.js +23 -0
- package/lib/models/sfdmu-types.js.map +1 -0
- package/lib/models/status-types.d.ts +38 -0
- package/lib/models/status-types.js +12 -0
- package/lib/models/status-types.js.map +1 -0
- package/lib/models/summary-bulk-types.d.ts +61 -0
- package/lib/models/summary-bulk-types.js +23 -0
- package/lib/models/summary-bulk-types.js.map +1 -0
- package/lib/models/user-details-types.d.ts +163 -0
- package/lib/models/user-details-types.js +9 -0
- package/lib/models/user-details-types.js.map +1 -0
- package/lib/models/year-range.d.ts +78 -0
- package/lib/models/year-range.js +153 -0
- package/lib/models/year-range.js.map +1 -0
- package/lib/operations/CompatibilityCheckOperation.d.ts +62 -0
- package/lib/operations/CompatibilityCheckOperation.js +102 -0
- package/lib/operations/CompatibilityCheckOperation.js.map +1 -0
- package/lib/operations/DefinitionCreateOperation.d.ts +411 -0
- package/lib/operations/DefinitionCreateOperation.js +1121 -0
- package/lib/operations/DefinitionCreateOperation.js.map +1 -0
- package/lib/operations/DefinitionExportOperation.d.ts +155 -0
- package/lib/operations/DefinitionExportOperation.js +281 -0
- package/lib/operations/DefinitionExportOperation.js.map +1 -0
- package/lib/operations/DefinitionImportOperation.d.ts +144 -0
- package/lib/operations/DefinitionImportOperation.js +357 -0
- package/lib/operations/DefinitionImportOperation.js.map +1 -0
- package/lib/operations/DefinitionListOperation.d.ts +66 -0
- package/lib/operations/DefinitionListOperation.js +108 -0
- package/lib/operations/DefinitionListOperation.js.map +1 -0
- package/lib/operations/DefinitionPurgeOperation.d.ts +199 -0
- package/lib/operations/DefinitionPurgeOperation.js +465 -0
- package/lib/operations/DefinitionPurgeOperation.js.map +1 -0
- package/lib/operations/DefinitionUpdateOperation.d.ts +78 -0
- package/lib/operations/DefinitionUpdateOperation.js +142 -0
- package/lib/operations/DefinitionUpdateOperation.js.map +1 -0
- package/lib/operations/OrgDetailsOperation.d.ts +253 -0
- package/lib/operations/OrgDetailsOperation.js +456 -0
- package/lib/operations/OrgDetailsOperation.js.map +1 -0
- package/lib/operations/OrgResetOperation.d.ts +114 -0
- package/lib/operations/OrgResetOperation.js +209 -0
- package/lib/operations/OrgResetOperation.js.map +1 -0
- package/lib/operations/ProfileOperation.d.ts +187 -0
- package/lib/operations/ProfileOperation.js +373 -0
- package/lib/operations/ProfileOperation.js.map +1 -0
- package/lib/operations/ProfileRequestCancelOperation.d.ts +59 -0
- package/lib/operations/ProfileRequestCancelOperation.js +137 -0
- package/lib/operations/ProfileRequestCancelOperation.js.map +1 -0
- package/lib/operations/ProfileRequestDeleteOperation.d.ts +64 -0
- package/lib/operations/ProfileRequestDeleteOperation.js +134 -0
- package/lib/operations/ProfileRequestDeleteOperation.js.map +1 -0
- package/lib/operations/ProfileRequestListOperation.d.ts +39 -0
- package/lib/operations/ProfileRequestListOperation.js +61 -0
- package/lib/operations/ProfileRequestListOperation.js.map +1 -0
- package/lib/operations/SummaryPurgeOperation.d.ts +134 -0
- package/lib/operations/SummaryPurgeOperation.js +257 -0
- package/lib/operations/SummaryPurgeOperation.js.map +1 -0
- package/lib/operations/SummaryReprofileOperation.d.ts +88 -0
- package/lib/operations/SummaryReprofileOperation.js +174 -0
- package/lib/operations/SummaryReprofileOperation.js.map +1 -0
- package/lib/operations/SummaryStopOperation.d.ts +87 -0
- package/lib/operations/SummaryStopOperation.js +175 -0
- package/lib/operations/SummaryStopOperation.js.map +1 -0
- package/lib/services/BulkExecutionService.d.ts +120 -0
- package/lib/services/BulkExecutionService.js +535 -0
- package/lib/services/BulkExecutionService.js.map +1 -0
- package/lib/services/CompatibilityService.d.ts +81 -0
- package/lib/services/CompatibilityService.js +118 -0
- package/lib/services/CompatibilityService.js.map +1 -0
- package/lib/services/ConfigureMode.d.ts +85 -0
- package/lib/services/ConfigureMode.js +390 -0
- package/lib/services/ConfigureMode.js.map +1 -0
- package/lib/services/ContactPointService.d.ts +111 -0
- package/lib/services/ContactPointService.js +286 -0
- package/lib/services/ContactPointService.js.map +1 -0
- package/lib/services/DataAvailabilityService.d.ts +81 -0
- package/lib/services/DataAvailabilityService.js +128 -0
- package/lib/services/DataAvailabilityService.js.map +1 -0
- package/lib/services/DefinitionFieldGenerationService.d.ts +309 -0
- package/lib/services/DefinitionFieldGenerationService.js +795 -0
- package/lib/services/DefinitionFieldGenerationService.js.map +1 -0
- package/lib/services/DefinitionQueryBuilder.d.ts +59 -0
- package/lib/services/DefinitionQueryBuilder.js +234 -0
- package/lib/services/DefinitionQueryBuilder.js.map +1 -0
- package/lib/services/ObjectDescribeService.d.ts +436 -0
- package/lib/services/ObjectDescribeService.js +869 -0
- package/lib/services/ObjectDescribeService.js.map +1 -0
- package/lib/services/ObjectFilteringService.d.ts +400 -0
- package/lib/services/ObjectFilteringService.js +878 -0
- package/lib/services/ObjectFilteringService.js.map +1 -0
- package/lib/services/ObjectListCommandService.d.ts +429 -0
- package/lib/services/ObjectListCommandService.js +873 -0
- package/lib/services/ObjectListCommandService.js.map +1 -0
- package/lib/services/ObjectListService.d.ts +201 -0
- package/lib/services/ObjectListService.js +345 -0
- package/lib/services/ObjectListService.js.map +1 -0
- package/lib/services/OrgInfoService.d.ts +485 -0
- package/lib/services/OrgInfoService.js +1122 -0
- package/lib/services/OrgInfoService.js.map +1 -0
- package/lib/services/PollingService.d.ts +105 -0
- package/lib/services/PollingService.js +117 -0
- package/lib/services/PollingService.js.map +1 -0
- package/lib/services/ProfileRequestService.d.ts +186 -0
- package/lib/services/ProfileRequestService.js +555 -0
- package/lib/services/ProfileRequestService.js.map +1 -0
- package/lib/services/ProfilingDefinitionService.d.ts +535 -0
- package/lib/services/ProfilingDefinitionService.js +981 -0
- package/lib/services/ProfilingDefinitionService.js.map +1 -0
- package/lib/services/ProfilingExecutionService.d.ts +122 -0
- package/lib/services/ProfilingExecutionService.js +320 -0
- package/lib/services/ProfilingExecutionService.js.map +1 -0
- package/lib/services/ProfilingSummaryService.d.ts +292 -0
- package/lib/services/ProfilingSummaryService.js +685 -0
- package/lib/services/ProfilingSummaryService.js.map +1 -0
- package/lib/services/RecordTypeService.d.ts +129 -0
- package/lib/services/RecordTypeService.js +284 -0
- package/lib/services/RecordTypeService.js.map +1 -0
- package/lib/services/SFDMUService.d.ts +133 -0
- package/lib/services/SFDMUService.js +295 -0
- package/lib/services/SFDMUService.js.map +1 -0
- package/lib/services/TabDetectionService.d.ts +105 -0
- package/lib/services/TabDetectionService.js +206 -0
- package/lib/services/TabDetectionService.js.map +1 -0
- package/lib/services/UnconfigureMode.d.ts +74 -0
- package/lib/services/UnconfigureMode.js +378 -0
- package/lib/services/UnconfigureMode.js.map +1 -0
- package/lib/services/UserConfigurationService.d.ts +155 -0
- package/lib/services/UserConfigurationService.js +573 -0
- package/lib/services/UserConfigurationService.js.map +1 -0
- package/lib/services/UserConfigurationTypes.d.ts +181 -0
- package/lib/services/UserConfigurationTypes.js +14 -0
- package/lib/services/UserConfigurationTypes.js.map +1 -0
- package/lib/services/UserReadinessService.d.ts +330 -0
- package/lib/services/UserReadinessService.js +831 -0
- package/lib/services/UserReadinessService.js.map +1 -0
- package/lib/services/constants.d.ts +53 -0
- package/lib/services/constants.js +71 -0
- package/lib/services/constants.js.map +1 -0
- package/lib/services/namespace-constants.d.ts +1 -0
- package/lib/services/namespace-constants.js +11 -0
- package/lib/services/namespace-constants.js.map +1 -0
- package/lib/services/validation.d.ts +47 -0
- package/lib/services/validation.js +119 -0
- package/lib/services/validation.js.map +1 -0
- package/lib/utils/batch-processor.d.ts +13 -0
- package/lib/utils/batch-processor.js +39 -0
- package/lib/utils/batch-processor.js.map +1 -0
- package/lib/utils/formatting/availability-grid.d.ts +81 -0
- package/lib/utils/formatting/availability-grid.js +94 -0
- package/lib/utils/formatting/availability-grid.js.map +1 -0
- package/lib/utils/formatting/business-process-grid.d.ts +51 -0
- package/lib/utils/formatting/business-process-grid.js +58 -0
- package/lib/utils/formatting/business-process-grid.js.map +1 -0
- package/lib/utils/formatting/command-display.d.ts +154 -0
- package/lib/utils/formatting/command-display.js +154 -0
- package/lib/utils/formatting/command-display.js.map +1 -0
- package/lib/utils/formatting/definition-create-display.d.ts +118 -0
- package/lib/utils/formatting/definition-create-display.js +231 -0
- package/lib/utils/formatting/definition-create-display.js.map +1 -0
- package/lib/utils/formatting/empty-states.d.ts +35 -0
- package/lib/utils/formatting/empty-states.js +70 -0
- package/lib/utils/formatting/empty-states.js.map +1 -0
- package/lib/utils/formatting/errors.d.ts +33 -0
- package/lib/utils/formatting/errors.js +72 -0
- package/lib/utils/formatting/errors.js.map +1 -0
- package/lib/utils/formatting/field-types.d.ts +32 -0
- package/lib/utils/formatting/field-types.js +88 -0
- package/lib/utils/formatting/field-types.js.map +1 -0
- package/lib/utils/formatting/index.d.ts +29 -0
- package/lib/utils/formatting/index.js +28 -0
- package/lib/utils/formatting/index.js.map +1 -0
- package/lib/utils/formatting/indicators.d.ts +113 -0
- package/lib/utils/formatting/indicators.js +161 -0
- package/lib/utils/formatting/indicators.js.map +1 -0
- package/lib/utils/formatting/loading-messages.d.ts +37 -0
- package/lib/utils/formatting/loading-messages.js +50 -0
- package/lib/utils/formatting/loading-messages.js.map +1 -0
- package/lib/utils/formatting/namespace-display.d.ts +31 -0
- package/lib/utils/formatting/namespace-display.js +64 -0
- package/lib/utils/formatting/namespace-display.js.map +1 -0
- package/lib/utils/formatting/numbers.d.ts +73 -0
- package/lib/utils/formatting/numbers.js +187 -0
- package/lib/utils/formatting/numbers.js.map +1 -0
- package/lib/utils/formatting/object-describe-display.d.ts +114 -0
- package/lib/utils/formatting/object-describe-display.js +440 -0
- package/lib/utils/formatting/object-describe-display.js.map +1 -0
- package/lib/utils/formatting/object-list-display.d.ts +213 -0
- package/lib/utils/formatting/object-list-display.js +672 -0
- package/lib/utils/formatting/object-list-display.js.map +1 -0
- package/lib/utils/formatting/org-identity.d.ts +15 -0
- package/lib/utils/formatting/org-identity.js +28 -0
- package/lib/utils/formatting/org-identity.js.map +1 -0
- package/lib/utils/formatting/record-age-grid.d.ts +41 -0
- package/lib/utils/formatting/record-age-grid.js +56 -0
- package/lib/utils/formatting/record-age-grid.js.map +1 -0
- package/lib/utils/formatting/sections.d.ts +108 -0
- package/lib/utils/formatting/sections.js +150 -0
- package/lib/utils/formatting/sections.js.map +1 -0
- package/lib/utils/formatting/tables.d.ts +90 -0
- package/lib/utils/formatting/tables.js +113 -0
- package/lib/utils/formatting/tables.js.map +1 -0
- package/lib/utils/formatting/user-details-display.d.ts +101 -0
- package/lib/utils/formatting/user-details-display.js +425 -0
- package/lib/utils/formatting/user-details-display.js.map +1 -0
- package/lib/utils/pagination/index.d.ts +11 -0
- package/lib/utils/pagination/index.js +18 -0
- package/lib/utils/pagination/index.js.map +1 -0
- package/lib/utils/pagination/keypress-reader.d.ts +20 -0
- package/lib/utils/pagination/keypress-reader.js +63 -0
- package/lib/utils/pagination/keypress-reader.js.map +1 -0
- package/lib/utils/pagination/paginate-output.d.ts +48 -0
- package/lib/utils/pagination/paginate-output.js +136 -0
- package/lib/utils/pagination/paginate-output.js.map +1 -0
- package/messages/compatibility.check.md +71 -0
- package/messages/cuneiform.access.md +138 -0
- package/messages/definition.create.md +511 -0
- package/messages/definition.export.md +84 -0
- package/messages/definition.get.md +147 -0
- package/messages/definition.import.md +65 -0
- package/messages/definition.list.md +264 -0
- package/messages/definition.purge.md +318 -0
- package/messages/definition.update.md +118 -0
- package/messages/mcp.serve.md +66 -0
- package/messages/object.describe.md +201 -0
- package/messages/object.list.md +443 -0
- package/messages/org.details.md +386 -0
- package/messages/org.reset.md +71 -0
- package/messages/profile.md +231 -0
- package/messages/profile.request.cancel.md +143 -0
- package/messages/profile.request.delete.md +139 -0
- package/messages/profile.request.list.md +89 -0
- package/messages/summary.purge.md +218 -0
- package/messages/summary.reprofile.md +150 -0
- package/messages/summary.stop.md +157 -0
- package/messages/user.details.md +501 -0
- package/oclif.lock +2887 -2149
- package/oclif.manifest.json +2813 -31
- package/package.json +94 -19
- package/lib/commands/cuneiform/about.d.ts +0 -13
- package/lib/commands/cuneiform/about.js +0 -26
- package/lib/commands/cuneiform/about.js.map +0 -1
- package/lib/commands/hello/world.d.ts +0 -14
- package/lib/commands/hello/world.js +0 -27
- package/lib/commands/hello/world.js.map +0 -1
- package/lib/index.d.ts +0 -2
- package/lib/index.js +0 -2
- package/lib/index.js.map +0 -1
- package/messages/cuneiform.about.md +0 -19
- package/messages/hello.world.md +0 -29
|
@@ -0,0 +1,1122 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026, PeerNova, Inc. All Rights Reserved.
|
|
3
|
+
* PROPRIETARY AND CONFIDENTIAL. Unauthorized copying, modification,
|
|
4
|
+
* or distribution is strictly prohibited. Use is governed by the
|
|
5
|
+
* Master Subscription Agreement (MSA) between PeerNova, Inc. and the
|
|
6
|
+
* licensee. See LICENSE file in the repo root.
|
|
7
|
+
*/
|
|
8
|
+
import { createSuccessResult, createFailureResult } from '../models/service-result.js';
|
|
9
|
+
import { ServiceErrorCodes, sanitizeSoqlIdentifier } from '../adapters/errors.js';
|
|
10
|
+
import { CuneiformQueryBuilder } from '../adapters/soql/cuneiform-query-builder.js';
|
|
11
|
+
import { CUNEIFORM_NAMESPACE, getProductName, CLOUD_NAMESPACE_MAP } from './namespace-constants.js';
|
|
12
|
+
/**
|
|
13
|
+
* License name patterns that indicate Sales Cloud.
|
|
14
|
+
*/
|
|
15
|
+
const SALES_CLOUD_LICENSE_PATTERNS = ['Salesforce', 'Sales Cloud', 'Sales User'];
|
|
16
|
+
/**
|
|
17
|
+
* License name patterns that indicate Service Cloud.
|
|
18
|
+
*/
|
|
19
|
+
const SERVICE_CLOUD_LICENSE_PATTERNS = ['Salesforce', 'Service Cloud', 'Service User'];
|
|
20
|
+
/**
|
|
21
|
+
* PermissionSetLicense patterns that indicate installed cloud products.
|
|
22
|
+
* Each entry maps label/name patterns to a cloud product name.
|
|
23
|
+
*/
|
|
24
|
+
const PSL_CLOUD_PATTERNS = [
|
|
25
|
+
{ patterns: ['Einstein Analytics', 'CRM Analytics', 'Einstein Prediction'], cloudName: 'Analytics Cloud' },
|
|
26
|
+
{ patterns: ['CPQ', 'Configure Price Quote', 'Revenue Cloud'], cloudName: 'Revenue Cloud' },
|
|
27
|
+
{ patterns: ['B2C Commerce', 'B2B Commerce', 'Commerce Cloud'], cloudName: 'Commerce Cloud' },
|
|
28
|
+
{ patterns: ['Marketing Cloud', 'Pardot', 'Account Engagement'], cloudName: 'Marketing Cloud' },
|
|
29
|
+
{ patterns: ['Tableau', 'Tableau CRM'], cloudName: 'Tableau' },
|
|
30
|
+
{ patterns: ['Education Cloud', 'EDA '], cloudName: 'Education Cloud' },
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Service for aggregating org-level metadata.
|
|
34
|
+
*
|
|
35
|
+
* Provides methods to retrieve org identity, API limits, installed packages,
|
|
36
|
+
* active namespaces, and Cuneiform version information.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const service = new OrgInfoService({
|
|
41
|
+
* connectionFacade,
|
|
42
|
+
* soqlAdapter,
|
|
43
|
+
* toolingAdapter, // optional
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* const result = await service.getOrgInfo();
|
|
47
|
+
* if (result.success) {
|
|
48
|
+
* console.log(`Org: ${result.data.identity.organizationName}`);
|
|
49
|
+
* console.log(`Cuneiform: ${result.data.cuneiformVersion ?? 'Not installed'}`);
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export class OrgInfoService {
|
|
54
|
+
connectionFacade;
|
|
55
|
+
soqlAdapter;
|
|
56
|
+
toolingAdapter;
|
|
57
|
+
restAdapter;
|
|
58
|
+
logger;
|
|
59
|
+
restClient;
|
|
60
|
+
constructor(config) {
|
|
61
|
+
this.connectionFacade = config.connectionFacade;
|
|
62
|
+
this.soqlAdapter = config.soqlAdapter;
|
|
63
|
+
this.toolingAdapter = config.toolingAdapter;
|
|
64
|
+
this.restAdapter = config.restAdapter;
|
|
65
|
+
this.logger = config.logger;
|
|
66
|
+
this.restClient = config.restClient;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Classifies the org type based on Organization record data.
|
|
70
|
+
*
|
|
71
|
+
* @param isSandbox - Whether the org is marked as sandbox
|
|
72
|
+
* @param orgType - The OrganizationType field value
|
|
73
|
+
* @param trialExpirationDate - Trial expiration date if set
|
|
74
|
+
* @returns The classified OrgType
|
|
75
|
+
*/
|
|
76
|
+
static classifyOrgType(isSandbox, orgType, trialExpirationDate) {
|
|
77
|
+
if (isSandbox) {
|
|
78
|
+
return 'Sandbox';
|
|
79
|
+
}
|
|
80
|
+
const normalizedType = orgType?.toLowerCase() ?? '';
|
|
81
|
+
if (normalizedType.includes('scratch')) {
|
|
82
|
+
return 'Scratch';
|
|
83
|
+
}
|
|
84
|
+
if (normalizedType.includes('developer')) {
|
|
85
|
+
return 'Developer';
|
|
86
|
+
}
|
|
87
|
+
if (trialExpirationDate) {
|
|
88
|
+
return 'Trial';
|
|
89
|
+
}
|
|
90
|
+
return 'Production';
|
|
91
|
+
}
|
|
92
|
+
// ── Private static methods ──────────────────────────────────────────
|
|
93
|
+
/**
|
|
94
|
+
* Transforms raw limits response to OrgLimit format.
|
|
95
|
+
*
|
|
96
|
+
* Calculates percentage utilization from Max and Remaining values.
|
|
97
|
+
* Returns 0% if Max is 0 to avoid division by zero.
|
|
98
|
+
*/
|
|
99
|
+
static transformLimit(name, rawLimit) {
|
|
100
|
+
const used = rawLimit.Max - rawLimit.Remaining;
|
|
101
|
+
const percentUsed = rawLimit.Max > 0 ? Math.round((used / rawLimit.Max) * 100 * 10) / 10 : 0;
|
|
102
|
+
return { name, max: rawLimit.Max, used, percentUsed };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Maps InstalledPackageRecord to InstalledPackage.
|
|
106
|
+
*
|
|
107
|
+
* Formats version as semantic version string (Major.Minor.Patch.Build)
|
|
108
|
+
* and looks up human-readable product name by namespace prefix.
|
|
109
|
+
*/
|
|
110
|
+
static mapPackage(record) {
|
|
111
|
+
const version = record.SubscriberPackageVersion;
|
|
112
|
+
const versionNumber = `${version.MajorVersion}.${version.MinorVersion}.${version.PatchVersion}.${version.BuildNumber}`;
|
|
113
|
+
const namespace = record.SubscriberPackage.NamespacePrefix;
|
|
114
|
+
return {
|
|
115
|
+
id: record.Id,
|
|
116
|
+
namespacePrefix: namespace,
|
|
117
|
+
name: record.SubscriberPackage.Name,
|
|
118
|
+
versionNumber,
|
|
119
|
+
isManaged: Boolean(namespace),
|
|
120
|
+
productName: namespace ? getProductName(namespace) : undefined,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Extracts active namespaces from installed packages.
|
|
125
|
+
*/
|
|
126
|
+
static extractActiveNamespaces(packages) {
|
|
127
|
+
const namespaces = packages.filter((p) => p.namespacePrefix).map((p) => p.namespacePrefix);
|
|
128
|
+
return [...new Set(namespaces)].sort();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Finds the Cuneiform package version from installed packages.
|
|
132
|
+
*/
|
|
133
|
+
static findCuneiformVersion(packages) {
|
|
134
|
+
const cuneiformPackage = packages.find((p) => p.namespacePrefix === CUNEIFORM_NAMESPACE);
|
|
135
|
+
return cuneiformPackage?.versionNumber ?? null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Detects additional cloud products from PermissionSetLicense data.
|
|
139
|
+
*/
|
|
140
|
+
static detectPslClouds(licenses) {
|
|
141
|
+
const pslLicenses = licenses.filter((lic) => lic.type === 'permissionSet');
|
|
142
|
+
const clouds = [];
|
|
143
|
+
for (const pslEntry of PSL_CLOUD_PATTERNS) {
|
|
144
|
+
const match = pslLicenses.find((lic) => pslEntry.patterns.some((pattern) => lic.masterLabel.includes(pattern) || lic.name.includes(pattern)));
|
|
145
|
+
if (match) {
|
|
146
|
+
clouds.push({
|
|
147
|
+
name: pslEntry.cloudName,
|
|
148
|
+
type: 'license',
|
|
149
|
+
installed: true,
|
|
150
|
+
totalLicenses: match.totalLicenses,
|
|
151
|
+
usedLicenses: match.usedLicenses,
|
|
152
|
+
status: match.status,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return clouds;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Groups objects by namespace from global describe result.
|
|
160
|
+
*
|
|
161
|
+
* Extracts namespace prefixes from custom object names matching pattern:
|
|
162
|
+
* Namespace__ObjectName__c
|
|
163
|
+
*
|
|
164
|
+
* @param globalDescribe - The global describe result
|
|
165
|
+
* @returns Map of namespace prefix to object names and count
|
|
166
|
+
*/
|
|
167
|
+
static groupNamespaceObjects(globalDescribe) {
|
|
168
|
+
const namespaceGroups = new Map();
|
|
169
|
+
for (const obj of globalDescribe.sobjects) {
|
|
170
|
+
// Custom objects with namespaces: Namespace__ObjectName__c (standard/custom only)
|
|
171
|
+
// Excludes __mdt (custom metadata), __e (platform events), __b (big objects)
|
|
172
|
+
const match = obj.name.match(/^([A-Za-z0-9]+)__\w+__c$/);
|
|
173
|
+
if (match) {
|
|
174
|
+
const namespace = match[1];
|
|
175
|
+
const group = namespaceGroups.get(namespace) ?? { count: 0, objects: [] };
|
|
176
|
+
group.count++;
|
|
177
|
+
group.objects.push(obj.name);
|
|
178
|
+
namespaceGroups.set(namespace, group);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return namespaceGroups;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Builds the SOQL string for a LIMIT 0 field-existence probe, sanitizing both
|
|
185
|
+
* identifiers via sanitizeSoqlIdentifier. Returns null when either identifier
|
|
186
|
+
* is invalid — callers MUST treat null as "field cannot exist" without invoking
|
|
187
|
+
* the SOQL adapter.
|
|
188
|
+
*
|
|
189
|
+
* Pure, side-effect-free, and exposed at the class level so the sanitizer
|
|
190
|
+
* hardening introduced in CLI-3174 can be unit-tested directly.
|
|
191
|
+
*/
|
|
192
|
+
static buildProbeQuery(objectName, fieldName) {
|
|
193
|
+
try {
|
|
194
|
+
const obj = sanitizeSoqlIdentifier(objectName);
|
|
195
|
+
const field = sanitizeSoqlIdentifier(fieldName);
|
|
196
|
+
return `SELECT ${field} FROM ${obj} LIMIT 0`;
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// ── Public instance methods ─────────────────────────────────────────
|
|
203
|
+
/**
|
|
204
|
+
* Retrieves complete org information.
|
|
205
|
+
*
|
|
206
|
+
* Executes multiple API calls in parallel for performance:
|
|
207
|
+
* - Identity + Organization SOQL (getOrgIdentity)
|
|
208
|
+
* - Limits API (getOrgLimits)
|
|
209
|
+
* - Installed packages via Tooling API (getInstalledPackages)
|
|
210
|
+
*
|
|
211
|
+
* @returns ServiceResult containing complete OrgInfo
|
|
212
|
+
*/
|
|
213
|
+
async getOrgInfo() {
|
|
214
|
+
const startTime = Date.now();
|
|
215
|
+
const warnings = [];
|
|
216
|
+
const emptyResult = {
|
|
217
|
+
identity: { userId: '', organizationId: '', username: '' },
|
|
218
|
+
limits: { limits: {} },
|
|
219
|
+
packages: [],
|
|
220
|
+
activeNamespaces: [],
|
|
221
|
+
cuneiformVersion: null,
|
|
222
|
+
};
|
|
223
|
+
// REST API path: delegate to server when restClient is available
|
|
224
|
+
if (this.restClient) {
|
|
225
|
+
return this.getOrgInfoViaRest(startTime);
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
this.logger?.log('Fetching org info...');
|
|
229
|
+
// Execute parallel API calls
|
|
230
|
+
const [identityResult, limitsResult, packagesResult] = await Promise.all([
|
|
231
|
+
this.getOrgIdentity(),
|
|
232
|
+
this.getOrgLimits(),
|
|
233
|
+
this.getInstalledPackages(),
|
|
234
|
+
]);
|
|
235
|
+
// Handle identity result
|
|
236
|
+
if (!identityResult.success) {
|
|
237
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_INFO_QUERY_FAILED, identityResult.message ?? 'Failed to get org identity', {
|
|
238
|
+
metadata: { duration: Date.now() - startTime },
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
// Handle limits result (warning if failed, not fatal)
|
|
242
|
+
let limits = emptyResult.limits;
|
|
243
|
+
if (!limitsResult.success) {
|
|
244
|
+
warnings.push(`Limits query failed: ${limitsResult.message ?? 'Unknown error'}`);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
limits = limitsResult.data;
|
|
248
|
+
}
|
|
249
|
+
// Handle packages result (warning if failed, not fatal)
|
|
250
|
+
let packages = emptyResult.packages;
|
|
251
|
+
if (!packagesResult.success) {
|
|
252
|
+
warnings.push(`Package query failed: ${packagesResult.message ?? 'Unknown error'}`);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
packages = packagesResult.data;
|
|
256
|
+
}
|
|
257
|
+
// Extract namespaces and Cuneiform version
|
|
258
|
+
const activeNamespaces = OrgInfoService.extractActiveNamespaces(packages);
|
|
259
|
+
const cuneiformVersion = OrgInfoService.findCuneiformVersion(packages);
|
|
260
|
+
const orgInfo = {
|
|
261
|
+
identity: identityResult.data,
|
|
262
|
+
limits,
|
|
263
|
+
packages,
|
|
264
|
+
activeNamespaces,
|
|
265
|
+
cuneiformVersion,
|
|
266
|
+
};
|
|
267
|
+
const duration = Date.now() - startTime;
|
|
268
|
+
this.logger?.log(`Org info retrieved in ${duration}ms`);
|
|
269
|
+
return createSuccessResult(orgInfo, {
|
|
270
|
+
message: `Org info retrieved for ${identityResult.data.organizationName ?? identityResult.data.organizationId}`,
|
|
271
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
272
|
+
metadata: { duration },
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
277
|
+
this.logger?.log(`Org info query failed: ${errorMessage}`);
|
|
278
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_INFO_QUERY_FAILED, errorMessage, {
|
|
279
|
+
metadata: { duration: Date.now() - startTime },
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Retrieves org identity information.
|
|
285
|
+
*
|
|
286
|
+
* Combines connection.identity() with Organization SOQL query for complete info.
|
|
287
|
+
*
|
|
288
|
+
* @returns ServiceResult containing OrgIdentity
|
|
289
|
+
*/
|
|
290
|
+
async getOrgIdentity() {
|
|
291
|
+
const startTime = Date.now();
|
|
292
|
+
const emptyResult = { userId: '', organizationId: '', username: '' };
|
|
293
|
+
try {
|
|
294
|
+
this.logger?.log('Fetching org identity...');
|
|
295
|
+
// Build Organization SOQL using CuneiformQueryBuilder
|
|
296
|
+
const orgSoql = new CuneiformQueryBuilder()
|
|
297
|
+
.select(['Id', 'Name', 'IsSandbox', 'OrganizationType', 'NamespacePrefix'])
|
|
298
|
+
.from('Organization')
|
|
299
|
+
.limit(1)
|
|
300
|
+
.toSOQL();
|
|
301
|
+
// Execute identity and org query in parallel
|
|
302
|
+
const [identity, orgResult] = await Promise.all([
|
|
303
|
+
this.connectionFacade.identity(),
|
|
304
|
+
this.soqlAdapter.query(orgSoql),
|
|
305
|
+
]);
|
|
306
|
+
const orgRecord = orgResult.success && orgResult.data.records.length > 0 ? orgResult.data.records[0] : null;
|
|
307
|
+
const result = {
|
|
308
|
+
userId: identity.user_id,
|
|
309
|
+
organizationId: identity.organization_id,
|
|
310
|
+
username: identity.username,
|
|
311
|
+
organizationName: orgRecord?.Name,
|
|
312
|
+
isSandbox: orgRecord?.IsSandbox,
|
|
313
|
+
organizationType: orgRecord?.OrganizationType,
|
|
314
|
+
namespacePrefix: orgRecord?.NamespacePrefix ?? undefined,
|
|
315
|
+
};
|
|
316
|
+
const duration = Date.now() - startTime;
|
|
317
|
+
this.logger?.log(`Org identity retrieved: ${result.organizationName ?? result.organizationId}`);
|
|
318
|
+
return createSuccessResult(result, {
|
|
319
|
+
message: `Identity retrieved for ${result.username}`,
|
|
320
|
+
metadata: { duration },
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
325
|
+
this.logger?.log(`Org identity failed: ${errorMessage}`);
|
|
326
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_IDENTITY_FAILED, errorMessage, {
|
|
327
|
+
metadata: { duration: Date.now() - startTime },
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Retrieves org API limits.
|
|
333
|
+
*
|
|
334
|
+
* Calls /limits REST endpoint and transforms to OrgLimits format.
|
|
335
|
+
*
|
|
336
|
+
* @returns ServiceResult containing OrgLimits
|
|
337
|
+
*/
|
|
338
|
+
async getOrgLimits() {
|
|
339
|
+
const startTime = Date.now();
|
|
340
|
+
const emptyResult = { limits: {} };
|
|
341
|
+
try {
|
|
342
|
+
this.logger?.log('Fetching org limits...');
|
|
343
|
+
const response = await this.connectionFacade.request('/limits');
|
|
344
|
+
// Transform response
|
|
345
|
+
const limits = {};
|
|
346
|
+
for (const [name, rawLimit] of Object.entries(response)) {
|
|
347
|
+
limits[name] = OrgInfoService.transformLimit(name, rawLimit);
|
|
348
|
+
}
|
|
349
|
+
const result = {
|
|
350
|
+
limits,
|
|
351
|
+
dailyApiRequests: limits['DailyApiRequests'],
|
|
352
|
+
dataStorageMB: limits['DataStorageMB'],
|
|
353
|
+
fileStorageMB: limits['FileStorageMB'],
|
|
354
|
+
dailyBulkApiRequests: limits['DailyBulkApiRequests'],
|
|
355
|
+
dailyBulkV2QueryJobs: limits['DailyBulkV2QueryJobs'],
|
|
356
|
+
dailyAsyncApexExecutions: limits['DailyAsyncApexExecutions'],
|
|
357
|
+
dailyStreamingApiEvents: limits['DailyStreamingApiEvents'],
|
|
358
|
+
};
|
|
359
|
+
const duration = Date.now() - startTime;
|
|
360
|
+
this.logger?.log(`Org limits retrieved: ${Object.keys(limits).length} limits`);
|
|
361
|
+
return createSuccessResult(result, {
|
|
362
|
+
message: `Retrieved ${Object.keys(limits).length} limits`,
|
|
363
|
+
metadata: { duration },
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
368
|
+
this.logger?.log(`Org limits failed: ${errorMessage}`);
|
|
369
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_LIMITS_FAILED, errorMessage, {
|
|
370
|
+
metadata: { duration: Date.now() - startTime },
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Retrieves installed packages via Tooling API.
|
|
376
|
+
*
|
|
377
|
+
* If Tooling adapter is not provided, returns empty array with warning.
|
|
378
|
+
*
|
|
379
|
+
* @returns ServiceResult containing array of InstalledPackage
|
|
380
|
+
*/
|
|
381
|
+
async getInstalledPackages() {
|
|
382
|
+
const startTime = Date.now();
|
|
383
|
+
if (!this.toolingAdapter) {
|
|
384
|
+
this.logger?.log('Tooling adapter not provided, skipping package query');
|
|
385
|
+
return createSuccessResult([], {
|
|
386
|
+
message: 'Package query skipped (no Tooling adapter)',
|
|
387
|
+
warnings: ['Tooling adapter not provided, cannot query installed packages'],
|
|
388
|
+
metadata: { duration: Date.now() - startTime },
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
this.logger?.log('Fetching installed packages...');
|
|
393
|
+
// PTA-1436: Cuneiform version is sourced from InstalledSubscriberPackage (Tooling API).
|
|
394
|
+
// The version number is constructed from MajorVersion.MinorVersion.PatchVersion.BuildNumber
|
|
395
|
+
// on the SubscriberPackageVersion relationship. License status is not exposed via standard
|
|
396
|
+
// Salesforce APIs — see getCuneiformPackageInfo() which defaults to 'Active' if installed.
|
|
397
|
+
const soql = new CuneiformQueryBuilder()
|
|
398
|
+
.select([
|
|
399
|
+
'Id',
|
|
400
|
+
'SubscriberPackage.Id',
|
|
401
|
+
'SubscriberPackage.Name',
|
|
402
|
+
'SubscriberPackage.NamespacePrefix',
|
|
403
|
+
'SubscriberPackageVersion.Id',
|
|
404
|
+
'SubscriberPackageVersion.MajorVersion',
|
|
405
|
+
'SubscriberPackageVersion.MinorVersion',
|
|
406
|
+
'SubscriberPackageVersion.PatchVersion',
|
|
407
|
+
'SubscriberPackageVersion.BuildNumber',
|
|
408
|
+
])
|
|
409
|
+
.from('InstalledSubscriberPackage')
|
|
410
|
+
.orderBy('SubscriberPackage.Name', 'ASC')
|
|
411
|
+
.toSOQL();
|
|
412
|
+
const result = await this.toolingAdapter.query(soql);
|
|
413
|
+
if (!result.success) {
|
|
414
|
+
return createFailureResult([], ServiceErrorCodes.ORG_PACKAGE_QUERY_FAILED, result.message ?? 'Package query failed', {
|
|
415
|
+
metadata: { duration: Date.now() - startTime },
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
const packages = result.data.records.map((record) => OrgInfoService.mapPackage(record));
|
|
419
|
+
const duration = Date.now() - startTime;
|
|
420
|
+
this.logger?.log(`Found ${packages.length} installed packages`);
|
|
421
|
+
return createSuccessResult(packages, {
|
|
422
|
+
message: `Found ${packages.length} installed packages`,
|
|
423
|
+
metadata: { duration },
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
428
|
+
this.logger?.log(`Package query failed: ${errorMessage}`);
|
|
429
|
+
return createFailureResult([], ServiceErrorCodes.ORG_PACKAGE_QUERY_FAILED, errorMessage, {
|
|
430
|
+
metadata: { duration: Date.now() - startTime },
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Retrieves active namespaces from installed packages.
|
|
436
|
+
*
|
|
437
|
+
* @returns ServiceResult containing array of namespace strings
|
|
438
|
+
*/
|
|
439
|
+
async getActiveNamespaces() {
|
|
440
|
+
const startTime = Date.now();
|
|
441
|
+
try {
|
|
442
|
+
const packagesResult = await this.getInstalledPackages();
|
|
443
|
+
if (!packagesResult.success) {
|
|
444
|
+
return createFailureResult([], ServiceErrorCodes.ORG_NAMESPACE_QUERY_FAILED, packagesResult.message ?? 'Failed to get namespaces', {
|
|
445
|
+
metadata: { duration: Date.now() - startTime },
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
const namespaces = OrgInfoService.extractActiveNamespaces(packagesResult.data);
|
|
449
|
+
const duration = Date.now() - startTime;
|
|
450
|
+
return createSuccessResult(namespaces, {
|
|
451
|
+
message: `Found ${namespaces.length} active namespaces`,
|
|
452
|
+
metadata: { duration },
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
catch (error) {
|
|
456
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
457
|
+
return createFailureResult([], ServiceErrorCodes.ORG_NAMESPACE_QUERY_FAILED, errorMessage, {
|
|
458
|
+
metadata: { duration: Date.now() - startTime },
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Retrieves the Cuneiform version if installed.
|
|
464
|
+
*
|
|
465
|
+
* @returns ServiceResult containing version string or null if not installed
|
|
466
|
+
*/
|
|
467
|
+
async getCuneiformVersion() {
|
|
468
|
+
const startTime = Date.now();
|
|
469
|
+
try {
|
|
470
|
+
const packagesResult = await this.getInstalledPackages();
|
|
471
|
+
if (!packagesResult.success) {
|
|
472
|
+
return createFailureResult(null, ServiceErrorCodes.ORG_CUNEIFORM_VERSION_FAILED, packagesResult.message ?? 'Failed to get Cuneiform version', {
|
|
473
|
+
metadata: { duration: Date.now() - startTime },
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
const version = OrgInfoService.findCuneiformVersion(packagesResult.data);
|
|
477
|
+
const duration = Date.now() - startTime;
|
|
478
|
+
return createSuccessResult(version, {
|
|
479
|
+
message: version ? `Cuneiform version: ${version}` : 'Cuneiform not installed',
|
|
480
|
+
metadata: { duration },
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
catch (error) {
|
|
484
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
485
|
+
return createFailureResult(null, ServiceErrorCodes.ORG_CUNEIFORM_VERSION_FAILED, errorMessage, {
|
|
486
|
+
metadata: { duration: Date.now() - startTime },
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Fetches the describeGlobal result from the REST adapter.
|
|
492
|
+
*
|
|
493
|
+
* Used by OrgDetailsOperation to cache the result across multiple service calls,
|
|
494
|
+
* avoiding redundant API round-trips for features, clouds, and namespace detection.
|
|
495
|
+
*
|
|
496
|
+
* @returns ServiceResult containing DescribeGlobalResult
|
|
497
|
+
*/
|
|
498
|
+
async fetchDescribeGlobal() {
|
|
499
|
+
if (!this.restAdapter) {
|
|
500
|
+
return createFailureResult({ maxBatchSize: 0, sobjects: [] }, ServiceErrorCodes.ORG_FEATURES_QUERY_FAILED, 'REST adapter not available');
|
|
501
|
+
}
|
|
502
|
+
return this.restAdapter.describeGlobal();
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Detects org features via object/field probing.
|
|
506
|
+
*
|
|
507
|
+
* Checks for:
|
|
508
|
+
* - Multi-Currency: CurrencyType object exists in describeGlobal
|
|
509
|
+
* - Enhanced Notes: ContentNote object is queryable
|
|
510
|
+
* - State/Country Picklists: BillingCountryCode field exists on Account
|
|
511
|
+
* - Territory Management: Territory2 object exists
|
|
512
|
+
* - Person Accounts: IsPersonAccount field exists on Account
|
|
513
|
+
* - Digital Experiences: Network object exists in describeGlobal
|
|
514
|
+
* - Einstein AI: AIRecordInsight object exists in describeGlobal
|
|
515
|
+
*
|
|
516
|
+
* @param cachedGlobal - Optional pre-fetched describeGlobal result to avoid redundant API calls
|
|
517
|
+
* @returns ServiceResult containing OrgFeatures
|
|
518
|
+
*/
|
|
519
|
+
async getOrgFeatures(cachedGlobal) {
|
|
520
|
+
const startTime = Date.now();
|
|
521
|
+
const emptyResult = {
|
|
522
|
+
multiCurrency: false,
|
|
523
|
+
enhancedNotes: false,
|
|
524
|
+
stateCountryPicklists: false,
|
|
525
|
+
territoryManagement: false,
|
|
526
|
+
personAccounts: false,
|
|
527
|
+
digitalExperiences: false,
|
|
528
|
+
einsteinAI: false,
|
|
529
|
+
};
|
|
530
|
+
if (!this.restAdapter) {
|
|
531
|
+
this.logger?.log('REST adapter not provided, skipping feature detection');
|
|
532
|
+
return createSuccessResult(emptyResult, {
|
|
533
|
+
message: 'Feature detection skipped (no REST adapter)',
|
|
534
|
+
warnings: ['REST adapter not provided, cannot detect org features'],
|
|
535
|
+
metadata: { duration: Date.now() - startTime },
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
try {
|
|
539
|
+
this.logger?.log('Detecting org features...');
|
|
540
|
+
// Use provided cache if available; otherwise fetch fresh
|
|
541
|
+
const globalResult = cachedGlobal ?? (await this.restAdapter.describeGlobal());
|
|
542
|
+
if (!globalResult.success) {
|
|
543
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_FEATURES_QUERY_FAILED, globalResult.message ?? 'Failed to detect org features', {
|
|
544
|
+
metadata: { duration: Date.now() - startTime },
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
const sobjectNames = new Set(globalResult.data.sobjects.map((obj) => obj.name));
|
|
548
|
+
// Check Multi-Currency: CurrencyType object exists
|
|
549
|
+
const multiCurrency = sobjectNames.has('CurrencyType');
|
|
550
|
+
// Check Enhanced Notes: ContentNote object exists and is queryable
|
|
551
|
+
const contentNoteObj = globalResult.data.sobjects.find((obj) => obj.name === 'ContentNote');
|
|
552
|
+
const enhancedNotes = contentNoteObj?.queryable ?? false;
|
|
553
|
+
// Check Territory Management: Territory2 object exists
|
|
554
|
+
const territoryManagement = sobjectNames.has('Territory2');
|
|
555
|
+
// Check Digital Experiences: Network object exists (Communities/Experience Cloud)
|
|
556
|
+
const digitalExperiences = sobjectNames.has('Network');
|
|
557
|
+
// Check Einstein AI: AIRecordInsight object exists (Einstein scoring features)
|
|
558
|
+
const einsteinAI = sobjectNames.has('AIRecordInsight');
|
|
559
|
+
// Check State/Country Picklists and Person Accounts via lightweight SOQL probes.
|
|
560
|
+
// A LIMIT 0 query succeeds if the field exists, fails with INVALID_FIELD if not.
|
|
561
|
+
// This avoids a full Account describe (~100-300KB for 2 boolean checks).
|
|
562
|
+
const [stateCountryPicklists, personAccounts] = await Promise.all([
|
|
563
|
+
this.probeFieldExists('Account', 'BillingCountryCode'),
|
|
564
|
+
this.probeFieldExists('Account', 'IsPersonAccount'),
|
|
565
|
+
]);
|
|
566
|
+
const features = {
|
|
567
|
+
multiCurrency,
|
|
568
|
+
enhancedNotes,
|
|
569
|
+
stateCountryPicklists,
|
|
570
|
+
territoryManagement,
|
|
571
|
+
personAccounts,
|
|
572
|
+
digitalExperiences,
|
|
573
|
+
einsteinAI,
|
|
574
|
+
};
|
|
575
|
+
const duration = Date.now() - startTime;
|
|
576
|
+
const enabledCount = Object.values(features).filter(Boolean).length;
|
|
577
|
+
this.logger?.log(`Org features detected: ${enabledCount}/7 enabled`);
|
|
578
|
+
return createSuccessResult(features, {
|
|
579
|
+
message: `Detected ${enabledCount}/7 org features enabled`,
|
|
580
|
+
metadata: { duration },
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
catch (error) {
|
|
584
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
585
|
+
this.logger?.log(`Org features detection failed: ${errorMessage}`);
|
|
586
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_FEATURES_QUERY_FAILED, errorMessage, {
|
|
587
|
+
metadata: { duration: Date.now() - startTime },
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Retrieves license information from UserLicense and PermissionSetLicense.
|
|
593
|
+
*
|
|
594
|
+
* @returns ServiceResult containing array of LicenseInfo
|
|
595
|
+
*/
|
|
596
|
+
async getLicenseInfo() {
|
|
597
|
+
const startTime = Date.now();
|
|
598
|
+
try {
|
|
599
|
+
this.logger?.log('Fetching license information...');
|
|
600
|
+
// Query UserLicense
|
|
601
|
+
const userLicenseQuery = new CuneiformQueryBuilder()
|
|
602
|
+
.select(['Id', 'Name', 'MasterLabel', 'TotalLicenses', 'UsedLicenses', 'Status'])
|
|
603
|
+
.from('UserLicense')
|
|
604
|
+
.where('TotalLicenses', '>', 0)
|
|
605
|
+
.orderBy('Name')
|
|
606
|
+
.toSOQL();
|
|
607
|
+
// Query PermissionSetLicense
|
|
608
|
+
const pslQuery = new CuneiformQueryBuilder()
|
|
609
|
+
.select(['Id', 'MasterLabel', 'DeveloperName', 'TotalLicenses', 'UsedLicenses'])
|
|
610
|
+
.from('PermissionSetLicense')
|
|
611
|
+
.where('TotalLicenses', '>', 0)
|
|
612
|
+
.orderBy('MasterLabel')
|
|
613
|
+
.toSOQL();
|
|
614
|
+
// Execute queries in parallel
|
|
615
|
+
const [userLicenseResult, pslResult] = await Promise.all([
|
|
616
|
+
this.soqlAdapter.query(userLicenseQuery),
|
|
617
|
+
this.soqlAdapter.query(pslQuery),
|
|
618
|
+
]);
|
|
619
|
+
const licenses = [];
|
|
620
|
+
const warnings = [];
|
|
621
|
+
// Process UserLicense results
|
|
622
|
+
if (userLicenseResult.success) {
|
|
623
|
+
for (const record of userLicenseResult.data.records) {
|
|
624
|
+
licenses.push({
|
|
625
|
+
id: record.Id,
|
|
626
|
+
name: record.Name,
|
|
627
|
+
masterLabel: record.MasterLabel,
|
|
628
|
+
totalLicenses: record.TotalLicenses,
|
|
629
|
+
usedLicenses: record.UsedLicenses,
|
|
630
|
+
status: record.Status,
|
|
631
|
+
type: 'user',
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
warnings.push(`UserLicense query failed: ${userLicenseResult.message ?? 'Unknown error'}`);
|
|
637
|
+
}
|
|
638
|
+
// Process PermissionSetLicense results
|
|
639
|
+
if (pslResult.success) {
|
|
640
|
+
for (const record of pslResult.data.records) {
|
|
641
|
+
licenses.push({
|
|
642
|
+
id: record.Id,
|
|
643
|
+
name: record.DeveloperName,
|
|
644
|
+
masterLabel: record.MasterLabel,
|
|
645
|
+
totalLicenses: record.TotalLicenses,
|
|
646
|
+
usedLicenses: record.UsedLicenses,
|
|
647
|
+
status: 'Active', // PSL doesn't have status field
|
|
648
|
+
type: 'permissionSet',
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
warnings.push(`PermissionSetLicense query failed: ${pslResult.message ?? 'Unknown error'}`);
|
|
654
|
+
}
|
|
655
|
+
const duration = Date.now() - startTime;
|
|
656
|
+
this.logger?.log(`Found ${licenses.length} licenses`);
|
|
657
|
+
return createSuccessResult(licenses, {
|
|
658
|
+
message: `Found ${licenses.length} licenses`,
|
|
659
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
660
|
+
metadata: { duration },
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
665
|
+
this.logger?.log(`License query failed: ${errorMessage}`);
|
|
666
|
+
return createFailureResult([], ServiceErrorCodes.ORG_NAMESPACE_QUERY_FAILED, errorMessage, {
|
|
667
|
+
metadata: { duration: Date.now() - startTime },
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Detects installed Salesforce Clouds (Sales Cloud, Service Cloud, Industry Clouds).
|
|
673
|
+
*
|
|
674
|
+
* Combines license-based detection (Sales/Service Cloud via UserLicense) with
|
|
675
|
+
* package-based detection (Industry Clouds via namespace lookup).
|
|
676
|
+
*
|
|
677
|
+
* @param cachedGlobal - Optional pre-fetched describeGlobal result to avoid redundant API calls
|
|
678
|
+
* @returns ServiceResult containing array of DetectedCloud
|
|
679
|
+
*/
|
|
680
|
+
async getDetectedClouds(cachedGlobal, cachedPackages) {
|
|
681
|
+
const startTime = Date.now();
|
|
682
|
+
try {
|
|
683
|
+
this.logger?.log('Detecting installed clouds...');
|
|
684
|
+
// Get licenses and packages in parallel (use cached packages if provided)
|
|
685
|
+
const [licensesResult, packagesResult] = await Promise.all([
|
|
686
|
+
this.getLicenseInfo(),
|
|
687
|
+
cachedPackages ?? this.getInstalledPackages(),
|
|
688
|
+
]);
|
|
689
|
+
const clouds = [];
|
|
690
|
+
const warnings = [];
|
|
691
|
+
// Detect Sales Cloud and Service Cloud via licenses
|
|
692
|
+
if (licensesResult.success) {
|
|
693
|
+
clouds.push(...(await this.detectLicenseClouds(licensesResult.data, cachedGlobal)));
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
warnings.push(`License query failed: ${licensesResult.message ?? 'Unknown error'}`);
|
|
697
|
+
}
|
|
698
|
+
// Detect Industry Clouds via packages
|
|
699
|
+
if (packagesResult.success) {
|
|
700
|
+
for (const [namespace, cloudName] of Object.entries(CLOUD_NAMESPACE_MAP)) {
|
|
701
|
+
const pkg = packagesResult.data.find((p) => p.namespacePrefix === namespace || p.namespacePrefix === `${namespace}__`);
|
|
702
|
+
clouds.push({
|
|
703
|
+
name: cloudName,
|
|
704
|
+
type: 'package',
|
|
705
|
+
installed: Boolean(pkg),
|
|
706
|
+
namespace: pkg ? pkg.namespacePrefix : namespace,
|
|
707
|
+
version: pkg?.versionNumber,
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
warnings.push(`Package query failed: ${packagesResult.message ?? 'Unknown error'}`);
|
|
713
|
+
}
|
|
714
|
+
// Detect additional clouds from PermissionSetLicense data
|
|
715
|
+
if (licensesResult.success) {
|
|
716
|
+
clouds.push(...OrgInfoService.detectPslClouds(licensesResult.data));
|
|
717
|
+
}
|
|
718
|
+
const duration = Date.now() - startTime;
|
|
719
|
+
const installedCount = clouds.filter((c) => c.installed).length;
|
|
720
|
+
this.logger?.log(`Detected ${installedCount}/${clouds.length} clouds installed`);
|
|
721
|
+
return createSuccessResult(clouds, {
|
|
722
|
+
message: `Detected ${installedCount} installed clouds`,
|
|
723
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
724
|
+
metadata: { duration },
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
catch (error) {
|
|
728
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
729
|
+
this.logger?.log(`Cloud detection failed: ${errorMessage}`);
|
|
730
|
+
return createFailureResult([], ServiceErrorCodes.ORG_FEATURES_QUERY_FAILED, errorMessage, {
|
|
731
|
+
metadata: { duration: Date.now() - startTime },
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Discovers installed namespaces via describeGlobal() with object counts.
|
|
737
|
+
*
|
|
738
|
+
* Extracts unique namespaces from custom object names (pattern: Namespace__ObjectName__c)
|
|
739
|
+
* and counts objects per namespace. Maps known namespaces to product names.
|
|
740
|
+
*
|
|
741
|
+
* @param cachedGlobal - Optional pre-fetched describeGlobal result to avoid redundant API calls
|
|
742
|
+
* @returns ServiceResult containing NamespaceSummary
|
|
743
|
+
*/
|
|
744
|
+
async getInstalledNamespaces(cachedGlobal) {
|
|
745
|
+
const startTime = Date.now();
|
|
746
|
+
const emptyResult = { count: 0, namespaces: [] };
|
|
747
|
+
if (!this.restAdapter) {
|
|
748
|
+
this.logger?.log('REST adapter not provided, skipping namespace discovery');
|
|
749
|
+
return createSuccessResult(emptyResult, {
|
|
750
|
+
message: 'Namespace discovery skipped (no REST adapter)',
|
|
751
|
+
warnings: ['REST adapter not provided, cannot discover namespaces'],
|
|
752
|
+
metadata: { duration: Date.now() - startTime },
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
try {
|
|
756
|
+
this.logger?.log('Discovering installed namespaces...');
|
|
757
|
+
// Use provided cache if available; otherwise fetch fresh
|
|
758
|
+
const globalResult = cachedGlobal ?? (await this.restAdapter.describeGlobal());
|
|
759
|
+
if (!globalResult.success) {
|
|
760
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_NAMESPACE_QUERY_FAILED, globalResult.message ?? 'Failed to discover namespaces', {
|
|
761
|
+
metadata: { duration: Date.now() - startTime },
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
// Group objects by namespace
|
|
765
|
+
const namespaceGroups = OrgInfoService.groupNamespaceObjects(globalResult.data);
|
|
766
|
+
// Fetch record counts for all namespace objects
|
|
767
|
+
const allObjects = Array.from(namespaceGroups.values()).flatMap((g) => g.objects);
|
|
768
|
+
const recordCounts = allObjects.length > 0 ? await this.restAdapter.getRecordCounts(allObjects) : undefined;
|
|
769
|
+
// Build namespace summary with populated/empty classification
|
|
770
|
+
const namespaces = [];
|
|
771
|
+
for (const [prefix, group] of namespaceGroups.entries()) {
|
|
772
|
+
let populated = 0;
|
|
773
|
+
let empty = 0;
|
|
774
|
+
if (recordCounts?.success) {
|
|
775
|
+
for (const objName of group.objects) {
|
|
776
|
+
if ((recordCounts.data.get(objName) ?? 0) > 0)
|
|
777
|
+
populated++;
|
|
778
|
+
else
|
|
779
|
+
empty++;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
// If record counts failed, fall back to all objects as empty
|
|
784
|
+
empty = group.count;
|
|
785
|
+
}
|
|
786
|
+
// Unrecognized namespaces show blank product name (not "Unknown") per PTA-1437
|
|
787
|
+
const productName = getProductName(prefix) ?? getProductName(`${prefix}__`) ?? '';
|
|
788
|
+
namespaces.push({
|
|
789
|
+
prefix,
|
|
790
|
+
productName,
|
|
791
|
+
objectCount: group.count,
|
|
792
|
+
populatedCount: populated,
|
|
793
|
+
emptyCount: empty,
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
// Sort by object count descending
|
|
797
|
+
namespaces.sort((a, b) => b.objectCount - a.objectCount);
|
|
798
|
+
const summary = {
|
|
799
|
+
count: namespaces.length,
|
|
800
|
+
namespaces,
|
|
801
|
+
};
|
|
802
|
+
const duration = Date.now() - startTime;
|
|
803
|
+
this.logger?.log(`Discovered ${summary.count} namespaces`);
|
|
804
|
+
return createSuccessResult(summary, {
|
|
805
|
+
message: `Discovered ${summary.count} namespaces`,
|
|
806
|
+
metadata: { duration },
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
catch (error) {
|
|
810
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
811
|
+
this.logger?.log(`Namespace discovery failed: ${errorMessage}`);
|
|
812
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_NAMESPACE_QUERY_FAILED, errorMessage, {
|
|
813
|
+
metadata: { duration: Date.now() - startTime },
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Retrieves enhanced Cuneiform package information with license details.
|
|
819
|
+
*
|
|
820
|
+
* @returns ServiceResult containing CuneiformPackageInfo
|
|
821
|
+
*/
|
|
822
|
+
async getCuneiformPackageInfo(cachedPackages) {
|
|
823
|
+
const startTime = Date.now();
|
|
824
|
+
const notInstalledResult = {
|
|
825
|
+
installed: false,
|
|
826
|
+
licenseStatus: 'Not Installed',
|
|
827
|
+
};
|
|
828
|
+
try {
|
|
829
|
+
this.logger?.log('Fetching Cuneiform package info...');
|
|
830
|
+
const packagesResult = cachedPackages ?? (await this.getInstalledPackages());
|
|
831
|
+
if (!packagesResult.success) {
|
|
832
|
+
return createFailureResult(notInstalledResult, ServiceErrorCodes.ORG_CUNEIFORM_VERSION_FAILED, packagesResult.message ?? 'Failed to get Cuneiform info', {
|
|
833
|
+
metadata: { duration: Date.now() - startTime },
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
const cuneiformPkg = packagesResult.data.find((p) => p.namespacePrefix === CUNEIFORM_NAMESPACE);
|
|
837
|
+
if (!cuneiformPkg) {
|
|
838
|
+
// CLI-1569: InstalledSubscriberPackage is empty in scratch orgs where packages are
|
|
839
|
+
// deployed as source (dev hub push) rather than installed from AppExchange.
|
|
840
|
+
// Fall back to namespace-based detection by querying a known Cuneiform CMT object.
|
|
841
|
+
const namespaceDetected = await this.detectCuneiformNamespace();
|
|
842
|
+
if (namespaceDetected) {
|
|
843
|
+
const duration = Date.now() - startTime;
|
|
844
|
+
this.logger?.log('Cuneiform detected via namespace (not subscriber package)');
|
|
845
|
+
return createSuccessResult({
|
|
846
|
+
installed: true,
|
|
847
|
+
licenseStatus: 'Active',
|
|
848
|
+
}, {
|
|
849
|
+
message: 'Cuneiform detected via namespace (not via subscriber package)',
|
|
850
|
+
metadata: { duration, detectionMethod: 'namespace' },
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
const duration = Date.now() - startTime;
|
|
854
|
+
return createSuccessResult(notInstalledResult, {
|
|
855
|
+
message: 'Cuneiform not installed',
|
|
856
|
+
metadata: { duration },
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
// Package is installed - license status is assumed Active (Salesforce doesn't expose license expiration via standard APIs)
|
|
860
|
+
const info = {
|
|
861
|
+
installed: true,
|
|
862
|
+
version: cuneiformPkg.versionNumber,
|
|
863
|
+
licenseStatus: 'Active', // Default to Active if installed
|
|
864
|
+
};
|
|
865
|
+
const duration = Date.now() - startTime;
|
|
866
|
+
const versionStr = info.version ?? 'unknown';
|
|
867
|
+
this.logger?.log(`Cuneiform package info: v${versionStr}, ${info.licenseStatus}`);
|
|
868
|
+
return createSuccessResult(info, {
|
|
869
|
+
message: `Cuneiform v${versionStr} installed`,
|
|
870
|
+
metadata: { duration },
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
catch (error) {
|
|
874
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
875
|
+
this.logger?.log(`Cuneiform package info failed: ${errorMessage}`);
|
|
876
|
+
return createFailureResult(notInstalledResult, ServiceErrorCodes.ORG_CUNEIFORM_VERSION_FAILED, errorMessage, {
|
|
877
|
+
metadata: { duration: Date.now() - startTime },
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Retrieves business processes with associated record type counts.
|
|
883
|
+
*
|
|
884
|
+
* Queries BusinessProcess records and enriches each with the count and names
|
|
885
|
+
* of RecordTypes linked via BusinessProcessId. If no business processes exist,
|
|
886
|
+
* the RecordType query is skipped entirely.
|
|
887
|
+
*
|
|
888
|
+
* @returns ServiceResult containing BusinessProcessSummary
|
|
889
|
+
*/
|
|
890
|
+
async getBusinessProcesses() {
|
|
891
|
+
const startTime = Date.now();
|
|
892
|
+
const emptyResult = { count: 0, processes: [] };
|
|
893
|
+
try {
|
|
894
|
+
this.logger?.log('Fetching business processes...');
|
|
895
|
+
// Query BusinessProcess records
|
|
896
|
+
const bpQuery = new CuneiformQueryBuilder()
|
|
897
|
+
.select(['Id', 'Name', 'TableEnumOrId', 'IsActive', 'Description'])
|
|
898
|
+
.from('BusinessProcess')
|
|
899
|
+
.orderBy('TableEnumOrId', 'ASC')
|
|
900
|
+
.toSOQL();
|
|
901
|
+
const bpResult = await this.soqlAdapter.query(bpQuery);
|
|
902
|
+
if (!bpResult.success) {
|
|
903
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_BUSINESS_PROCESS_QUERY_FAILED, bpResult.message ?? 'BusinessProcess query failed', { metadata: { duration: Date.now() - startTime } });
|
|
904
|
+
}
|
|
905
|
+
const bpRecords = bpResult.data.records;
|
|
906
|
+
// Short-circuit if no business processes exist
|
|
907
|
+
if (bpRecords.length === 0) {
|
|
908
|
+
const duration = Date.now() - startTime;
|
|
909
|
+
return createSuccessResult(emptyResult, {
|
|
910
|
+
message: 'No business processes found',
|
|
911
|
+
metadata: { duration },
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
// Query RecordTypes linked to business processes
|
|
915
|
+
const warnings = [];
|
|
916
|
+
let rtRecords = [];
|
|
917
|
+
const rtQuery = new CuneiformQueryBuilder()
|
|
918
|
+
.select(['Id', 'Name', 'SobjectType', 'BusinessProcessId', 'IsActive'])
|
|
919
|
+
.from('RecordType')
|
|
920
|
+
.andWhereNull('BusinessProcessId', false)
|
|
921
|
+
.orderBy('Name', 'ASC')
|
|
922
|
+
.toSOQL();
|
|
923
|
+
const rtResult = await this.soqlAdapter.query(rtQuery);
|
|
924
|
+
if (rtResult.success) {
|
|
925
|
+
rtRecords = rtResult.data.records;
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
warnings.push(`RecordType query failed: ${rtResult.message ?? 'Unknown error'}`);
|
|
929
|
+
}
|
|
930
|
+
// Group record types by BusinessProcessId
|
|
931
|
+
const rtByProcessId = new Map();
|
|
932
|
+
for (const rt of rtRecords) {
|
|
933
|
+
const group = rtByProcessId.get(rt.BusinessProcessId) ?? [];
|
|
934
|
+
group.push(rt);
|
|
935
|
+
rtByProcessId.set(rt.BusinessProcessId, group);
|
|
936
|
+
}
|
|
937
|
+
// Build process info with record type enrichment
|
|
938
|
+
const processes = bpRecords.map((bp) => {
|
|
939
|
+
const linkedRts = rtByProcessId.get(bp.Id) ?? [];
|
|
940
|
+
return {
|
|
941
|
+
name: bp.Name,
|
|
942
|
+
objectName: bp.TableEnumOrId,
|
|
943
|
+
isActive: bp.IsActive,
|
|
944
|
+
description: bp.Description ?? undefined,
|
|
945
|
+
recordTypeCount: linkedRts.length,
|
|
946
|
+
recordTypeNames: linkedRts.map((rt) => rt.Name),
|
|
947
|
+
};
|
|
948
|
+
});
|
|
949
|
+
const summary = {
|
|
950
|
+
count: processes.length,
|
|
951
|
+
processes,
|
|
952
|
+
};
|
|
953
|
+
const duration = Date.now() - startTime;
|
|
954
|
+
this.logger?.log(`Found ${summary.count} business processes`);
|
|
955
|
+
return createSuccessResult(summary, {
|
|
956
|
+
message: `Found ${summary.count} business processes`,
|
|
957
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
958
|
+
metadata: { duration },
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
catch (error) {
|
|
962
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
963
|
+
this.logger?.log(`Business process query failed: ${errorMessage}`);
|
|
964
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_BUSINESS_PROCESS_QUERY_FAILED, errorMessage, {
|
|
965
|
+
metadata: { duration: Date.now() - startTime },
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
// ── Private instance methods ────────────────────────────────────────
|
|
970
|
+
/**
|
|
971
|
+
* Delegates org info retrieval to the ISV REST API.
|
|
972
|
+
*
|
|
973
|
+
* @param startTime - Timestamp for duration tracking
|
|
974
|
+
* @returns ServiceResult containing OrgInfo from REST endpoint
|
|
975
|
+
*/
|
|
976
|
+
async getOrgInfoViaRest(startTime) {
|
|
977
|
+
this.logger?.log('Delegating org info to ISV REST API (org-info)');
|
|
978
|
+
const emptyResult = {
|
|
979
|
+
identity: { userId: '', organizationId: '', username: '' },
|
|
980
|
+
limits: { limits: {} },
|
|
981
|
+
packages: [],
|
|
982
|
+
activeNamespaces: [],
|
|
983
|
+
cuneiformVersion: null,
|
|
984
|
+
};
|
|
985
|
+
const restResult = await this.restClient.getOrgInfo();
|
|
986
|
+
if (!restResult.success) {
|
|
987
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_INFO_QUERY_FAILED, restResult.message ?? 'REST org-info failed', {
|
|
988
|
+
metadata: { duration: Date.now() - startTime },
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
const response = restResult.data;
|
|
992
|
+
if (!response.success) {
|
|
993
|
+
const errorMsg = response.errors?.length > 0 ? response.errors[0] : 'Server-side org info query failed';
|
|
994
|
+
return createFailureResult(emptyResult, ServiceErrorCodes.ORG_INFO_QUERY_FAILED, errorMsg, {
|
|
995
|
+
metadata: { duration: Date.now() - startTime },
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
return createSuccessResult(response.result ?? emptyResult, {
|
|
999
|
+
message: 'Org info retrieved via REST API',
|
|
1000
|
+
metadata: { duration: Date.now() - startTime, strategyUsed: 'rest-api' },
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Detects whether the Cuneiform namespace exists by querying a known CMT object.
|
|
1005
|
+
*
|
|
1006
|
+
* CLI-1569: InstalledSubscriberPackage is empty in scratch orgs where packages are
|
|
1007
|
+
* deployed as source metadata. This method queries pnova__Active_Configuration__mdt
|
|
1008
|
+
* as a lightweight namespace presence check — if the query succeeds (even with 0
|
|
1009
|
+
* records), the namespace exists. If it fails with "sObject type not supported",
|
|
1010
|
+
* the namespace is absent.
|
|
1011
|
+
*
|
|
1012
|
+
* @returns true if the pnova namespace is present, false otherwise
|
|
1013
|
+
*/
|
|
1014
|
+
async detectCuneiformNamespace() {
|
|
1015
|
+
try {
|
|
1016
|
+
const soql = new CuneiformQueryBuilder()
|
|
1017
|
+
.select(['Id'])
|
|
1018
|
+
.from(`${CUNEIFORM_NAMESPACE}__Active_Configuration__mdt`)
|
|
1019
|
+
.limit(1)
|
|
1020
|
+
.toSOQL();
|
|
1021
|
+
const result = await this.soqlAdapter.query(soql);
|
|
1022
|
+
// If the query succeeds (regardless of record count), the namespace exists
|
|
1023
|
+
return result.success;
|
|
1024
|
+
}
|
|
1025
|
+
catch {
|
|
1026
|
+
// Any exception means namespace detection failed — treat as not installed
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Probes whether a field exists on an object using a LIMIT 0 SOQL query.
|
|
1032
|
+
* Returns true if the query succeeds (field exists), false if it fails or the
|
|
1033
|
+
* sanitizer rejects the inputs. Much lighter than a full describe.
|
|
1034
|
+
*
|
|
1035
|
+
* **Input source contract**: callers must pass Salesforce API names already
|
|
1036
|
+
* classified as safe (hardcoded constants, describeGlobal results, or upstream
|
|
1037
|
+
* validated values). The sanitizer in buildProbeQuery defends against malformed
|
|
1038
|
+
* identifiers as a last line of defence, but this method is NOT a user-input
|
|
1039
|
+
* gateway — never wire it directly to a flag value or untrusted external input.
|
|
1040
|
+
*
|
|
1041
|
+
* Today's only call sites pass hardcoded constants from getOrgFeatures()
|
|
1042
|
+
* (Account / BillingCountryCode / IsPersonAccount). The sanitizer is in place
|
|
1043
|
+
* defensively so a future caller cannot accidentally introduce SOQL injection.
|
|
1044
|
+
*/
|
|
1045
|
+
async probeFieldExists(objectName, fieldName) {
|
|
1046
|
+
const soql = OrgInfoService.buildProbeQuery(objectName, fieldName);
|
|
1047
|
+
if (soql === null) {
|
|
1048
|
+
return false;
|
|
1049
|
+
}
|
|
1050
|
+
try {
|
|
1051
|
+
const result = await this.soqlAdapter.query(soql);
|
|
1052
|
+
return result.success;
|
|
1053
|
+
}
|
|
1054
|
+
catch {
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Detects Sales Cloud and Service Cloud from license data and global describe.
|
|
1060
|
+
*
|
|
1061
|
+
* Uses license patterns to find Sales/Service Cloud licenses, then confirms
|
|
1062
|
+
* by checking for Opportunity (Sales) and Case (Service) objects in the org.
|
|
1063
|
+
*/
|
|
1064
|
+
async detectLicenseClouds(licenses, cachedGlobal) {
|
|
1065
|
+
const salesCloudLicense = licenses.find((lic) => SALES_CLOUD_LICENSE_PATTERNS.some((pattern) => lic.masterLabel.includes(pattern) || lic.name.includes(pattern)));
|
|
1066
|
+
const serviceCloudLicense = licenses.find((lic) => SERVICE_CLOUD_LICENSE_PATTERNS.some((pattern) => lic.masterLabel.includes(pattern) || lic.name.includes(pattern)));
|
|
1067
|
+
// Check for Opportunity/Case objects to confirm Sales/Service Cloud
|
|
1068
|
+
const { hasOpportunity, hasCase } = await this.resolveCloudObjects(cachedGlobal);
|
|
1069
|
+
const clouds = [];
|
|
1070
|
+
if (salesCloudLicense && hasOpportunity) {
|
|
1071
|
+
clouds.push({
|
|
1072
|
+
name: 'Sales Cloud',
|
|
1073
|
+
type: 'license',
|
|
1074
|
+
installed: true,
|
|
1075
|
+
totalLicenses: salesCloudLicense.totalLicenses,
|
|
1076
|
+
usedLicenses: salesCloudLicense.usedLicenses,
|
|
1077
|
+
status: salesCloudLicense.status,
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
clouds.push({ name: 'Sales Cloud', type: 'license', installed: false });
|
|
1082
|
+
}
|
|
1083
|
+
if (serviceCloudLicense && hasCase) {
|
|
1084
|
+
clouds.push({
|
|
1085
|
+
name: 'Service Cloud',
|
|
1086
|
+
type: 'license',
|
|
1087
|
+
installed: true,
|
|
1088
|
+
totalLicenses: serviceCloudLicense.totalLicenses,
|
|
1089
|
+
usedLicenses: serviceCloudLicense.usedLicenses,
|
|
1090
|
+
status: serviceCloudLicense.status,
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
else {
|
|
1094
|
+
clouds.push({ name: 'Service Cloud', type: 'license', installed: false });
|
|
1095
|
+
}
|
|
1096
|
+
return clouds;
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Resolves whether Opportunity and Case objects exist in the org.
|
|
1100
|
+
* Uses cached global describe if available, otherwise fetches fresh.
|
|
1101
|
+
*/
|
|
1102
|
+
async resolveCloudObjects(cachedGlobal) {
|
|
1103
|
+
if (cachedGlobal?.success) {
|
|
1104
|
+
const sobjectNames = new Set(cachedGlobal.data.sobjects.map((obj) => obj.name));
|
|
1105
|
+
return { hasOpportunity: sobjectNames.has('Opportunity'), hasCase: sobjectNames.has('Case') };
|
|
1106
|
+
}
|
|
1107
|
+
if (this.restAdapter) {
|
|
1108
|
+
try {
|
|
1109
|
+
const globalResult = await this.restAdapter.describeGlobal();
|
|
1110
|
+
if (globalResult.success) {
|
|
1111
|
+
const sobjectNames = new Set(globalResult.data.sobjects.map((obj) => obj.name));
|
|
1112
|
+
return { hasOpportunity: sobjectNames.has('Opportunity'), hasCase: sobjectNames.has('Case') };
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
catch {
|
|
1116
|
+
// Ignore error, use license-only detection
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return { hasOpportunity: false, hasCase: false };
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
//# sourceMappingURL=OrgInfoService.js.map
|