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