@peernova/cuneiform-sf 1.0.2 → 1.0.4-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (488) hide show
  1. package/LICENSE +81 -30
  2. package/README.md +168 -134
  3. package/lib/adapters/connection-facade.d.ts +458 -0
  4. package/lib/adapters/connection-facade.js +379 -0
  5. package/lib/adapters/connection-facade.js.map +1 -0
  6. package/lib/adapters/errors.d.ts +547 -0
  7. package/lib/adapters/errors.js +937 -0
  8. package/lib/adapters/errors.js.map +1 -0
  9. package/lib/adapters/lifecycle.d.ts +112 -0
  10. package/lib/adapters/lifecycle.js +94 -0
  11. package/lib/adapters/lifecycle.js.map +1 -0
  12. package/lib/adapters/rest/cache.d.ts +69 -0
  13. package/lib/adapters/rest/cache.js +133 -0
  14. package/lib/adapters/rest/cache.js.map +1 -0
  15. package/lib/adapters/rest/index.d.ts +11 -0
  16. package/lib/adapters/rest/index.js +18 -0
  17. package/lib/adapters/rest/index.js.map +1 -0
  18. package/lib/adapters/rest/profiling-rest-client.d.ts +137 -0
  19. package/lib/adapters/rest/profiling-rest-client.js +115 -0
  20. package/lib/adapters/rest/profiling-rest-client.js.map +1 -0
  21. package/lib/adapters/rest/rest-api-adapter.d.ts +393 -0
  22. package/lib/adapters/rest/rest-api-adapter.js +764 -0
  23. package/lib/adapters/rest/rest-api-adapter.js.map +1 -0
  24. package/lib/adapters/rest/types.d.ts +34 -0
  25. package/lib/adapters/rest/types.js +9 -0
  26. package/lib/adapters/rest/types.js.map +1 -0
  27. package/lib/adapters/retry.d.ts +91 -0
  28. package/lib/adapters/retry.js +215 -0
  29. package/lib/adapters/retry.js.map +1 -0
  30. package/lib/adapters/soql/cuneiform-query-builder.d.ts +418 -0
  31. package/lib/adapters/soql/cuneiform-query-builder.js +606 -0
  32. package/lib/adapters/soql/cuneiform-query-builder.js.map +1 -0
  33. package/lib/adapters/soql/soql-query-adapter.d.ts +141 -0
  34. package/lib/adapters/soql/soql-query-adapter.js +259 -0
  35. package/lib/adapters/soql/soql-query-adapter.js.map +1 -0
  36. package/lib/adapters/soql/types.d.ts +37 -0
  37. package/lib/adapters/soql/types.js +19 -0
  38. package/lib/adapters/soql/types.js.map +1 -0
  39. package/lib/adapters/testing/index.d.ts +37 -0
  40. package/lib/adapters/testing/index.js +20 -0
  41. package/lib/adapters/testing/index.js.map +1 -0
  42. package/lib/adapters/testing/mock-connection.d.ts +77 -0
  43. package/lib/adapters/testing/mock-connection.js +207 -0
  44. package/lib/adapters/testing/mock-connection.js.map +1 -0
  45. package/lib/adapters/testing/mock-logger.d.ts +29 -0
  46. package/lib/adapters/testing/mock-logger.js +57 -0
  47. package/lib/adapters/testing/mock-logger.js.map +1 -0
  48. package/lib/adapters/testing/mock-mcp-adapters.d.ts +32 -0
  49. package/lib/adapters/testing/mock-mcp-adapters.js +52 -0
  50. package/lib/adapters/testing/mock-mcp-adapters.js.map +1 -0
  51. package/lib/adapters/testing/mock-oclif-config.d.ts +22 -0
  52. package/lib/adapters/testing/mock-oclif-config.js +90 -0
  53. package/lib/adapters/testing/mock-oclif-config.js.map +1 -0
  54. package/lib/adapters/testing/mock-rest-adapter.d.ts +26 -0
  55. package/lib/adapters/testing/mock-rest-adapter.js +243 -0
  56. package/lib/adapters/testing/mock-rest-adapter.js.map +1 -0
  57. package/lib/adapters/testing/mock-salesforce-connection.d.ts +40 -0
  58. package/lib/adapters/testing/mock-salesforce-connection.js +61 -0
  59. package/lib/adapters/testing/mock-salesforce-connection.js.map +1 -0
  60. package/lib/adapters/testing/mock-soql-adapter.d.ts +30 -0
  61. package/lib/adapters/testing/mock-soql-adapter.js +120 -0
  62. package/lib/adapters/testing/mock-soql-adapter.js.map +1 -0
  63. package/lib/adapters/testing/mock-tooling-adapter.d.ts +24 -0
  64. package/lib/adapters/testing/mock-tooling-adapter.js +163 -0
  65. package/lib/adapters/testing/mock-tooling-adapter.js.map +1 -0
  66. package/lib/adapters/testing/stub-connection.d.ts +93 -0
  67. package/lib/adapters/testing/stub-connection.js +97 -0
  68. package/lib/adapters/testing/stub-connection.js.map +1 -0
  69. package/lib/adapters/testing/stub-rest-adapter.d.ts +52 -0
  70. package/lib/adapters/testing/stub-rest-adapter.js +58 -0
  71. package/lib/adapters/testing/stub-rest-adapter.js.map +1 -0
  72. package/lib/adapters/testing/stub-soql-adapter.d.ts +56 -0
  73. package/lib/adapters/testing/stub-soql-adapter.js +50 -0
  74. package/lib/adapters/testing/stub-soql-adapter.js.map +1 -0
  75. package/lib/adapters/testing/types.d.ts +71 -0
  76. package/lib/adapters/testing/types.js +9 -0
  77. package/lib/adapters/testing/types.js.map +1 -0
  78. package/lib/adapters/tooling/index.d.ts +10 -0
  79. package/lib/adapters/tooling/index.js +17 -0
  80. package/lib/adapters/tooling/index.js.map +1 -0
  81. package/lib/adapters/tooling/tooling-api-adapter.d.ts +157 -0
  82. package/lib/adapters/tooling/tooling-api-adapter.js +339 -0
  83. package/lib/adapters/tooling/tooling-api-adapter.js.map +1 -0
  84. package/lib/adapters/tooling/types.d.ts +81 -0
  85. package/lib/adapters/tooling/types.js +9 -0
  86. package/lib/adapters/tooling/types.js.map +1 -0
  87. package/lib/adapters/types.d.ts +112 -0
  88. package/lib/adapters/types.js +169 -0
  89. package/lib/adapters/types.js.map +1 -0
  90. package/lib/base/cuneiform-command.d.ts +175 -0
  91. package/lib/base/cuneiform-command.js +326 -0
  92. package/lib/base/cuneiform-command.js.map +1 -0
  93. package/lib/commands/cuneiform/compatibility/check.d.ts +43 -0
  94. package/lib/commands/cuneiform/compatibility/check.js +114 -0
  95. package/lib/commands/cuneiform/compatibility/check.js.map +1 -0
  96. package/lib/commands/cuneiform/definition/create.d.ts +120 -0
  97. package/lib/commands/cuneiform/definition/create.js +737 -0
  98. package/lib/commands/cuneiform/definition/create.js.map +1 -0
  99. package/lib/commands/cuneiform/definition/export.d.ts +57 -0
  100. package/lib/commands/cuneiform/definition/export.js +133 -0
  101. package/lib/commands/cuneiform/definition/export.js.map +1 -0
  102. package/lib/commands/cuneiform/definition/get.d.ts +86 -0
  103. package/lib/commands/cuneiform/definition/get.js +277 -0
  104. package/lib/commands/cuneiform/definition/get.js.map +1 -0
  105. package/lib/commands/cuneiform/definition/import.d.ts +54 -0
  106. package/lib/commands/cuneiform/definition/import.js +118 -0
  107. package/lib/commands/cuneiform/definition/import.js.map +1 -0
  108. package/lib/commands/cuneiform/definition/list.d.ts +110 -0
  109. package/lib/commands/cuneiform/definition/list.js +351 -0
  110. package/lib/commands/cuneiform/definition/list.js.map +1 -0
  111. package/lib/commands/cuneiform/definition/purge.d.ts +109 -0
  112. package/lib/commands/cuneiform/definition/purge.js +578 -0
  113. package/lib/commands/cuneiform/definition/purge.js.map +1 -0
  114. package/lib/commands/cuneiform/definition/update.d.ts +58 -0
  115. package/lib/commands/cuneiform/definition/update.js +209 -0
  116. package/lib/commands/cuneiform/definition/update.js.map +1 -0
  117. package/lib/commands/cuneiform/mcp/serve.d.ts +56 -0
  118. package/lib/commands/cuneiform/mcp/serve.js +109 -0
  119. package/lib/commands/cuneiform/mcp/serve.js.map +1 -0
  120. package/lib/commands/cuneiform/object/describe.d.ts +61 -0
  121. package/lib/commands/cuneiform/object/describe.js +461 -0
  122. package/lib/commands/cuneiform/object/describe.js.map +1 -0
  123. package/lib/commands/cuneiform/object/list.d.ts +123 -0
  124. package/lib/commands/cuneiform/object/list.js +264 -0
  125. package/lib/commands/cuneiform/object/list.js.map +1 -0
  126. package/lib/commands/cuneiform/org/details.d.ts +99 -0
  127. package/lib/commands/cuneiform/org/details.js +521 -0
  128. package/lib/commands/cuneiform/org/details.js.map +1 -0
  129. package/lib/commands/cuneiform/org/reset.d.ts +46 -0
  130. package/lib/commands/cuneiform/org/reset.js +135 -0
  131. package/lib/commands/cuneiform/org/reset.js.map +1 -0
  132. package/lib/commands/cuneiform/profile/request/cancel.d.ts +59 -0
  133. package/lib/commands/cuneiform/profile/request/cancel.js +202 -0
  134. package/lib/commands/cuneiform/profile/request/cancel.js.map +1 -0
  135. package/lib/commands/cuneiform/profile/request/delete.d.ts +59 -0
  136. package/lib/commands/cuneiform/profile/request/delete.js +223 -0
  137. package/lib/commands/cuneiform/profile/request/delete.js.map +1 -0
  138. package/lib/commands/cuneiform/profile/request/list.d.ts +35 -0
  139. package/lib/commands/cuneiform/profile/request/list.js +102 -0
  140. package/lib/commands/cuneiform/profile/request/list.js.map +1 -0
  141. package/lib/commands/cuneiform/profile.d.ts +93 -0
  142. package/lib/commands/cuneiform/profile.js +353 -0
  143. package/lib/commands/cuneiform/profile.js.map +1 -0
  144. package/lib/commands/cuneiform/summary/purge.d.ts +80 -0
  145. package/lib/commands/cuneiform/summary/purge.js +467 -0
  146. package/lib/commands/cuneiform/summary/purge.js.map +1 -0
  147. package/lib/commands/cuneiform/summary/reprofile.d.ts +60 -0
  148. package/lib/commands/cuneiform/summary/reprofile.js +236 -0
  149. package/lib/commands/cuneiform/summary/reprofile.js.map +1 -0
  150. package/lib/commands/cuneiform/summary/stop.d.ts +59 -0
  151. package/lib/commands/cuneiform/summary/stop.js +234 -0
  152. package/lib/commands/cuneiform/summary/stop.js.map +1 -0
  153. package/lib/commands/cuneiform/user/details.d.ts +77 -0
  154. package/lib/commands/cuneiform/user/details.js +414 -0
  155. package/lib/commands/cuneiform/user/details.js.map +1 -0
  156. package/lib/constants/namespace-constants.d.ts +102 -0
  157. package/lib/constants/namespace-constants.js +225 -0
  158. package/lib/constants/namespace-constants.js.map +1 -0
  159. package/lib/debug/command-debug-proxy.d.ts +101 -0
  160. package/lib/debug/command-debug-proxy.js +171 -0
  161. package/lib/debug/command-debug-proxy.js.map +1 -0
  162. package/lib/debug/debug-logger.d.ts +85 -0
  163. package/lib/debug/debug-logger.js +133 -0
  164. package/lib/debug/debug-logger.js.map +1 -0
  165. package/lib/debug/service-debug-proxy.d.ts +30 -0
  166. package/lib/debug/service-debug-proxy.js +102 -0
  167. package/lib/debug/service-debug-proxy.js.map +1 -0
  168. package/lib/hooks/prerun.d.ts +25 -0
  169. package/lib/hooks/prerun.js +47 -0
  170. package/lib/hooks/prerun.js.map +1 -0
  171. package/lib/mcp/config/mcp-config.d.ts +55 -0
  172. package/lib/mcp/config/mcp-config.js +51 -0
  173. package/lib/mcp/config/mcp-config.js.map +1 -0
  174. package/lib/mcp/config/pagination.d.ts +96 -0
  175. package/lib/mcp/config/pagination.js +108 -0
  176. package/lib/mcp/config/pagination.js.map +1 -0
  177. package/lib/mcp/config/system-prompts.d.ts +18 -0
  178. package/lib/mcp/config/system-prompts.js +92 -0
  179. package/lib/mcp/config/system-prompts.js.map +1 -0
  180. package/lib/mcp/errors.d.ts +23 -0
  181. package/lib/mcp/errors.js +27 -0
  182. package/lib/mcp/errors.js.map +1 -0
  183. package/lib/mcp/schemas/input-schemas.d.ts +327 -0
  184. package/lib/mcp/schemas/input-schemas.js +310 -0
  185. package/lib/mcp/schemas/input-schemas.js.map +1 -0
  186. package/lib/mcp/server.d.ts +40 -0
  187. package/lib/mcp/server.js +316 -0
  188. package/lib/mcp/server.js.map +1 -0
  189. package/lib/mcp/tools/contactpoint-tools.d.ts +14 -0
  190. package/lib/mcp/tools/contactpoint-tools.js +34 -0
  191. package/lib/mcp/tools/contactpoint-tools.js.map +1 -0
  192. package/lib/mcp/tools/definition-io-tools.d.ts +19 -0
  193. package/lib/mcp/tools/definition-io-tools.js +152 -0
  194. package/lib/mcp/tools/definition-io-tools.js.map +1 -0
  195. package/lib/mcp/tools/definition-tools.d.ts +51 -0
  196. package/lib/mcp/tools/definition-tools.js +220 -0
  197. package/lib/mcp/tools/definition-tools.js.map +1 -0
  198. package/lib/mcp/tools/index.d.ts +37 -0
  199. package/lib/mcp/tools/index.js +88 -0
  200. package/lib/mcp/tools/index.js.map +1 -0
  201. package/lib/mcp/tools/object-tools.d.ts +22 -0
  202. package/lib/mcp/tools/object-tools.js +327 -0
  203. package/lib/mcp/tools/object-tools.js.map +1 -0
  204. package/lib/mcp/tools/org-tools.d.ts +14 -0
  205. package/lib/mcp/tools/org-tools.js +177 -0
  206. package/lib/mcp/tools/org-tools.js.map +1 -0
  207. package/lib/mcp/tools/profile-tools.d.ts +59 -0
  208. package/lib/mcp/tools/profile-tools.js +213 -0
  209. package/lib/mcp/tools/profile-tools.js.map +1 -0
  210. package/lib/mcp/tools/summary-tools.d.ts +14 -0
  211. package/lib/mcp/tools/summary-tools.js +38 -0
  212. package/lib/mcp/tools/summary-tools.js.map +1 -0
  213. package/lib/mcp/tools/tool-factory.d.ts +63 -0
  214. package/lib/mcp/tools/tool-factory.js +146 -0
  215. package/lib/mcp/tools/tool-factory.js.map +1 -0
  216. package/lib/mcp/tools/user-tools.d.ts +25 -0
  217. package/lib/mcp/tools/user-tools.js +167 -0
  218. package/lib/mcp/tools/user-tools.js.map +1 -0
  219. package/lib/models/cascade-skip-accumulator.d.ts +25 -0
  220. package/lib/models/cascade-skip-accumulator.js +9 -0
  221. package/lib/models/cascade-skip-accumulator.js.map +1 -0
  222. package/lib/models/date-literal.d.ts +280 -0
  223. package/lib/models/date-literal.js +1164 -0
  224. package/lib/models/date-literal.js.map +1 -0
  225. package/lib/models/object-describe-types.d.ts +173 -0
  226. package/lib/models/object-describe-types.js +9 -0
  227. package/lib/models/object-describe-types.js.map +1 -0
  228. package/lib/models/portability-recipe.d.ts +35 -0
  229. package/lib/models/portability-recipe.js +113 -0
  230. package/lib/models/portability-recipe.js.map +1 -0
  231. package/lib/models/profile-request-types.d.ts +118 -0
  232. package/lib/models/profile-request-types.js +23 -0
  233. package/lib/models/profile-request-types.js.map +1 -0
  234. package/lib/models/profiling-execution-types.d.ts +154 -0
  235. package/lib/models/profiling-execution-types.js +14 -0
  236. package/lib/models/profiling-execution-types.js.map +1 -0
  237. package/lib/models/service-result.d.ts +114 -0
  238. package/lib/models/service-result.js +81 -0
  239. package/lib/models/service-result.js.map +1 -0
  240. package/lib/models/sfdmu-types.d.ts +49 -0
  241. package/lib/models/sfdmu-types.js +23 -0
  242. package/lib/models/sfdmu-types.js.map +1 -0
  243. package/lib/models/status-types.d.ts +38 -0
  244. package/lib/models/status-types.js +12 -0
  245. package/lib/models/status-types.js.map +1 -0
  246. package/lib/models/summary-bulk-types.d.ts +61 -0
  247. package/lib/models/summary-bulk-types.js +23 -0
  248. package/lib/models/summary-bulk-types.js.map +1 -0
  249. package/lib/models/user-details-types.d.ts +188 -0
  250. package/lib/models/user-details-types.js +9 -0
  251. package/lib/models/user-details-types.js.map +1 -0
  252. package/lib/models/year-range.d.ts +78 -0
  253. package/lib/models/year-range.js +153 -0
  254. package/lib/models/year-range.js.map +1 -0
  255. package/lib/operations/CompatibilityCheckOperation.d.ts +62 -0
  256. package/lib/operations/CompatibilityCheckOperation.js +102 -0
  257. package/lib/operations/CompatibilityCheckOperation.js.map +1 -0
  258. package/lib/operations/DefinitionCreateOperation.d.ts +427 -0
  259. package/lib/operations/DefinitionCreateOperation.js +1270 -0
  260. package/lib/operations/DefinitionCreateOperation.js.map +1 -0
  261. package/lib/operations/DefinitionExportOperation.d.ts +155 -0
  262. package/lib/operations/DefinitionExportOperation.js +281 -0
  263. package/lib/operations/DefinitionExportOperation.js.map +1 -0
  264. package/lib/operations/DefinitionImportOperation.d.ts +144 -0
  265. package/lib/operations/DefinitionImportOperation.js +357 -0
  266. package/lib/operations/DefinitionImportOperation.js.map +1 -0
  267. package/lib/operations/DefinitionListOperation.d.ts +66 -0
  268. package/lib/operations/DefinitionListOperation.js +108 -0
  269. package/lib/operations/DefinitionListOperation.js.map +1 -0
  270. package/lib/operations/DefinitionPurgeOperation.d.ts +203 -0
  271. package/lib/operations/DefinitionPurgeOperation.js +465 -0
  272. package/lib/operations/DefinitionPurgeOperation.js.map +1 -0
  273. package/lib/operations/DefinitionUpdateOperation.d.ts +78 -0
  274. package/lib/operations/DefinitionUpdateOperation.js +142 -0
  275. package/lib/operations/DefinitionUpdateOperation.js.map +1 -0
  276. package/lib/operations/OrgDetailsOperation.d.ts +253 -0
  277. package/lib/operations/OrgDetailsOperation.js +456 -0
  278. package/lib/operations/OrgDetailsOperation.js.map +1 -0
  279. package/lib/operations/OrgResetOperation.d.ts +114 -0
  280. package/lib/operations/OrgResetOperation.js +209 -0
  281. package/lib/operations/OrgResetOperation.js.map +1 -0
  282. package/lib/operations/ProfileOperation.d.ts +192 -0
  283. package/lib/operations/ProfileOperation.js +371 -0
  284. package/lib/operations/ProfileOperation.js.map +1 -0
  285. package/lib/operations/ProfileRequestCancelOperation.d.ts +59 -0
  286. package/lib/operations/ProfileRequestCancelOperation.js +137 -0
  287. package/lib/operations/ProfileRequestCancelOperation.js.map +1 -0
  288. package/lib/operations/ProfileRequestDeleteOperation.d.ts +64 -0
  289. package/lib/operations/ProfileRequestDeleteOperation.js +134 -0
  290. package/lib/operations/ProfileRequestDeleteOperation.js.map +1 -0
  291. package/lib/operations/ProfileRequestListOperation.d.ts +39 -0
  292. package/lib/operations/ProfileRequestListOperation.js +61 -0
  293. package/lib/operations/ProfileRequestListOperation.js.map +1 -0
  294. package/lib/operations/SummaryPurgeOperation.d.ts +134 -0
  295. package/lib/operations/SummaryPurgeOperation.js +257 -0
  296. package/lib/operations/SummaryPurgeOperation.js.map +1 -0
  297. package/lib/operations/SummaryReprofileOperation.d.ts +88 -0
  298. package/lib/operations/SummaryReprofileOperation.js +174 -0
  299. package/lib/operations/SummaryReprofileOperation.js.map +1 -0
  300. package/lib/operations/SummaryStopOperation.d.ts +87 -0
  301. package/lib/operations/SummaryStopOperation.js +175 -0
  302. package/lib/operations/SummaryStopOperation.js.map +1 -0
  303. package/lib/services/BulkExecutionService.d.ts +120 -0
  304. package/lib/services/BulkExecutionService.js +535 -0
  305. package/lib/services/BulkExecutionService.js.map +1 -0
  306. package/lib/services/CompatibilityService.d.ts +81 -0
  307. package/lib/services/CompatibilityService.js +118 -0
  308. package/lib/services/CompatibilityService.js.map +1 -0
  309. package/lib/services/ConfigureMode.d.ts +98 -0
  310. package/lib/services/ConfigureMode.js +413 -0
  311. package/lib/services/ConfigureMode.js.map +1 -0
  312. package/lib/services/ContactPointService.d.ts +111 -0
  313. package/lib/services/ContactPointService.js +286 -0
  314. package/lib/services/ContactPointService.js.map +1 -0
  315. package/lib/services/DataAvailabilityService.d.ts +81 -0
  316. package/lib/services/DataAvailabilityService.js +128 -0
  317. package/lib/services/DataAvailabilityService.js.map +1 -0
  318. package/lib/services/DefinitionFieldGenerationService.d.ts +357 -0
  319. package/lib/services/DefinitionFieldGenerationService.js +899 -0
  320. package/lib/services/DefinitionFieldGenerationService.js.map +1 -0
  321. package/lib/services/DefinitionQueryBuilder.d.ts +92 -0
  322. package/lib/services/DefinitionQueryBuilder.js +328 -0
  323. package/lib/services/DefinitionQueryBuilder.js.map +1 -0
  324. package/lib/services/ObjectDescribeService.d.ts +436 -0
  325. package/lib/services/ObjectDescribeService.js +881 -0
  326. package/lib/services/ObjectDescribeService.js.map +1 -0
  327. package/lib/services/ObjectFilteringService.d.ts +484 -0
  328. package/lib/services/ObjectFilteringService.js +1080 -0
  329. package/lib/services/ObjectFilteringService.js.map +1 -0
  330. package/lib/services/ObjectListCommandService.d.ts +467 -0
  331. package/lib/services/ObjectListCommandService.js +904 -0
  332. package/lib/services/ObjectListCommandService.js.map +1 -0
  333. package/lib/services/ObjectListService.d.ts +201 -0
  334. package/lib/services/ObjectListService.js +350 -0
  335. package/lib/services/ObjectListService.js.map +1 -0
  336. package/lib/services/OrgInfoService.d.ts +493 -0
  337. package/lib/services/OrgInfoService.js +1142 -0
  338. package/lib/services/OrgInfoService.js.map +1 -0
  339. package/lib/services/PollingService.d.ts +105 -0
  340. package/lib/services/PollingService.js +117 -0
  341. package/lib/services/PollingService.js.map +1 -0
  342. package/lib/services/ProfileRequestService.d.ts +186 -0
  343. package/lib/services/ProfileRequestService.js +555 -0
  344. package/lib/services/ProfileRequestService.js.map +1 -0
  345. package/lib/services/ProfilingDefinitionService.d.ts +575 -0
  346. package/lib/services/ProfilingDefinitionService.js +1029 -0
  347. package/lib/services/ProfilingDefinitionService.js.map +1 -0
  348. package/lib/services/ProfilingExecutionService.d.ts +122 -0
  349. package/lib/services/ProfilingExecutionService.js +320 -0
  350. package/lib/services/ProfilingExecutionService.js.map +1 -0
  351. package/lib/services/ProfilingSummaryService.d.ts +292 -0
  352. package/lib/services/ProfilingSummaryService.js +688 -0
  353. package/lib/services/ProfilingSummaryService.js.map +1 -0
  354. package/lib/services/RecordTypeService.d.ts +129 -0
  355. package/lib/services/RecordTypeService.js +284 -0
  356. package/lib/services/RecordTypeService.js.map +1 -0
  357. package/lib/services/SFDMUService.d.ts +146 -0
  358. package/lib/services/SFDMUService.js +323 -0
  359. package/lib/services/SFDMUService.js.map +1 -0
  360. package/lib/services/TabDetectionService.d.ts +105 -0
  361. package/lib/services/TabDetectionService.js +206 -0
  362. package/lib/services/TabDetectionService.js.map +1 -0
  363. package/lib/services/UnconfigureMode.d.ts +74 -0
  364. package/lib/services/UnconfigureMode.js +378 -0
  365. package/lib/services/UnconfigureMode.js.map +1 -0
  366. package/lib/services/UserConfigurationService.d.ts +158 -0
  367. package/lib/services/UserConfigurationService.js +574 -0
  368. package/lib/services/UserConfigurationService.js.map +1 -0
  369. package/lib/services/UserConfigurationTypes.d.ts +181 -0
  370. package/lib/services/UserConfigurationTypes.js +14 -0
  371. package/lib/services/UserConfigurationTypes.js.map +1 -0
  372. package/lib/services/UserReadinessService.d.ts +347 -0
  373. package/lib/services/UserReadinessService.js +891 -0
  374. package/lib/services/UserReadinessService.js.map +1 -0
  375. package/lib/services/constants.d.ts +54 -0
  376. package/lib/services/constants.js +71 -0
  377. package/lib/services/constants.js.map +1 -0
  378. package/lib/services/namespace-constants.d.ts +1 -0
  379. package/lib/services/namespace-constants.js +11 -0
  380. package/lib/services/namespace-constants.js.map +1 -0
  381. package/lib/services/namespace-filter.d.ts +36 -0
  382. package/lib/services/namespace-filter.js +109 -0
  383. package/lib/services/namespace-filter.js.map +1 -0
  384. package/lib/services/validation.d.ts +47 -0
  385. package/lib/services/validation.js +119 -0
  386. package/lib/services/validation.js.map +1 -0
  387. package/lib/utils/batch-processor.d.ts +13 -0
  388. package/lib/utils/batch-processor.js +39 -0
  389. package/lib/utils/batch-processor.js.map +1 -0
  390. package/lib/utils/formatting/availability-grid.d.ts +81 -0
  391. package/lib/utils/formatting/availability-grid.js +94 -0
  392. package/lib/utils/formatting/availability-grid.js.map +1 -0
  393. package/lib/utils/formatting/business-process-grid.d.ts +51 -0
  394. package/lib/utils/formatting/business-process-grid.js +58 -0
  395. package/lib/utils/formatting/business-process-grid.js.map +1 -0
  396. package/lib/utils/formatting/command-display.d.ts +154 -0
  397. package/lib/utils/formatting/command-display.js +154 -0
  398. package/lib/utils/formatting/command-display.js.map +1 -0
  399. package/lib/utils/formatting/definition-create-display.d.ts +118 -0
  400. package/lib/utils/formatting/definition-create-display.js +230 -0
  401. package/lib/utils/formatting/definition-create-display.js.map +1 -0
  402. package/lib/utils/formatting/empty-states.d.ts +35 -0
  403. package/lib/utils/formatting/empty-states.js +70 -0
  404. package/lib/utils/formatting/empty-states.js.map +1 -0
  405. package/lib/utils/formatting/errors.d.ts +33 -0
  406. package/lib/utils/formatting/errors.js +72 -0
  407. package/lib/utils/formatting/errors.js.map +1 -0
  408. package/lib/utils/formatting/field-types.d.ts +32 -0
  409. package/lib/utils/formatting/field-types.js +88 -0
  410. package/lib/utils/formatting/field-types.js.map +1 -0
  411. package/lib/utils/formatting/index.d.ts +29 -0
  412. package/lib/utils/formatting/index.js +28 -0
  413. package/lib/utils/formatting/index.js.map +1 -0
  414. package/lib/utils/formatting/indicators.d.ts +113 -0
  415. package/lib/utils/formatting/indicators.js +161 -0
  416. package/lib/utils/formatting/indicators.js.map +1 -0
  417. package/lib/utils/formatting/loading-messages.d.ts +37 -0
  418. package/lib/utils/formatting/loading-messages.js +50 -0
  419. package/lib/utils/formatting/loading-messages.js.map +1 -0
  420. package/lib/utils/formatting/namespace-display.d.ts +31 -0
  421. package/lib/utils/formatting/namespace-display.js +64 -0
  422. package/lib/utils/formatting/namespace-display.js.map +1 -0
  423. package/lib/utils/formatting/numbers.d.ts +73 -0
  424. package/lib/utils/formatting/numbers.js +187 -0
  425. package/lib/utils/formatting/numbers.js.map +1 -0
  426. package/lib/utils/formatting/object-describe-display.d.ts +117 -0
  427. package/lib/utils/formatting/object-describe-display.js +447 -0
  428. package/lib/utils/formatting/object-describe-display.js.map +1 -0
  429. package/lib/utils/formatting/object-list-display.d.ts +225 -0
  430. package/lib/utils/formatting/object-list-display.js +718 -0
  431. package/lib/utils/formatting/object-list-display.js.map +1 -0
  432. package/lib/utils/formatting/org-identity.d.ts +15 -0
  433. package/lib/utils/formatting/org-identity.js +28 -0
  434. package/lib/utils/formatting/org-identity.js.map +1 -0
  435. package/lib/utils/formatting/record-age-grid.d.ts +41 -0
  436. package/lib/utils/formatting/record-age-grid.js +56 -0
  437. package/lib/utils/formatting/record-age-grid.js.map +1 -0
  438. package/lib/utils/formatting/sections.d.ts +108 -0
  439. package/lib/utils/formatting/sections.js +150 -0
  440. package/lib/utils/formatting/sections.js.map +1 -0
  441. package/lib/utils/formatting/tables.d.ts +90 -0
  442. package/lib/utils/formatting/tables.js +113 -0
  443. package/lib/utils/formatting/tables.js.map +1 -0
  444. package/lib/utils/formatting/user-details-display.d.ts +101 -0
  445. package/lib/utils/formatting/user-details-display.js +425 -0
  446. package/lib/utils/formatting/user-details-display.js.map +1 -0
  447. package/lib/utils/pagination/keypress-reader.d.ts +20 -0
  448. package/lib/utils/pagination/keypress-reader.js +63 -0
  449. package/lib/utils/pagination/keypress-reader.js.map +1 -0
  450. package/lib/utils/pagination/paginate-output.d.ts +48 -0
  451. package/lib/utils/pagination/paginate-output.js +136 -0
  452. package/lib/utils/pagination/paginate-output.js.map +1 -0
  453. package/messages/compatibility.check.md +71 -0
  454. package/messages/cuneiform.access.md +138 -0
  455. package/messages/definition.create.md +525 -0
  456. package/messages/definition.export.md +84 -0
  457. package/messages/definition.get.md +147 -0
  458. package/messages/definition.import.md +65 -0
  459. package/messages/definition.list.md +264 -0
  460. package/messages/definition.purge.md +330 -0
  461. package/messages/definition.update.md +118 -0
  462. package/messages/mcp.serve.md +66 -0
  463. package/messages/object.describe.md +205 -0
  464. package/messages/object.list.md +463 -0
  465. package/messages/org.details.md +386 -0
  466. package/messages/org.reset.md +71 -0
  467. package/messages/profile.md +243 -0
  468. package/messages/profile.request.cancel.md +143 -0
  469. package/messages/profile.request.delete.md +139 -0
  470. package/messages/profile.request.list.md +89 -0
  471. package/messages/summary.purge.md +218 -0
  472. package/messages/summary.reprofile.md +150 -0
  473. package/messages/summary.stop.md +157 -0
  474. package/messages/user.details.md +501 -0
  475. package/oclif.lock +3267 -2148
  476. package/oclif.manifest.json +2829 -31
  477. package/package.json +104 -18
  478. package/lib/commands/cuneiform/about.d.ts +0 -13
  479. package/lib/commands/cuneiform/about.js +0 -26
  480. package/lib/commands/cuneiform/about.js.map +0 -1
  481. package/lib/commands/hello/world.d.ts +0 -14
  482. package/lib/commands/hello/world.js +0 -27
  483. package/lib/commands/hello/world.js.map +0 -1
  484. package/lib/index.d.ts +0 -2
  485. package/lib/index.js +0 -2
  486. package/lib/index.js.map +0 -1
  487. package/messages/cuneiform.about.md +0 -19
  488. package/messages/hello.world.md +0 -29
@@ -0,0 +1,891 @@
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 { CuneiformQueryBuilder } from '../adapters/soql/cuneiform-query-builder.js';
9
+ import { createSuccessResult, createFailureResult } from '../models/service-result.js';
10
+ import { ServiceErrorCodes } from '../adapters/errors.js';
11
+ import { CUNEIFORM_NAMESPACE } from './namespace-constants.js';
12
+ import { validateRequiredString } from './validation.js';
13
+ /**
14
+ * Permission set names required for Cuneiform operations.
15
+ */
16
+ export const REQUIRED_PERMISSION_SETS = [
17
+ 'Cuneiform_for_CRM_PRO_Administrative_User',
18
+ 'Cuneiform_for_CRM_Global_Profiling_Support',
19
+ ];
20
+ /**
21
+ * Maps required permission set API names to their human-readable labels.
22
+ * Used as a fallback when the live PermissionSet.Label cannot be queried
23
+ * (e.g., the org lacks the package or the user lacks read access). Live
24
+ * labels from the org are preferred — these values must mirror the package
25
+ * `PermissionSet.label` so display stays consistent regardless of source.
26
+ */
27
+ export const REQUIRED_PERMISSION_SET_LABELS = {
28
+ [REQUIRED_PERMISSION_SETS[0]]: 'Cuneiform for CRM (Pro): Administrative User',
29
+ [REQUIRED_PERMISSION_SETS[1]]: 'Cuneiform for CRM: API-Based Profiling',
30
+ };
31
+ /**
32
+ * Org types that allow configure operations.
33
+ * Production is the only blocked type.
34
+ */
35
+ export const CONFIGURE_ALLOWED_ORG_TYPES = ['Developer', 'Sandbox', 'Scratch', 'Trial'];
36
+ /**
37
+ * Service for validating user readiness to use Cuneiform.
38
+ *
39
+ * Checks whether a user has the required permission sets and configuration
40
+ * profile to execute Cuneiform operations. Also detects if Cuneiform is
41
+ * installed in the org.
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const service = new UserReadinessService({ soqlAdapter });
46
+ *
47
+ * const result = await service.checkReadiness(userId);
48
+ * if (result.success) {
49
+ * if (result.data.isReady) {
50
+ * console.log('User is ready to use Cuneiform');
51
+ * } else {
52
+ * console.log('Issues:', result.data.messages.join(', '));
53
+ * }
54
+ * }
55
+ * ```
56
+ */
57
+ export class UserReadinessService {
58
+ soqlAdapter;
59
+ logger;
60
+ restClient;
61
+ constructor(config) {
62
+ this.soqlAdapter = config.soqlAdapter;
63
+ this.logger = config.logger;
64
+ this.restClient = config.restClient;
65
+ }
66
+ /**
67
+ * Checks whether configure operations are allowed in the given org type.
68
+ *
69
+ * Only Production orgs are blocked. Developer, Sandbox, Scratch, and Trial
70
+ * orgs are all allowed to run configure operations.
71
+ *
72
+ * @param orgType - The classified org type
73
+ * @returns true if configure is allowed, false for Production
74
+ */
75
+ static isConfigureAllowed(orgType) {
76
+ return CONFIGURE_ALLOWED_ORG_TYPES.includes(orgType);
77
+ }
78
+ /**
79
+ * Creates an empty UserReadiness result for error cases.
80
+ */
81
+ static createEmptyReadiness(userId) {
82
+ return {
83
+ userId,
84
+ isReady: false,
85
+ isCuneiformInstalled: false,
86
+ permissionSets: {
87
+ hasAllRequired: false,
88
+ assignments: [],
89
+ missingRequired: [...REQUIRED_PERMISSION_SETS],
90
+ },
91
+ configProfile: { isConfigured: false },
92
+ messages: [],
93
+ };
94
+ }
95
+ /**
96
+ * Creates an empty UserInfo result for error cases.
97
+ */
98
+ static createEmptyUserInfo(userId) {
99
+ return {
100
+ id: userId,
101
+ username: '',
102
+ fullName: '',
103
+ email: '',
104
+ profileName: '',
105
+ roleName: null,
106
+ isActive: false,
107
+ };
108
+ }
109
+ /**
110
+ * Checks if an error message indicates Cuneiform namespace is not found.
111
+ *
112
+ * This typically means the Cuneiform for Salesforce package is not installed in the org.
113
+ * The Salesforce API returns "sObject type not supported" or similar when
114
+ * querying namespaced objects that don't exist.
115
+ */
116
+ static isCuneiformNamespaceNotFoundError(message) {
117
+ const lowerMessage = message.toLowerCase();
118
+ return (lowerMessage.includes('sobject type') &&
119
+ (lowerMessage.includes('not supported') || lowerMessage.includes('invalid')));
120
+ }
121
+ /**
122
+ * Maps the ISV REST API payload to the canonical UserReadiness shape.
123
+ *
124
+ * The Apex endpoint returns flat booleans and differently-named fields
125
+ * (e.g. `assigned` instead of `isAssigned`, `cuneiformInstalled` instead
126
+ * of `isCuneiformInstalled`). This mapper bridges the wire format to
127
+ * the TypeScript domain type so downstream code (buildResult, display)
128
+ * works unchanged.
129
+ */
130
+ static mapRestPayloadToUserReadiness(userId, payload) {
131
+ const assignments = (payload.permissionSets ?? []).map((ps) => ({
132
+ name: ps.name,
133
+ label: ps.label,
134
+ isAssigned: ps.assigned,
135
+ isRequired: REQUIRED_PERMISSION_SETS.includes(ps.name),
136
+ }));
137
+ return {
138
+ userId,
139
+ // Normalize isReady: the ISV REST endpoint may return isReady=true when only
140
+ // permission sets are assigned but API-only profiling is not enabled at the org
141
+ // level. Override to false when apiOnlyProfilingEnabled is not explicitly true
142
+ // so the display layer stays consistent with validateProfilingAccess() gate 3.
143
+ isReady: payload.isReady,
144
+ isCuneiformInstalled: payload.cuneiformInstalled,
145
+ permissionSets: {
146
+ hasAllRequired: payload.allPermissionsAssigned,
147
+ assignments,
148
+ missingRequired: payload.missingPermissionSets ?? [],
149
+ },
150
+ configProfile: {
151
+ isConfigured: payload.hasConfigurationProfile,
152
+ profileLabel: payload.configurationProfileLabel,
153
+ profileDeveloperName: payload.configurationProfileDeveloperName,
154
+ apiOnlyProfilingEnabled: payload.apiOnlyProfilingEnabled,
155
+ selfRegistrationEnabled: payload.selfRegistrationEnabled,
156
+ },
157
+ messages: [],
158
+ };
159
+ }
160
+ /**
161
+ * Retrieves user identity information.
162
+ *
163
+ * Queries the User object with Profile and UserRole relationships to build
164
+ * a complete user identity record. Handles null UserRole gracefully since
165
+ * users may not have a role assigned.
166
+ *
167
+ * @param userId - The Salesforce user ID to query
168
+ * @returns ServiceResult containing UserInfo
169
+ *
170
+ * @example
171
+ * ```typescript
172
+ * const result = await service.getUserInfo(userId);
173
+ * if (result.success) {
174
+ * console.log(`User: ${result.data.fullName} (${result.data.profileName})`);
175
+ * if (result.data.roleName) {
176
+ * console.log(`Role: ${result.data.roleName}`);
177
+ * }
178
+ * }
179
+ * ```
180
+ */
181
+ async getUserInfo(userId) {
182
+ const startTime = Date.now();
183
+ const emptyResult = UserReadinessService.createEmptyUserInfo(userId);
184
+ // Validate userId
185
+ const validationError = validateRequiredString(userId, 'User ID');
186
+ if (validationError) {
187
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_ID_REQUIRED, validationError, {
188
+ metadata: { duration: Date.now() - startTime },
189
+ });
190
+ }
191
+ try {
192
+ this.logger?.log(`Querying user info for: ${userId}`);
193
+ const soql = new CuneiformQueryBuilder()
194
+ .select(['Id', 'Username', 'Name', 'Email', 'Profile.Name', 'UserRole.Name', 'IsActive'])
195
+ .from('User')
196
+ .where('Id', '=', userId)
197
+ .limit(1)
198
+ .toSOQL();
199
+ const result = await this.soqlAdapter.query(soql);
200
+ if (!result.success) {
201
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_INFO_QUERY_FAILED, result.message ?? 'User query failed', {
202
+ metadata: { duration: Date.now() - startTime },
203
+ });
204
+ }
205
+ if (result.data.records.length === 0) {
206
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_INFO_QUERY_FAILED, `User not found: ${userId}`, {
207
+ metadata: { duration: Date.now() - startTime },
208
+ });
209
+ }
210
+ const record = result.data.records[0];
211
+ const userInfo = {
212
+ id: record.Id,
213
+ username: record.Username,
214
+ fullName: record.Name,
215
+ email: record.Email,
216
+ profileName: record.Profile.Name,
217
+ roleName: record.UserRole?.Name ?? null,
218
+ isActive: record.IsActive,
219
+ };
220
+ const duration = Date.now() - startTime;
221
+ this.logger?.log(`User info retrieved: ${userInfo.fullName}`);
222
+ return createSuccessResult(userInfo, {
223
+ message: `User: ${userInfo.fullName}`,
224
+ metadata: { duration },
225
+ });
226
+ }
227
+ catch (error) {
228
+ const errorMessage = error instanceof Error ? error.message : String(error);
229
+ this.logger?.log(`User info query failed: ${errorMessage}`);
230
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_INFO_QUERY_FAILED, errorMessage, {
231
+ metadata: { duration: Date.now() - startTime },
232
+ });
233
+ }
234
+ }
235
+ /**
236
+ * Checks complete user readiness for Cuneiform operations.
237
+ *
238
+ * Validates:
239
+ * 1. Cuneiform is installed (by querying CMT)
240
+ * 2. User has required permission sets
241
+ * 3. Configuration profile is set up
242
+ *
243
+ * @param userId - The Salesforce user ID to check
244
+ * @returns ServiceResult containing UserReadiness
245
+ */
246
+ async checkReadiness(userId) {
247
+ const startTime = Date.now();
248
+ // Validate userId
249
+ const validationError = validateRequiredString(userId, 'User ID');
250
+ if (validationError) {
251
+ const emptyResult = UserReadinessService.createEmptyReadiness(userId);
252
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_ID_REQUIRED, validationError, {
253
+ metadata: { duration: Date.now() - startTime },
254
+ });
255
+ }
256
+ // REST API path: delegate to server when restClient is available.
257
+ // Falls back to local SOQL if REST returns unexpected results.
258
+ const restFallbackResult = await this.tryRestReadiness(userId, startTime);
259
+ if (restFallbackResult) {
260
+ return restFallbackResult;
261
+ }
262
+ const messages = [];
263
+ try {
264
+ this.logger?.log(`Checking readiness for user: ${userId}`);
265
+ // Check permission sets and config profile in parallel
266
+ const [permissionResult, configResult] = await Promise.all([
267
+ this.hasRequiredPermissionSets(userId),
268
+ this.hasConfigProfile(),
269
+ ]);
270
+ // Determine if Cuneiform is installed
271
+ let isCuneiformInstalled = true;
272
+ if (!configResult.success && configResult.errorCode === ServiceErrorCodes.USER_CUNEIFORM_NAMESPACE_NOT_FOUND) {
273
+ isCuneiformInstalled = false;
274
+ messages.push('Cuneiform for Salesforce is not installed in this org');
275
+ }
276
+ // Build permission set status
277
+ let permissionSets;
278
+ if (!permissionResult.success) {
279
+ permissionSets = {
280
+ hasAllRequired: false,
281
+ assignments: [],
282
+ missingRequired: [...REQUIRED_PERMISSION_SETS],
283
+ };
284
+ messages.push(`Permission check failed: ${permissionResult.message ?? 'Unknown error'}`);
285
+ }
286
+ else {
287
+ permissionSets = permissionResult.data;
288
+ if (!permissionSets.hasAllRequired) {
289
+ messages.push(`Missing permission sets: ${permissionSets.missingRequired.join(', ')}`);
290
+ }
291
+ }
292
+ // Build config profile status
293
+ let configProfile;
294
+ if (!configResult.success) {
295
+ configProfile = { isConfigured: false };
296
+ if (configResult.errorCode !== ServiceErrorCodes.USER_CUNEIFORM_NAMESPACE_NOT_FOUND) {
297
+ messages.push(`Config profile check failed: ${configResult.message ?? 'Unknown error'}`);
298
+ }
299
+ }
300
+ else {
301
+ configProfile = configResult.data;
302
+ if (!configProfile.isConfigured) {
303
+ messages.push('No active configuration profile found');
304
+ }
305
+ }
306
+ // Determine overall readiness — mirrors the gate chain in validateProfilingAccess().
307
+ // configProfile.isConfigured alone is not enough: the org must also have API-only
308
+ // profiling explicitly enabled, or the user will hit E4659 on every other command.
309
+ const isReady = isCuneiformInstalled &&
310
+ permissionSets.hasAllRequired &&
311
+ configProfile.isConfigured &&
312
+ configProfile.apiOnlyProfilingEnabled === true;
313
+ if (isReady) {
314
+ messages.push('User is ready to use Cuneiform');
315
+ }
316
+ const result = {
317
+ userId,
318
+ isReady,
319
+ isCuneiformInstalled,
320
+ permissionSets,
321
+ configProfile,
322
+ messages,
323
+ };
324
+ const duration = Date.now() - startTime;
325
+ this.logger?.log(`Readiness check complete: ${isReady ? 'READY' : 'NOT READY'}`);
326
+ return createSuccessResult(result, {
327
+ message: isReady ? 'User is ready' : 'User is not ready',
328
+ metadata: { duration },
329
+ });
330
+ }
331
+ catch (error) {
332
+ const errorMessage = error instanceof Error ? error.message : String(error);
333
+ this.logger?.log(`Readiness check failed: ${errorMessage}`);
334
+ const emptyResult = UserReadinessService.createEmptyReadiness(userId);
335
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_READINESS_QUERY_FAILED, errorMessage, {
336
+ metadata: { duration: Date.now() - startTime },
337
+ });
338
+ }
339
+ }
340
+ /**
341
+ * Checks if the user has required Cuneiform permission sets.
342
+ *
343
+ * @param userId - The Salesforce user ID to check
344
+ * @returns ServiceResult containing PermissionSetStatus
345
+ */
346
+ async hasRequiredPermissionSets(userId) {
347
+ const startTime = Date.now();
348
+ // Validate userId
349
+ const validationError = validateRequiredString(userId, 'User ID');
350
+ if (validationError) {
351
+ const emptyResult = {
352
+ hasAllRequired: false,
353
+ assignments: [],
354
+ missingRequired: [...REQUIRED_PERMISSION_SETS],
355
+ };
356
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_ID_REQUIRED, validationError, {
357
+ metadata: { duration: Date.now() - startTime },
358
+ });
359
+ }
360
+ try {
361
+ this.logger?.log(`Checking permission sets for user: ${userId}`);
362
+ // Step 1: Check direct PermissionSetAssignment records
363
+ const directSoql = new CuneiformQueryBuilder()
364
+ .select([
365
+ 'Id',
366
+ 'PermissionSet.Id',
367
+ 'PermissionSet.Name',
368
+ 'PermissionSet.Label',
369
+ 'PermissionSet.NamespacePrefix',
370
+ ])
371
+ .from('PermissionSetAssignment')
372
+ .where('AssigneeId', '=', userId)
373
+ .andWhereIn('PermissionSet.Name', [...REQUIRED_PERMISSION_SETS])
374
+ .andWhere('PermissionSet.NamespacePrefix', '=', CUNEIFORM_NAMESPACE)
375
+ .toSOQL();
376
+ const directResult = await this.soqlAdapter.query(directSoql);
377
+ if (!directResult.success) {
378
+ const emptyResult = {
379
+ hasAllRequired: false,
380
+ assignments: [],
381
+ missingRequired: [...REQUIRED_PERMISSION_SETS],
382
+ };
383
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, directResult.message ?? 'Permission set query failed', {
384
+ metadata: { duration: Date.now() - startTime },
385
+ });
386
+ }
387
+ // Build map of assigned permission set names to their labels
388
+ const assignedLabels = new Map(directResult.data.records.map((r) => [r.PermissionSet.Name, r.PermissionSet.Label]));
389
+ // Step 2: If any required permission sets are still missing, check group-based access
390
+ const directMissing = REQUIRED_PERMISSION_SETS.filter((name) => !assignedLabels.has(name));
391
+ if (directMissing.length > 0) {
392
+ const groupLabels = await this.getGroupAssignedPermissionSets(userId, directMissing);
393
+ for (const [name, label] of groupLabels) {
394
+ assignedLabels.set(name, label);
395
+ }
396
+ }
397
+ // Query canonical labels from PermissionSet so unassigned permsets
398
+ // display the same label as assigned ones. The assigned path joins
399
+ // `PermissionSet.Label` and is already authoritative; only query when
400
+ // at least one required permset is missing from `assignedLabels`.
401
+ const labelGap = REQUIRED_PERMISSION_SETS.some((name) => !assignedLabels.has(name));
402
+ const canonicalLabels = labelGap ? await this.getCanonicalPermissionSetLabels() : new Map();
403
+ // Build final status from combined direct + group results
404
+ const assignments = REQUIRED_PERMISSION_SETS.map((name) => ({
405
+ name,
406
+ label: assignedLabels.get(name) ?? canonicalLabels.get(name),
407
+ isAssigned: assignedLabels.has(name),
408
+ isRequired: true,
409
+ }));
410
+ const missingRequired = REQUIRED_PERMISSION_SETS.filter((name) => !assignedLabels.has(name));
411
+ const hasAllRequired = missingRequired.length === 0;
412
+ const status = {
413
+ hasAllRequired,
414
+ assignments,
415
+ missingRequired,
416
+ };
417
+ const duration = Date.now() - startTime;
418
+ this.logger?.log(`Permission check: ${hasAllRequired ? 'all assigned' : `missing ${missingRequired.length}`}`);
419
+ return createSuccessResult(status, {
420
+ message: hasAllRequired
421
+ ? 'All required permission sets assigned'
422
+ : `Missing ${missingRequired.length} permission sets`,
423
+ metadata: { duration },
424
+ });
425
+ }
426
+ catch (error) {
427
+ const errorMessage = error instanceof Error ? error.message : String(error);
428
+ this.logger?.log(`Permission set check failed: ${errorMessage}`);
429
+ const emptyResult = {
430
+ hasAllRequired: false,
431
+ assignments: [],
432
+ missingRequired: [...REQUIRED_PERMISSION_SETS],
433
+ };
434
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, errorMessage, {
435
+ metadata: { duration: Date.now() - startTime },
436
+ });
437
+ }
438
+ }
439
+ /**
440
+ * Retrieves the ID of a permission set by name and namespace.
441
+ *
442
+ * Queries the PermissionSet object directly to find a permission set by
443
+ * its Name and NamespacePrefix. This method returns the permission set ID
444
+ * for use in permission set assignment operations.
445
+ *
446
+ * @param permissionSetName - The permission set API name (e.g., 'Cuneiform_for_CRM_Global_Profiling_Support')
447
+ * @param namespacePrefix - The namespace prefix (e.g., 'pnova')
448
+ * @returns ServiceResult containing the permission set ID or null if not found
449
+ *
450
+ * @example
451
+ * ```typescript
452
+ * const result = await service.getPermissionSetId(
453
+ * 'Cuneiform_for_CRM_Global_Profiling_Support',
454
+ * 'pnova'
455
+ * );
456
+ * if (result.success && result.data) {
457
+ * console.log(`Permission Set ID: ${result.data}`);
458
+ * } else if (result.success && result.data === null) {
459
+ * console.log('Permission set not found in org');
460
+ * }
461
+ * ```
462
+ */
463
+ async getPermissionSetId(permissionSetName, namespacePrefix) {
464
+ const startTime = Date.now();
465
+ const emptyResult = null;
466
+ // Validate permissionSetName
467
+ const nameError = validateRequiredString(permissionSetName, 'Permission set name');
468
+ if (nameError) {
469
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, nameError, {
470
+ metadata: { duration: Date.now() - startTime },
471
+ });
472
+ }
473
+ // Validate namespacePrefix
474
+ const namespaceError = validateRequiredString(namespacePrefix, 'Namespace prefix');
475
+ if (namespaceError) {
476
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, namespaceError, {
477
+ metadata: { duration: Date.now() - startTime },
478
+ });
479
+ }
480
+ try {
481
+ this.logger?.log(`Querying permission set: ${namespacePrefix}__${permissionSetName}`);
482
+ const soql = new CuneiformQueryBuilder()
483
+ .select(['Id'])
484
+ .from('PermissionSet')
485
+ .where('Name', '=', permissionSetName)
486
+ .andWhere('NamespacePrefix', '=', namespacePrefix)
487
+ .limit(1)
488
+ .toSOQL();
489
+ const result = await this.soqlAdapter.query(soql);
490
+ if (!result.success) {
491
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, result.message ?? 'Permission set query failed', {
492
+ metadata: { duration: Date.now() - startTime },
493
+ });
494
+ }
495
+ // Permission set not found - return null (not an error)
496
+ if (result.data.records.length === 0) {
497
+ const duration = Date.now() - startTime;
498
+ this.logger?.log(`Permission set not found: ${namespacePrefix}__${permissionSetName}`);
499
+ return createSuccessResult(null, {
500
+ message: `Permission set not found: ${namespacePrefix}__${permissionSetName}`,
501
+ warnings: [`Permission set '${permissionSetName}' not found in namespace '${namespacePrefix}'`],
502
+ metadata: { duration },
503
+ });
504
+ }
505
+ const permissionSetId = result.data.records[0].Id;
506
+ const duration = Date.now() - startTime;
507
+ this.logger?.log(`Permission set found: ${permissionSetId}`);
508
+ return createSuccessResult(permissionSetId, {
509
+ message: `Permission set found: ${permissionSetId}`,
510
+ metadata: { duration },
511
+ });
512
+ }
513
+ catch (error) {
514
+ const errorMessage = error instanceof Error ? error.message : String(error);
515
+ this.logger?.log(`Permission set query failed: ${errorMessage}`);
516
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_PERMISSION_SET_CHECK_FAILED, errorMessage, {
517
+ metadata: { duration: Date.now() - startTime },
518
+ });
519
+ }
520
+ }
521
+ /**
522
+ * Checks if a configuration profile is configured.
523
+ *
524
+ * Queries the Cuneiform Active Configuration CMT to determine if a profile
525
+ * is set up. If the CMT doesn't exist, returns E4654 indicating Cuneiform
526
+ * is not installed.
527
+ *
528
+ * @returns ServiceResult containing ConfigProfileStatus
529
+ */
530
+ async hasConfigProfile() {
531
+ const startTime = Date.now();
532
+ try {
533
+ this.logger?.log('Checking configuration profile...');
534
+ const soql = new CuneiformQueryBuilder()
535
+ .select([
536
+ 'Id',
537
+ 'DeveloperName',
538
+ 'pnova__Active_Configuration_Profile__r.DeveloperName',
539
+ 'pnova__Active_Configuration_Profile__r.Label',
540
+ 'pnova__Active_Configuration_Profile__r.pnova__Enable_API_Only_Profiling__c',
541
+ 'pnova__Active_Configuration_Profile__r.pnova__Enable_API_Only_Profiling_Registration__c',
542
+ ])
543
+ .from('pnova__Active_Configuration__mdt')
544
+ .where('DeveloperName', '=', 'Active_Configuration')
545
+ .limit(1)
546
+ .toSOQL();
547
+ const result = await this.soqlAdapter.query(soql);
548
+ // Check for namespace not found error (Cuneiform not installed)
549
+ if (!result.success) {
550
+ if (UserReadinessService.isCuneiformNamespaceNotFoundError(result.message ?? '')) {
551
+ return createFailureResult({ isConfigured: false }, ServiceErrorCodes.USER_CUNEIFORM_NAMESPACE_NOT_FOUND, 'Cuneiform namespace not found - package may not be installed', { metadata: { duration: Date.now() - startTime } });
552
+ }
553
+ return createFailureResult({ isConfigured: false }, ServiceErrorCodes.USER_CONFIG_PROFILE_CHECK_FAILED, result.message ?? 'Config profile query failed', { metadata: { duration: Date.now() - startTime } });
554
+ }
555
+ // No config record found
556
+ if (result.data.records.length === 0) {
557
+ const duration = Date.now() - startTime;
558
+ return createSuccessResult({ isConfigured: false }, {
559
+ message: 'No active configuration found',
560
+ metadata: { duration },
561
+ });
562
+ }
563
+ const record = result.data.records[0];
564
+ const profile = record.pnova__Active_Configuration_Profile__r;
565
+ // Config record exists but no profile linked
566
+ if (!profile) {
567
+ const duration = Date.now() - startTime;
568
+ return createSuccessResult({ isConfigured: false }, {
569
+ message: 'Configuration exists but no profile is linked',
570
+ metadata: { duration },
571
+ });
572
+ }
573
+ const status = {
574
+ isConfigured: true,
575
+ profileDeveloperName: profile.DeveloperName,
576
+ profileLabel: profile.Label,
577
+ apiOnlyProfilingEnabled: profile.pnova__Enable_API_Only_Profiling__c,
578
+ selfRegistrationEnabled: profile.pnova__Enable_API_Only_Profiling_Registration__c,
579
+ };
580
+ const duration = Date.now() - startTime;
581
+ this.logger?.log(`Config profile found: ${status.profileLabel ?? 'Unknown'}`);
582
+ return createSuccessResult(status, {
583
+ message: `Active profile: ${status.profileLabel ?? 'Unknown'}`,
584
+ metadata: { duration },
585
+ });
586
+ }
587
+ catch (error) {
588
+ const errorMessage = error instanceof Error ? error.message : String(error);
589
+ this.logger?.log(`Config profile check failed: ${errorMessage}`);
590
+ // Check for namespace not found in exception
591
+ if (UserReadinessService.isCuneiformNamespaceNotFoundError(errorMessage)) {
592
+ return createFailureResult({ isConfigured: false }, ServiceErrorCodes.USER_CUNEIFORM_NAMESPACE_NOT_FOUND, 'Cuneiform namespace not found - package may not be installed', { metadata: { duration: Date.now() - startTime } });
593
+ }
594
+ return createFailureResult({ isConfigured: false }, ServiceErrorCodes.USER_CONFIG_PROFILE_CHECK_FAILED, errorMessage, { metadata: { duration: Date.now() - startTime } });
595
+ }
596
+ }
597
+ /**
598
+ * Validates that the user has profiling access to use Cuneiform commands.
599
+ *
600
+ * Checks in order:
601
+ * 1. Cuneiform is installed in the org
602
+ * 2. Required permission sets are assigned
603
+ * 3. Global Profiling is enabled
604
+ *
605
+ * Returns success if all checks pass. Returns a specific error code for the
606
+ * first failing check, with a user-friendly message including remediation steps.
607
+ *
608
+ * @param userId - The Salesforce user ID to validate
609
+ * @returns ServiceResult<void> — success or failure with specific error code
610
+ */
611
+ async validateProfilingAccess(userId) {
612
+ const startTime = Date.now();
613
+ const readinessResult = await this.checkReadiness(userId);
614
+ if (!readinessResult.success) {
615
+ return createFailureResult(undefined, readinessResult.errorCode ?? ServiceErrorCodes.USER_READINESS_QUERY_FAILED, readinessResult.message ?? 'Readiness check failed', { metadata: { duration: Date.now() - startTime } });
616
+ }
617
+ const readiness = readinessResult.data;
618
+ // Gate 1: Cuneiform must be installed
619
+ if (!readiness.isCuneiformInstalled) {
620
+ return createFailureResult(undefined, ServiceErrorCodes.USER_CUNEIFORM_NAMESPACE_NOT_FOUND, 'Cuneiform for Salesforce is not installed in this org.', { metadata: { duration: Date.now() - startTime } });
621
+ }
622
+ // Gate 2: Required permission sets must be assigned
623
+ if (!readiness.permissionSets.hasAllRequired) {
624
+ const missingLabels = readiness.permissionSets.missingRequired.map((name) => REQUIRED_PERMISSION_SET_LABELS[name] ?? name);
625
+ return createFailureResult(undefined, ServiceErrorCodes.PROFILING_ACCESS_DENIED, "Cuneiform is installed, but you don't have profiling access. " +
626
+ `Missing permission sets: ${missingLabels.join(', ')}. ` +
627
+ 'Run "sf cuneiform user details --target-org <org>" for a full readiness diagnostic.', { metadata: { duration: Date.now() - startTime } });
628
+ }
629
+ // Gate 3: Global Profiling must be explicitly enabled
630
+ // Checks both: (a) config profile must exist, and (b) apiOnlyProfilingEnabled must be exactly true.
631
+ // When apiOnlyProfilingEnabled is undefined (e.g., Salesforce field returns null), access is denied.
632
+ if (!readiness.configProfile.isConfigured || readiness.configProfile.apiOnlyProfilingEnabled !== true) {
633
+ return createFailureResult(undefined, ServiceErrorCodes.GLOBAL_PROFILING_NOT_ENABLED, 'Cuneiform is installed and your permission sets are assigned, but Global Profiling is not enabled. ' +
634
+ 'Run "sf cuneiform user details --target-org <org> --configure" to enable it, ' +
635
+ 'or ask your Salesforce administrator to enable API-Only Profiling in the Configuration Profile.', { metadata: { duration: Date.now() - startTime } });
636
+ }
637
+ // Gate 4: Self-registration must be enabled for CLI self-provisioning
638
+ // This gate only applies to the configure flow (checked by callers), but validateProfilingAccess
639
+ // does NOT enforce it — it validates read access only. The configure flow checks this gate separately.
640
+ // See UserConfigurationService.buildConfigurationActions() for the enforcement point.
641
+ return createSuccessResult(undefined, {
642
+ message: 'Profiling access validated',
643
+ metadata: { duration: Date.now() - startTime },
644
+ });
645
+ }
646
+ /**
647
+ * Checks the feature status via the ISV profiling status REST endpoint.
648
+ *
649
+ * Calls `GET /services/apexrest/pnova/v1/profiling/status` to determine whether
650
+ * API-Based Profiling is enabled for this organization. This is Gate 0 — if the
651
+ * feature is not enabled, all other gates are moot.
652
+ *
653
+ * Graceful degradation:
654
+ * - On 404 (endpoint not found): returns success with `featureEnabled: true`.
655
+ * Older ISV packages may not expose this endpoint; we don't block them.
656
+ * - On other errors: returns failure with `FEATURE_STATUS_CHECK_FAILED`.
657
+ *
658
+ * @param restAdapter - The REST API adapter for making the HTTP request
659
+ * @returns ServiceResult containing FeatureStatus
660
+ *
661
+ * @example
662
+ * ```typescript
663
+ * const result = await service.checkFeatureStatus(restAdapter);
664
+ * if (result.success && !result.data.featureEnabled) {
665
+ * // Block command execution — feature is disabled
666
+ * }
667
+ * ```
668
+ */
669
+ async checkFeatureStatus(restAdapter) {
670
+ const startTime = Date.now();
671
+ const statusUrl = '/services/apexrest/pnova/v1/profiling/status';
672
+ try {
673
+ this.logger?.log('Checking feature status via REST endpoint');
674
+ const result = await restAdapter.request({
675
+ url: statusUrl,
676
+ method: 'GET',
677
+ });
678
+ if (result.success) {
679
+ const duration = Date.now() - startTime;
680
+ this.logger?.log(`Feature status: featureEnabled=${String(result.data.featureEnabled)}`);
681
+ return createSuccessResult(result.data, {
682
+ message: `Feature status: ${result.data.featureEnabled ? 'enabled' : 'disabled'}`,
683
+ metadata: { duration },
684
+ });
685
+ }
686
+ // Check for 404 — graceful degradation for older ISV packages
687
+ const errorMessage = result.message ?? '';
688
+ if (errorMessage.includes('404') || errorMessage.includes('Not Found') || errorMessage.includes('NOT_FOUND')) {
689
+ this.logger?.log('Feature status endpoint not found (404) — assuming enabled (older ISV package)');
690
+ const duration = Date.now() - startTime;
691
+ return createSuccessResult({
692
+ featureEnabled: true,
693
+ apiBasedProfilingEnabled: true,
694
+ selfRegistrationEnabled: false,
695
+ packageVersion: 'unknown',
696
+ }, {
697
+ message: 'Feature status endpoint not available — assuming enabled (older ISV package)',
698
+ warnings: ['Status endpoint not found; feature status assumed enabled for backward compatibility'],
699
+ metadata: { duration },
700
+ });
701
+ }
702
+ // Other errors — return failure
703
+ const duration = Date.now() - startTime;
704
+ return createFailureResult({
705
+ featureEnabled: false,
706
+ apiBasedProfilingEnabled: false,
707
+ selfRegistrationEnabled: false,
708
+ packageVersion: 'unknown',
709
+ }, ServiceErrorCodes.FEATURE_STATUS_CHECK_FAILED, errorMessage || 'Feature status check failed', { metadata: { duration } });
710
+ }
711
+ catch (error) {
712
+ const errorMessage = error instanceof Error ? error.message : String(error);
713
+ this.logger?.log(`Feature status check failed: ${errorMessage}`);
714
+ // Check for 404 in exception messages too
715
+ if (errorMessage.includes('404') || errorMessage.includes('Not Found') || errorMessage.includes('NOT_FOUND')) {
716
+ const duration = Date.now() - startTime;
717
+ return createSuccessResult({
718
+ featureEnabled: true,
719
+ apiBasedProfilingEnabled: true,
720
+ selfRegistrationEnabled: false,
721
+ packageVersion: 'unknown',
722
+ }, {
723
+ message: 'Feature status endpoint not available — assuming enabled (older ISV package)',
724
+ warnings: ['Status endpoint not found; feature status assumed enabled for backward compatibility'],
725
+ metadata: { duration },
726
+ });
727
+ }
728
+ return createFailureResult({
729
+ featureEnabled: false,
730
+ apiBasedProfilingEnabled: false,
731
+ selfRegistrationEnabled: false,
732
+ packageVersion: 'unknown',
733
+ }, ServiceErrorCodes.FEATURE_STATUS_CHECK_FAILED, errorMessage, { metadata: { duration: Date.now() - startTime } });
734
+ }
735
+ }
736
+ /**
737
+ * Queries the org for the canonical `PermissionSet.Label` of each required
738
+ * permission set, regardless of assignment state.
739
+ *
740
+ * Without this, label resolution in `hasRequiredPermissionSets` would
741
+ * differ by assignment state: assigned permsets pull their label from the
742
+ * `PermissionSetAssignment.PermissionSet.Label` join, while unassigned
743
+ * permsets fall back to the `REQUIRED_PERMISSION_SET_LABELS` constant —
744
+ * which drifts whenever the package renames a permset. Failures are
745
+ * non-fatal: callers fall back to the constant.
746
+ *
747
+ * @returns Map of permission set Name → Label. Empty map on query failure.
748
+ */
749
+ async getCanonicalPermissionSetLabels() {
750
+ try {
751
+ const soql = new CuneiformQueryBuilder()
752
+ .select(['Name', 'Label'])
753
+ .from('PermissionSet')
754
+ .where('NamespacePrefix', '=', CUNEIFORM_NAMESPACE)
755
+ .andWhereIn('Name', [...REQUIRED_PERMISSION_SETS])
756
+ .toSOQL();
757
+ const result = await this.soqlAdapter.query(soql);
758
+ if (!result.success) {
759
+ this.logger?.log(`Canonical label query failed: ${result.message ?? 'unknown'} — using fallback constants`);
760
+ return new Map();
761
+ }
762
+ return new Map(result.data.records.map((r) => [r.Name, r.Label]));
763
+ }
764
+ catch (error) {
765
+ const errorMessage = error instanceof Error ? error.message : String(error);
766
+ this.logger?.log(`Canonical label query threw: ${errorMessage} — using fallback constants`);
767
+ return new Map();
768
+ }
769
+ }
770
+ /**
771
+ * Attempts REST delegation for user readiness.
772
+ * Returns the successful result, or null to signal fallback to the local SOQL path.
773
+ * Falls back when REST returns cuneiformInstalled=false (Apex endpoint bug on some orgs).
774
+ */
775
+ async tryRestReadiness(userId, startTime) {
776
+ if (!this.restClient) {
777
+ return null;
778
+ }
779
+ const restResult = await this.checkReadinessViaRest(userId, startTime);
780
+ if (restResult.success && restResult.data.isCuneiformInstalled) {
781
+ return restResult;
782
+ }
783
+ this.logger?.log('REST readiness returned not-installed or failed — falling back to local SOQL');
784
+ return null;
785
+ }
786
+ /**
787
+ * Delegates user readiness check to the ISV REST API.
788
+ *
789
+ * @param userId - The Salesforce user ID to check
790
+ * @param startTime - Timestamp for duration tracking
791
+ * @returns ServiceResult containing UserReadiness from REST endpoint
792
+ */
793
+ async checkReadinessViaRest(userId, startTime) {
794
+ this.logger?.log('Delegating user readiness to ISV REST API (user-readiness)');
795
+ const emptyResult = UserReadinessService.createEmptyReadiness(userId);
796
+ const restResult = await this.restClient.checkUserReadiness(userId);
797
+ if (!restResult.success) {
798
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_READINESS_QUERY_FAILED, restResult.message ?? 'REST user-readiness failed', {
799
+ metadata: { duration: Date.now() - startTime },
800
+ });
801
+ }
802
+ const response = restResult.data;
803
+ if (!response.success) {
804
+ const errorMsg = response.errors?.length > 0 ? response.errors[0] : 'Server-side readiness check failed';
805
+ return createFailureResult(emptyResult, ServiceErrorCodes.USER_READINESS_QUERY_FAILED, errorMsg, {
806
+ metadata: { duration: Date.now() - startTime },
807
+ });
808
+ }
809
+ const mappedResult = response.result
810
+ ? UserReadinessService.mapRestPayloadToUserReadiness(userId, response.result)
811
+ : emptyResult;
812
+ // Ensure every required permset has an entry — the server may omit
813
+ // unassigned ones — and override REST-supplied labels with the org's
814
+ // canonical PermissionSet.Label only when REST didn't return one.
815
+ if (response.result) {
816
+ const restByName = new Map(mappedResult.permissionSets.assignments.map((a) => [a.name, a]));
817
+ const labelGap = REQUIRED_PERMISSION_SETS.some((name) => !restByName.get(name)?.label);
818
+ const canonicalLabels = labelGap ? await this.getCanonicalPermissionSetLabels() : new Map();
819
+ mappedResult.permissionSets.assignments = REQUIRED_PERMISSION_SETS.map((name) => {
820
+ const existing = restByName.get(name);
821
+ return {
822
+ name,
823
+ label: existing?.label ?? canonicalLabels.get(name) ?? REQUIRED_PERMISSION_SET_LABELS[name] ?? name,
824
+ isAssigned: existing?.isAssigned ?? false,
825
+ isRequired: true,
826
+ };
827
+ });
828
+ }
829
+ return createSuccessResult(mappedResult, {
830
+ message: 'User readiness checked via REST API',
831
+ metadata: { duration: Date.now() - startTime, strategyUsed: 'rest-api' },
832
+ });
833
+ }
834
+ /**
835
+ * Checks for required permission sets accessible through PermissionSetGroup membership.
836
+ *
837
+ * Two-step query approach for precision:
838
+ * 1. Get the specific PermissionSetGroup IDs assigned to this user
839
+ * 2. Check if those specific groups contain any of the missing required permission sets
840
+ *
841
+ * Both queries are tightly filtered to avoid pulling excess records:
842
+ * - Group query: filtered by AssigneeId AND PermissionSetGroupId != null
843
+ * - Component query: filtered by specific group IDs, permission set names, AND namespace
844
+ *
845
+ * @param userId - The Salesforce user ID
846
+ * @param missingNames - Permission set names not found via direct assignment
847
+ * @returns Map of permission set names to labels found through group membership
848
+ */
849
+ async getGroupAssignedPermissionSets(userId, missingNames) {
850
+ const foundLabels = new Map();
851
+ try {
852
+ // Step 2a: Get the user's PermissionSetGroup assignments
853
+ // Precise filter: only this user, only rows that ARE group assignments
854
+ const groupSoql = new CuneiformQueryBuilder()
855
+ .select(['PermissionSetGroupId'])
856
+ .from('PermissionSetAssignment')
857
+ .where('AssigneeId', '=', userId)
858
+ .andWhereNull('PermissionSetGroupId', false)
859
+ .toSOQL();
860
+ const groupResult = await this.soqlAdapter.query(groupSoql);
861
+ if (!groupResult.success || groupResult.data.records.length === 0) {
862
+ return foundLabels;
863
+ }
864
+ const groupIds = groupResult.data.records.map((r) => r.PermissionSetGroupId);
865
+ this.logger?.log(`User belongs to ${groupIds.length} permission set group(s)`);
866
+ // Step 2b: Check if those groups contain any of the missing required permission sets
867
+ // Precise filter: only the user's groups, only the missing names, only our namespace
868
+ const componentSoql = new CuneiformQueryBuilder()
869
+ .select(['PermissionSet.Name', 'PermissionSet.Label'])
870
+ .from('PermissionSetGroupComponent')
871
+ .where('PermissionSet.NamespacePrefix', '=', CUNEIFORM_NAMESPACE)
872
+ .andWhereIn('PermissionSetGroupId', groupIds)
873
+ .andWhereIn('PermissionSet.Name', [...missingNames])
874
+ .toSOQL();
875
+ const componentResult = await this.soqlAdapter.query(componentSoql);
876
+ if (!componentResult.success) {
877
+ return foundLabels;
878
+ }
879
+ for (const record of componentResult.data.records) {
880
+ foundLabels.set(record.PermissionSet.Name, record.PermissionSet.Label);
881
+ }
882
+ this.logger?.log(`Found ${foundLabels.size} permission set(s) via group membership`);
883
+ }
884
+ catch (error) {
885
+ // Group check is supplementary — don't fail the entire permission check
886
+ this.logger?.log(`Group permission check failed: ${error instanceof Error ? error.message : String(error)}`);
887
+ }
888
+ return foundLabels;
889
+ }
890
+ }
891
+ //# sourceMappingURL=UserReadinessService.js.map