< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Languages.CSharpDelegateBuilder
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Languages/CSharpDelegateBuilder.cs
Tag: Kestrun/Kestrun@2d87023b37eb91155071c91dd3d6a2eeb3004705
Line coverage
83%
Covered lines: 202
Uncovered lines: 41
Coverable lines: 243
Total lines: 665
Line coverage: 83.1%
Branch coverage
68%
Covered branches: 123
Total branches: 180
Branch coverage: 68.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 08/26/2025 - 01:25:22 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@07f821172e5dc3657f1be7e6818f18d6721cf38a08/26/2025 - 01:49:10 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@07f821172e5dc3657f1be7e6818f18d6721cf38a08/26/2025 - 14:53:17 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@78d1e497d8ba989d121b57aa39aa3c6b22de743108/26/2025 - 18:44:21 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@d4eca24e86ad16771e391821efd443d6dc96606b08/27/2025 - 16:39:45 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@4c6ca8f799d2de93b1edf9355074f3da4297dc6208/28/2025 - 13:31:25 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@c90b3fa538340336a4939f46ad7235e29212eb5908/29/2025 - 15:33:49 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@d302841c78e142037a1aae3165383d903a00b5f408/29/2025 - 16:01:24 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@ad4a22f390590bbcb102eb1a8fa13cdf7ebd5c5b08/29/2025 - 19:42:14 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@3c9ce084a1c53128813d4002ea9ff6c197712a0e08/30/2025 - 03:33:49 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@1211de166c0177afac78a94a8f91658b8dd4981809/01/2025 - 04:08:24 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@d6f26a131219b7a7fcb4e129af3193ec2ec4892909/01/2025 - 15:55:00 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@19d70630c420f79cdedeb90ba1187c4ef9ff547409/03/2025 - 13:55:51 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@80ce2a54be2f719c7be1c21a92a8156bfdc48eb409/03/2025 - 18:37:02 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@86fc5b3bca9d694f07b5e51c954ccc23c5594b4909/04/2025 - 17:02:01 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@f3880b25ea131298aa2f8b1e0d0a8d55eb160bc009/04/2025 - 17:09:47 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@2afb92fed8453d222b7af7c048460fff3c488fbc09/04/2025 - 18:11:31 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@de99e24698289f3f61ac7b73e96092732ae12b0509/04/2025 - 20:03:06 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@aac43c4a9d431237cc28e06bebdc8b9fca48b30609/05/2025 - 01:20:27 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@307328b32323080f64e2bbfdbf3f7087235cf22d09/05/2025 - 16:51:52 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@c672b8f05e89891aff034d6863debc0ea95b234d09/06/2025 - 04:23:39 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@a13e6122733c75e6e50f39500972849d262774e509/06/2025 - 18:30:33 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@aeddbedb8a96e9137aac94c2d5edd011b57ac87109/07/2025 - 18:41:40 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@2192d4ccb46312ce89b7f7fda1aa8c915bfa228409/08/2025 - 14:52:50 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@819d430c57fbf6a6ffbecb11f265d9b5cbe754a909/09/2025 - 05:44:24 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@a26a91936c400a7f2324671b2222643fb772438109/09/2025 - 20:36:29 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@eec6e531f6ea893bb4939db76943e009d9fd963c09/09/2025 - 21:56:59 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@739093f321f10605cc4d1029da7300e3bb4dcba909/10/2025 - 17:50:34 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@5f41b7385e6492ec5892c0c7887658952f6fb87e09/10/2025 - 19:58:59 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@9d3a582b2d63930269564a7591aa77ef297cadeb09/12/2025 - 13:06:50 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@0cd8aff9570206f0b23cbe71d96e965f66ee14fa09/12/2025 - 13:32:05 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@63ea5841fe73fd164406accba17a956e8c08357f09/12/2025 - 13:43:03 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@bd4d239cd63303706ae393e27d54f868ef64f9ff09/12/2025 - 16:20:13 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@bd014be0a15f3c9298922d2ff67068869adda2a009/12/2025 - 17:01:20 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@383c1560c6be4027d79f183f7ed839edb887efea09/12/2025 - 17:39:52 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@7189783cf8fc454ea15d22b08b77bebbd01f968709/12/2025 - 21:57:57 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@7f3035804f966a691bd6936a199a8086730a784509/13/2025 - 17:19:56 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@ea635f1ee1937c260a89d1a43a3c203cd8767c7b09/13/2025 - 21:11:10 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@c00c04d65edffc6840698a5c67a70cae1ad411d909/14/2025 - 21:23:16 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@c9d2f0b3dd164d7dc0dc2407a9f006293d92422309/15/2025 - 19:16:35 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@bfb58693b9baaed61644ace5b29e014d9ffacbc909/16/2025 - 04:01:29 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@e5263347b0baba68d9fd62ffbf60a7dd87f994bb09/16/2025 - 16:28:42 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@d5c0d6132e97ca542441289c02a4c9e9d0364d4909/16/2025 - 18:38:10 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@1ec65c49ba24bea275273220d19054072659b62a09/16/2025 - 19:34:38 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@cd031acff6ee2a77514aa4ff9c66847f9e475e6010/13/2025 - 16:52:37 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e10/15/2025 - 01:01:18 Line coverage: 83.1% (202/243) Branch coverage: 68.8% (124/180) Total lines: 665 Tag: Kestrun/Kestrun@7c4ce528870211ad6c2d2398c31ec13097fc584010/16/2025 - 12:37:55 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@fe9b0d5b0046c725ac092fdc6c0e022ea3ddbd0610/17/2025 - 15:48:30 Line coverage: 83.1% (202/243) Branch coverage: 68.8% (124/180) Total lines: 665 Tag: Kestrun/Kestrun@b8199aff869a847b75e185d0527ba45e04a43d8610/17/2025 - 17:13:00 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@72ffc26b6676ccf0f60d50c94e4bb52de23743be 08/26/2025 - 01:25:22 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@07f821172e5dc3657f1be7e6818f18d6721cf38a08/26/2025 - 01:49:10 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@07f821172e5dc3657f1be7e6818f18d6721cf38a08/26/2025 - 14:53:17 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@78d1e497d8ba989d121b57aa39aa3c6b22de743108/26/2025 - 18:44:21 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@d4eca24e86ad16771e391821efd443d6dc96606b08/27/2025 - 16:39:45 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@4c6ca8f799d2de93b1edf9355074f3da4297dc6208/28/2025 - 13:31:25 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@c90b3fa538340336a4939f46ad7235e29212eb5908/29/2025 - 15:33:49 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@d302841c78e142037a1aae3165383d903a00b5f408/29/2025 - 16:01:24 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@ad4a22f390590bbcb102eb1a8fa13cdf7ebd5c5b08/29/2025 - 19:42:14 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@3c9ce084a1c53128813d4002ea9ff6c197712a0e08/30/2025 - 03:33:49 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@1211de166c0177afac78a94a8f91658b8dd4981809/01/2025 - 04:08:24 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@d6f26a131219b7a7fcb4e129af3193ec2ec4892909/01/2025 - 15:55:00 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@19d70630c420f79cdedeb90ba1187c4ef9ff547409/03/2025 - 13:55:51 Line coverage: 83.8% (145/173) Branch coverage: 66.9% (83/124) Total lines: 443 Tag: Kestrun/Kestrun@80ce2a54be2f719c7be1c21a92a8156bfdc48eb409/03/2025 - 18:37:02 Line coverage: 83.8% (145/173) Branch coverage: 66.1% (82/124) Total lines: 443 Tag: Kestrun/Kestrun@86fc5b3bca9d694f07b5e51c954ccc23c5594b4909/04/2025 - 17:02:01 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@f3880b25ea131298aa2f8b1e0d0a8d55eb160bc009/04/2025 - 17:09:47 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@2afb92fed8453d222b7af7c048460fff3c488fbc09/04/2025 - 18:11:31 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@de99e24698289f3f61ac7b73e96092732ae12b0509/04/2025 - 20:03:06 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@aac43c4a9d431237cc28e06bebdc8b9fca48b30609/05/2025 - 01:20:27 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@307328b32323080f64e2bbfdbf3f7087235cf22d09/05/2025 - 16:51:52 Line coverage: 80.9% (187/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@c672b8f05e89891aff034d6863debc0ea95b234d09/06/2025 - 04:23:39 Line coverage: 80.9% (187/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@a13e6122733c75e6e50f39500972849d262774e509/06/2025 - 18:30:33 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@aeddbedb8a96e9137aac94c2d5edd011b57ac87109/07/2025 - 18:41:40 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@2192d4ccb46312ce89b7f7fda1aa8c915bfa228409/08/2025 - 14:52:50 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@819d430c57fbf6a6ffbecb11f265d9b5cbe754a909/09/2025 - 05:44:24 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@a26a91936c400a7f2324671b2222643fb772438109/09/2025 - 20:36:29 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@eec6e531f6ea893bb4939db76943e009d9fd963c09/09/2025 - 21:56:59 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@739093f321f10605cc4d1029da7300e3bb4dcba909/10/2025 - 17:50:34 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@5f41b7385e6492ec5892c0c7887658952f6fb87e09/10/2025 - 19:58:59 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@9d3a582b2d63930269564a7591aa77ef297cadeb09/12/2025 - 13:06:50 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@0cd8aff9570206f0b23cbe71d96e965f66ee14fa09/12/2025 - 13:32:05 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@63ea5841fe73fd164406accba17a956e8c08357f09/12/2025 - 13:43:03 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@bd4d239cd63303706ae393e27d54f868ef64f9ff09/12/2025 - 16:20:13 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@bd014be0a15f3c9298922d2ff67068869adda2a009/12/2025 - 17:01:20 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@383c1560c6be4027d79f183f7ed839edb887efea09/12/2025 - 17:39:52 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@7189783cf8fc454ea15d22b08b77bebbd01f968709/12/2025 - 21:57:57 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@7f3035804f966a691bd6936a199a8086730a784509/13/2025 - 17:19:56 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@ea635f1ee1937c260a89d1a43a3c203cd8767c7b09/13/2025 - 21:11:10 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@c00c04d65edffc6840698a5c67a70cae1ad411d909/14/2025 - 21:23:16 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@c9d2f0b3dd164d7dc0dc2407a9f006293d92422309/15/2025 - 19:16:35 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@bfb58693b9baaed61644ace5b29e014d9ffacbc909/16/2025 - 04:01:29 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@e5263347b0baba68d9fd62ffbf60a7dd87f994bb09/16/2025 - 16:28:42 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@d5c0d6132e97ca542441289c02a4c9e9d0364d4909/16/2025 - 18:38:10 Line coverage: 82.2% (190/231) Branch coverage: 69.2% (126/182) Total lines: 586 Tag: Kestrun/Kestrun@1ec65c49ba24bea275273220d19054072659b62a09/16/2025 - 19:34:38 Line coverage: 82.2% (190/231) Branch coverage: 68.6% (125/182) Total lines: 586 Tag: Kestrun/Kestrun@cd031acff6ee2a77514aa4ff9c66847f9e475e6010/13/2025 - 16:52:37 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e10/15/2025 - 01:01:18 Line coverage: 83.1% (202/243) Branch coverage: 68.8% (124/180) Total lines: 665 Tag: Kestrun/Kestrun@7c4ce528870211ad6c2d2398c31ec13097fc584010/16/2025 - 12:37:55 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@fe9b0d5b0046c725ac092fdc6c0e022ea3ddbd0610/17/2025 - 15:48:30 Line coverage: 83.1% (202/243) Branch coverage: 68.8% (124/180) Total lines: 665 Tag: Kestrun/Kestrun@b8199aff869a847b75e185d0527ba45e04a43d8610/17/2025 - 17:13:00 Line coverage: 83.1% (202/243) Branch coverage: 68.3% (123/180) Total lines: 665 Tag: Kestrun/Kestrun@72ffc26b6676ccf0f60d50c94e4bb52de23743be

Metrics

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Languages/CSharpDelegateBuilder.cs

#LineLine coverage
 1using System.Collections.Immutable;
 2using System.Reflection;
 3using System.Text;
 4using Kestrun.SharedState;
 5using Microsoft.CodeAnalysis;
 6using Microsoft.CodeAnalysis.CSharp;
 7using Microsoft.CodeAnalysis.CSharp.Scripting;
 8using Microsoft.CodeAnalysis.Scripting;
 9using Serilog.Events;
 10using Kestrun.Logging;
 11using Kestrun.Hosting;
 12
 13namespace Kestrun.Languages;
 14
 15
 16internal static class CSharpDelegateBuilder
 17{
 18    /// <summary>
 19    /// Builds a C# delegate for handling HTTP requests.
 20    /// </summary>
 21    /// <param name="host">The Kestrun host instance.</param>
 22    /// <param name="code">The C# code to execute.</param>
 23    /// <param name="args">Arguments to inject as variables into the script.</param>
 24    /// <param name="extraImports">Additional namespaces to import.</param>
 25    /// <param name="extraRefs">Additional assemblies to reference.</param>
 26    /// <param name="languageVersion">The C# language version to use.</param>
 27    /// <returns>A delegate that handles HTTP requests.</returns>
 28    /// <exception cref="ArgumentNullException">Thrown if the code is null or whitespace.</exception>
 29    /// <exception cref="CompilationErrorException">Thrown if the C# code compilation fails.</exception>
 30    /// <remarks>
 31    /// This method compiles the provided C# code into a script and returns a delegate that can be used to handle HTTP r
 32    /// It supports additional imports and references, and can inject global variables into the script.
 33    /// The delegate will execute the provided C# code within the context of an HTTP request, allowing access to the req
 34    /// </remarks>
 35    internal static RequestDelegate Build(KestrunHost host,
 36            string code, Dictionary<string, object?>? args, string[]? extraImports,
 37            Assembly[]? extraRefs, LanguageVersion languageVersion = LanguageVersion.CSharp12)
 38    {
 3739        if (host.Logger.IsEnabled(LogEventLevel.Debug))
 40        {
 2641            host.Logger.Debug("Building C# delegate, script length={Length}, imports={ImportsCount}, refs={RefsCount}, l
 2642                code?.Length, extraImports?.Length ?? 0, extraRefs?.Length ?? 0, languageVersion);
 43        }
 44
 45        // Validate inputs
 3746        if (string.IsNullOrWhiteSpace(code))
 47        {
 148            throw new ArgumentNullException(nameof(code), "C# code cannot be null or whitespace.");
 49        }
 50        // 1. Compile the C# code into a script
 51        //    - Use CSharpScript.Create() to create a script with the provided code
 52        //    - Use ScriptOptions to specify imports, references, and language version
 53        //    - Inject the provided arguments into the globals
 3654        var script = Compile(code, host.Logger, extraImports, extraRefs, null, languageVersion);
 55
 56        // 2. Return a delegate that executes the script
 57        //    - The delegate takes an HttpContext and returns a Task
 58        //    - It creates a KestrunContext and KestrunResponse from the HttpContext
 59        //    - It executes the script with the provided globals and locals
 60        //    - It applies the response to the HttpContext
 3661        if (host.Logger.IsEnabled(LogEventLevel.Debug))
 62        {
 2563            host.Logger.Debug("C# delegate built successfully, script length={Length}, imports={ImportsCount}, refs={Ref
 2564                code?.Length, extraImports?.Length ?? 0, extraRefs?.Length ?? 0, languageVersion);
 65        }
 66
 3667        return async ctx =>
 3668        {
 3669            try
 3670            {
 1571                if (host.Logger.IsEnabled(LogEventLevel.Debug))
 3672                {
 373                    host.Logger.DebugSanitized("Preparing execution for C# script at {Path}", ctx.Request.Path);
 3674                }
 3675
 1576                var (Globals, Response, Context) = await DelegateBuilder.PrepareExecutionAsync(host, ctx, args).Configur
 3677
 3678                // Execute the script with the current context and shared state
 1579                if (host.Logger.IsEnabled(LogEventLevel.Debug))
 3680                {
 381                    host.Logger.DebugSanitized("Executing C# script for {Path}", ctx.Request.Path);
 3682                }
 3683
 1584                _ = await script.RunAsync(Globals).ConfigureAwait(false);
 1085                if (host.Logger.IsEnabled(LogEventLevel.Debug))
 3686                {
 387                    host.Logger.DebugSanitized("C# script executed successfully for {Path}", ctx.Request.Path);
 3688                }
 3689
 3690                // Apply the response to the Kestrun context
 1091                await DelegateBuilder.ApplyResponseAsync(ctx, Response, host.Logger).ConfigureAwait(false);
 1092            }
 3693            finally
 3694            {
 3695                // Intentionally do not call Response.CompleteAsync here to keep the pipeline open
 3696                // for middleware like StatusCodePages to generate bodies for status-only responses.
 3697            }
 4698        };
 99    }
 100
 101    /// <summary>
 102    /// Compiles the provided C# code into a script.
 103    /// This method supports additional imports and references, and can inject global variables into the script.
 104    /// It returns a compiled script that can be executed later.
 105    /// </summary>
 106    /// <param name="code">The C# code to compile.</param>
 107    /// <param name="log">The logger instance.</param>
 108    /// <param name="extraImports">Additional namespaces to import.</param>
 109    /// <param name="extraRefs">Additional assembly references.</param>
 110    /// <param name="locals">Local variables to inject into the script.</param>
 111    /// <param name="languageVersion">The C# language version to use.</param>
 112    /// <returns>A compiled script that can be executed later.</returns>
 113    /// <exception cref="ArgumentNullException">Thrown when the code is null or whitespace.</exception>
 114    /// <exception cref="CompilationErrorException">Thrown when there are compilation errors.</exception>
 115    /// <remarks>
 116    /// This method compiles the provided C# code into a script using Roslyn.
 117    /// It supports additional imports and references, and can inject global variables into the script.
 118    /// The script can be executed later with the provided globals and locals.
 119    /// It is useful for scenarios where dynamic C# code execution is required, such as in web applications or scripting
 120    /// </remarks>
 121    internal static Script<object> Compile(
 122            string? code, Serilog.ILogger log, string[]? extraImports,
 123            Assembly[]? extraRefs, IReadOnlyDictionary<string, object?>? locals, LanguageVersion languageVersion = Langu
 124            )
 125    {
 59126        if (log.IsEnabled(LogEventLevel.Debug))
 127        {
 48128            log.Debug("Compiling C# script, length={Length}, imports={ImportsCount}, refs={RefsCount}, lang={Lang}",
 48129                code?.Length, extraImports?.Length ?? 0, extraRefs?.Length ?? 0, languageVersion);
 130        }
 131
 132        // Validate inputs
 59133        if (string.IsNullOrWhiteSpace(code))
 134        {
 0135            throw new ArgumentNullException(nameof(code), "C# code cannot be null or whitespace.");
 136        }
 137
 138        // References and imports
 59139        var coreRefs = DelegateBuilder.BuildBaselineReferences();
 140        // Core references + Kestrun + extras
 141        // Note: Order matters, Kestrun must come after core to avoid conflicts
 59142        var kestrunAssembly = typeof(KestrunHost).Assembly; // Kestrun.dll
 59143        var kestrunRef = MetadataReference.CreateFromFile(kestrunAssembly.Location);
 59144        var kestrunNamespaces = CollectKestrunNamespaces(kestrunAssembly);
 145        // Create script options
 59146        var opts = CreateScriptOptions(DelegateBuilder.PlatformImports, kestrunNamespaces, coreRefs, kestrunRef);
 59147        opts = AddExtraImports(opts, extraImports);
 59148        opts = AddExtraReferences(opts, extraRefs, log);
 149
 150        // Include currently loaded assemblies (deduplicated) to minimize missing reference issues.
 59151        opts = AddLoadedAssemblyReferences(opts, log);
 152
 153        // Globals/locals injection plus dynamic discovery of namespaces & assemblies needed
 59154        var (CodeWithPreamble, DynamicImports, DynamicReferences) = BuildGlobalsAndLocalsPreamble(code, locals, log);
 59155        code = CodeWithPreamble;
 156
 59157        if (DynamicImports.Count > 0)
 158        {
 35159            var newImports = DynamicImports.Except(opts.Imports, StringComparer.Ordinal).ToArray();
 35160            if (newImports.Length > 0)
 161            {
 0162                opts = opts.WithImports(opts.Imports.Concat(newImports));
 0163                if (log.IsEnabled(LogEventLevel.Debug))
 164                {
 0165                    log.Debug("Added {ImportCount} dynamic imports derived from globals/locals: {Imports}", newImports.L
 166                }
 167            }
 168        }
 169
 59170        if (DynamicReferences.Count > 0)
 171        {
 172            // Avoid duplicates by location
 35173            var existingRefPaths = new HashSet<string>(opts.MetadataReferences
 35174                .OfType<PortableExecutableReference>()
 8973175                .Select(r => r.FilePath ?? string.Empty)
 9008176                .Where(p => !string.IsNullOrEmpty(p)), StringComparer.OrdinalIgnoreCase);
 177
 35178            var newRefs = DynamicReferences
 45179                .Where(r => !string.IsNullOrEmpty(r.Location) && File.Exists(r.Location) && !existingRefPaths.Contains(r
 0180                .Select(r => MetadataReference.CreateFromFile(r.Location))
 35181                .ToArray();
 182
 35183            if (newRefs.Length > 0)
 184            {
 0185                opts = opts.WithReferences(opts.MetadataReferences.Concat(newRefs));
 0186                if (log.IsEnabled(LogEventLevel.Debug))
 187                {
 0188                    log.Debug("Added {RefCount} dynamic assembly reference(s) derived from globals/locals.", newRefs.Len
 189                }
 190            }
 191        }
 192
 193        // Compile
 59194        var script = CSharpScript.Create(code, opts, typeof(CsGlobals));
 59195        var diagnostics = CompileAndGetDiagnostics(script, log);
 59196        ThrowIfDiagnosticsNull(diagnostics);
 59197        ThrowOnErrors(diagnostics, log);
 58198        LogWarnings(diagnostics, log);
 58199        LogSuccessIfNoWarnings(diagnostics, log);
 200
 58201        return script;
 202    }
 203
 204    /// <summary>Collects metadata references for all non-dynamic loaded assemblies with a physical location.</summary>
 205    /// <param name="log">Logger.</param>
 206    /// <returns>Tuple of references and total count considered.</returns>
 207    private static (IEnumerable<MetadataReference> Refs, int Total) CollectLoadedAssemblyReferences(Serilog.ILogger log)
 208    {
 209        try
 210        {
 59211            var loaded = AppDomain.CurrentDomain.GetAssemblies();
 59212            var refs = new List<MetadataReference>(loaded.Length);
 59213            var considered = 0;
 35512214            foreach (var a in loaded)
 215            {
 17697216                considered++;
 17697217                if (a.IsDynamic)
 218                {
 219                    continue;
 220                }
 17548221                if (string.IsNullOrEmpty(a.Location) || !File.Exists(a.Location))
 222                {
 223                    continue;
 224                }
 225                try
 226                {
 15218227                    refs.Add(MetadataReference.CreateFromFile(a.Location));
 15218228                }
 0229                catch (Exception ex)
 230                {
 0231                    if (log.IsEnabled(LogEventLevel.Debug))
 232                    {
 0233                        log.Debug(ex, "Failed to add loaded assembly reference: {Assembly}", a.FullName);
 234                    }
 0235                }
 236            }
 59237            return (refs, considered);
 238        }
 0239        catch (Exception ex)
 240        {
 0241            log.Warning(ex, "Failed to enumerate loaded assemblies for dynamic references.");
 0242            return (Array.Empty<MetadataReference>(), 0);
 243        }
 59244    }
 245
 246    /// <summary>
 247    /// Builds the core assembly references for the script.
 248    /// </summary>
 249    /// <returns>The core assembly references.</returns>
 250
 251    /// <summary>
 252    /// Collects the namespaces from the Kestrun assembly.
 253    /// </summary>
 254    /// <param name="kestrunAssembly">The Kestrun assembly.</param>
 255    /// <returns>The collected namespaces.</returns>
 256    private static string[] CollectKestrunNamespaces(Assembly kestrunAssembly)
 257    {
 59258        return [.. kestrunAssembly
 59259            .GetExportedTypes()
 9204260            .Select(t => t.Namespace)
 9204261            .Where(ns => !string.IsNullOrEmpty(ns) && ns!.StartsWith("Kestrun", StringComparison.Ordinal))
 9086262            .Select(ns => ns!)
 59263            .Distinct()];
 264    }
 265
 266    /// <summary>
 267    /// Creates script options for the VB.NET script.
 268    /// </summary>
 269    /// <param name="platformImports">The platform-specific namespaces to import.</param>
 270    /// <param name="kestrunNamespaces">The Kestrun-specific namespaces to import.</param>
 271    /// <param name="coreRefs">The core assembly references to include.</param>
 272    /// <param name="kestrunRef">The Kestrun assembly reference to include.</param>
 273    /// <returns>The created script options.</returns>
 274    private static ScriptOptions CreateScriptOptions(
 275        IEnumerable<string> platformImports,
 276        IEnumerable<string> kestrunNamespaces,
 277        IEnumerable<MetadataReference> coreRefs,
 278        MetadataReference kestrunRef)
 279    {
 59280        var allImports = platformImports.Concat(kestrunNamespaces) ?? [];
 281        // Keep default references then add our core + Kestrun to avoid losing essential BCL assemblies
 59282        var opts = ScriptOptions.Default
 59283            .WithImports(allImports)
 59284            .AddReferences(coreRefs)
 59285            .AddReferences(kestrunRef);
 59286        return opts;
 287    }
 288
 289    /// <summary>
 290    /// Adds extra using directives to the script options.
 291    /// </summary>
 292    /// <param name="opts">The script options to modify.</param>
 293    /// <param name="extraImports">The extra using directives to add.</param>
 294    /// <returns>The modified script options.</returns>
 295    private static ScriptOptions AddExtraImports(ScriptOptions opts, string[]? extraImports)
 296    {
 59297        extraImports ??= ["Kestrun"];
 59298        if (!extraImports.Contains("Kestrun"))
 299        {
 1300            var importsList = extraImports.ToList();
 1301            importsList.Add("Kestrun");
 1302            extraImports = [.. importsList];
 303        }
 59304        return extraImports.Length > 0
 59305            ? opts.WithImports(opts.Imports.Concat(extraImports))
 59306            : opts;
 307    }
 308
 309    /// <summary>
 310    /// Adds extra assembly references to the script options.
 311    /// </summary>
 312    /// <param name="opts">The script options to modify.</param>
 313    /// <param name="extraRefs">The extra assembly references to add.</param>
 314    /// <param name="log">The logger to use for logging.</param>
 315    /// <returns>The modified script options.</returns>
 316    private static ScriptOptions AddExtraReferences(ScriptOptions opts, Assembly[]? extraRefs, Serilog.ILogger log)
 317    {
 59318        if (extraRefs is not { Length: > 0 })
 319        {
 59320            return opts;
 321        }
 322
 0323        foreach (var r in extraRefs)
 324        {
 0325            if (string.IsNullOrEmpty(r.Location))
 326            {
 0327                log.Warning("Skipping dynamic assembly with no location: {Assembly}", r.FullName);
 328            }
 0329            else if (!File.Exists(r.Location))
 330            {
 0331                log.Warning("Skipping missing assembly file: {Location}", r.Location);
 332            }
 333        }
 334
 0335        var safeRefs = extraRefs
 0336            .Where(r => !string.IsNullOrEmpty(r.Location) && File.Exists(r.Location))
 0337            .Select(r => MetadataReference.CreateFromFile(r.Location));
 338
 0339        return opts.WithReferences(opts.MetadataReferences.Concat(safeRefs));
 340    }
 341
 342    /// <summary>
 343    /// Adds references for all currently loaded (non-duplicate) assemblies to the script options.
 344    /// </summary>
 345    /// <param name="opts">Current script options.</param>
 346    /// <param name="log">Logger.</param>
 347    /// <returns>Updated script options.</returns>
 348    private static ScriptOptions AddLoadedAssemblyReferences(ScriptOptions opts, Serilog.ILogger log)
 349    {
 350        // Optionally include all currently loaded assemblies to reduce missing reference issues.
 351        // Roslyn will de-duplicate by file path internally but we still filter to avoid redundant work.
 59352        var (loadedRefs, loadedCount) = CollectLoadedAssemblyReferences(log);
 59353        if (loadedCount <= 0)
 354        {
 0355            return opts;
 356        }
 357
 59358        var existingPaths = new HashSet<string>(opts.MetadataReferences
 59359            .OfType<PortableExecutableReference>()
 10950360            .Select(r => r.FilePath ?? string.Empty)
 11009361            .Where(p => !string.IsNullOrEmpty(p)), StringComparer.OrdinalIgnoreCase);
 362
 59363        var newLoadedRefs = loadedRefs
 15218364            .Where(r => r is PortableExecutableReference pe && !string.IsNullOrEmpty(pe.FilePath) && !existingPaths.Cont
 59365            .ToArray();
 366
 59367        if (newLoadedRefs.Length == 0)
 368        {
 0369            return opts;
 370        }
 371
 59372        var updated = opts.WithReferences(opts.MetadataReferences.Concat(newLoadedRefs));
 59373        if (log.IsEnabled(LogEventLevel.Debug))
 374        {
 48375            log.Debug("Added {RefCount} loaded assembly reference(s) (of {TotalLoaded}) for dynamic script compilation."
 376        }
 59377        return updated;
 378    }
 379
 380    /// <summary>
 381    /// Prepends global and local variable declarations to the provided code.
 382    /// </summary>
 383    /// <param name="code">The original code to modify.</param>
 384    /// <param name="locals">The local variables to include.</param>
 385    /// <returns>The modified code with global and local variable declarations.</returns>
 386    /// <summary>Builds the preamble variable declarations for globals &amp; locals and discovers required namespaces an
 387    /// <param name="log">Logger instance.</param>
 388    /// <returns>Tuple containing code with preamble, dynamic imports, dynamic references.</returns>
 389    private static (string CodeWithPreamble, List<string> DynamicImports, List<Assembly> DynamicReferences) BuildGlobals
 390        string? code,
 391        IReadOnlyDictionary<string, object?>? locals,
 392        Serilog.ILogger log)
 393    {
 394        // Merge globals + locals
 59395        var merged = MergeGlobalsAndLocals(locals);
 396
 397        // Build preamble & discover dynamic imports/refs
 59398        var (preamble, imports, refs) = GeneratePreambleAndDiscover(merged);
 399
 400        // Append original code
 59401        var finalCode = preamble.Length > 0 ? preamble + (code ?? string.Empty) : code ?? string.Empty;
 402
 403        // Filter references to only those with locations
 104404        var filteredRefs = refs.Where(r => !string.IsNullOrEmpty(r.Location)).ToList();
 405
 59406        LogDynamicDiscovery(imports, filteredRefs, log);
 407
 59408        return (finalCode, imports.ToList(), filteredRefs);
 409    }
 410
 411    /// <summary>
 412    /// Creates a merged dictionary of global shared state and the supplied <paramref name="locals"/>.
 413    /// Local values override globals when a key collision occurs (case-insensitive).
 414    /// </summary>
 415    /// <param name="locals">Optional locals dictionary passed in at compile time.</param>
 416    /// <returns>A mutable dictionary keyed by variable name mapping to its source store name and value.</returns>
 417    private static Dictionary<string, (string Dict, object? Value)> MergeGlobalsAndLocals(IReadOnlyDictionary<string, ob
 418    {
 59419        var merged = new Dictionary<string, (string Dict, object? Value)>(StringComparer.OrdinalIgnoreCase);
 59420        var allGlobals = SharedStateStore.Snapshot();
 672421        foreach (var g in allGlobals)
 422        {
 277423            merged[g.Key] = ("Globals", g.Value);
 424        }
 59425        if (locals is { Count: > 0 })
 426        {
 76427            foreach (var l in locals)
 428            {
 20429                merged[l.Key] = ("Locals", l.Value);
 430            }
 431        }
 59432        return merged;
 433    }
 434
 435    /// <summary>
 436    /// Iterates all merged global + local variables and builds a textual preamble of variable declarations.
 437    /// While building, collects the required namespace imports and assembly references inferred from the runtime value 
 438    /// </summary>
 439    /// <param name="merged">Merged globals + locals produced by <see cref="MergeGlobalsAndLocals"/>.</param>
 440    /// <returns>Tuple containing the preamble text builder, discovered namespace import set, and assembly reference set
 441    private static (StringBuilder Preamble, HashSet<string> Imports, HashSet<Assembly> Refs) GeneratePreambleAndDiscover
 442        Dictionary<string, (string Dict, object? Value)> merged)
 443    {
 59444        var preambleBuilder = new StringBuilder();
 59445        var dynamicImports = new HashSet<string>(StringComparer.Ordinal);
 59446        var dynamicRefs = new HashSet<Assembly>();
 447
 708448        foreach (var kvp in merged)
 449        {
 295450            AppendVariableDeclarationAndCollect(kvp, preambleBuilder, dynamicImports, dynamicRefs);
 451        }
 452
 59453        return (preambleBuilder, dynamicImports, dynamicRefs);
 454    }
 455
 456    /// <summary>
 457    /// Appends a single variable declaration for the provided key/value and gathers any dynamic imports &amp; reference
 458    /// </summary>
 459    /// <param name="kvp">The merged key and its (source dictionary name, value).</param>
 460    /// <param name="preambleBuilder">Builder accumulating declaration lines.</param>
 461    /// <param name="dynamicImports">Set capturing namespaces to import.</param>
 462    /// <param name="dynamicRefs">Set capturing assemblies that must be referenced.</param>
 463    private static void AppendVariableDeclarationAndCollect(
 464        KeyValuePair<string, (string Dict, object? Value)> kvp,
 465        StringBuilder preambleBuilder,
 466        HashSet<string> dynamicImports,
 467        HashSet<Assembly> dynamicRefs)
 468    {
 295469        var valueType = kvp.Value.Value?.GetType();
 295470        var typeName = FormatTypeName(valueType);
 295471        _ = preambleBuilder.AppendLine($"var {kvp.Key} = ({typeName}){kvp.Value.Dict}[\"{kvp.Key}\"]; ");
 472
 295473        if (valueType == null)
 474        {
 160475            return;
 476        }
 477
 135478        TryAddNamespace(dynamicImports, valueType.Namespace);
 135479        CollectGenericArguments(valueType, dynamicImports, dynamicRefs);
 135480        _ = dynamicRefs.Add(valueType.Assembly);
 135481    }
 482
 483    /// <summary>
 484    /// Adds a namespace string to the imports set if it is non-empty.
 485    /// </summary>
 486    /// <param name="imports">Namespace accumulator set.</param>
 487    /// <param name="ns">Namespace candidate.</param>
 488    private static void TryAddNamespace(HashSet<string> imports, string? ns)
 489    {
 202490        if (!string.IsNullOrEmpty(ns))
 491        {
 202492            _ = imports.Add(ns!);
 493        }
 202494    }
 495
 496    /// <summary>
 497    /// For a generic type, collects namespaces and assemblies for each generic argument.
 498    /// </summary>
 499    /// <param name="valueType">The possibly generic value type.</param>
 500    /// <param name="imports">Namespace accumulator set.</param>
 501    /// <param name="refs">Assembly reference accumulator set.</param>
 502    private static void CollectGenericArguments(Type valueType, HashSet<string> imports, HashSet<Assembly> refs)
 503    {
 135504        if (!valueType.IsGenericType)
 505        {
 88506            return;
 507        }
 228508        foreach (var ga in valueType.GetGenericArguments())
 509        {
 67510            TryAddNamespace(imports, ga.Namespace);
 67511            _ = refs.Add(ga.Assembly);
 512        }
 47513    }
 514
 515    /// <summary>
 516    /// Emits a debug log summarizing the dynamic imports and assembly references that were discovered.
 517    /// </summary>
 518    /// <param name="imports">Collected imports.</param>
 519    /// <param name="refs">Collected assembly references (filtered to those with physical locations).</param>
 520    /// <param name="log">Logger.</param>
 521    private static void LogDynamicDiscovery(HashSet<string> imports, List<Assembly> refs, Serilog.ILogger log)
 522    {
 59523        if (log.IsEnabled(LogEventLevel.Debug) && (imports.Count > 0 || refs.Count > 0))
 524        {
 24525            log.Debug("Discovered {ImportCount} dynamic import(s) and {RefCount} reference(s) from globals/locals.", imp
 526        }
 59527    }
 528
 529    // Produces a C# friendly type name for reflection types (handles generics, arrays, nullable, and fallbacks).
 530    private static string FormatTypeName(Type? t)
 531    {
 363532        if (t == null)
 533        {
 160534            return "object";
 535        }
 203536        if (t.IsGenericParameter)
 537        {
 0538            return "object";
 539        }
 203540        if (t.IsArray)
 541        {
 1542            return FormatTypeName(t.GetElementType()) + "[]";
 543        }
 544        // Nullable<T>
 202545        if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
 546        {
 0547            return FormatTypeName(t.GetGenericArguments()[0]) + "?";
 548        }
 202549        if (t.IsGenericType)
 550        {
 551            try
 552            {
 47553                var genericDefName = t.Name;
 47554                var tickIndex = genericDefName.IndexOf('`');
 47555                if (tickIndex > 0)
 556                {
 47557                    genericDefName = genericDefName[..tickIndex];
 558                }
 47559                var args = t.GetGenericArguments().Select(FormatTypeName);
 47560                return (t.Namespace != null ? t.Namespace + "." : string.Empty) + genericDefName + "<" + string.Join(","
 561            }
 0562            catch
 563            {
 0564                return "object";
 565            }
 566        }
 567        // Non generic
 155568        return t.FullName ?? t.Name ?? "object";
 47569    }
 570
 571    /// <summary>
 572    /// Compiles the provided VB.NET script and returns any diagnostics.
 573    /// </summary>
 574    /// <param name="script">The VB.NET script to compile.</param>
 575    /// <param name="log">The logger to use for logging.</param>
 576    /// <returns>A collection of diagnostics produced during compilation, or null if compilation failed.</returns>
 577    private static ImmutableArray<Diagnostic>? CompileAndGetDiagnostics(Script<object> script, Serilog.ILogger log)
 578    {
 579        try
 580        {
 59581            return script.Compile();
 582        }
 0583        catch (CompilationErrorException ex)
 584        {
 0585            log.Error(ex, "C# script compilation failed with errors.");
 0586            return null;
 587        }
 59588    }
 589
 590    private static void ThrowIfDiagnosticsNull(ImmutableArray<Diagnostic>? diagnostics)
 591    {
 59592        if (diagnostics == null)
 593        {
 0594            throw new CompilationErrorException("C# script compilation failed with no diagnostics.", []);
 595        }
 59596    }
 597
 598    /// <summary>
 599    /// Throws a CompilationErrorException if the diagnostics are null.
 600    /// </summary>
 601    /// <param name="diagnostics">The compilation diagnostics.</param>
 602    /// <param name="log">The logger to use for logging.</param>
 603    /// <exception cref="CompilationErrorException"></exception>
 604    private static void ThrowOnErrors(ImmutableArray<Diagnostic>? diagnostics, Serilog.ILogger log)
 605    {
 60606        if (diagnostics?.Any(d => d.Severity == DiagnosticSeverity.Error) != true)
 607        {
 58608            return;
 609        }
 610
 2611        var errors = diagnostics?.Where(d => d.Severity == DiagnosticSeverity.Error).ToArray();
 1612        if (errors is not { Length: > 0 })
 613        {
 0614            return;
 615        }
 616
 1617        var sb = new StringBuilder();
 1618        _ = sb.AppendLine($"C# script compilation completed with {errors.Length} error(s):");
 4619        foreach (var error in errors)
 620        {
 1621            var location = error.Location.IsInSource
 1622                ? $" at line {error.Location.GetLineSpan().StartLinePosition.Line + 1}"
 1623                : string.Empty;
 1624            var msg = $"  Error [{error.Id}]: {error.GetMessage()}{location}";
 1625            log.Error(msg);
 1626            _ = sb.AppendLine(msg);
 627        }
 1628        throw new CompilationErrorException("C# route code compilation failed\n" + sb.ToString(), diagnostics ?? []);
 629    }
 630
 631    /// <summary>
 632    /// Logs warning messages if the compilation succeeded with warnings.
 633    /// </summary>
 634    /// <param name="diagnostics">The compilation diagnostics.</param>
 635    /// <param name="log">The logger to use for logging.</param>
 636    private static void LogWarnings(ImmutableArray<Diagnostic>? diagnostics, Serilog.ILogger log)
 637    {
 58638        var warnings = diagnostics?.Where(d => d.Severity == DiagnosticSeverity.Warning).ToArray();
 58639        if (warnings is not null && warnings.Length != 0)
 640        {
 0641            log.Warning($"C# script compilation completed with {warnings.Length} warning(s):");
 0642            foreach (var warning in warnings)
 643            {
 0644                var location = warning.Location.IsInSource
 0645                    ? $" at line {warning.Location.GetLineSpan().StartLinePosition.Line + 1}"
 0646                    : string.Empty;
 0647                log.Warning($"  Warning [{warning.Id}]: {warning.GetMessage()}{location}");
 648            }
 649        }
 58650    }
 651
 652    /// <summary>
 653    /// Logs a success message if the compilation succeeded without warnings.
 654    /// </summary>
 655    /// <param name="diagnostics">The compilation diagnostics.</param>
 656    /// <param name="log">The logger to use for logging.</param>
 657    private static void LogSuccessIfNoWarnings(ImmutableArray<Diagnostic>? diagnostics, Serilog.ILogger log)
 658    {
 58659        var warnings = diagnostics?.Where(d => d.Severity == DiagnosticSeverity.Warning).ToArray();
 58660        if (warnings != null && warnings.Length == 0 && log.IsEnabled(LogEventLevel.Debug))
 661        {
 47662            log.Debug("C# script compiled successfully with no warnings.");
 663        }
 58664    }
 665}

Methods/Properties

Build(Kestrun.Hosting.KestrunHost,System.String,System.Collections.Generic.Dictionary`2<System.String,System.Object>,System.String[],System.Reflection.Assembly[],Microsoft.CodeAnalysis.CSharp.LanguageVersion)
Compile(System.String,Serilog.ILogger,System.String[],System.Reflection.Assembly[],System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>,Microsoft.CodeAnalysis.CSharp.LanguageVersion)
CollectLoadedAssemblyReferences(Serilog.ILogger)
CollectKestrunNamespaces(System.Reflection.Assembly)
CreateScriptOptions(System.Collections.Generic.IEnumerable`1<System.String>,System.Collections.Generic.IEnumerable`1<System.String>,System.Collections.Generic.IEnumerable`1<Microsoft.CodeAnalysis.MetadataReference>,Microsoft.CodeAnalysis.MetadataReference)
AddExtraImports(Microsoft.CodeAnalysis.Scripting.ScriptOptions,System.String[])
AddExtraReferences(Microsoft.CodeAnalysis.Scripting.ScriptOptions,System.Reflection.Assembly[],Serilog.ILogger)
AddLoadedAssemblyReferences(Microsoft.CodeAnalysis.Scripting.ScriptOptions,Serilog.ILogger)
BuildGlobalsAndLocalsPreamble(System.String,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>,Serilog.ILogger)
MergeGlobalsAndLocals(System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>)
GeneratePreambleAndDiscover(System.Collections.Generic.Dictionary`2<System.String,System.ValueTuple`2<System.String,System.Object>>)
AppendVariableDeclarationAndCollect(System.Collections.Generic.KeyValuePair`2<System.String,System.ValueTuple`2<System.String,System.Object>>,System.Text.StringBuilder,System.Collections.Generic.HashSet`1<System.String>,System.Collections.Generic.HashSet`1<System.Reflection.Assembly>)
TryAddNamespace(System.Collections.Generic.HashSet`1<System.String>,System.String)
CollectGenericArguments(System.Type,System.Collections.Generic.HashSet`1<System.String>,System.Collections.Generic.HashSet`1<System.Reflection.Assembly>)
LogDynamicDiscovery(System.Collections.Generic.HashSet`1<System.String>,System.Collections.Generic.List`1<System.Reflection.Assembly>,Serilog.ILogger)
FormatTypeName(System.Type)
CompileAndGetDiagnostics(Microsoft.CodeAnalysis.Scripting.Script`1<System.Object>,Serilog.ILogger)
ThrowIfDiagnosticsNull(System.Nullable`1<System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.Diagnostic>>)
ThrowOnErrors(System.Nullable`1<System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.Diagnostic>>,Serilog.ILogger)
LogWarnings(System.Nullable`1<System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.Diagnostic>>,Serilog.ILogger)
LogSuccessIfNoWarnings(System.Nullable`1<System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.Diagnostic>>,Serilog.ILogger)