@actsecurity/iam-lens 0.1.156

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 (383) hide show
  1. package/LICENSE.txt +661 -0
  2. package/README.md +203 -0
  3. package/dist/cjs/cli.d.ts +3 -0
  4. package/dist/cjs/cli.d.ts.map +1 -0
  5. package/dist/cjs/cli.js +206 -0
  6. package/dist/cjs/cli.js.map +1 -0
  7. package/dist/cjs/collect/client.d.ts +550 -0
  8. package/dist/cjs/collect/client.d.ts.map +1 -0
  9. package/dist/cjs/collect/client.js +1119 -0
  10. package/dist/cjs/collect/client.js.map +1 -0
  11. package/dist/cjs/collect/collect.d.ts +44 -0
  12. package/dist/cjs/collect/collect.d.ts.map +1 -0
  13. package/dist/cjs/collect/collect.js +55 -0
  14. package/dist/cjs/collect/collect.js.map +1 -0
  15. package/dist/cjs/collect/inMemoryClient.d.ts +6 -0
  16. package/dist/cjs/collect/inMemoryClient.d.ts.map +1 -0
  17. package/dist/cjs/collect/inMemoryClient.js +11 -0
  18. package/dist/cjs/collect/inMemoryClient.js.map +1 -0
  19. package/dist/cjs/index.d.ts +13 -0
  20. package/dist/cjs/index.d.ts.map +1 -0
  21. package/dist/cjs/index.js +28 -0
  22. package/dist/cjs/index.js.map +1 -0
  23. package/dist/cjs/package.json +3 -0
  24. package/dist/cjs/principalCan/permission.d.ts +125 -0
  25. package/dist/cjs/principalCan/permission.d.ts.map +1 -0
  26. package/dist/cjs/principalCan/permission.js +1229 -0
  27. package/dist/cjs/principalCan/permission.js.map +1 -0
  28. package/dist/cjs/principalCan/permissionSet.d.ts +140 -0
  29. package/dist/cjs/principalCan/permissionSet.d.ts.map +1 -0
  30. package/dist/cjs/principalCan/permissionSet.js +437 -0
  31. package/dist/cjs/principalCan/permissionSet.js.map +1 -0
  32. package/dist/cjs/principalCan/principalCan.d.ts +26 -0
  33. package/dist/cjs/principalCan/principalCan.d.ts.map +1 -0
  34. package/dist/cjs/principalCan/principalCan.js +195 -0
  35. package/dist/cjs/principalCan/principalCan.js.map +1 -0
  36. package/dist/cjs/principalCan/principalConstraints.d.ts +147 -0
  37. package/dist/cjs/principalCan/principalConstraints.d.ts.map +1 -0
  38. package/dist/cjs/principalCan/principalConstraints.js +472 -0
  39. package/dist/cjs/principalCan/principalConstraints.js.map +1 -0
  40. package/dist/cjs/principalCan/resources/actions.d.ts +9 -0
  41. package/dist/cjs/principalCan/resources/actions.d.ts.map +1 -0
  42. package/dist/cjs/principalCan/resources/actions.js +27 -0
  43. package/dist/cjs/principalCan/resources/actions.js.map +1 -0
  44. package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.d.ts +31 -0
  45. package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.d.ts.map +1 -0
  46. package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.js +114 -0
  47. package/dist/cjs/principalCan/resources/resourceTypes/iamRoles.js.map +1 -0
  48. package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.d.ts +30 -0
  49. package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.d.ts.map +1 -0
  50. package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.js +110 -0
  51. package/dist/cjs/principalCan/resources/resourceTypes/kmsKeys.js.map +1 -0
  52. package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.d.ts +18 -0
  53. package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.d.ts.map +1 -0
  54. package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.js +123 -0
  55. package/dist/cjs/principalCan/resources/resourceTypes/s3Buckets.js.map +1 -0
  56. package/dist/cjs/principalCan/resources/statements.d.ts +27 -0
  57. package/dist/cjs/principalCan/resources/statements.d.ts.map +1 -0
  58. package/dist/cjs/principalCan/resources/statements.js +119 -0
  59. package/dist/cjs/principalCan/resources/statements.js.map +1 -0
  60. package/dist/cjs/principalIndex/makePrincipalIndex.d.ts +8 -0
  61. package/dist/cjs/principalIndex/makePrincipalIndex.d.ts.map +1 -0
  62. package/dist/cjs/principalIndex/makePrincipalIndex.js +105 -0
  63. package/dist/cjs/principalIndex/makePrincipalIndex.js.map +1 -0
  64. package/dist/cjs/principals.d.ts +54 -0
  65. package/dist/cjs/principals.d.ts.map +1 -0
  66. package/dist/cjs/principals.js +143 -0
  67. package/dist/cjs/principals.js.map +1 -0
  68. package/dist/cjs/resources.d.ts +32 -0
  69. package/dist/cjs/resources.d.ts.map +1 -0
  70. package/dist/cjs/resources.js +71 -0
  71. package/dist/cjs/resources.js.map +1 -0
  72. package/dist/cjs/simulate/contextKeys.d.ts +68 -0
  73. package/dist/cjs/simulate/contextKeys.d.ts.map +1 -0
  74. package/dist/cjs/simulate/contextKeys.js +293 -0
  75. package/dist/cjs/simulate/contextKeys.js.map +1 -0
  76. package/dist/cjs/simulate/simulate.d.ts +72 -0
  77. package/dist/cjs/simulate/simulate.d.ts.map +1 -0
  78. package/dist/cjs/simulate/simulate.js +296 -0
  79. package/dist/cjs/simulate/simulate.js.map +1 -0
  80. package/dist/cjs/test-datasets/testClient.d.ts +17 -0
  81. package/dist/cjs/test-datasets/testClient.d.ts.map +1 -0
  82. package/dist/cjs/test-datasets/testClient.js +58 -0
  83. package/dist/cjs/test-datasets/testClient.js.map +1 -0
  84. package/dist/cjs/utils/arn.d.ts +22 -0
  85. package/dist/cjs/utils/arn.d.ts.map +1 -0
  86. package/dist/cjs/utils/arn.js +49 -0
  87. package/dist/cjs/utils/arn.js.map +1 -0
  88. package/dist/cjs/utils/bitset.d.ts +45 -0
  89. package/dist/cjs/utils/bitset.d.ts.map +1 -0
  90. package/dist/cjs/utils/bitset.js +149 -0
  91. package/dist/cjs/utils/bitset.js.map +1 -0
  92. package/dist/cjs/utils/dynamicImport.d.ts +31 -0
  93. package/dist/cjs/utils/dynamicImport.d.ts.map +1 -0
  94. package/dist/cjs/utils/dynamicImport.js +64 -0
  95. package/dist/cjs/utils/dynamicImport.js.map +1 -0
  96. package/dist/cjs/utils/packageVersion.d.ts +5 -0
  97. package/dist/cjs/utils/packageVersion.d.ts.map +1 -0
  98. package/dist/cjs/utils/packageVersion.js +25 -0
  99. package/dist/cjs/utils/packageVersion.js.map +1 -0
  100. package/dist/cjs/utils/readPackageFile.d.ts +3 -0
  101. package/dist/cjs/utils/readPackageFile.d.ts.map +1 -0
  102. package/dist/cjs/utils/readPackageFile.js +16 -0
  103. package/dist/cjs/utils/readPackageFile.js.map +1 -0
  104. package/dist/cjs/utils/s3Abac.d.ts +3 -0
  105. package/dist/cjs/utils/s3Abac.d.ts.map +1 -0
  106. package/dist/cjs/utils/s3Abac.js +5 -0
  107. package/dist/cjs/utils/s3Abac.js.map +1 -0
  108. package/dist/cjs/utils/stringOrFileArgument.d.ts +9 -0
  109. package/dist/cjs/utils/stringOrFileArgument.d.ts.map +1 -0
  110. package/dist/cjs/utils/stringOrFileArgument.js +25 -0
  111. package/dist/cjs/utils/stringOrFileArgument.js.map +1 -0
  112. package/dist/cjs/utils/sts.d.ts +2 -0
  113. package/dist/cjs/utils/sts.d.ts.map +1 -0
  114. package/dist/cjs/utils/sts.js +9 -0
  115. package/dist/cjs/utils/sts.js.map +1 -0
  116. package/dist/cjs/utils/testUtils.d.ts +56 -0
  117. package/dist/cjs/utils/testUtils.d.ts.map +1 -0
  118. package/dist/cjs/utils/testUtils.js +95 -0
  119. package/dist/cjs/utils/testUtils.js.map +1 -0
  120. package/dist/cjs/utils/workerScript.d.ts +8 -0
  121. package/dist/cjs/utils/workerScript.d.ts.map +1 -0
  122. package/dist/cjs/utils/workerScript.js +23 -0
  123. package/dist/cjs/utils/workerScript.js.map +1 -0
  124. package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts +73 -0
  125. package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -0
  126. package/dist/cjs/whoCan/WhoCanMainThreadWorker.js +80 -0
  127. package/dist/cjs/whoCan/WhoCanMainThreadWorker.js.map +1 -0
  128. package/dist/cjs/whoCan/WhoCanProcessor.d.ts +400 -0
  129. package/dist/cjs/whoCan/WhoCanProcessor.d.ts.map +1 -0
  130. package/dist/cjs/whoCan/WhoCanProcessor.js +1088 -0
  131. package/dist/cjs/whoCan/WhoCanProcessor.js.map +1 -0
  132. package/dist/cjs/whoCan/WhoCanWorker.d.ts +87 -0
  133. package/dist/cjs/whoCan/WhoCanWorker.d.ts.map +1 -0
  134. package/dist/cjs/whoCan/WhoCanWorker.js +201 -0
  135. package/dist/cjs/whoCan/WhoCanWorker.js.map +1 -0
  136. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.d.ts +2 -0
  137. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.d.ts.map +1 -0
  138. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js +189 -0
  139. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js.map +1 -0
  140. package/dist/cjs/whoCan/principalArnFilter.d.ts +84 -0
  141. package/dist/cjs/whoCan/principalArnFilter.d.ts.map +1 -0
  142. package/dist/cjs/whoCan/principalArnFilter.js +258 -0
  143. package/dist/cjs/whoCan/principalArnFilter.js.map +1 -0
  144. package/dist/cjs/whoCan/principalScope.d.ts +35 -0
  145. package/dist/cjs/whoCan/principalScope.d.ts.map +1 -0
  146. package/dist/cjs/whoCan/principalScope.js +104 -0
  147. package/dist/cjs/whoCan/principalScope.js.map +1 -0
  148. package/dist/cjs/whoCan/requestAnalysis.d.ts +73 -0
  149. package/dist/cjs/whoCan/requestAnalysis.d.ts.map +1 -0
  150. package/dist/cjs/whoCan/requestAnalysis.js +87 -0
  151. package/dist/cjs/whoCan/requestAnalysis.js.map +1 -0
  152. package/dist/cjs/whoCan/untrustingActions.d.ts +7 -0
  153. package/dist/cjs/whoCan/untrustingActions.d.ts.map +1 -0
  154. package/dist/cjs/whoCan/untrustingActions.js +30 -0
  155. package/dist/cjs/whoCan/untrustingActions.js.map +1 -0
  156. package/dist/cjs/whoCan/whoCan.d.ts +295 -0
  157. package/dist/cjs/whoCan/whoCan.d.ts.map +1 -0
  158. package/dist/cjs/whoCan/whoCan.js +574 -0
  159. package/dist/cjs/whoCan/whoCan.js.map +1 -0
  160. package/dist/cjs/whoCan/workerBootstrapPlugin.d.ts +17 -0
  161. package/dist/cjs/whoCan/workerBootstrapPlugin.d.ts.map +1 -0
  162. package/dist/cjs/whoCan/workerBootstrapPlugin.js +3 -0
  163. package/dist/cjs/whoCan/workerBootstrapPlugin.js.map +1 -0
  164. package/dist/cjs/workers/ArrayStreamingWorkQueue.d.ts +15 -0
  165. package/dist/cjs/workers/ArrayStreamingWorkQueue.d.ts.map +1 -0
  166. package/dist/cjs/workers/ArrayStreamingWorkQueue.js +35 -0
  167. package/dist/cjs/workers/ArrayStreamingWorkQueue.js.map +1 -0
  168. package/dist/cjs/workers/JobRunner.d.ts +47 -0
  169. package/dist/cjs/workers/JobRunner.d.ts.map +1 -0
  170. package/dist/cjs/workers/JobRunner.js +101 -0
  171. package/dist/cjs/workers/JobRunner.js.map +1 -0
  172. package/dist/cjs/workers/RingQueue.d.ts +14 -0
  173. package/dist/cjs/workers/RingQueue.d.ts.map +1 -0
  174. package/dist/cjs/workers/RingQueue.js +43 -0
  175. package/dist/cjs/workers/RingQueue.js.map +1 -0
  176. package/dist/cjs/workers/SharedArrayBufferMainCache.d.ts +10 -0
  177. package/dist/cjs/workers/SharedArrayBufferMainCache.d.ts.map +1 -0
  178. package/dist/cjs/workers/SharedArrayBufferMainCache.js +37 -0
  179. package/dist/cjs/workers/SharedArrayBufferMainCache.js.map +1 -0
  180. package/dist/cjs/workers/SharedArrayBufferWorkerCache.d.ts +10 -0
  181. package/dist/cjs/workers/SharedArrayBufferWorkerCache.d.ts.map +1 -0
  182. package/dist/cjs/workers/SharedArrayBufferWorkerCache.js +49 -0
  183. package/dist/cjs/workers/SharedArrayBufferWorkerCache.js.map +1 -0
  184. package/dist/cjs/workers/StreamingWorkQueue.d.ts +18 -0
  185. package/dist/cjs/workers/StreamingWorkQueue.d.ts.map +1 -0
  186. package/dist/cjs/workers/StreamingWorkQueue.js +67 -0
  187. package/dist/cjs/workers/StreamingWorkQueue.js.map +1 -0
  188. package/dist/cjs/workers/buffers.d.ts +4 -0
  189. package/dist/cjs/workers/buffers.d.ts.map +1 -0
  190. package/dist/cjs/workers/buffers.js +26 -0
  191. package/dist/cjs/workers/buffers.js.map +1 -0
  192. package/dist/esm/cli.d.ts +3 -0
  193. package/dist/esm/cli.d.ts.map +1 -0
  194. package/dist/esm/cli.js +204 -0
  195. package/dist/esm/cli.js.map +1 -0
  196. package/dist/esm/collect/client.d.ts +550 -0
  197. package/dist/esm/collect/client.d.ts.map +1 -0
  198. package/dist/esm/collect/client.js +1114 -0
  199. package/dist/esm/collect/client.js.map +1 -0
  200. package/dist/esm/collect/collect.d.ts +44 -0
  201. package/dist/esm/collect/collect.d.ts.map +1 -0
  202. package/dist/esm/collect/collect.js +51 -0
  203. package/dist/esm/collect/collect.js.map +1 -0
  204. package/dist/esm/collect/inMemoryClient.d.ts +6 -0
  205. package/dist/esm/collect/inMemoryClient.d.ts.map +1 -0
  206. package/dist/esm/collect/inMemoryClient.js +8 -0
  207. package/dist/esm/collect/inMemoryClient.js.map +1 -0
  208. package/dist/esm/index.d.ts +13 -0
  209. package/dist/esm/index.d.ts.map +1 -0
  210. package/dist/esm/index.js +11 -0
  211. package/dist/esm/index.js.map +1 -0
  212. package/dist/esm/package.json +3 -0
  213. package/dist/esm/principalCan/permission.d.ts +125 -0
  214. package/dist/esm/principalCan/permission.d.ts.map +1 -0
  215. package/dist/esm/principalCan/permission.js +1212 -0
  216. package/dist/esm/principalCan/permission.js.map +1 -0
  217. package/dist/esm/principalCan/permissionSet.d.ts +140 -0
  218. package/dist/esm/principalCan/permissionSet.d.ts.map +1 -0
  219. package/dist/esm/principalCan/permissionSet.js +429 -0
  220. package/dist/esm/principalCan/permissionSet.js.map +1 -0
  221. package/dist/esm/principalCan/principalCan.d.ts +26 -0
  222. package/dist/esm/principalCan/principalCan.d.ts.map +1 -0
  223. package/dist/esm/principalCan/principalCan.js +193 -0
  224. package/dist/esm/principalCan/principalCan.js.map +1 -0
  225. package/dist/esm/principalCan/principalConstraints.d.ts +147 -0
  226. package/dist/esm/principalCan/principalConstraints.d.ts.map +1 -0
  227. package/dist/esm/principalCan/principalConstraints.js +456 -0
  228. package/dist/esm/principalCan/principalConstraints.js.map +1 -0
  229. package/dist/esm/principalCan/resources/actions.d.ts +9 -0
  230. package/dist/esm/principalCan/resources/actions.d.ts.map +1 -0
  231. package/dist/esm/principalCan/resources/actions.js +24 -0
  232. package/dist/esm/principalCan/resources/actions.js.map +1 -0
  233. package/dist/esm/principalCan/resources/resourceTypes/iamRoles.d.ts +31 -0
  234. package/dist/esm/principalCan/resources/resourceTypes/iamRoles.d.ts.map +1 -0
  235. package/dist/esm/principalCan/resources/resourceTypes/iamRoles.js +110 -0
  236. package/dist/esm/principalCan/resources/resourceTypes/iamRoles.js.map +1 -0
  237. package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.d.ts +30 -0
  238. package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.d.ts.map +1 -0
  239. package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.js +106 -0
  240. package/dist/esm/principalCan/resources/resourceTypes/kmsKeys.js.map +1 -0
  241. package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.d.ts +18 -0
  242. package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.d.ts.map +1 -0
  243. package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.js +120 -0
  244. package/dist/esm/principalCan/resources/resourceTypes/s3Buckets.js.map +1 -0
  245. package/dist/esm/principalCan/resources/statements.d.ts +27 -0
  246. package/dist/esm/principalCan/resources/statements.d.ts.map +1 -0
  247. package/dist/esm/principalCan/resources/statements.js +117 -0
  248. package/dist/esm/principalCan/resources/statements.js.map +1 -0
  249. package/dist/esm/principalIndex/makePrincipalIndex.d.ts +8 -0
  250. package/dist/esm/principalIndex/makePrincipalIndex.d.ts.map +1 -0
  251. package/dist/esm/principalIndex/makePrincipalIndex.js +104 -0
  252. package/dist/esm/principalIndex/makePrincipalIndex.js.map +1 -0
  253. package/dist/esm/principals.d.ts +54 -0
  254. package/dist/esm/principals.d.ts.map +1 -0
  255. package/dist/esm/principals.js +133 -0
  256. package/dist/esm/principals.js.map +1 -0
  257. package/dist/esm/resources.d.ts +32 -0
  258. package/dist/esm/resources.d.ts.map +1 -0
  259. package/dist/esm/resources.js +69 -0
  260. package/dist/esm/resources.js.map +1 -0
  261. package/dist/esm/simulate/contextKeys.d.ts +68 -0
  262. package/dist/esm/simulate/contextKeys.d.ts.map +1 -0
  263. package/dist/esm/simulate/contextKeys.js +287 -0
  264. package/dist/esm/simulate/contextKeys.js.map +1 -0
  265. package/dist/esm/simulate/simulate.d.ts +72 -0
  266. package/dist/esm/simulate/simulate.d.ts.map +1 -0
  267. package/dist/esm/simulate/simulate.js +295 -0
  268. package/dist/esm/simulate/simulate.js.map +1 -0
  269. package/dist/esm/test-datasets/testClient.d.ts +17 -0
  270. package/dist/esm/test-datasets/testClient.d.ts.map +1 -0
  271. package/dist/esm/test-datasets/testClient.js +56 -0
  272. package/dist/esm/test-datasets/testClient.js.map +1 -0
  273. package/dist/esm/utils/arn.d.ts +22 -0
  274. package/dist/esm/utils/arn.d.ts.map +1 -0
  275. package/dist/esm/utils/arn.js +43 -0
  276. package/dist/esm/utils/arn.js.map +1 -0
  277. package/dist/esm/utils/bitset.d.ts +45 -0
  278. package/dist/esm/utils/bitset.d.ts.map +1 -0
  279. package/dist/esm/utils/bitset.js +141 -0
  280. package/dist/esm/utils/bitset.js.map +1 -0
  281. package/dist/esm/utils/dynamicImport.d.ts +31 -0
  282. package/dist/esm/utils/dynamicImport.d.ts.map +1 -0
  283. package/dist/esm/utils/dynamicImport.js +60 -0
  284. package/dist/esm/utils/dynamicImport.js.map +1 -0
  285. package/dist/esm/utils/packageVersion.d.ts +5 -0
  286. package/dist/esm/utils/packageVersion.d.ts.map +1 -0
  287. package/dist/esm/utils/packageVersion.js +22 -0
  288. package/dist/esm/utils/packageVersion.js.map +1 -0
  289. package/dist/esm/utils/readPackageFile.d.ts +3 -0
  290. package/dist/esm/utils/readPackageFile.d.ts.map +1 -0
  291. package/dist/esm/utils/readPackageFile.js +15 -0
  292. package/dist/esm/utils/readPackageFile.js.map +1 -0
  293. package/dist/esm/utils/readPackageFileEsm.d.ts.map +1 -0
  294. package/dist/esm/utils/s3Abac.d.ts +3 -0
  295. package/dist/esm/utils/s3Abac.d.ts.map +1 -0
  296. package/dist/esm/utils/s3Abac.js +2 -0
  297. package/dist/esm/utils/s3Abac.js.map +1 -0
  298. package/dist/esm/utils/stringOrFileArgument.d.ts +9 -0
  299. package/dist/esm/utils/stringOrFileArgument.d.ts.map +1 -0
  300. package/dist/esm/utils/stringOrFileArgument.js +22 -0
  301. package/dist/esm/utils/stringOrFileArgument.js.map +1 -0
  302. package/dist/esm/utils/sts.d.ts +2 -0
  303. package/dist/esm/utils/sts.d.ts.map +1 -0
  304. package/dist/esm/utils/sts.js +6 -0
  305. package/dist/esm/utils/sts.js.map +1 -0
  306. package/dist/esm/utils/testUtils.d.ts +56 -0
  307. package/dist/esm/utils/testUtils.d.ts.map +1 -0
  308. package/dist/esm/utils/testUtils.js +90 -0
  309. package/dist/esm/utils/testUtils.js.map +1 -0
  310. package/dist/esm/utils/workerScript.d.ts +8 -0
  311. package/dist/esm/utils/workerScript.d.ts.map +1 -0
  312. package/dist/esm/utils/workerScript.js +18 -0
  313. package/dist/esm/utils/workerScript.js.map +1 -0
  314. package/dist/esm/utils/workerScriptEsm.d.ts.map +1 -0
  315. package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts +73 -0
  316. package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -0
  317. package/dist/esm/whoCan/WhoCanMainThreadWorker.js +81 -0
  318. package/dist/esm/whoCan/WhoCanMainThreadWorker.js.map +1 -0
  319. package/dist/esm/whoCan/WhoCanProcessor.d.ts +400 -0
  320. package/dist/esm/whoCan/WhoCanProcessor.d.ts.map +1 -0
  321. package/dist/esm/whoCan/WhoCanProcessor.js +1080 -0
  322. package/dist/esm/whoCan/WhoCanProcessor.js.map +1 -0
  323. package/dist/esm/whoCan/WhoCanWorker.d.ts +87 -0
  324. package/dist/esm/whoCan/WhoCanWorker.d.ts.map +1 -0
  325. package/dist/esm/whoCan/WhoCanWorker.js +198 -0
  326. package/dist/esm/whoCan/WhoCanWorker.js.map +1 -0
  327. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.d.ts +2 -0
  328. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.d.ts.map +1 -0
  329. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js +193 -0
  330. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js.map +1 -0
  331. package/dist/esm/whoCan/principalArnFilter.d.ts +84 -0
  332. package/dist/esm/whoCan/principalArnFilter.d.ts.map +1 -0
  333. package/dist/esm/whoCan/principalArnFilter.js +253 -0
  334. package/dist/esm/whoCan/principalArnFilter.js.map +1 -0
  335. package/dist/esm/whoCan/principalScope.d.ts +35 -0
  336. package/dist/esm/whoCan/principalScope.d.ts.map +1 -0
  337. package/dist/esm/whoCan/principalScope.js +102 -0
  338. package/dist/esm/whoCan/principalScope.js.map +1 -0
  339. package/dist/esm/whoCan/requestAnalysis.d.ts +73 -0
  340. package/dist/esm/whoCan/requestAnalysis.d.ts.map +1 -0
  341. package/dist/esm/whoCan/requestAnalysis.js +83 -0
  342. package/dist/esm/whoCan/requestAnalysis.js.map +1 -0
  343. package/dist/esm/whoCan/untrustingActions.d.ts +7 -0
  344. package/dist/esm/whoCan/untrustingActions.d.ts.map +1 -0
  345. package/dist/esm/whoCan/untrustingActions.js +27 -0
  346. package/dist/esm/whoCan/untrustingActions.js.map +1 -0
  347. package/dist/esm/whoCan/whoCan.d.ts +295 -0
  348. package/dist/esm/whoCan/whoCan.d.ts.map +1 -0
  349. package/dist/esm/whoCan/whoCan.js +571 -0
  350. package/dist/esm/whoCan/whoCan.js.map +1 -0
  351. package/dist/esm/whoCan/workerBootstrapPlugin.d.ts +17 -0
  352. package/dist/esm/whoCan/workerBootstrapPlugin.d.ts.map +1 -0
  353. package/dist/esm/whoCan/workerBootstrapPlugin.js +2 -0
  354. package/dist/esm/whoCan/workerBootstrapPlugin.js.map +1 -0
  355. package/dist/esm/workers/ArrayStreamingWorkQueue.d.ts +15 -0
  356. package/dist/esm/workers/ArrayStreamingWorkQueue.d.ts.map +1 -0
  357. package/dist/esm/workers/ArrayStreamingWorkQueue.js +31 -0
  358. package/dist/esm/workers/ArrayStreamingWorkQueue.js.map +1 -0
  359. package/dist/esm/workers/JobRunner.d.ts +47 -0
  360. package/dist/esm/workers/JobRunner.d.ts.map +1 -0
  361. package/dist/esm/workers/JobRunner.js +94 -0
  362. package/dist/esm/workers/JobRunner.js.map +1 -0
  363. package/dist/esm/workers/RingQueue.d.ts +14 -0
  364. package/dist/esm/workers/RingQueue.d.ts.map +1 -0
  365. package/dist/esm/workers/RingQueue.js +36 -0
  366. package/dist/esm/workers/RingQueue.js.map +1 -0
  367. package/dist/esm/workers/SharedArrayBufferMainCache.d.ts +10 -0
  368. package/dist/esm/workers/SharedArrayBufferMainCache.d.ts.map +1 -0
  369. package/dist/esm/workers/SharedArrayBufferMainCache.js +34 -0
  370. package/dist/esm/workers/SharedArrayBufferMainCache.js.map +1 -0
  371. package/dist/esm/workers/SharedArrayBufferWorkerCache.d.ts +10 -0
  372. package/dist/esm/workers/SharedArrayBufferWorkerCache.d.ts.map +1 -0
  373. package/dist/esm/workers/SharedArrayBufferWorkerCache.js +46 -0
  374. package/dist/esm/workers/SharedArrayBufferWorkerCache.js.map +1 -0
  375. package/dist/esm/workers/StreamingWorkQueue.d.ts +18 -0
  376. package/dist/esm/workers/StreamingWorkQueue.d.ts.map +1 -0
  377. package/dist/esm/workers/StreamingWorkQueue.js +61 -0
  378. package/dist/esm/workers/StreamingWorkQueue.js.map +1 -0
  379. package/dist/esm/workers/buffers.d.ts +4 -0
  380. package/dist/esm/workers/buffers.d.ts.map +1 -0
  381. package/dist/esm/workers/buffers.js +21 -0
  382. package/dist/esm/workers/buffers.js.map +1 -0
  383. package/package.json +132 -0
@@ -0,0 +1,1088 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WhoCanProcessor = void 0;
4
+ const job_1 = require("@actsecurity/job");
5
+ const worker_threads_1 = require("worker_threads");
6
+ const collect_js_1 = require("../collect/collect.js");
7
+ const resources_js_1 = require("../resources.js");
8
+ const arn_js_1 = require("../utils/arn.js");
9
+ const workerScript_js_1 = require("../utils/workerScript.js");
10
+ const SharedArrayBufferMainCache_js_1 = require("../workers/SharedArrayBufferMainCache.js");
11
+ const StreamingWorkQueue_js_1 = require("../workers/StreamingWorkQueue.js");
12
+ const WhoCanMainThreadWorker_js_1 = require("./WhoCanMainThreadWorker.js");
13
+ const principalScope_js_1 = require("./principalScope.js");
14
+ const whoCan_js_1 = require("./whoCan.js");
15
+ const iam_utils_1 = require("@actsecurity/iam-utils");
16
+ const principalArnFilter_js_1 = require("./principalArnFilter.js");
17
+ const untrustingActions_js_1 = require("./untrustingActions.js");
18
+ const log_1 = require("@actsecurity/log");
19
+ // ──────────────────────────────────────────────────────────────────────────────
20
+ // Helpers
21
+ // ──────────────────────────────────────────────────────────────────────────────
22
+ let nextRequestId = 0;
23
+ /**
24
+ * Generates a unique request ID for a new request.
25
+ *
26
+ * @returns a unique string ID
27
+ */
28
+ function generateRequestId() {
29
+ return `req-${++nextRequestId}`;
30
+ }
31
+ /**
32
+ * Get the number of worker threads to use, defaulting to number of CPUs - 1.
33
+ *
34
+ * @param overrideValue the override value, if any
35
+ * @returns the override value if provided, otherwise number of CPUs - 1
36
+ */
37
+ function getNumberOfWorkers(overrideValue) {
38
+ if (typeof overrideValue === 'number' && overrideValue >= 0) {
39
+ return Math.floor(overrideValue);
40
+ }
41
+ return Math.max(0, (0, job_1.numberOfCpus)() - 1);
42
+ }
43
+ /**
44
+ * A queue-first bulk processor that accepts many whoCan requests, expands
45
+ * scenarios on the main thread, and feeds a shared simulation scheduler used
46
+ * by worker threads and an optional main-thread runner.
47
+ *
48
+ * Results are delivered through the {@link WhoCanProcessorConfig.onRequestSettled}
49
+ * callback as each request completes — they are not stored inside the processor.
50
+ *
51
+ * Use {@link enqueueWhoCan} to submit requests, then {@link waitForIdle} to
52
+ * wait for all work to complete. Call {@link shutdown} when done to terminate
53
+ * worker threads.
54
+ */
55
+ class WhoCanProcessor {
56
+ workers;
57
+ collectClient;
58
+ config;
59
+ isShutdown = false;
60
+ workersDead = false;
61
+ // Admission state
62
+ pendingRequests = [];
63
+ activeRequestOrder = [];
64
+ requestStates = new Map();
65
+ admissionPumpRunning = false;
66
+ draining = false;
67
+ // Preparation queue
68
+ preparationQueue;
69
+ // Idle / drain tracking
70
+ idleWaiters = [];
71
+ // Main thread simulation runner
72
+ mainThreadWorker;
73
+ // Processor-fatal error
74
+ fatalError;
75
+ // Tracks a single in-progress shutdown so repeated calls are safe
76
+ shutdownPromise;
77
+ constructor(workers, collectClient, config, preparationQueue) {
78
+ this.workers = workers;
79
+ this.collectClient = collectClient;
80
+ this.config = config;
81
+ this.preparationQueue = preparationQueue;
82
+ }
83
+ /**
84
+ * Waits for every worker to post a `{ type: 'ready' }` message. If any
85
+ * worker fails (error, non-zero exit, or explicit `startupError` message)
86
+ * the remaining workers are terminated and the returned promise rejects.
87
+ *
88
+ * @param workers - The worker instances to wait on.
89
+ */
90
+ static async awaitWorkersReady(workers) {
91
+ // Each entry resolves on 'ready' or rejects on failure.
92
+ const perWorker = workers.map((worker) => new Promise((resolve, reject) => {
93
+ const onMessage = (msg) => {
94
+ if (msg.type === 'ready') {
95
+ cleanup();
96
+ resolve();
97
+ }
98
+ else if (msg.type === 'startupError') {
99
+ cleanup();
100
+ reject(new Error(`Worker startup failed: ${msg.error}`));
101
+ }
102
+ };
103
+ const onError = (err) => {
104
+ cleanup();
105
+ reject(new Error(`Worker error during startup: ${err.message}`));
106
+ };
107
+ const onExit = (code) => {
108
+ cleanup();
109
+ if (code !== 0) {
110
+ reject(new Error(`Worker exited with code ${code} during startup`));
111
+ }
112
+ };
113
+ const cleanup = () => {
114
+ worker.off('message', onMessage);
115
+ worker.off('error', onError);
116
+ worker.off('exit', onExit);
117
+ };
118
+ worker.on('message', onMessage);
119
+ worker.on('error', onError);
120
+ worker.on('exit', onExit);
121
+ }));
122
+ try {
123
+ await Promise.all(perWorker);
124
+ }
125
+ catch (err) {
126
+ // At least one worker failed — terminate all workers so none are
127
+ // left orphaned (no processor instance will be returned to the caller).
128
+ await Promise.allSettled(workers.map((w) => w.terminate()));
129
+ throw err;
130
+ }
131
+ }
132
+ /**
133
+ * Creates a new WhoCanProcessor with worker threads, a shared cache, and
134
+ * lifetime-scoped message routing. The processor is ready to accept requests
135
+ * immediately after creation.
136
+ *
137
+ * @param config - The configuration for the processor, including collect configs,
138
+ * partition, simulation options, tuning, and the onRequestSettled callback.
139
+ * @returns a new WhoCanProcessor instance
140
+ */
141
+ static async create(config) {
142
+ const numWorkers = getNumberOfWorkers(config.tuning?.workerThreads);
143
+ const perWorkerConcurrency = config.tuning?.perWorkerConcurrency ?? 50;
144
+ const mainThreadConcurrency = config.tuning?.mainThreadConcurrency ?? 50;
145
+ const workerPath = (0, workerScript_js_1.getWorkerScriptPath)('whoCan/WhoCanWorkerThreadWorker.js');
146
+ if ((!workerPath || numWorkers === 0) && mainThreadConcurrency <= 0) {
147
+ throw new Error('WhoCanProcessor: no worker script found and mainThreadConcurrency is 0 — no work can be processed');
148
+ }
149
+ const workers = !workerPath
150
+ ? []
151
+ : new Array(numWorkers).fill(undefined).map(() => {
152
+ return new worker_threads_1.Worker(workerPath, {
153
+ workerData: {
154
+ collectConfigs: config.collectConfigs,
155
+ partition: config.partition,
156
+ concurrency: perWorkerConcurrency,
157
+ s3AbacOverride: config.s3AbacOverride,
158
+ collectGrantDetails: config.collectGrantDetails,
159
+ clientFactoryPlugin: config.clientFactoryPlugin,
160
+ workerBootstrapPlugin: config.workerBootstrapPlugin
161
+ }
162
+ });
163
+ });
164
+ // Install the shared-cache bridge before waiting for workers to become
165
+ // ready. Workers may issue getCache messages during their getCollectClient()
166
+ // call, which runs concurrently with the ready handshake. If the cache
167
+ // listeners are not installed yet those messages are dropped and the worker
168
+ // deadlocks waiting for a cache response.
169
+ const sharedCache = new SharedArrayBufferMainCache_js_1.SharedArrayBufferMainCache(workers);
170
+ // Wait for all workers to complete bootstrap and signal ready before
171
+ // proceeding. This ensures bootstrap failures are caught reliably and
172
+ // all workers are fully initialized before work is dispatched.
173
+ if (workers.length > 0) {
174
+ await WhoCanProcessor.awaitWorkersReady(workers);
175
+ }
176
+ const collectClient = await (0, collect_js_1.getCollectClient)(config.collectConfigs, config.partition, {
177
+ cacheProvider: sharedCache,
178
+ clientFactoryPlugin: config.clientFactoryPlugin
179
+ });
180
+ const preparationConcurrency = config.tuning?.preparationConcurrency ?? Math.min(50, Math.max(1, (0, job_1.numberOfCpus)() * 2));
181
+ const preparationQueue = new job_1.StreamingJobQueue(preparationConcurrency, console, async () => { });
182
+ const processor = new WhoCanProcessor(workers, collectClient, config, preparationQueue);
183
+ processor.installLifetimeWorkerListeners();
184
+ // Signal workers to start pulling tasks. This must happen after
185
+ // installLifetimeWorkerListeners() so the main thread's requestTask
186
+ // handler is installed before the workers begin emitting requestTask
187
+ // messages.
188
+ for (const worker of workers) {
189
+ worker.postMessage({ type: 'start' });
190
+ }
191
+ processor.createMainThreadRunner();
192
+ return processor;
193
+ }
194
+ /**
195
+ * Enqueues a whoCan request for processing. Returns a unique request ID
196
+ * that will appear in the corresponding {@link WhoCanSettledEvent}.
197
+ *
198
+ * This method never activates a request directly — it appends to
199
+ * pendingRequests and signals the admission pump.
200
+ *
201
+ * @param request - The whoCan request parameters.
202
+ * @returns the unique request ID assigned to this request.
203
+ * @throws if the processor is shut down or draining via waitForIdle.
204
+ */
205
+ enqueueWhoCan(request) {
206
+ if (this.isShutdown) {
207
+ throw new Error('WhoCanProcessor has been shut down');
208
+ }
209
+ if (this.draining) {
210
+ throw new Error('Cannot enqueue while draining — waitForIdle() is in progress');
211
+ }
212
+ const requestId = generateRequestId();
213
+ this.pendingRequests.push({ requestId, request });
214
+ this.wakeAdmissionPump();
215
+ return requestId;
216
+ }
217
+ /**
218
+ * Returns a promise that resolves when all pending and active work has
219
+ * completed and all onRequestSettled callbacks have finished.
220
+ *
221
+ * While draining, new calls to {@link enqueueWhoCan} will throw. Once
222
+ * the drain completes, the processor re-opens for new enqueues.
223
+ *
224
+ * @returns a promise that resolves when idle, or rejects if a worker crashes.
225
+ */
226
+ async waitForIdle() {
227
+ if (this.isShutdown) {
228
+ throw new Error('WhoCanProcessor has been shut down');
229
+ }
230
+ // If already idle, return immediately
231
+ if (this.isIdle()) {
232
+ return;
233
+ }
234
+ this.draining = true;
235
+ try {
236
+ await new Promise((resolve, reject) => {
237
+ this.idleWaiters.push({ resolve, reject });
238
+ });
239
+ }
240
+ finally {
241
+ // Only clear draining when the last waiter has been notified
242
+ if (this.idleWaiters.length === 0) {
243
+ this.draining = false;
244
+ }
245
+ }
246
+ }
247
+ /**
248
+ * Shuts down the processor by rejecting all pending requests, waiting for
249
+ * active requests to settle, and terminating all worker threads.
250
+ *
251
+ * This method is idempotent — calling it multiple times is safe.
252
+ */
253
+ async shutdown() {
254
+ // If already shutting down or shut down, return the existing promise
255
+ if (this.shutdownPromise) {
256
+ return this.shutdownPromise;
257
+ }
258
+ this.shutdownPromise = this.executeShutdown();
259
+ return this.shutdownPromise;
260
+ }
261
+ /**
262
+ * Internal shutdown implementation. Rejects pending requests, waits for
263
+ * active requests to drain, then terminates workers.
264
+ */
265
+ async executeShutdown() {
266
+ this.isShutdown = true;
267
+ // Reject all pending requests that haven't been admitted
268
+ while (this.pendingRequests.length > 0) {
269
+ const submitted = this.pendingRequests.shift();
270
+ const event = {
271
+ status: 'rejected',
272
+ requestId: submitted.requestId,
273
+ request: submitted.request,
274
+ error: new Error('WhoCanProcessor was shut down before this request was processed')
275
+ };
276
+ try {
277
+ await this.config.onRequestSettled(event);
278
+ }
279
+ catch (err) {
280
+ await this.handleSettlementFailure(event, err);
281
+ }
282
+ }
283
+ // Wait for active requests to finish naturally (includes draining in-flight work)
284
+ if (this.activeRequestOrder.length > 0) {
285
+ await new Promise((resolve) => {
286
+ if (this.activeRequestOrder.length === 0) {
287
+ resolve();
288
+ }
289
+ else {
290
+ this.idleWaiters.push({ resolve, reject: () => resolve() });
291
+ }
292
+ });
293
+ }
294
+ if (this.workersDead) {
295
+ return;
296
+ }
297
+ // Drain main thread worker
298
+ if (this.mainThreadWorker) {
299
+ await this.mainThreadWorker.finishAllWork();
300
+ this.mainThreadWorker = undefined;
301
+ }
302
+ // Gracefully shut down workers
303
+ const workerPromises = this.workers.map((worker) => {
304
+ return new Promise((resolve) => {
305
+ worker.on('message', (msg) => {
306
+ if (msg.type === 'finished') {
307
+ worker.terminate().then(() => resolve());
308
+ }
309
+ });
310
+ worker.on('error', () => {
311
+ worker
312
+ .terminate()
313
+ .then(() => resolve())
314
+ .catch(() => resolve());
315
+ });
316
+ worker.postMessage({ type: 'finishWork' });
317
+ });
318
+ });
319
+ await Promise.all(workerPromises);
320
+ this.workersDead = true;
321
+ }
322
+ // ──────────────────────────────────────────────────────────────────────────
323
+ // Lifetime worker listeners
324
+ // ──────────────────────────────────────────────────────────────────────────
325
+ /**
326
+ * Installs lifetime-scoped message, error, and exit listeners on all workers.
327
+ * Message listeners route simulation results and deny-detail checks to the
328
+ * correct request state using requestId. Error/exit listeners detect crashes
329
+ * and mark the processor as fatally failed.
330
+ */
331
+ installLifetimeWorkerListeners() {
332
+ for (const worker of this.workers) {
333
+ worker.on('message', (msg) => {
334
+ this.handleWorkerMessage(msg, worker);
335
+ });
336
+ worker.on('error', (err) => {
337
+ if (!this.isShutdown) {
338
+ this.handleWorkerFailure(new Error(`Worker error: ${err.message}`));
339
+ }
340
+ });
341
+ worker.on('exit', (code) => {
342
+ if (!this.isShutdown && code !== 0) {
343
+ this.handleWorkerFailure(new Error(`Worker exited unexpectedly with code ${code}`));
344
+ }
345
+ });
346
+ }
347
+ }
348
+ /**
349
+ * Routes a message from a worker thread to the appropriate handler based
350
+ * on message type and requestId.
351
+ *
352
+ * @param msg - The message received from the worker.
353
+ * @param worker - The worker that sent the message.
354
+ */
355
+ handleWorkerMessage(msg, worker) {
356
+ if (msg.type === 'requestTask') {
357
+ const task = this.dequeueNextScenario();
358
+ worker.postMessage({ type: 'task', workerId: msg.workerId, task });
359
+ }
360
+ else if (msg.type === 'result') {
361
+ this.handleSimulationResult(msg.requestId, msg.result, !!msg.denyDetailsCheckWillFollow);
362
+ }
363
+ else if (msg.type === 'checkDenyDetails') {
364
+ this.handleCheckDenyDetails(msg.requestId, msg.checkId, msg.lightAnalysis, worker);
365
+ }
366
+ else if (msg.type === 'denyDetailsResult') {
367
+ this.handleDenyDetailsResult(msg.requestId, msg.denyDetail);
368
+ }
369
+ }
370
+ /**
371
+ * Creates the main-thread simulation runner if mainThreadConcurrency > 0.
372
+ * The runner pulls from the FIFO scheduler and routes results by requestId.
373
+ */
374
+ createMainThreadRunner() {
375
+ const mainThreadConcurrency = this.config.tuning?.mainThreadConcurrency ?? 50;
376
+ if (mainThreadConcurrency <= 0) {
377
+ return;
378
+ }
379
+ const { collectGrantDetails, s3AbacOverride } = this.config;
380
+ this.mainThreadWorker = (0, WhoCanMainThreadWorker_js_1.createMainThreadStreamingWorkQueue)(() => this.dequeueNextScenario(), (requestId, result) => this.handleSimulationResult(requestId, result), (requestId, lightAnalysis) => {
381
+ const state = this.requestStates.get(requestId);
382
+ if (state && !state.settled) {
383
+ return state.denyDetailsCallback?.(lightAnalysis) ?? false;
384
+ }
385
+ return false;
386
+ }, (requestId, detail) => this.handleDenyDetailsResult(requestId, detail), this.collectClient, s3AbacOverride, collectGrantDetails ?? false, mainThreadConcurrency);
387
+ }
388
+ // ──────────────────────────────────────────────────────────────────────────
389
+ // FIFO queue-of-queues scheduler
390
+ // ──────────────────────────────────────────────────────────────────────────
391
+ /**
392
+ * Dequeues the next simulation scenario using FIFO request priority.
393
+ * Prefers the oldest active request that has ready scenarios. If the oldest
394
+ * is temporarily empty (still preparing), falls back to the next request
395
+ * with ready scenarios so cores do not idle.
396
+ *
397
+ * @returns the next work item, or undefined if no scenarios are ready.
398
+ */
399
+ dequeueNextScenario() {
400
+ for (const requestId of this.activeRequestOrder) {
401
+ const state = this.requestStates.get(requestId);
402
+ if (!state || state.settled)
403
+ continue;
404
+ const item = state.scenarios.dequeue();
405
+ if (item) {
406
+ return { ...item, requestId };
407
+ }
408
+ }
409
+ return undefined;
410
+ }
411
+ /**
412
+ * Notifies all simulation consumers (workers and main thread) that new
413
+ * work may be available in the scheduler.
414
+ */
415
+ notifySimulationConsumers() {
416
+ this.mainThreadWorker?.notifyWorkAvailable();
417
+ for (const worker of this.workers) {
418
+ worker.postMessage({ type: 'workAvailable' });
419
+ }
420
+ }
421
+ // ──────────────────────────────────────────────────────────────────────────
422
+ // Admission pump
423
+ // ──────────────────────────────────────────────────────────────────────────
424
+ /**
425
+ * Wakes the admission pump to process pending requests. If the pump is
426
+ * already running, this is a no-op — the running pump will pick up new
427
+ * pending requests on its next iteration.
428
+ */
429
+ wakeAdmissionPump() {
430
+ if (this.admissionPumpRunning)
431
+ return;
432
+ this.admissionPumpRunning = true;
433
+ // Run asynchronously so enqueueWhoCan returns immediately
434
+ void this.runAdmissionPump();
435
+ }
436
+ /**
437
+ * The admission pump loop. Drains pendingRequests into active processing
438
+ * up to maxRequestsInProgress. Only one instance of this loop runs at a time,
439
+ * guarded by admissionPumpRunning.
440
+ */
441
+ async runAdmissionPump() {
442
+ const maxActive = this.config.tuning?.maxRequestsInProgress ?? 30;
443
+ try {
444
+ while (this.pendingRequests.length > 0 && this.activeRequestOrder.length < maxActive) {
445
+ if (this.isShutdown)
446
+ break;
447
+ const submitted = this.pendingRequests.shift();
448
+ const state = this.createRequestState(submitted);
449
+ this.requestStates.set(submitted.requestId, state);
450
+ this.activeRequestOrder.push(submitted.requestId);
451
+ // Enqueue the root preparation job for this request
452
+ this.enqueueRootPreparation(state);
453
+ }
454
+ }
455
+ finally {
456
+ this.admissionPumpRunning = false;
457
+ }
458
+ // After admitting, check if we became idle
459
+ this.checkIdle();
460
+ }
461
+ /**
462
+ * Creates a fresh RequestState for an admitted request.
463
+ *
464
+ * @param submitted - The submitted request to create state for.
465
+ * @returns the new RequestState.
466
+ */
467
+ createRequestState(submitted) {
468
+ return {
469
+ requestId: submitted.requestId,
470
+ request: submitted.request,
471
+ allScenariosCreated: false,
472
+ scenarios: new StreamingWorkQueue_js_1.StreamingWorkQueue(),
473
+ created: 0,
474
+ completed: 0,
475
+ pendingPreparationJobs: 0,
476
+ allowed: [],
477
+ principalsNotFound: [],
478
+ accountsNotFound: [],
479
+ organizationsNotFound: [],
480
+ organizationalUnitsNotFound: [],
481
+ allAccountsChecked: false,
482
+ denyDetails: [],
483
+ simulationCount: 0,
484
+ denyDetailsCallback: submitted.request.denyDetailsCallback,
485
+ pendingDenyDetailsChecks: 0,
486
+ settled: false,
487
+ callbackInvoked: false,
488
+ simulationErrors: []
489
+ };
490
+ }
491
+ // ──────────────────────────────────────────────────────────────────────────
492
+ // Preparation
493
+ // ──────────────────────────────────────────────────────────────────────────
494
+ /**
495
+ * Enqueues the root preparation job for a request. This job performs resource
496
+ * account resolution, resource policy lookup, action expansion, principal scope
497
+ * handling, and then enqueues follow-up preparation jobs to enumerate principals.
498
+ *
499
+ * @param state - The request state to prepare.
500
+ */
501
+ enqueueRootPreparation(state) {
502
+ state.pendingPreparationJobs++;
503
+ this.preparationQueue.enqueue({
504
+ properties: {},
505
+ execute: async () => {
506
+ try {
507
+ await this.executeRootPreparation(state);
508
+ }
509
+ catch (err) {
510
+ this.settleRequestAsError(state, err instanceof Error ? err : new Error(String(err)));
511
+ }
512
+ finally {
513
+ state.pendingPreparationJobs--;
514
+ this.checkRequestCompletion(state);
515
+ }
516
+ }
517
+ });
518
+ }
519
+ /**
520
+ * Executes the root preparation for a request: resolves the resource account,
521
+ * fetches the resource policy, expands actions, determines which accounts and
522
+ * principals to check, and enqueues follow-up preparation jobs.
523
+ *
524
+ * @param state - The request state to prepare.
525
+ */
526
+ async executeRootPreparation(state) {
527
+ if (state.settled)
528
+ return;
529
+ const { request } = state;
530
+ const { resource } = request;
531
+ const collectClient = this.collectClient;
532
+ if (!request.resourceAccount && !request.resource) {
533
+ throw new Error('Either resourceAccount or resource must be provided in the request.');
534
+ }
535
+ const resourceAccount = request.resourceAccount || (await (0, resources_js_1.getAccountIdForResource)(collectClient, resource));
536
+ if (!resourceAccount) {
537
+ throw new Error(`Could not determine account ID for resource ${resource}. Please use a different ARN or specify resourceAccount.`);
538
+ }
539
+ const actions = await (0, whoCan_js_1.actionsForWhoCan)({
540
+ actions: request.actions,
541
+ resource: request.resource
542
+ });
543
+ if (!actions || actions.length === 0) {
544
+ throw new Error('No valid actions provided or found for the resource.');
545
+ }
546
+ const untrustingActions = await (0, untrustingActions_js_1.actionsThatDoNotAutomaticallyTrustTheCurrentAccount)();
547
+ let resourcePolicy = undefined;
548
+ if (resource) {
549
+ resourcePolicy = await (0, resources_js_1.getResourcePolicyForResource)(collectClient, resource, resourceAccount);
550
+ const resourceArn = new arn_js_1.Arn(resource);
551
+ if ((resourceArn.matches({ service: 'iam', resourceType: 'role' }) ||
552
+ resourceArn.matches({ service: 'kms', resourceType: 'key' })) &&
553
+ !resourcePolicy) {
554
+ throw new Error(`Unable to find resource policy for ${resource}. Cannot determine who can access the resource.`);
555
+ }
556
+ if (resourcePolicy && resourcePolicy.errors.length > 0) {
557
+ log_1.log.error({
558
+ iamLensResourcePolicyValidationErrors: true,
559
+ errors: resourcePolicy.errors,
560
+ resource: resource
561
+ });
562
+ throw new Error(`Resource policy for ${resource} has validation errors and cannot be used for simulation.`);
563
+ }
564
+ }
565
+ const accountsToCheck = await (0, whoCan_js_1.accountsToCheckBasedOnResourcePolicy)(resourcePolicy, resourceAccount, collectClient);
566
+ const principalArnFilter = (0, principalArnFilter_js_1.buildPrincipalArnFilter)(resourcePolicy);
567
+ const uniqueAccounts = await (0, whoCan_js_1.uniqueAccountsToCheck)(collectClient, accountsToCheck);
568
+ // Store not-found arrays on the request state
569
+ state.accountsNotFound = uniqueAccounts.accountsNotFound;
570
+ state.organizationsNotFound = uniqueAccounts.organizationsNotFound;
571
+ state.organizationalUnitsNotFound = uniqueAccounts.organizationalUnitsNotFound;
572
+ state.allAccountsChecked = request.principalScope ? false : accountsToCheck.allAccounts;
573
+ // The resource account is always a candidate for search — for non-untrusting
574
+ // actions same-account principals can access via identity policy alone. Include
575
+ // it in the base accounts list so the principalScope intersection can see it.
576
+ const baseAccounts = uniqueAccounts.accounts.includes(resourceAccount)
577
+ ? uniqueAccounts.accounts
578
+ : [...uniqueAccounts.accounts, resourceAccount];
579
+ let accountsForSearch = baseAccounts;
580
+ let principalsForSearch = accountsToCheck.specificPrincipals;
581
+ let scopeIncludesResourceAccount = true;
582
+ if (request.principalScope) {
583
+ const resolved = await (0, principalScope_js_1.resolvePrincipalScope)(collectClient, request.principalScope);
584
+ const intersection = (0, principalScope_js_1.intersectWithPrincipalScope)(baseAccounts, accountsToCheck.specificPrincipals, accountsToCheck.allAccounts, resolved.accounts, resolved.principals);
585
+ accountsForSearch = intersection.accounts;
586
+ principalsForSearch = intersection.principals;
587
+ scopeIncludesResourceAccount = resolved.accounts.has(resourceAccount);
588
+ }
589
+ // Principals explicitly named in the resource policy are enqueued via the
590
+ // specific-principals path (which skips the PrincipalArn filter). Track them
591
+ // so the account-enumeration paths can skip duplicates without needing to
592
+ // store all enumerated principals in memory.
593
+ const specificPrincipalSet = new Set(principalsForSearch);
594
+ // Enqueue follow-up preparation jobs for account/principal enumeration
595
+ const principalIndexExists = !this.config.ignorePrincipalIndex && (await collectClient.principalIndexExists());
596
+ if (principalIndexExists) {
597
+ // Use the principal index to find relevant principals directly
598
+ state.pendingPreparationJobs++;
599
+ this.preparationQueue.enqueue({
600
+ properties: {},
601
+ execute: async () => {
602
+ try {
603
+ if (state.settled)
604
+ return;
605
+ // Pre-compute an accounts list without the resource account for
606
+ // untrusting actions where the policy does not trust it.
607
+ const accountsWithoutResourceAccount = scopeIncludesResourceAccount && !accountsToCheck.resourceAccountTrustedByPolicy
608
+ ? accountsForSearch.filter((a) => a !== resourceAccount)
609
+ : accountsForSearch;
610
+ for (const action of actions) {
611
+ const isUntrusting = untrustingActions.has(action.toLowerCase());
612
+ const allFromAccount = scopeIncludesResourceAccount && accountsToCheck.checkAllFromResourceAccount
613
+ ? resourceAccount
614
+ : undefined;
615
+ const actionAccountsForSearch = isUntrusting
616
+ ? accountsWithoutResourceAccount
617
+ : accountsForSearch;
618
+ const indexedPrincipals = await collectClient.getPrincipalsWithActionAllowed(allFromAccount, actionAccountsForSearch, action);
619
+ for (const principal of indexedPrincipals || []) {
620
+ if (specificPrincipalSet.has(principal))
621
+ continue;
622
+ if (principalArnFilter &&
623
+ !(0, iam_utils_1.isServicePrincipal)(principal) &&
624
+ !(0, principalArnFilter_js_1.principalMatchesFilter)(principal, action, resourceAccount, principalArnFilter)) {
625
+ continue;
626
+ }
627
+ state.scenarios.enqueue({
628
+ resource,
629
+ action,
630
+ principal,
631
+ resourceAccount,
632
+ strictContextKeys: state.request.strictContextKeys,
633
+ collectDenyDetails: !!state.denyDetailsCallback
634
+ });
635
+ state.created++;
636
+ }
637
+ }
638
+ this.notifySimulationConsumers();
639
+ }
640
+ catch (err) {
641
+ this.settleRequestAsError(state, err instanceof Error ? err : new Error(String(err)));
642
+ }
643
+ finally {
644
+ state.pendingPreparationJobs--;
645
+ this.checkRequestCompletion(state);
646
+ }
647
+ }
648
+ });
649
+ }
650
+ else {
651
+ // No principal index — enumerate all principals per account
652
+ for (const account of accountsForSearch) {
653
+ state.pendingPreparationJobs++;
654
+ this.preparationQueue.enqueue({
655
+ properties: {},
656
+ execute: async () => {
657
+ try {
658
+ if (state.settled)
659
+ return;
660
+ const principals = await collectClient.getAllPrincipalsInAccount(account);
661
+ for (const principal of principals) {
662
+ if (specificPrincipalSet.has(principal))
663
+ continue;
664
+ const skipFilter = !principalArnFilter || (0, iam_utils_1.isServicePrincipal)(principal);
665
+ for (const action of actions) {
666
+ if (!skipFilter &&
667
+ !(0, principalArnFilter_js_1.principalMatchesFilter)(principal, action, resourceAccount, principalArnFilter)) {
668
+ continue;
669
+ }
670
+ state.scenarios.enqueue({
671
+ resource,
672
+ action,
673
+ principal,
674
+ resourceAccount,
675
+ strictContextKeys: state.request.strictContextKeys,
676
+ collectDenyDetails: !!state.denyDetailsCallback
677
+ });
678
+ state.created++;
679
+ }
680
+ }
681
+ this.notifySimulationConsumers();
682
+ }
683
+ catch (err) {
684
+ this.settleRequestAsError(state, err instanceof Error ? err : new Error(String(err)));
685
+ }
686
+ finally {
687
+ state.pendingPreparationJobs--;
688
+ this.checkRequestCompletion(state);
689
+ }
690
+ }
691
+ });
692
+ }
693
+ }
694
+ // Enqueue specific principals from resource policy (iterate the Set to
695
+ // deduplicate — the same principal can appear in the list more than once
696
+ // when multiple statements reference it, e.g. an explicit Principal element
697
+ // and a StringEquals aws:PrincipalArn condition).
698
+ for (const principal of specificPrincipalSet) {
699
+ state.pendingPreparationJobs++;
700
+ this.preparationQueue.enqueue({
701
+ properties: {},
702
+ execute: async () => {
703
+ try {
704
+ if (state.settled)
705
+ return;
706
+ if ((0, iam_utils_1.isServicePrincipal)(principal)) {
707
+ for (const action of actions) {
708
+ state.scenarios.enqueue({
709
+ resource,
710
+ action,
711
+ principal,
712
+ resourceAccount,
713
+ strictContextKeys: state.request.strictContextKeys,
714
+ collectDenyDetails: !!state.denyDetailsCallback
715
+ });
716
+ state.created++;
717
+ }
718
+ }
719
+ else if ((0, iam_utils_1.isIamUserArn)(principal) ||
720
+ (0, iam_utils_1.isIamRoleArn)(principal) ||
721
+ (0, iam_utils_1.isAssumedRoleArn)(principal)) {
722
+ const resolvedPrincipal = await collectClient.resolvePrincipalArn(principal);
723
+ if (!resolvedPrincipal) {
724
+ state.principalsNotFound.push(principal);
725
+ }
726
+ else {
727
+ for (const action of actions) {
728
+ state.scenarios.enqueue({
729
+ resource,
730
+ action,
731
+ principal,
732
+ resultPrincipal: resolvedPrincipal,
733
+ resourceAccount,
734
+ strictContextKeys: state.request.strictContextKeys,
735
+ collectDenyDetails: !!state.denyDetailsCallback
736
+ });
737
+ state.created++;
738
+ }
739
+ }
740
+ }
741
+ else {
742
+ state.principalsNotFound.push(principal);
743
+ }
744
+ this.notifySimulationConsumers();
745
+ }
746
+ catch (err) {
747
+ this.settleRequestAsError(state, err instanceof Error ? err : new Error(String(err)));
748
+ }
749
+ finally {
750
+ state.pendingPreparationJobs--;
751
+ this.checkRequestCompletion(state);
752
+ }
753
+ }
754
+ });
755
+ }
756
+ // All follow-up prep jobs have been enqueued. Mark scenarios as fully specified
757
+ // once the root prep and all follow-ups complete (tracked by pendingPreparationJobs).
758
+ state.allScenariosCreated = true;
759
+ // Notify consumers that scenarios may be available
760
+ this.notifySimulationConsumers();
761
+ }
762
+ // ──────────────────────────────────────────────────────────────────────────
763
+ // Simulation result handling
764
+ // ──────────────────────────────────────────────────────────────────────────
765
+ /**
766
+ * Handles a simulation result from a worker or the main thread runner.
767
+ * Routes the result to the correct request state and checks for completion.
768
+ *
769
+ * @param requestId - The ID of the request this result belongs to.
770
+ * @param result - The simulation job result.
771
+ */
772
+ handleSimulationResult(requestId, result, denyDetailsCheckWillFollow = false) {
773
+ const state = this.requestStates.get(requestId);
774
+ if (!state)
775
+ return;
776
+ state.completed++;
777
+ if (denyDetailsCheckWillFollow) {
778
+ state.pendingDenyDetailsChecks++;
779
+ }
780
+ if (state.settled) {
781
+ // Request already settled (e.g., failed). Still count the result so
782
+ // the drain check can fire, but discard the actual data.
783
+ this.checkRequestCompletion(state);
784
+ return;
785
+ }
786
+ state.simulationCount++;
787
+ if (result.status === 'fulfilled' && result.value) {
788
+ state.allowed.push(result.value);
789
+ }
790
+ else if (result.status === 'rejected') {
791
+ log_1.log.error('Error running simulation', { error: result.reason });
792
+ state.simulationErrors.push(result);
793
+ }
794
+ this.checkRequestCompletion(state);
795
+ }
796
+ /**
797
+ * Handles a checkDenyDetails request from a worker thread. Looks up the
798
+ * request's denyDetailsCallback and responds.
799
+ *
800
+ * @param requestId - The ID of the request.
801
+ * @param checkId - The unique check ID for this deny-details round trip.
802
+ * @param lightAnalysis - The light analysis to pass to the callback.
803
+ * @param worker - The worker to respond to.
804
+ */
805
+ handleCheckDenyDetails(requestId, checkId, lightAnalysis, worker) {
806
+ const state = this.requestStates.get(requestId);
807
+ const shouldInclude = state && !state.settled ? (state.denyDetailsCallback?.(lightAnalysis) ?? false) : false;
808
+ if (!shouldInclude && state) {
809
+ // No denyDetailsResult message will follow — decrement the counter
810
+ state.pendingDenyDetailsChecks--;
811
+ this.checkRequestCompletion(state);
812
+ }
813
+ worker.postMessage({
814
+ type: 'denyDetailsCheckResult',
815
+ checkId,
816
+ shouldInclude
817
+ });
818
+ }
819
+ /**
820
+ * Handles a deny details result from a worker thread. Decrements the
821
+ * pending deny-details counter and checks for request completion.
822
+ *
823
+ * @param requestId - The ID of the request.
824
+ * @param denyDetail - The deny detail to store.
825
+ */
826
+ handleDenyDetailsResult(requestId, denyDetail) {
827
+ const state = this.requestStates.get(requestId);
828
+ if (!state)
829
+ return;
830
+ if (state.pendingDenyDetailsChecks > 0) {
831
+ state.pendingDenyDetailsChecks--;
832
+ }
833
+ if (!state.settled) {
834
+ state.denyDetails.push(denyDetail);
835
+ }
836
+ this.checkRequestCompletion(state);
837
+ }
838
+ // ──────────────────────────────────────────────────────────────────────────
839
+ // Request completion and settlement
840
+ // ──────────────────────────────────────────────────────────────────────────
841
+ /**
842
+ * Checks whether a request has completed all preparation and simulation work.
843
+ * If so, settles the request as successful.
844
+ *
845
+ * @param state - The request state to check.
846
+ */
847
+ checkRequestCompletion(state) {
848
+ if (state.settled) {
849
+ this.checkRequestDrain(state);
850
+ return;
851
+ }
852
+ if (!state.allScenariosCreated)
853
+ return;
854
+ if (state.pendingPreparationJobs > 0)
855
+ return;
856
+ if (state.created !== state.completed)
857
+ return;
858
+ if (state.pendingDenyDetailsChecks > 0)
859
+ return;
860
+ // All work done — settle as success
861
+ if (state.simulationErrors.length > 0) {
862
+ this.settleRequestAsError(state, new Error(`Completed with ${state.simulationErrors.length} simulation errors. See previous logs.`));
863
+ }
864
+ else {
865
+ this.settleRequestAsSuccess(state);
866
+ }
867
+ }
868
+ /**
869
+ * Settles a request as successful: builds the WhoCanResponse, awaits
870
+ * onRequestSettled, removes the request from active state, and wakes
871
+ * the admission pump.
872
+ *
873
+ * @param state - The request state to settle.
874
+ */
875
+ settleRequestAsSuccess(state) {
876
+ if (state.settled)
877
+ return;
878
+ state.settled = true;
879
+ const result = {
880
+ simulationCount: state.simulationCount,
881
+ allowed: state.allowed,
882
+ allAccountsChecked: state.allAccountsChecked,
883
+ accountsNotFound: state.accountsNotFound,
884
+ organizationsNotFound: state.organizationsNotFound,
885
+ organizationalUnitsNotFound: state.organizationalUnitsNotFound,
886
+ principalsNotFound: state.principalsNotFound,
887
+ denyDetails: state.denyDetailsCallback ? state.denyDetails : undefined
888
+ };
889
+ if (state.request.sort) {
890
+ (0, whoCan_js_1.sortWhoCanResults)(result);
891
+ }
892
+ void this.invokeSettledCallbackAndCleanup(state, {
893
+ status: 'fulfilled',
894
+ requestId: state.requestId,
895
+ request: state.request,
896
+ result
897
+ });
898
+ }
899
+ /**
900
+ * Settles a request as failed: invokes onRequestSettled with the error
901
+ * immediately, but keeps the request in active state until all in-flight
902
+ * work drains (created === completed). Late results for settled requests
903
+ * are discarded but still counted so the drain completes.
904
+ *
905
+ * @param state - The request state to settle.
906
+ * @param error - The error that caused the failure.
907
+ */
908
+ settleRequestAsError(state, error) {
909
+ if (state.settled)
910
+ return;
911
+ state.settled = true;
912
+ // Await the callback (backpressure), then mark it done so checkRequestDrain
913
+ // can free the slot once all in-flight work also completes.
914
+ void (async () => {
915
+ await this.invokeSettledCallback({
916
+ status: 'rejected',
917
+ requestId: state.requestId,
918
+ request: state.request,
919
+ error
920
+ });
921
+ state.callbackInvoked = true;
922
+ this.checkRequestDrain(state);
923
+ })();
924
+ }
925
+ /**
926
+ * Invokes the onRequestSettled callback and routes any errors through
927
+ * {@link handleSettlementFailure}.
928
+ *
929
+ * @param event - The settlement event to deliver.
930
+ */
931
+ async invokeSettledCallback(event) {
932
+ try {
933
+ await this.config.onRequestSettled(event);
934
+ }
935
+ catch (err) {
936
+ await this.handleSettlementFailure(event, err);
937
+ }
938
+ }
939
+ /**
940
+ * Awaits the onRequestSettled callback, then removes the request from
941
+ * active state and wakes the admission pump. Used for successful settlements
942
+ * where all work is already complete.
943
+ *
944
+ * @param state - The request state being settled.
945
+ * @param event - The settlement event to deliver.
946
+ */
947
+ async invokeSettledCallbackAndCleanup(state, event) {
948
+ await this.invokeSettledCallback(event);
949
+ this.removeFromActiveState(state);
950
+ }
951
+ /**
952
+ * Checks whether a settled request has fully drained: the onRequestSettled
953
+ * callback has been awaited, all preparation jobs have finished, all
954
+ * simulation results have been received, and all deny-detail round trips
955
+ * have completed. Only then is the request removed from active state.
956
+ *
957
+ * @param state - The request state to check.
958
+ */
959
+ checkRequestDrain(state) {
960
+ if (!state.settled)
961
+ return;
962
+ if (!state.callbackInvoked)
963
+ return;
964
+ if (state.pendingPreparationJobs > 0)
965
+ return;
966
+ if (state.created !== state.completed)
967
+ return;
968
+ if (state.pendingDenyDetailsChecks > 0)
969
+ return;
970
+ this.removeFromActiveState(state);
971
+ }
972
+ /**
973
+ * Removes a request from active state, wakes the admission pump to fill
974
+ * the freed slot, and checks if the processor is now idle.
975
+ *
976
+ * @param state - The request state to remove.
977
+ */
978
+ removeFromActiveState(state) {
979
+ const idx = this.activeRequestOrder.indexOf(state.requestId);
980
+ if (idx !== -1) {
981
+ this.activeRequestOrder.splice(idx, 1);
982
+ }
983
+ this.requestStates.delete(state.requestId);
984
+ this.wakeAdmissionPump();
985
+ this.checkIdle();
986
+ }
987
+ // ──────────────────────────────────────────────────────────────────────────
988
+ // Idle checking
989
+ // ──────────────────────────────────────────────────────────────────────────
990
+ /**
991
+ * Returns true if the processor has no pending, active, or in-flight work.
992
+ *
993
+ * @returns true if fully idle.
994
+ */
995
+ isIdle() {
996
+ return this.pendingRequests.length === 0 && this.activeRequestOrder.length === 0;
997
+ }
998
+ /**
999
+ * Checks whether the processor has become idle and resolves or rejects the
1000
+ * waitForIdle promise if so.
1001
+ */
1002
+ checkIdle() {
1003
+ if (!this.isIdle())
1004
+ return;
1005
+ if (this.idleWaiters.length === 0)
1006
+ return;
1007
+ const waiters = this.idleWaiters.splice(0);
1008
+ if (this.fatalError) {
1009
+ for (const waiter of waiters) {
1010
+ waiter.reject(this.fatalError);
1011
+ }
1012
+ }
1013
+ else {
1014
+ for (const waiter of waiters) {
1015
+ waiter.resolve();
1016
+ }
1017
+ }
1018
+ }
1019
+ /**
1020
+ * Handles an error thrown by the `onRequestSettled` callback. If the
1021
+ * `onSettlementFailure` callback is defined, it is invoked with the event
1022
+ * and error; any error it throws is silently ignored. If not defined, a
1023
+ * warning is logged.
1024
+ *
1025
+ * @param event - The settlement event that was being delivered.
1026
+ * @param err - The raw error thrown by the `onRequestSettled` callback.
1027
+ */
1028
+ async handleSettlementFailure(event, err) {
1029
+ const error = err instanceof Error ? err : new Error(String(err));
1030
+ if (this.config.onSettlementFailure) {
1031
+ try {
1032
+ await this.config.onSettlementFailure(event, error);
1033
+ }
1034
+ catch {
1035
+ // onSettlementFailure itself failed — silently ignored
1036
+ }
1037
+ }
1038
+ else {
1039
+ log_1.log.warn('onRequestSettled callback failed and no onSettlementFailure handler is defined', {
1040
+ requestId: event.requestId,
1041
+ error
1042
+ });
1043
+ }
1044
+ }
1045
+ /**
1046
+ * Handles an unexpected worker failure by marking the processor as dead,
1047
+ * terminating remaining workers, and rejecting all active and pending requests.
1048
+ *
1049
+ * @param error - The error that caused the worker failure.
1050
+ */
1051
+ handleWorkerFailure(error) {
1052
+ this.workersDead = true;
1053
+ this.isShutdown = true;
1054
+ this.fatalError = error;
1055
+ // Terminate remaining workers (fire-and-forget)
1056
+ for (const worker of this.workers) {
1057
+ worker.terminate().catch(() => { });
1058
+ }
1059
+ // Settle all active requests as failed
1060
+ for (const requestId of [...this.activeRequestOrder]) {
1061
+ const state = this.requestStates.get(requestId);
1062
+ if (state && !state.settled) {
1063
+ this.settleRequestAsError(state, error);
1064
+ }
1065
+ }
1066
+ // Reject all pending requests
1067
+ while (this.pendingRequests.length > 0) {
1068
+ const submitted = this.pendingRequests.shift();
1069
+ const event = {
1070
+ status: 'rejected',
1071
+ requestId: submitted.requestId,
1072
+ request: submitted.request,
1073
+ error
1074
+ };
1075
+ void this.config
1076
+ .onRequestSettled(event)
1077
+ .catch((err) => this.handleSettlementFailure(event, err))
1078
+ .catch(() => { });
1079
+ }
1080
+ // Reject all idle waiters
1081
+ const waiters = this.idleWaiters.splice(0);
1082
+ for (const waiter of waiters) {
1083
+ waiter.reject(error);
1084
+ }
1085
+ }
1086
+ }
1087
+ exports.WhoCanProcessor = WhoCanProcessor;
1088
+ //# sourceMappingURL=WhoCanProcessor.js.map