@pisell/pisellos 0.10.15 → 0.10.17

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 (331) hide show
  1. package/dist/core/index.js +6 -9
  2. package/dist/model/strategy/adapter/promotion/index.js +0 -9
  3. package/dist/model/strategy/adapter/walletPass/example.js +3 -26
  4. package/dist/model/strategy/strategy-example.js +5 -19
  5. package/dist/modules/Cart/index.js +0 -3
  6. package/dist/modules/Cart/utils/cartProduct.js +0 -1
  7. package/dist/modules/Date/index.js +1 -5
  8. package/dist/modules/Discount/index.js +2 -4
  9. package/dist/modules/OpenData/index.d.ts +5 -2
  10. package/dist/modules/OpenData/index.js +27 -14
  11. package/dist/modules/OpenData/types.d.ts +42 -0
  12. package/dist/modules/OpenData/types.js +7 -1
  13. package/dist/modules/OpenData/utils.d.ts +15 -1
  14. package/dist/modules/OpenData/utils.js +85 -0
  15. package/dist/modules/Order/index.d.ts +4 -2
  16. package/dist/modules/Order/index.js +131 -123
  17. package/dist/modules/Order/types.d.ts +8 -1
  18. package/dist/modules/Order/types.js +1 -0
  19. package/dist/modules/Order/utils.js +7 -1
  20. package/dist/modules/Payment/index.js +97 -162
  21. package/dist/modules/Product/types.d.ts +2 -0
  22. package/dist/modules/ProductList/index.js +96 -74
  23. package/dist/modules/Rules/index.js +12 -12
  24. package/dist/modules/SalesSummary/index.js +6 -4
  25. package/dist/modules/Schedule/index.js +0 -1
  26. package/dist/modules/Schedule/utils.js +0 -1
  27. package/dist/modules/Step/index.js +0 -1
  28. package/dist/plugins/request.js +1 -4
  29. package/dist/plugins/window.js +2 -6
  30. package/dist/server/index.d.ts +11 -0
  31. package/dist/server/index.js +977 -641
  32. package/dist/server/modules/menu/index.js +32 -41
  33. package/dist/server/modules/order/index.d.ts +8 -0
  34. package/dist/server/modules/order/index.js +253 -219
  35. package/dist/server/modules/products/index.js +24 -42
  36. package/dist/server/modules/quotation/index.js +29 -38
  37. package/dist/server/modules/resource/index.js +3 -4
  38. package/dist/server/modules/schedule/index.js +27 -35
  39. package/dist/server/utils/product.js +0 -1
  40. package/dist/server/utils/small-ticket.d.ts +9 -1
  41. package/dist/server/utils/small-ticket.js +117 -43
  42. package/dist/solution/BaseSales/index.d.ts +7 -0
  43. package/dist/solution/BaseSales/index.js +350 -215
  44. package/dist/solution/BaseSales/types.d.ts +2 -0
  45. package/dist/solution/BaseSales/utils/cartPromotion.d.ts +5 -0
  46. package/dist/solution/BaseSales/utils/cartPromotion.js +15 -6
  47. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +3 -0
  48. package/dist/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +7 -1
  49. package/dist/solution/BaseSales/utils.js +6 -3
  50. package/dist/solution/BookingByStep/index.d.ts +2 -2
  51. package/dist/solution/BookingByStep/index.js +7 -59
  52. package/dist/solution/BookingByStep/utils/capacity.js +1 -11
  53. package/dist/solution/BookingByStep/utils/resources.js +1 -3
  54. package/dist/solution/BookingByStep/utils/timeslots.js +2 -43
  55. package/dist/solution/BookingTicket/index.d.ts +14 -19
  56. package/dist/solution/BookingTicket/index.js +553 -433
  57. package/dist/solution/BookingTicket/utils/addProductDecision.js +3 -1
  58. package/dist/solution/BookingTicket/utils/scan/cloudSearch.js +6 -7
  59. package/dist/solution/BookingTicket/utils/scan/handleScan.js +25 -31
  60. package/dist/solution/BookingTicket/utils/scan/index.js +6 -10
  61. package/dist/solution/BuyTickets/index.js +9 -12
  62. package/dist/solution/Checkout/index.js +177 -252
  63. package/dist/solution/Checkout/utils/index.js +4 -16
  64. package/dist/solution/RegisterAndLogin/index.js +30 -76
  65. package/dist/solution/Sales/index.d.ts +20 -4
  66. package/dist/solution/Sales/index.js +348 -75
  67. package/dist/solution/Sales/types.d.ts +10 -0
  68. package/dist/solution/ScanOrder/index.js +50 -60
  69. package/dist/solution/ScanOrder/types.d.ts +2 -0
  70. package/dist/solution/ScanOrder/utils.js +6 -3
  71. package/dist/solution/ShopDiscount/index.js +2 -7
  72. package/dist/solution/UnifiedBookingSales/index.d.ts +5 -4
  73. package/dist/solution/UnifiedBookingSales/index.js +495 -422
  74. package/dist/solution/UnifiedBookingSales/types.d.ts +7 -1
  75. package/dist/solution/VenueBooking/index.js +45 -55
  76. package/dist/utils/task.js +15 -18
  77. package/dist/utils/watch.js +0 -1
  78. package/lib/apis/picoding.js +2 -0
  79. package/lib/core/index.js +131 -131
  80. package/lib/effects/index.js +46 -57
  81. package/lib/index.js +82 -49
  82. package/lib/model/index.js +14 -21
  83. package/lib/model/strategy/adapter/dataVariant/adapter.js +38 -50
  84. package/lib/model/strategy/adapter/dataVariant/evaluator.js +209 -315
  85. package/lib/model/strategy/adapter/dataVariant/examples.js +229 -237
  86. package/lib/model/strategy/adapter/dataVariant/index.js +39 -34
  87. package/lib/model/strategy/adapter/dataVariant/type.js +57 -28
  88. package/lib/model/strategy/adapter/index.js +100 -64
  89. package/lib/model/strategy/adapter/itemRule/adapter.js +156 -144
  90. package/lib/model/strategy/adapter/itemRule/evaluator.js +36 -48
  91. package/lib/model/strategy/adapter/itemRule/examples.js +295 -225
  92. package/lib/model/strategy/adapter/itemRule/index.js +83 -57
  93. package/lib/model/strategy/adapter/itemRule/type.js +97 -36
  94. package/lib/model/strategy/adapter/promotion/adapter.js +91 -69
  95. package/lib/model/strategy/adapter/promotion/evaluator.js +444 -310
  96. package/lib/model/strategy/adapter/promotion/examples.js +222 -234
  97. package/lib/model/strategy/adapter/promotion/index.js +1 -51
  98. package/lib/model/strategy/adapter/promotion/type.js +243 -36
  99. package/lib/model/strategy/adapter/type.js +4 -16
  100. package/lib/model/strategy/adapter/walletPass/evaluator.js +180 -150
  101. package/lib/model/strategy/adapter/walletPass/example.js +217 -190
  102. package/lib/model/strategy/adapter/walletPass/index.js +75 -51
  103. package/lib/model/strategy/adapter/walletPass/locales.js +36 -58
  104. package/lib/model/strategy/adapter/walletPass/type.js +4 -16
  105. package/lib/model/strategy/adapter/walletPass/utils.js +588 -378
  106. package/lib/model/strategy/engine.js +270 -168
  107. package/lib/model/strategy/index.js +49 -34
  108. package/lib/model/strategy/strategy-example.js +243 -239
  109. package/lib/model/strategy/type.js +100 -40
  110. package/lib/modules/Account/index.js +39 -53
  111. package/lib/modules/Account/types.js +20 -33
  112. package/lib/modules/AccountList/index.js +116 -154
  113. package/lib/modules/AccountList/types.js +30 -31
  114. package/lib/modules/AccountList/utils.js +18 -30
  115. package/lib/modules/BaseModule.js +23 -42
  116. package/lib/modules/BookingContext/index.js +158 -136
  117. package/lib/modules/BookingContext/types.js +46 -32
  118. package/lib/modules/BookingContext/utils/buildCacheItemFromOrderLine.js +138 -146
  119. package/lib/modules/BookingContext/utils/buildNormalProductCacheItemFromOrderLine.js +78 -83
  120. package/lib/modules/BookingContext/utils/cacheItemToBookingInput.js +170 -150
  121. package/lib/modules/BookingContext/utils/flexible.js +38 -55
  122. package/lib/modules/BookingContext/utils/formatResourceList.js +19 -34
  123. package/lib/modules/BookingContext/utils/index.js +124 -41
  124. package/lib/modules/BookingContext/utils/noResource.js +58 -70
  125. package/lib/modules/BookingContext/utils/orderLineDisplay.js +14 -41
  126. package/lib/modules/BookingContext/utils/productExtend.js +202 -155
  127. package/lib/modules/BookingContext/utils/resourceErrors.js +53 -53
  128. package/lib/modules/BookingContext/utils/resourceSelection.js +19 -41
  129. package/lib/modules/BookingContext/utils/resources.js +209 -167
  130. package/lib/modules/BookingContext/utils/serviceTimes.js +115 -116
  131. package/lib/modules/BookingContext/utils/timeSlices.js +76 -86
  132. package/lib/modules/Cart/index.js +170 -124
  133. package/lib/modules/Cart/types.js +46 -51
  134. package/lib/modules/Cart/utils/cartAccount.js +40 -39
  135. package/lib/modules/Cart/utils/cartDate.js +56 -61
  136. package/lib/modules/Cart/utils/cartDiscount.js +28 -33
  137. package/lib/modules/Cart/utils/cartNote.js +27 -32
  138. package/lib/modules/Cart/utils/cartProduct.js +321 -251
  139. package/lib/modules/Cart/utils/cartRelationForms.js +29 -35
  140. package/lib/modules/Cart/utils/cartResource.js +70 -78
  141. package/lib/modules/Cart/utils/changePrice.js +22 -50
  142. package/lib/modules/Cart/utils/index.js +106 -48
  143. package/lib/modules/Customer/constants.js +13 -34
  144. package/lib/modules/Customer/index.js +235 -172
  145. package/lib/modules/Customer/types.js +38 -35
  146. package/lib/modules/Date/index.js +116 -115
  147. package/lib/modules/Date/types.js +16 -28
  148. package/lib/modules/Date/utils.js +174 -123
  149. package/lib/modules/Discount/index.js +133 -143
  150. package/lib/modules/Discount/types.js +9 -31
  151. package/lib/modules/Guests/index.js +30 -56
  152. package/lib/modules/Guests/types.js +23 -33
  153. package/lib/modules/Holder/index.js +209 -189
  154. package/lib/modules/Holder/types.js +4 -16
  155. package/lib/modules/Holder/utils.js +20 -49
  156. package/lib/modules/OpenData/index.d.ts +5 -2
  157. package/lib/modules/OpenData/index.js +119 -78
  158. package/lib/modules/OpenData/types.d.ts +42 -0
  159. package/lib/modules/OpenData/types.js +12 -16
  160. package/lib/modules/OpenData/utils.d.ts +15 -1
  161. package/lib/modules/OpenData/utils.js +129 -78
  162. package/lib/modules/Order/index.d.ts +4 -2
  163. package/lib/modules/Order/index.js +1843 -2251
  164. package/lib/modules/Order/payment-utils.js +77 -120
  165. package/lib/modules/Order/types.d.ts +8 -1
  166. package/lib/modules/Order/types.js +88 -35
  167. package/lib/modules/Order/utils/bundleDiscountHydration.js +41 -74
  168. package/lib/modules/Order/utils/discountProductLineIdentity.js +142 -100
  169. package/lib/modules/Order/utils/manualProductDiscount.js +124 -123
  170. package/lib/modules/Order/utils/orderCollectionIdentity.js +184 -244
  171. package/lib/modules/Order/utils/virtualSettlement.js +120 -171
  172. package/lib/modules/Order/utils.js +834 -668
  173. package/lib/modules/Payment/cash.js +20 -33
  174. package/lib/modules/Payment/cashRecommendationAlgorithm.js +145 -76
  175. package/lib/modules/Payment/eftpos.js +16 -30
  176. package/lib/modules/Payment/index.js +532 -399
  177. package/lib/modules/Payment/mx51.js +1 -0
  178. package/lib/modules/Payment/types.js +230 -108
  179. package/lib/modules/Payment/utils.js +52 -51
  180. package/lib/modules/Payment/walletpass.js +485 -660
  181. package/lib/modules/Product/index.js +51 -46
  182. package/lib/modules/Product/types.d.ts +2 -0
  183. package/lib/modules/Product/types.js +4 -16
  184. package/lib/modules/Product/utils.js +32 -33
  185. package/lib/modules/ProductList/index.js +141 -161
  186. package/lib/modules/ProductList/types.js +9 -31
  187. package/lib/modules/Quotation/index.js +66 -100
  188. package/lib/modules/Quotation/types.js +4 -16
  189. package/lib/modules/Resource/index.js +27 -59
  190. package/lib/modules/Resource/types.js +22 -32
  191. package/lib/modules/Resource/utils.js +30 -38
  192. package/lib/modules/ResourcePlanner/index.js +25 -23
  193. package/lib/modules/ResourcePlanner/localClock.js +54 -119
  194. package/lib/modules/ResourcePlanner/planner.js +591 -772
  195. package/lib/modules/ResourcePlanner/types.js +10 -29
  196. package/lib/modules/Rules/index.js +1224 -963
  197. package/lib/modules/Rules/types.js +17 -40
  198. package/lib/modules/SalesSummary/index.js +80 -89
  199. package/lib/modules/SalesSummary/types.js +4 -16
  200. package/lib/modules/SalesSummary/utils.js +359 -442
  201. package/lib/modules/ScanOrderLogger/index.js +59 -79
  202. package/lib/modules/ScanOrderLogger/providers/feishu.js +54 -78
  203. package/lib/modules/ScanOrderLogger/providers/grafana.js +13 -36
  204. package/lib/modules/ScanOrderLogger/types.js +4 -16
  205. package/lib/modules/Schedule/getDateIsInSchedule.js +334 -163
  206. package/lib/modules/Schedule/index.js +100 -114
  207. package/lib/modules/Schedule/type.js +4 -16
  208. package/lib/modules/Schedule/types.js +4 -16
  209. package/lib/modules/Schedule/utils.js +433 -291
  210. package/lib/modules/Step/index.js +40 -52
  211. package/lib/modules/Step/tyeps.js +4 -16
  212. package/lib/modules/Summary/index.js +66 -71
  213. package/lib/modules/Summary/types.js +4 -16
  214. package/lib/modules/Summary/utils.js +773 -418
  215. package/lib/modules/SurchargeList/index.js +46 -60
  216. package/lib/modules/SurchargeList/types.js +4 -16
  217. package/lib/modules/index.js +245 -63
  218. package/lib/plugins/app.js +4 -16
  219. package/lib/plugins/index.js +36 -25
  220. package/lib/plugins/request.js +137 -126
  221. package/lib/plugins/shopStore.js +4 -16
  222. package/lib/plugins/user.js +4 -16
  223. package/lib/plugins/window.js +171 -179
  224. package/lib/server/index.d.ts +11 -0
  225. package/lib/server/index.js +3432 -2757
  226. package/lib/server/modules/floor-plan/index.js +102 -123
  227. package/lib/server/modules/floor-plan/types.js +15 -30
  228. package/lib/server/modules/index.js +106 -68
  229. package/lib/server/modules/menu/index.js +146 -122
  230. package/lib/server/modules/menu/types.js +18 -31
  231. package/lib/server/modules/order/index.d.ts +8 -0
  232. package/lib/server/modules/order/index.js +781 -956
  233. package/lib/server/modules/order/types.js +122 -33
  234. package/lib/server/modules/order/utils/filterBookings.js +219 -194
  235. package/lib/server/modules/order/utils/filterOrders.js +118 -92
  236. package/lib/server/modules/payment/index.js +59 -83
  237. package/lib/server/modules/payment/types.js +4 -16
  238. package/lib/server/modules/products/index.js +575 -459
  239. package/lib/server/modules/products/types.js +44 -34
  240. package/lib/server/modules/quotation/index.js +137 -172
  241. package/lib/server/modules/quotation/types.js +21 -31
  242. package/lib/server/modules/resource/index.js +213 -184
  243. package/lib/server/modules/resource/types.js +42 -33
  244. package/lib/server/modules/schedule/index.js +135 -123
  245. package/lib/server/modules/schedule/types.js +14 -21
  246. package/lib/server/modules/schedule/utils.js +334 -163
  247. package/lib/server/types.js +4 -16
  248. package/lib/server/utils/index.js +25 -23
  249. package/lib/server/utils/product.js +196 -139
  250. package/lib/server/utils/schedule.js +34 -41
  251. package/lib/server/utils/small-ticket.d.ts +9 -1
  252. package/lib/server/utils/small-ticket.js +565 -470
  253. package/lib/server/utils/time.js +40 -49
  254. package/lib/solution/BaseSales/index.d.ts +7 -0
  255. package/lib/solution/BaseSales/index.js +1385 -1503
  256. package/lib/solution/BaseSales/types.d.ts +2 -0
  257. package/lib/solution/BaseSales/types.js +42 -38
  258. package/lib/solution/BaseSales/utils/cartPromotion.d.ts +5 -0
  259. package/lib/solution/BaseSales/utils/cartPromotion.js +652 -498
  260. package/lib/solution/BaseSales/utils/parseSalesResponse.js +85 -105
  261. package/lib/solution/BaseSales/utils/quotationPrice.js +56 -114
  262. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.d.ts +3 -0
  263. package/lib/solution/BaseSales/utils/transformBaseProductToOrderProduct.js +138 -180
  264. package/lib/solution/BaseSales/utils.js +161 -143
  265. package/lib/solution/BookingByStep/index.d.ts +2 -2
  266. package/lib/solution/BookingByStep/index.js +1527 -1340
  267. package/lib/solution/BookingByStep/types.js +40 -99
  268. package/lib/solution/BookingByStep/utils/capacity.js +192 -160
  269. package/lib/solution/BookingByStep/utils/products.js +42 -52
  270. package/lib/solution/BookingByStep/utils/resources.js +527 -330
  271. package/lib/solution/BookingByStep/utils/stock.js +65 -44
  272. package/lib/solution/BookingByStep/utils/timeslots.js +129 -131
  273. package/lib/solution/BookingTicket/index.d.ts +14 -19
  274. package/lib/solution/BookingTicket/index.js +663 -526
  275. package/lib/solution/BookingTicket/types.js +99 -77
  276. package/lib/solution/BookingTicket/utils/addProductDecision.js +229 -190
  277. package/lib/solution/BookingTicket/utils/addTimeAvailability.js +58 -89
  278. package/lib/solution/BookingTicket/utils/bookingStatus.js +107 -78
  279. package/lib/solution/BookingTicket/utils/cartView.js +114 -99
  280. package/lib/solution/BookingTicket/utils/exampleData.js +185 -0
  281. package/lib/solution/BookingTicket/utils/orderCustomer.js +18 -41
  282. package/lib/solution/BookingTicket/utils/resolveBestAddTimePlan.js +219 -86
  283. package/lib/solution/BookingTicket/utils/scan/applyGlobalScan.js +168 -116
  284. package/lib/solution/BookingTicket/utils/scan/cloudSearch.js +79 -91
  285. package/lib/solution/BookingTicket/utils/scan/formatGlobalScan.js +54 -41
  286. package/lib/solution/BookingTicket/utils/scan/handleScan.js +84 -81
  287. package/lib/solution/BookingTicket/utils/scan/index.js +98 -104
  288. package/lib/solution/BookingTicket/utils/scan/scanCache.js +90 -43
  289. package/lib/solution/BookingTicket/utils/scan/scanProductValues.js +52 -64
  290. package/lib/solution/BookingTicket/utils/sessionCatalogStale.js +56 -58
  291. package/lib/solution/BookingTicket/utils/weighingProductSku.js +34 -48
  292. package/lib/solution/BuyTickets/index.js +67 -70
  293. package/lib/solution/BuyTickets/types.js +22 -38
  294. package/lib/solution/Checkout/index.js +1451 -1158
  295. package/lib/solution/Checkout/types.js +91 -61
  296. package/lib/solution/Checkout/utils/index.js +228 -156
  297. package/lib/solution/PlaceOrder/index.js +55 -0
  298. package/lib/solution/RegisterAndLogin/config.js +493 -460
  299. package/lib/solution/RegisterAndLogin/index.js +633 -630
  300. package/lib/solution/RegisterAndLogin/types.js +189 -76
  301. package/lib/solution/RegisterAndLogin/utils.js +183 -125
  302. package/lib/solution/Sales/index.d.ts +20 -4
  303. package/lib/solution/Sales/index.js +502 -330
  304. package/lib/solution/Sales/types.d.ts +10 -0
  305. package/lib/solution/Sales/types.js +27 -33
  306. package/lib/solution/ScanOrder/index.js +822 -852
  307. package/lib/solution/ScanOrder/types.d.ts +2 -0
  308. package/lib/solution/ScanOrder/types.js +33 -34
  309. package/lib/solution/ScanOrder/utils.js +412 -374
  310. package/lib/solution/ShopDiscount/index.js +226 -232
  311. package/lib/solution/ShopDiscount/types.js +15 -37
  312. package/lib/solution/ShopDiscount/utils.js +300 -172
  313. package/lib/solution/UnifiedBookingSales/index.d.ts +5 -4
  314. package/lib/solution/UnifiedBookingSales/index.js +1087 -1222
  315. package/lib/solution/UnifiedBookingSales/remoteOccupancyCache.js +99 -159
  316. package/lib/solution/UnifiedBookingSales/types.d.ts +7 -1
  317. package/lib/solution/UnifiedBookingSales/types.js +10 -32
  318. package/lib/solution/VenueBooking/index.js +811 -879
  319. package/lib/solution/VenueBooking/types.js +19 -39
  320. package/lib/solution/VenueBooking/utils/dateSummary.js +27 -56
  321. package/lib/solution/VenueBooking/utils/resource.js +31 -54
  322. package/lib/solution/VenueBooking/utils/slotMerge.js +94 -107
  323. package/lib/solution/VenueBooking/utils/timeSlot.js +132 -132
  324. package/lib/solution/VenueBooking/utils.js +130 -67
  325. package/lib/solution/index.js +124 -41
  326. package/lib/store/createStore.js +32 -29
  327. package/lib/types/index.js +4 -16
  328. package/lib/utils/payment-number.js +26 -46
  329. package/lib/utils/task.js +36 -36
  330. package/lib/utils/watch.js +81 -47
  331. package/package.json +2 -1
@@ -1,69 +1,48 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __export = (target, all) => {
6
- for (var name in all)
7
- __defProp(target, name, { get: all[name], enumerable: true });
8
- };
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
16
- };
17
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
1
+ "use strict";
18
2
 
19
- // src/modules/ResourcePlanner/planner.ts
20
- var planner_exports = {};
21
- __export(planner_exports, {
22
- createAvailabilityProjection: () => createAvailabilityProjection,
23
- getProductTimeRanges: () => getProductTimeRanges,
24
- queryAvailabilityProjection: () => queryAvailabilityProjection,
25
- validateAvailabilitySelection: () => validateAvailabilitySelection
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
26
5
  });
27
- module.exports = __toCommonJS(planner_exports);
28
- var import_types = require("./types");
29
- var import_localClock = require("./localClock");
30
- var DEFAULT_SLOT_STEP_MINUTES = 30;
31
- var DEFAULT_BUSINESS_HOURS = {
32
- startTime: "09:00",
33
- endTime: "18:00"
6
+ exports.createAvailabilityProjection = createAvailabilityProjection;
7
+ exports.getProductTimeRanges = getProductTimeRanges;
8
+ exports.queryAvailabilityProjection = queryAvailabilityProjection;
9
+ exports.validateAvailabilitySelection = validateAvailabilitySelection;
10
+ var _types = require("./types");
11
+ var _localClock = require("./localClock");
12
+ const DEFAULT_SLOT_STEP_MINUTES = 30;
13
+ const DEFAULT_BUSINESS_HOURS = {
14
+ startTime: '09:00',
15
+ endTime: '18:00'
34
16
  };
35
17
  function availabilityError(code, message, details) {
36
- return new import_types.AvailabilityError(code, message, details);
18
+ return new _types.AvailabilityError(code, message, details);
37
19
  }
38
20
  function sameId(left, right) {
39
- if (left === void 0 || left === null || right === void 0 || right === null)
40
- return false;
21
+ if (left === undefined || left === null || right === undefined || right === null) return false;
41
22
  return String(left) === String(right);
42
23
  }
43
24
  function uniqById(values = []) {
44
- const map = /* @__PURE__ */ new Map();
45
- values.forEach((value) => {
25
+ const map = new Map();
26
+ values.forEach(value => {
46
27
  map.set(String(value), value);
47
28
  });
48
29
  return Array.from(map.values());
49
30
  }
50
31
  function normalizePartySize(partySize) {
51
- if (partySize === void 0 || partySize === null)
52
- return void 0;
32
+ if (partySize === undefined || partySize === null) return undefined;
53
33
  const normalized = Number(partySize);
54
34
  if (!Number.isInteger(normalized) || normalized <= 0) {
55
- throw availabilityError("INVALID_TARGET_FILTER", "partySize 必须是正整数", {
35
+ throw availabilityError('INVALID_TARGET_FILTER', 'partySize 必须是正整数', {
56
36
  partySize
57
37
  });
58
38
  }
59
39
  return normalized;
60
40
  }
61
41
  function normalizeBookingCount(bookingCount) {
62
- if (bookingCount === void 0 || bookingCount === null)
63
- return 1;
42
+ if (bookingCount === undefined || bookingCount === null) return 1;
64
43
  const normalized = Number(bookingCount);
65
44
  if (!Number.isInteger(normalized) || normalized <= 0) {
66
- throw availabilityError("INVALID_TARGET_FILTER", "bookingCount 必须是正整数", {
45
+ throw availabilityError('INVALID_TARGET_FILTER', 'bookingCount 必须是正整数', {
67
46
  bookingCount
68
47
  });
69
48
  }
@@ -71,49 +50,51 @@ function normalizeBookingCount(bookingCount) {
71
50
  }
72
51
  function toDateTime(value, _timezoneName) {
73
52
  try {
74
- return (0, import_localClock.localDateTimeToScalar)((0, import_localClock.parseLocalDateTime)(value));
53
+ return (0, _localClock.localDateTimeToScalar)((0, _localClock.parseLocalDateTime)(value));
75
54
  } catch (_error) {
76
55
  return Number.NaN;
77
56
  }
78
57
  }
79
58
  function toDateStart(value, _timezoneName) {
80
59
  try {
81
- return (0, import_localClock.localDateToScalarStart)(value);
60
+ return (0, _localClock.localDateToScalarStart)(value);
82
61
  } catch (_error) {
83
62
  return Number.NaN;
84
63
  }
85
64
  }
86
65
  function toDateEnd(value, _timezoneName) {
87
66
  const start = toDateStart(value);
88
- return Number.isFinite(start) ? (0, import_localClock.addLocalCalendarDays)(start, 1) - 1 : Number.NaN;
67
+ return Number.isFinite(start) ? (0, _localClock.addLocalCalendarDays)(start, 1) - 1 : Number.NaN;
89
68
  }
90
69
  function toDateTimeInDate(date, time, _timezoneName) {
91
70
  return toDateTime(`${date}T${time.length === 5 ? `${time}:00` : time}`);
92
71
  }
93
72
  function addMinutesZoned(value, minutes, _timezoneName) {
94
- return (0, import_localClock.addLocalMinutes)(value, minutes);
73
+ return (0, _localClock.addLocalMinutes)(value, minutes);
95
74
  }
96
75
  function ceilToTenMinutes(value, _timezoneName) {
97
- return (0, import_localClock.ceilLocalScalarToTenMinutes)(value);
76
+ return (0, _localClock.ceilLocalScalarToTenMinutes)(value);
98
77
  }
99
78
  function nextDate(value, _timezoneName) {
100
- return (0, import_localClock.nextDate)(value);
79
+ return (0, _localClock.nextDate)(value);
101
80
  }
102
81
  function formatDateTime(value, _timezoneName, fixedOffsetMinutes) {
103
- return (0, import_localClock.formatLocalDateTime)(value, fixedOffsetMinutes ?? (0, import_localClock.captureRuntimeOffsetMinutes)());
82
+ return (0, _localClock.formatLocalDateTime)(value, fixedOffsetMinutes ?? (0, _localClock.captureRuntimeOffsetMinutes)());
104
83
  }
105
84
  function formatDate(value, _timezoneName) {
106
- return (0, import_localClock.formatLocalDate)(value);
85
+ return (0, _localClock.formatLocalDate)(value);
107
86
  }
108
87
  function formatTime(value, _timezoneName) {
109
- return (0, import_localClock.formatLocalTime)(value, false);
88
+ return (0, _localClock.formatLocalTime)(value, false);
110
89
  }
111
90
  function normalizeAbsoluteRange(range, _timezoneName) {
112
91
  const start = toDateTime(range.startAt);
113
92
  const end = toDateTime(range.endAt);
114
- if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start)
115
- return null;
116
- return { start, end };
93
+ if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
94
+ return {
95
+ start,
96
+ end
97
+ };
117
98
  }
118
99
  function getCandidateBookingRange(candidate) {
119
100
  return {
@@ -128,13 +109,7 @@ function getCandidateRangeCacheKey(candidate) {
128
109
  }
129
110
  function getProductTimeRangeIdentity(range) {
130
111
  const bookingRange = getCandidateBookingRange(range);
131
- return [
132
- String(range.productId),
133
- range.startAt,
134
- range.endAt,
135
- bookingRange.startAt,
136
- bookingRange.endAt
137
- ].join(":");
112
+ return [String(range.productId), range.startAt, range.endAt, bookingRange.startAt, bookingRange.endAt].join(':');
138
113
  }
139
114
  function toIndexedRange(range) {
140
115
  return {
@@ -145,107 +120,97 @@ function toIndexedRange(range) {
145
120
  }
146
121
  function normalizeNonNegativeNumber(value) {
147
122
  const normalized = Number(value);
148
- if (!Number.isFinite(normalized) || normalized < 0)
149
- return null;
123
+ if (!Number.isFinite(normalized) || normalized < 0) return null;
150
124
  return normalized;
151
125
  }
152
126
  function resolveCutOffPolicyIssue(policy) {
153
- if (policy.type === "invalid") {
154
- return policy.reason || "invalid_cutoff_policy";
127
+ if (policy.type === 'invalid') {
128
+ return policy.reason || 'invalid_cutoff_policy';
155
129
  }
156
- if ("futureDay" in policy && policy.futureDay !== void 0 && normalizeNonNegativeNumber(policy.futureDay) === null) {
157
- return "invalid_future_day";
130
+ if ('futureDay' in policy && policy.futureDay !== undefined && normalizeNonNegativeNumber(policy.futureDay) === null) {
131
+ return 'invalid_future_day';
158
132
  }
159
- if (policy.type === "ongoing" && policy.mode === "after_start" && normalizeNonNegativeNumber(policy.graceMinutes) === null) {
160
- return "invalid_grace_minutes";
133
+ if (policy.type === 'ongoing' && policy.mode === 'after_start' && normalizeNonNegativeNumber(policy.graceMinutes) === null) {
134
+ return 'invalid_grace_minutes';
161
135
  }
162
- if (policy.type === "advance") {
163
- if (normalizeNonNegativeNumber(policy.unit) === null)
164
- return "invalid_advance_unit";
165
- if (!["days", "hours", "minutes"].includes(policy.unitType))
166
- return "invalid_advance_unit_type";
136
+ if (policy.type === 'advance') {
137
+ if (normalizeNonNegativeNumber(policy.unit) === null) return 'invalid_advance_unit';
138
+ if (!['days', 'hours', 'minutes'].includes(policy.unitType)) return 'invalid_advance_unit_type';
167
139
  }
168
140
  return null;
169
141
  }
170
142
  function addLeadWindow(base, unit, unitType, _timezoneName) {
171
- if (unitType === "days")
172
- return (0, import_localClock.addLocalCalendarDays)(base, unit);
173
- if (unitType === "hours")
174
- return (0, import_localClock.addLocalHours)(base, unit);
175
- return (0, import_localClock.addLocalMinutes)(base, unit);
143
+ if (unitType === 'days') return (0, _localClock.addLocalCalendarDays)(base, unit);
144
+ if (unitType === 'hours') return (0, _localClock.addLocalHours)(base, unit);
145
+ return (0, _localClock.addLocalMinutes)(base, unit);
176
146
  }
177
147
  function evaluateCandidateTimeGate(params) {
178
- const { product, candidateRange, evaluationContext, timezoneName } = params;
148
+ const {
149
+ product,
150
+ candidateRange,
151
+ evaluationContext,
152
+ timezoneName
153
+ } = params;
179
154
  const now = evaluationContext.evaluatedAt;
180
155
  if (candidateRange.endMs <= evaluationContext.evaluatedAtMs) {
181
156
  return {
182
- status: "past",
183
- reasonCodes: ["past"],
184
- conflictCode: "past"
157
+ status: 'past',
158
+ reasonCodes: ['past'],
159
+ conflictCode: 'past'
185
160
  };
186
161
  }
187
162
  const policy = product.cutOffPolicy;
188
- if (!policy)
189
- return null;
163
+ if (!policy) return null;
190
164
  if (resolveCutOffPolicyIssue(policy)) {
191
165
  return {
192
- status: "unavailable",
193
- reasonCodes: ["booking_cutoff_invalid"],
194
- conflictCode: "booking_cutoff"
166
+ status: 'unavailable',
167
+ reasonCodes: ['booking_cutoff_invalid'],
168
+ conflictCode: 'booking_cutoff'
195
169
  };
196
170
  }
197
- if ("futureDay" in policy && policy.futureDay !== void 0) {
171
+ if ('futureDay' in policy && policy.futureDay !== undefined) {
198
172
  const futureDay = normalizeNonNegativeNumber(policy.futureDay);
199
- const lastAllowedDateOrdinal = (0, import_localClock.dateOrdinalFromLocalDate)(formatDate(now)) + futureDay;
200
- if ((0, import_localClock.dateOrdinalFromLocalDate)(formatDate(candidateRange.start)) > lastAllowedDateOrdinal) {
173
+ const lastAllowedDateOrdinal = (0, _localClock.dateOrdinalFromLocalDate)(formatDate(now)) + futureDay;
174
+ if ((0, _localClock.dateOrdinalFromLocalDate)(formatDate(candidateRange.start)) > lastAllowedDateOrdinal) {
201
175
  return {
202
- status: "unavailable",
203
- reasonCodes: ["booking_cutoff_future_day"],
204
- conflictCode: "booking_cutoff"
176
+ status: 'unavailable',
177
+ reasonCodes: ['booking_cutoff_future_day'],
178
+ conflictCode: 'booking_cutoff'
205
179
  };
206
180
  }
207
181
  }
208
- if (policy.type === "ongoing") {
209
- if (policy.mode === "before_end")
210
- return null;
182
+ if (policy.type === 'ongoing') {
183
+ if (policy.mode === 'before_end') return null;
211
184
  const graceMinutes = normalizeNonNegativeNumber(policy.graceMinutes);
212
185
  const graceDeadline = addMinutesZoned(candidateRange.start, graceMinutes, timezoneName);
213
- if (now < graceDeadline)
214
- return null;
186
+ if (now < graceDeadline) return null;
215
187
  return {
216
- status: "unavailable",
217
- reasonCodes: ["booking_cutoff_after_start"],
218
- conflictCode: "booking_cutoff"
188
+ status: 'unavailable',
189
+ reasonCodes: ['booking_cutoff_after_start'],
190
+ conflictCode: 'booking_cutoff'
219
191
  };
220
192
  }
221
- if (policy.type === "before_start") {
222
- if (now < candidateRange.start)
223
- return null;
193
+ if (policy.type === 'before_start') {
194
+ if (now < candidateRange.start) return null;
224
195
  return {
225
- status: "unavailable",
226
- reasonCodes: ["booking_cutoff_before_start"],
227
- conflictCode: "booking_cutoff"
196
+ status: 'unavailable',
197
+ reasonCodes: ['booking_cutoff_before_start'],
198
+ conflictCode: 'booking_cutoff'
228
199
  };
229
200
  }
230
- if (policy.type === "advance") {
231
- const leadWindow = addLeadWindow(
232
- now,
233
- normalizeNonNegativeNumber(policy.unit),
234
- policy.unitType,
235
- timezoneName
236
- );
237
- if (leadWindow <= candidateRange.start)
238
- return null;
201
+ if (policy.type === 'advance') {
202
+ const leadWindow = addLeadWindow(now, normalizeNonNegativeNumber(policy.unit), policy.unitType, timezoneName);
203
+ if (leadWindow <= candidateRange.start) return null;
239
204
  return {
240
- status: "unavailable",
241
- reasonCodes: ["booking_cutoff_advance"],
242
- conflictCode: "booking_cutoff"
205
+ status: 'unavailable',
206
+ reasonCodes: ['booking_cutoff_advance'],
207
+ conflictCode: 'booking_cutoff'
243
208
  };
244
209
  }
245
210
  return {
246
- status: "unavailable",
247
- reasonCodes: ["booking_cutoff_invalid"],
248
- conflictCode: "booking_cutoff"
211
+ status: 'unavailable',
212
+ reasonCodes: ['booking_cutoff_invalid'],
213
+ conflictCode: 'booking_cutoff'
249
214
  };
250
215
  }
251
216
  function overlaps(left, right) {
@@ -257,9 +222,11 @@ function contains(outer, inner) {
257
222
  function clipRange(base, clip) {
258
223
  const start = Math.max(base.start, clip.start);
259
224
  const end = Math.min(base.end, clip.end);
260
- if (end <= start)
261
- return null;
262
- return { start, end };
225
+ if (end <= start) return null;
226
+ return {
227
+ start,
228
+ end
229
+ };
263
230
  }
264
231
  function sortRanges(ranges) {
265
232
  return [...ranges].sort((left, right) => left.start - right.start);
@@ -267,7 +234,7 @@ function sortRanges(ranges) {
267
234
  function mergeRanges(ranges) {
268
235
  const sorted = sortRanges(ranges);
269
236
  const merged = [];
270
- sorted.forEach((range) => {
237
+ sorted.forEach(range => {
271
238
  const last = merged[merged.length - 1];
272
239
  if (!last) {
273
240
  merged.push(range);
@@ -283,11 +250,10 @@ function mergeRanges(ranges) {
283
250
  }
284
251
  function intersectRangeLists(left, right) {
285
252
  const results = [];
286
- left.forEach((leftRange) => {
287
- right.forEach((rightRange) => {
253
+ left.forEach(leftRange => {
254
+ right.forEach(rightRange => {
288
255
  const clipped = clipRange(leftRange, rightRange);
289
- if (clipped)
290
- results.push(clipped);
256
+ if (clipped) results.push(clipped);
291
257
  });
292
258
  });
293
259
  return mergeRanges(results);
@@ -304,25 +270,28 @@ function enumerateDates(startDate, endDate) {
304
270
  function normalizeCoverage(input) {
305
271
  const startDate = input.coverage.startDate;
306
272
  const endDate = input.coverage.endDate;
307
- const isCanonicalDate = (value) => {
273
+ const isCanonicalDate = value => {
308
274
  try {
309
- return (0, import_localClock.formatLocalDate)((0, import_localClock.parseLocalDate)(value)) === value;
275
+ return (0, _localClock.formatLocalDate)((0, _localClock.parseLocalDate)(value)) === value;
310
276
  } catch (_error) {
311
277
  return false;
312
278
  }
313
279
  };
314
280
  if (!startDate || !endDate || !isCanonicalDate(startDate) || !isCanonicalDate(endDate)) {
315
- throw availabilityError("INVALID_DATE_RANGE", "coverage.startDate / coverage.endDate 非法", {
281
+ throw availabilityError('INVALID_DATE_RANGE', 'coverage.startDate / coverage.endDate 非法', {
316
282
  coverage: input.coverage
317
283
  });
318
284
  }
319
285
  if (endDate < startDate) {
320
- throw availabilityError("INVALID_DATE_RANGE", "coverage.endDate 不能早于 startDate", {
286
+ throw availabilityError('INVALID_DATE_RANGE', 'coverage.endDate 不能早于 startDate', {
321
287
  coverage: input.coverage
322
288
  });
323
289
  }
324
290
  return {
325
- dateRange: { startDate, endDate },
291
+ dateRange: {
292
+ startDate,
293
+ endDate
294
+ },
326
295
  absolute: {
327
296
  start: toDateStart(startDate, input.timezone),
328
297
  end: toDateEnd(nextDate(endDate, input.timezone), input.timezone)
@@ -330,17 +299,13 @@ function normalizeCoverage(input) {
330
299
  };
331
300
  }
332
301
  function normalizeResourceWindow(resource, coverage, timezoneName) {
333
- return mergeRanges(
334
- (resource.windows || []).map((window) => normalizeAbsoluteRange(window, timezoneName)).filter((window) => Boolean(window)).map((window) => clipRange(window, coverage)).filter((window) => Boolean(window))
335
- );
302
+ return mergeRanges((resource.windows || []).map(window => normalizeAbsoluteRange(window, timezoneName)).filter(window => Boolean(window)).map(window => clipRange(window, coverage)).filter(window => Boolean(window)));
336
303
  }
337
304
  function normalizeOccupancy(occupancy, coverage, timezoneName, fixedOffsetMinutes) {
338
305
  const normalized = normalizeAbsoluteRange(occupancy, timezoneName);
339
- if (!normalized)
340
- return null;
306
+ if (!normalized) return null;
341
307
  const clipped = clipRange(normalized, coverage);
342
- if (!clipped)
343
- return null;
308
+ if (!clipped) return null;
344
309
  return {
345
310
  ...occupancy,
346
311
  startAt: formatDateTime(clipped.start, timezoneName, fixedOffsetMinutes),
@@ -349,7 +314,7 @@ function normalizeOccupancy(occupancy, coverage, timezoneName, fixedOffsetMinute
349
314
  };
350
315
  }
351
316
  function normalizeRequirementGroup(product, requirementGroup) {
352
- const resourceType = "single";
317
+ const resourceType = 'single';
353
318
  const required = requirementGroup.required !== false;
354
319
  const min = required ? 1 : 0;
355
320
  return {
@@ -360,107 +325,97 @@ function normalizeRequirementGroup(product, requirementGroup) {
360
325
  min,
361
326
  max: 1,
362
327
  resourceIds: uniqById(requirementGroup.resourceIds || []),
363
- capacityRequired: Math.max(
364
- 1,
365
- Number(requirementGroup.capacityRequired || product.capacityRequired || 1) || 1
366
- )
328
+ capacityRequired: Math.max(1, Number(requirementGroup.capacityRequired || product.capacityRequired || 1) || 1)
367
329
  };
368
330
  }
369
331
  function normalizeProducts(products) {
370
- return products.map((product) => ({
332
+ return products.map(product => ({
371
333
  ...product,
372
334
  quantity: Math.max(1, Number(product.quantity || 1) || 1),
373
335
  capacityRequired: Math.max(1, Number(product.capacityRequired || 1) || 1),
374
336
  durationMinutes: Math.max(1, Number(product.durationMinutes || 30) || 30),
375
- cutOffPolicy: product.cutOffPolicy ? { ...product.cutOffPolicy } : void 0,
376
- requirementGroups: (product.requirementGroups || []).map((group) => normalizeRequirementGroup(product, group)),
377
- sessionRules: (product.sessionRules || []).map((rule) => ({
337
+ cutOffPolicy: product.cutOffPolicy ? {
338
+ ...product.cutOffPolicy
339
+ } : undefined,
340
+ requirementGroups: (product.requirementGroups || []).map(group => normalizeRequirementGroup(product, group)),
341
+ sessionRules: (product.sessionRules || []).map(rule => ({
378
342
  ...rule,
379
- weekdays: rule.weekdays ? [...rule.weekdays] : void 0,
380
- dates: rule.dates ? [...rule.dates] : void 0,
381
- includeDates: rule.includeDates ? [...rule.includeDates] : void 0,
382
- excludeDates: rule.excludeDates ? [...rule.excludeDates] : void 0,
383
- scheduleRef: rule.scheduleRef ? { ...rule.scheduleRef } : void 0
343
+ weekdays: rule.weekdays ? [...rule.weekdays] : undefined,
344
+ dates: rule.dates ? [...rule.dates] : undefined,
345
+ includeDates: rule.includeDates ? [...rule.includeDates] : undefined,
346
+ excludeDates: rule.excludeDates ? [...rule.excludeDates] : undefined,
347
+ scheduleRef: rule.scheduleRef ? {
348
+ ...rule.scheduleRef
349
+ } : undefined
384
350
  })),
385
- sessionRanges: (product.sessionRanges || []).map((range) => {
386
- var _a;
387
- return {
388
- ...range,
389
- scheduleRefs: (_a = range.scheduleRefs) == null ? void 0 : _a.map((scheduleRef) => ({ ...scheduleRef }))
390
- };
391
- })
351
+ sessionRanges: (product.sessionRanges || []).map(range => ({
352
+ ...range,
353
+ scheduleRefs: range.scheduleRefs?.map(scheduleRef => ({
354
+ ...scheduleRef
355
+ }))
356
+ }))
392
357
  }));
393
358
  }
394
359
  function normalizeResources(resources) {
395
- return resources.map((resource) => ({
360
+ return resources.map(resource => ({
396
361
  ...resource,
397
- resourceType: "single",
362
+ resourceType: 'single',
398
363
  capacity: Math.max(1, Number(resource.capacity || 1) || 1),
399
- windows: (resource.windows || []).map((window) => ({ ...window }))
364
+ windows: (resource.windows || []).map(window => ({
365
+ ...window
366
+ }))
400
367
  }));
401
368
  }
402
369
  function normalizeOperatingHours(params) {
403
- var _a, _b, _c;
404
- const fallbackWindows = enumerateDates(params.dateRange.startDate, params.dateRange.endDate).map((date) => {
370
+ const fallbackWindows = enumerateDates(params.dateRange.startDate, params.dateRange.endDate).map(date => {
405
371
  const start = toDateTimeInDate(date, params.businessHours.startTime, params.timezoneName);
406
372
  let end = toDateTimeInDate(date, params.businessHours.endTime, params.timezoneName);
407
373
  if (end <= start) {
408
374
  end = toDateTimeInDate(nextDate(date), params.businessHours.endTime, params.timezoneName);
409
375
  }
410
- return { start, end };
376
+ return {
377
+ start,
378
+ end
379
+ };
411
380
  });
412
- const sourceWindows = params.input ? params.input.windows : fallbackWindows.map((range) => ({
381
+ const sourceWindows = params.input ? params.input.windows : fallbackWindows.map(range => ({
413
382
  startAt: formatDateTime(range.start, params.timezoneName, params.fixedOffsetMinutes),
414
383
  endAt: formatDateTime(range.end, params.timezoneName, params.fixedOffsetMinutes)
415
384
  }));
416
- const windows = mergeRanges(
417
- sourceWindows.map((window) => normalizeAbsoluteRange(window, params.timezoneName)).filter((window) => Boolean(window)).map((window) => clipRange(window, params.coverage)).filter((window) => Boolean(window))
418
- ).map((range) => ({
385
+ const windows = mergeRanges(sourceWindows.map(window => normalizeAbsoluteRange(window, params.timezoneName)).filter(window => Boolean(window)).map(window => clipRange(window, params.coverage)).filter(window => Boolean(window))).map(range => ({
419
386
  startAt: formatDateTime(range.start, params.timezoneName, params.fixedOffsetMinutes),
420
387
  endAt: formatDateTime(range.end, params.timezoneName, params.fixedOffsetMinutes)
421
388
  }));
422
389
  return {
423
- source: ((_a = params.input) == null ? void 0 : _a.source) || params.fallbackSource,
424
- scheduleIds: uniqById(((_b = params.input) == null ? void 0 : _b.scheduleIds) || []),
425
- missingScheduleIds: uniqById(((_c = params.input) == null ? void 0 : _c.missingScheduleIds) || []),
390
+ source: params.input?.source || params.fallbackSource,
391
+ scheduleIds: uniqById(params.input?.scheduleIds || []),
392
+ missingScheduleIds: uniqById(params.input?.missingScheduleIds || []),
426
393
  windows
427
394
  };
428
395
  }
429
396
  function matchesRequirementGroup(requirementGroup, resource, product, partySize = 1) {
430
- var _a;
431
- if (requirementGroup.formId !== void 0 && resource.formId !== void 0 && !sameId(requirementGroup.formId, resource.formId)) {
397
+ if (requirementGroup.formId !== undefined && resource.formId !== undefined && !sameId(requirementGroup.formId, resource.formId)) {
432
398
  return false;
433
399
  }
434
- if (((_a = requirementGroup.resourceIds) == null ? void 0 : _a.length) && !requirementGroup.resourceIds.some((resourceId) => sameId(resourceId, resource.id))) {
400
+ if (requirementGroup.resourceIds?.length && !requirementGroup.resourceIds.some(resourceId => sameId(resourceId, resource.id))) {
435
401
  return false;
436
402
  }
437
403
  return resource.capacity >= resolveRequiredCapacity(requirementGroup, product, partySize);
438
404
  }
439
405
  function resolveRequiredCapacity(requirementGroup, product, partySize) {
440
- return Math.max(
441
- 1,
442
- Number(
443
- partySize ?? requirementGroup.capacityRequired ?? product.capacityRequired ?? 1
444
- ) || 1
445
- );
406
+ return Math.max(1, Number(partySize ?? requirementGroup.capacityRequired ?? product.capacityRequired ?? 1) || 1);
446
407
  }
447
408
  function buildCellKey(params) {
448
- return [
449
- String(params.productId),
450
- params.groupId,
451
- String(params.resourceId),
452
- params.start,
453
- params.end
454
- ].join(":");
409
+ return [String(params.productId), params.groupId, String(params.resourceId), params.start, params.end].join(':');
455
410
  }
456
411
  function dedupeOccupancyRefKey(occupancy) {
457
- if (occupancy.scheduleEventId !== void 0 && occupancy.scheduleEventId !== null) {
412
+ if (occupancy.scheduleEventId !== undefined && occupancy.scheduleEventId !== null) {
458
413
  return `scheduleEvent:${String(occupancy.resourceId)}:${String(occupancy.scheduleEventId)}`;
459
414
  }
460
- if (occupancy.bookingUid !== void 0 && occupancy.bookingUid !== null) {
415
+ if (occupancy.bookingUid !== undefined && occupancy.bookingUid !== null) {
461
416
  return `bookingUid:${String(occupancy.resourceId)}:${String(occupancy.bookingUid)}`;
462
417
  }
463
- if (occupancy.productUid !== void 0 && occupancy.productUid !== null) {
418
+ if (occupancy.productUid !== undefined && occupancy.productUid !== null) {
464
419
  return `productUid:${String(occupancy.resourceId)}:${String(occupancy.productUid)}`;
465
420
  }
466
421
  return `range:${String(occupancy.resourceId)}:${occupancy.startAt}:${occupancy.endAt}:${String(occupancy.pax || 1)}`;
@@ -472,22 +427,27 @@ function buildCells(projection) {
472
427
  };
473
428
  const cells = [];
474
429
  const timezoneName = projection.meta.timezone;
475
- const fixedOffsetMinutes = (0, import_localClock.extractFixedOffsetMinutes)(projection.meta.now) ?? (0, import_localClock.captureRuntimeOffsetMinutes)();
430
+ const fixedOffsetMinutes = (0, _localClock.extractFixedOffsetMinutes)(projection.meta.now) ?? (0, _localClock.captureRuntimeOffsetMinutes)();
476
431
  const projectionNow = toDateTime(projection.meta.now);
477
- projection.products.forEach((product) => {
478
- (product.requirementGroups || []).forEach((requirementGroup) => {
479
- projection.resources.filter((resource) => matchesRequirementGroup(requirementGroup, resource, product)).forEach((resource) => {
432
+ projection.products.forEach(product => {
433
+ (product.requirementGroups || []).forEach(requirementGroup => {
434
+ projection.resources.filter(resource => matchesRequirementGroup(requirementGroup, resource, product)).forEach(resource => {
480
435
  const windows = normalizeResourceWindow(resource, coverage, timezoneName);
481
- const occupancies = projection.resourceOccupancies.filter((occupancy) => sameId(occupancy.resourceId, resource.id)).map((occupancy) => ({
436
+ const occupancies = projection.resourceOccupancies.filter(occupancy => sameId(occupancy.resourceId, resource.id)).map(occupancy => ({
482
437
  occupancy,
483
438
  range: normalizeAbsoluteRange(occupancy, timezoneName)
484
- })).filter((item) => Boolean(item.range)).map((item) => ({ ...item, range: clipRange(item.range, coverage) })).filter((item) => Boolean(item.range));
485
- const boundaries = /* @__PURE__ */ new Set([coverage.start, coverage.end]);
486
- windows.forEach((window) => {
439
+ })).filter(item => Boolean(item.range)).map(item => ({
440
+ ...item,
441
+ range: clipRange(item.range, coverage)
442
+ })).filter(item => Boolean(item.range));
443
+ const boundaries = new Set([coverage.start, coverage.end]);
444
+ windows.forEach(window => {
487
445
  boundaries.add(window.start);
488
446
  boundaries.add(window.end);
489
447
  });
490
- occupancies.forEach(({ range }) => {
448
+ occupancies.forEach(({
449
+ range
450
+ }) => {
491
451
  boundaries.add(range.start);
492
452
  boundaries.add(range.end);
493
453
  });
@@ -495,39 +455,38 @@ function buildCells(projection) {
495
455
  for (let index = 0; index < sortedBoundaries.length - 1; index += 1) {
496
456
  const start = sortedBoundaries[index];
497
457
  const end = sortedBoundaries[index + 1];
498
- if (end <= start)
499
- continue;
500
- const segment = { start, end };
501
- const isInsideWorkingWindow = windows.some((window) => contains(window, segment));
502
- const segmentOccupancies = isInsideWorkingWindow ? occupancies.filter(({ range }) => overlaps(range, segment)).map(({ occupancy }) => occupancy) : [];
503
- const usedCapacity = segmentOccupancies.reduce(
504
- (sum, occupancy) => sum + Math.max(1, Number(occupancy.pax || 1) || 1),
505
- 0
506
- );
458
+ if (end <= start) continue;
459
+ const segment = {
460
+ start,
461
+ end
462
+ };
463
+ const isInsideWorkingWindow = windows.some(window => contains(window, segment));
464
+ const segmentOccupancies = isInsideWorkingWindow ? occupancies.filter(({
465
+ range
466
+ }) => overlaps(range, segment)).map(({
467
+ occupancy
468
+ }) => occupancy) : [];
469
+ const usedCapacity = segmentOccupancies.reduce((sum, occupancy) => sum + Math.max(1, Number(occupancy.pax || 1) || 1), 0);
507
470
  const remainingCapacity = Math.max(0, resource.capacity - usedCapacity);
508
471
  const requiredCapacity = resolveRequiredCapacity(requirementGroup, product);
509
- const reasonCodes = Array.from(new Set(
510
- segmentOccupancies.flatMap((occupancy) => {
511
- const source = occupancy.source || "remote";
512
- if (source === "selection")
513
- return ["selection_occupied"];
514
- if (source === "cart")
515
- return ["cart_occupied"];
516
- return ["resource_occupied"];
517
- })
518
- ));
519
- let status = "available";
472
+ const reasonCodes = Array.from(new Set(segmentOccupancies.flatMap(occupancy => {
473
+ const source = occupancy.source || 'remote';
474
+ if (source === 'selection') return ['selection_occupied'];
475
+ if (source === 'cart') return ['cart_occupied'];
476
+ return ['resource_occupied'];
477
+ })));
478
+ let status = 'available';
520
479
  if (end <= projectionNow) {
521
- status = "past";
522
- reasonCodes.push("past");
480
+ status = 'past';
481
+ reasonCodes.push('past');
523
482
  } else if (!isInsideWorkingWindow) {
524
- status = "unavailable";
525
- reasonCodes.push("outside_resource_window");
483
+ status = 'unavailable';
484
+ reasonCodes.push('outside_resource_window');
526
485
  } else if (remainingCapacity < requiredCapacity) {
527
- status = "full";
528
- reasonCodes.push("capacity_full");
486
+ status = 'full';
487
+ reasonCodes.push('capacity_full');
529
488
  } else if (remainingCapacity < resource.capacity) {
530
- status = "limited";
489
+ status = 'limited';
531
490
  }
532
491
  cells.push({
533
492
  key: buildCellKey({
@@ -554,7 +513,7 @@ function buildCells(projection) {
554
513
  remainingCapacity,
555
514
  maxCapacity: resource.capacity,
556
515
  reasonCodes: Array.from(new Set(reasonCodes)),
557
- occupancyRefs: segmentOccupancies.map((occupancy) => ({
516
+ occupancyRefs: segmentOccupancies.map(occupancy => ({
558
517
  id: occupancy.id,
559
518
  resourceId: occupancy.resourceId,
560
519
  source: occupancy.source,
@@ -572,18 +531,8 @@ function buildCells(projection) {
572
531
  }
573
532
  function compressCells(cells) {
574
533
  const sorted = [...cells].sort((left, right) => {
575
- const keyLeft = [
576
- String(left.productId),
577
- String(left.requirementGroupId),
578
- String(left.resourceId),
579
- left.startAt
580
- ].join(":");
581
- const keyRight = [
582
- String(right.productId),
583
- String(right.requirementGroupId),
584
- String(right.resourceId),
585
- right.startAt
586
- ].join(":");
534
+ const keyLeft = [String(left.productId), String(left.requirementGroupId), String(left.resourceId), left.startAt].join(':');
535
+ const keyRight = [String(right.productId), String(right.requirementGroupId), String(right.resourceId), right.startAt].join(':');
587
536
  return keyLeft.localeCompare(keyRight);
588
537
  });
589
538
  return sorted.reduce((result, cell) => {
@@ -593,7 +542,9 @@ function compressCells(cells) {
593
542
  last.endTime = cell.endTime;
594
543
  return result;
595
544
  }
596
- result.push({ ...cell });
545
+ result.push({
546
+ ...cell
547
+ });
597
548
  return result;
598
549
  }, []);
599
550
  }
@@ -606,68 +557,45 @@ function makeCoverageAbsolute(dateRange, timezoneName, fixedOffsetMinutes) {
606
557
  }
607
558
  function normalizeProjectionInstant(value, fallbackScalar, timezoneName, fixedOffsetMinutes) {
608
559
  const scalar = value ? toDateTime(value) : fallbackScalar;
609
- return formatDateTime(
610
- Number.isFinite(scalar) ? scalar : fallbackScalar,
611
- timezoneName,
612
- fixedOffsetMinutes
613
- );
560
+ return formatDateTime(Number.isFinite(scalar) ? scalar : fallbackScalar, timezoneName, fixedOffsetMinutes);
614
561
  }
615
562
  function cloneSnapshotMetadataValue(value) {
616
563
  if (Array.isArray(value)) {
617
- return value.map((item) => cloneSnapshotMetadataValue(item));
564
+ return value.map(item => cloneSnapshotMetadataValue(item));
618
565
  }
619
- if (!value || typeof value !== "object")
620
- return value;
566
+ if (!value || typeof value !== 'object') return value;
621
567
  return Object.keys(value).reduce((result, key) => {
622
568
  result[key] = cloneSnapshotMetadataValue(value[key]);
623
569
  return result;
624
570
  }, {});
625
571
  }
626
572
  function deepFreeze(value) {
627
- if (!value || typeof value !== "object" || Object.isFrozen(value))
628
- return value;
573
+ if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
629
574
  Object.freeze(value);
630
- Object.getOwnPropertyNames(value).forEach((key) => {
631
- if (key === "raw")
632
- return;
575
+ Object.getOwnPropertyNames(value).forEach(key => {
576
+ // `raw` is an explicit escape hatch to the backend/catalog record. It is not owned by the
577
+ // projection and must not freeze mutable ProductList / Order data shared elsewhere.
578
+ if (key === 'raw') return;
633
579
  const child = value[key];
634
- if (child && (typeof child === "object" || typeof child === "function")) {
580
+ if (child && (typeof child === 'object' || typeof child === 'function')) {
635
581
  deepFreeze(child);
636
582
  }
637
583
  });
638
584
  return value;
639
585
  }
640
586
  function createAvailabilityProjection(input) {
641
- var _a, _b;
642
587
  const coverage = normalizeCoverage(input);
643
- const fixedOffsetMinutes = (0, import_localClock.captureRuntimeOffsetMinutes)();
644
- const runtimeNow = (0, import_localClock.localDateToScalar)(/* @__PURE__ */ new Date());
588
+ const fixedOffsetMinutes = (0, _localClock.captureRuntimeOffsetMinutes)();
589
+ const runtimeNow = (0, _localClock.localDateToScalar)(new Date());
645
590
  const products = normalizeProducts(input.products || []);
646
591
  const resources = normalizeResources(input.resources || []);
647
- const resourceOccupancies = uniqByOccupancy(
648
- (input.resourceOccupancies || []).map((occupancy) => normalizeOccupancy(
649
- occupancy,
650
- coverage.absolute,
651
- input.timezone,
652
- fixedOffsetMinutes
653
- )).filter((occupancy) => Boolean(occupancy))
654
- );
655
- const preparedAt = normalizeProjectionInstant(
656
- input.preparedAt,
657
- runtimeNow,
658
- input.timezone,
659
- fixedOffsetMinutes
660
- );
592
+ const resourceOccupancies = uniqByOccupancy((input.resourceOccupancies || []).map(occupancy => normalizeOccupancy(occupancy, coverage.absolute, input.timezone, fixedOffsetMinutes)).filter(occupancy => Boolean(occupancy)));
593
+ const preparedAt = normalizeProjectionInstant(input.preparedAt, runtimeNow, input.timezone, fixedOffsetMinutes);
661
594
  const now = normalizeProjectionInstant(input.now, runtimeNow, input.timezone, fixedOffsetMinutes);
662
- const durationCandidateAnchorAt = normalizeProjectionInstant(
663
- input.durationCandidateAnchorAt || now,
664
- runtimeNow,
665
- input.timezone,
666
- fixedOffsetMinutes
667
- );
595
+ const durationCandidateAnchorAt = normalizeProjectionInstant(input.durationCandidateAnchorAt || now, runtimeNow, input.timezone, fixedOffsetMinutes);
668
596
  const businessHours = {
669
- startTime: ((_a = input.businessHours) == null ? void 0 : _a.startTime) || DEFAULT_BUSINESS_HOURS.startTime,
670
- endTime: ((_b = input.businessHours) == null ? void 0 : _b.endTime) || DEFAULT_BUSINESS_HOURS.endTime
597
+ startTime: input.businessHours?.startTime || DEFAULT_BUSINESS_HOURS.startTime,
598
+ endTime: input.businessHours?.endTime || DEFAULT_BUSINESS_HOURS.endTime
671
599
  };
672
600
  const operatingHours = normalizeOperatingHours({
673
601
  input: input.operatingHours,
@@ -676,14 +604,14 @@ function createAvailabilityProjection(input) {
676
604
  dateRange: coverage.dateRange,
677
605
  timezoneName: input.timezone,
678
606
  fixedOffsetMinutes,
679
- fallbackSource: input.businessHours ? "prepare_business_hours" : "default_business_hours"
607
+ fallbackSource: input.businessHours ? 'prepare_business_hours' : 'default_business_hours'
680
608
  });
681
609
  const meta = {
682
610
  snapshotId: input.snapshotId || `availability-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
683
611
  preparedAt,
684
612
  timezone: input.timezone,
685
613
  orderRevision: input.orderRevision,
686
- mode: input.mode || "appointment",
614
+ mode: input.mode || 'appointment',
687
615
  coverage: {
688
616
  ...coverage.dateRange,
689
617
  ...makeCoverageAbsolute(coverage.dateRange, input.timezone, fixedOffsetMinutes)
@@ -697,8 +625,8 @@ function createAvailabilityProjection(input) {
697
625
  },
698
626
  now,
699
627
  durationCandidateAnchorAt,
700
- anchors: input.anchors ? cloneSnapshotMetadataValue(input.anchors) : void 0,
701
- prepareParams: input.prepareParams ? cloneSnapshotMetadataValue(input.prepareParams) : void 0
628
+ anchors: input.anchors ? cloneSnapshotMetadataValue(input.anchors) : undefined,
629
+ prepareParams: input.prepareParams ? cloneSnapshotMetadataValue(input.prepareParams) : undefined
702
630
  };
703
631
  const projectionBase = {
704
632
  meta,
@@ -712,79 +640,77 @@ function createAvailabilityProjection(input) {
712
640
  });
713
641
  }
714
642
  function uniqByOccupancy(occupancies) {
715
- const map = /* @__PURE__ */ new Map();
716
- occupancies.forEach((occupancy) => {
643
+ const map = new Map();
644
+ occupancies.forEach(occupancy => {
717
645
  map.set(dedupeOccupancyRefKey(occupancy), occupancy);
718
646
  });
719
647
  return Array.from(map.values());
720
648
  }
721
649
  function resolveProjectionDateRange(projection, requestDateRange) {
722
- const startDate = (requestDateRange == null ? void 0 : requestDateRange.startDate) || projection.meta.coverage.startDate;
723
- const endDate = (requestDateRange == null ? void 0 : requestDateRange.endDate) || projection.meta.coverage.endDate;
724
- const isCanonicalDate = (value) => {
650
+ const startDate = requestDateRange?.startDate || projection.meta.coverage.startDate;
651
+ const endDate = requestDateRange?.endDate || projection.meta.coverage.endDate;
652
+ const isCanonicalDate = value => {
725
653
  try {
726
- return (0, import_localClock.formatLocalDate)((0, import_localClock.parseLocalDate)(value)) === value;
654
+ return (0, _localClock.formatLocalDate)((0, _localClock.parseLocalDate)(value)) === value;
727
655
  } catch (_error) {
728
656
  return false;
729
657
  }
730
658
  };
731
659
  if (!isCanonicalDate(startDate) || !isCanonicalDate(endDate) || endDate < startDate) {
732
- throw availabilityError("INVALID_DATE_RANGE", "查询日期范围必须是有效的 YYYY-MM-DD,且结束日期不能早于开始日期", {
660
+ throw availabilityError('INVALID_DATE_RANGE', '查询日期范围必须是有效的 YYYY-MM-DD,且结束日期不能早于开始日期', {
733
661
  requested: requestDateRange
734
662
  });
735
663
  }
736
664
  if (startDate < projection.meta.coverage.startDate || endDate > projection.meta.coverage.endDate) {
737
- throw availabilityError("OUT_OF_PROJECTION_COVERAGE", "查询范围超出 projection.coverage", {
665
+ throw availabilityError('OUT_OF_PROJECTION_COVERAGE', '查询范围超出 projection.coverage', {
738
666
  requested: requestDateRange,
739
667
  coverage: projection.meta.coverage
740
668
  });
741
669
  }
742
- return { startDate, endDate };
670
+ return {
671
+ startDate,
672
+ endDate
673
+ };
743
674
  }
744
675
  function buildOperatingRanges(projection, dateRange) {
745
676
  const coverage = {
746
677
  start: toDateStart(dateRange.startDate, projection.meta.timezone),
747
678
  end: toDateEnd(nextDate(dateRange.endDate), projection.meta.timezone)
748
679
  };
749
- return mergeRanges(
750
- projection.meta.defaults.operatingHours.windows.map((window) => normalizeAbsoluteRange(window, projection.meta.timezone)).filter((window) => Boolean(window)).map((window) => clipRange(window, coverage)).filter((window) => Boolean(window))
751
- );
680
+ return mergeRanges(projection.meta.defaults.operatingHours.windows.map(window => normalizeAbsoluteRange(window, projection.meta.timezone)).filter(window => Boolean(window)).map(window => clipRange(window, coverage)).filter(window => Boolean(window)));
752
681
  }
753
682
  function buildResourceWindowIndex(projection) {
754
683
  const coverage = {
755
684
  start: toDateTime(projection.meta.coverage.startAt, projection.meta.timezone),
756
685
  end: toDateTime(projection.meta.coverage.endAt, projection.meta.timezone)
757
686
  };
758
- return new Map(
759
- projection.resources.map((resource) => [
760
- String(resource.id),
761
- normalizeResourceWindow(resource, coverage, projection.meta.timezone)
762
- ])
763
- );
687
+ return new Map(projection.resources.map(resource => [String(resource.id), normalizeResourceWindow(resource, coverage, projection.meta.timezone)]));
764
688
  }
765
689
  function buildGroupWorkingRanges(params) {
766
- const { projection, requirementGroup, dateRange, resourceIds, partySize } = params;
690
+ const {
691
+ projection,
692
+ requirementGroup,
693
+ dateRange,
694
+ resourceIds,
695
+ partySize
696
+ } = params;
767
697
  const guardEndDate = nextDate(dateRange.endDate, projection.meta.timezone);
768
698
  const coverage = {
769
699
  start: toDateStart(dateRange.startDate, projection.meta.timezone),
770
700
  end: toDateEnd(guardEndDate, projection.meta.timezone)
771
701
  };
772
- const filteredResourceIdSet = (resourceIds == null ? void 0 : resourceIds.length) ? new Set(resourceIds.map(String)) : null;
773
- const resourceWindows = projection.resources.filter((resource) => matchesRequirementGroup(requirementGroup, resource, params.product, partySize)).filter((resource) => !filteredResourceIdSet || filteredResourceIdSet.has(String(resource.id))).map((resource) => {
774
- var _a;
775
- const windows = ((_a = params.resourceWindowIndex) == null ? void 0 : _a.get(String(resource.id))) || normalizeResourceWindow(resource, coverage, projection.meta.timezone);
776
- return windows.map((window) => clipRange(window, coverage)).filter((window) => Boolean(window));
702
+ const filteredResourceIdSet = resourceIds?.length ? new Set(resourceIds.map(String)) : null;
703
+ const resourceWindows = projection.resources.filter(resource => matchesRequirementGroup(requirementGroup, resource, params.product, partySize)).filter(resource => !filteredResourceIdSet || filteredResourceIdSet.has(String(resource.id))).map(resource => {
704
+ const windows = params.resourceWindowIndex?.get(String(resource.id)) || normalizeResourceWindow(resource, coverage, projection.meta.timezone);
705
+ return windows.map(window => clipRange(window, coverage)).filter(window => Boolean(window));
777
706
  });
778
- const requiredConcurrentResources = Math.max(
779
- requirementGroup.required === false ? 0 : 1,
780
- Number(requirementGroup.min ?? (requirementGroup.required === false ? 0 : 1)) || 0
781
- );
707
+ const requiredConcurrentResources = Math.max(requirementGroup.required === false ? 0 : 1, Number(requirementGroup.min ?? (requirementGroup.required === false ? 0 : 1)) || 0);
782
708
  if (requiredConcurrentResources <= 1) {
783
- return mergeRanges(resourceWindows.flatMap((windows) => windows));
709
+ return mergeRanges(resourceWindows.flatMap(windows => windows));
784
710
  }
785
- const boundaries = /* @__PURE__ */ new Set();
786
- resourceWindows.forEach((windows) => {
787
- windows.forEach((window) => {
711
+ const boundaries = new Set();
712
+ resourceWindows.forEach(windows => {
713
+ windows.forEach(window => {
788
714
  boundaries.add(window.start);
789
715
  boundaries.add(window.end);
790
716
  });
@@ -794,10 +720,12 @@ function buildGroupWorkingRanges(params) {
794
720
  for (let index = 0; index < sortedBoundaries.length - 1; index += 1) {
795
721
  const start = sortedBoundaries[index];
796
722
  const end = sortedBoundaries[index + 1];
797
- if (end <= start)
798
- continue;
799
- const segment = { start, end };
800
- const activeResources = resourceWindows.reduce((count, windows) => windows.some((window) => contains(window, segment)) ? count + 1 : count, 0);
723
+ if (end <= start) continue;
724
+ const segment = {
725
+ start,
726
+ end
727
+ };
728
+ const activeResources = resourceWindows.reduce((count, windows) => windows.some(window => contains(window, segment)) ? count + 1 : count, 0);
801
729
  if (activeResources >= requiredConcurrentResources) {
802
730
  satisfiedRanges.push(segment);
803
731
  }
@@ -806,21 +734,19 @@ function buildGroupWorkingRanges(params) {
806
734
  }
807
735
  function materializeScheduleRules(rules, dateRange, timezoneName, fixedOffsetMinutes) {
808
736
  const dates = enumerateDates(dateRange.startDate, dateRange.endDate);
809
- const seen = /* @__PURE__ */ new Map();
810
- rules.forEach((rule) => {
811
- dates.forEach((date) => {
812
- if (!matchesScheduleRuleDate(rule, date, timezoneName))
813
- return;
737
+ const seen = new Map();
738
+ rules.forEach(rule => {
739
+ dates.forEach(date => {
740
+ if (!matchesScheduleRuleDate(rule, date, timezoneName)) return;
814
741
  const start = toDateTimeInDate(date, rule.startTime, timezoneName);
815
742
  let end = toDateTimeInDate(date, rule.endTime, timezoneName);
816
- if (end <= start)
817
- end = toDateTimeInDate(nextDate(date, timezoneName), rule.endTime, timezoneName);
743
+ if (end <= start) end = toDateTimeInDate(nextDate(date, timezoneName), rule.endTime, timezoneName);
818
744
  const key = `${date}:${start}:${end}`;
819
745
  if (!seen.has(key)) {
820
746
  seen.set(key, {
821
747
  key,
822
- productId: "",
823
- source: "session_schedule",
748
+ productId: '',
749
+ source: 'session_schedule',
824
750
  startAt: formatDateTime(start, timezoneName, fixedOffsetMinutes),
825
751
  endAt: formatDateTime(end, timezoneName, fixedOffsetMinutes),
826
752
  bookingStartAt: formatDateTime(start, timezoneName, fixedOffsetMinutes),
@@ -834,85 +760,66 @@ function materializeScheduleRules(rules, dateRange, timezoneName, fixedOffsetMin
834
760
  }
835
761
  const current = seen.get(key);
836
762
  const scheduleRef = {
837
- ...rule.scheduleRef || {},
763
+ ...(rule.scheduleRef || {}),
838
764
  ruleId: rule.id
839
765
  };
840
766
  current.scheduleRefs = mergeScheduleRefs(current.scheduleRefs, [scheduleRef]);
841
- if (!current.primaryScheduleRef)
842
- current.primaryScheduleRef = scheduleRef;
767
+ if (!current.primaryScheduleRef) current.primaryScheduleRef = scheduleRef;
843
768
  });
844
769
  });
845
770
  return Array.from(seen.values());
846
771
  }
847
772
  function matchesScheduleRuleDate(rule, date, _timezoneName) {
848
- var _a, _b, _c, _d;
849
- if ((_a = rule.includeDates) == null ? void 0 : _a.includes(date))
850
- return true;
851
- if ((_b = rule.excludeDates) == null ? void 0 : _b.includes(date))
852
- return false;
853
- if (rule.startDate && date < rule.startDate)
854
- return false;
855
- if (rule.endDate && date > rule.endDate)
856
- return false;
857
- if (rule.mode === "designation") {
858
- if ((_c = rule.dates) == null ? void 0 : _c.includes(date))
859
- return true;
773
+ if (rule.includeDates?.includes(date)) return true;
774
+ if (rule.excludeDates?.includes(date)) return false;
775
+ if (rule.startDate && date < rule.startDate) return false;
776
+ if (rule.endDate && date > rule.endDate) return false;
777
+ if (rule.mode === 'designation') {
778
+ if (rule.dates?.includes(date)) return true;
860
779
  return false;
861
780
  }
862
- if (rule.mode === "weekly") {
863
- if (!((_d = rule.weekdays) == null ? void 0 : _d.length))
864
- return true;
781
+ if (rule.mode === 'weekly') {
782
+ if (!rule.weekdays?.length) return true;
865
783
  const freq = Math.max(1, Number(rule.frequency || 1) || 1);
866
- if (!rule.weekdays.includes((0, import_localClock.weekdayFromLocalDate)(date)))
867
- return false;
868
- if (!rule.startDate)
869
- return true;
870
- const targetWeekStart = (0, import_localClock.dateOrdinalFromLocalDate)(date) - (0, import_localClock.weekdayFromLocalDate)(date);
871
- const ruleWeekStart = (0, import_localClock.dateOrdinalFromLocalDate)(rule.startDate) - (0, import_localClock.weekdayFromLocalDate)(rule.startDate);
784
+ if (!rule.weekdays.includes((0, _localClock.weekdayFromLocalDate)(date))) return false;
785
+ if (!rule.startDate) return true;
786
+ const targetWeekStart = (0, _localClock.dateOrdinalFromLocalDate)(date) - (0, _localClock.weekdayFromLocalDate)(date);
787
+ const ruleWeekStart = (0, _localClock.dateOrdinalFromLocalDate)(rule.startDate) - (0, _localClock.weekdayFromLocalDate)(rule.startDate);
872
788
  return Math.floor((targetWeekStart - ruleWeekStart) / 7) % freq === 0;
873
789
  }
874
- if (rule.mode === "daily" && rule.startDate) {
790
+ if (rule.mode === 'daily' && rule.startDate) {
875
791
  const freq = Math.max(1, Number(rule.frequency || 1) || 1);
876
- return ((0, import_localClock.dateOrdinalFromLocalDate)(date) - (0, import_localClock.dateOrdinalFromLocalDate)(rule.startDate)) % freq === 0;
792
+ return ((0, _localClock.dateOrdinalFromLocalDate)(date) - (0, _localClock.dateOrdinalFromLocalDate)(rule.startDate)) % freq === 0;
877
793
  }
878
794
  return true;
879
795
  }
880
796
  function mergeScheduleRefs(base, next) {
881
- const map = /* @__PURE__ */ new Map();
882
- [...base, ...next].forEach((ref) => {
883
- const key = [
884
- ref.scheduleId ?? "",
885
- ref.timeSlotId ?? "",
886
- ref.ruleId ?? "",
887
- ref.source ?? "",
888
- ref.label ?? ""
889
- ].join(":");
797
+ const map = new Map();
798
+ [...base, ...next].forEach(ref => {
799
+ const key = [ref.scheduleId ?? '', ref.timeSlotId ?? '', ref.ruleId ?? '', ref.source ?? '', ref.label ?? ''].join(':');
890
800
  map.set(key, ref);
891
801
  });
892
802
  return Array.from(map.values());
893
803
  }
894
804
  function buildProductIndex(projection) {
895
- return new Map(projection.products.map((product) => [String(product.id), product]));
805
+ return new Map(projection.products.map(product => [String(product.id), product]));
896
806
  }
897
807
  function buildGroupResourceIndex(params) {
898
- var _a;
899
- const resourceIdSet = ((_a = params.resourceIds) == null ? void 0 : _a.length) ? new Set(params.resourceIds.map(String)) : null;
900
- const index = /* @__PURE__ */ new Map();
901
- params.projection.products.forEach((product) => {
902
- (product.requirementGroups || []).forEach((requirementGroup) => {
903
- const key = [String(product.id), requirementGroup.id].join(":");
904
- index.set(key, params.projection.resources.filter((resource) => matchesRequirementGroup(requirementGroup, resource, product, params.partySize)).filter((resource) => !resourceIdSet || resourceIdSet.has(String(resource.id))));
808
+ const resourceIdSet = params.resourceIds?.length ? new Set(params.resourceIds.map(String)) : null;
809
+ const index = new Map();
810
+ params.projection.products.forEach(product => {
811
+ (product.requirementGroups || []).forEach(requirementGroup => {
812
+ const key = [String(product.id), requirementGroup.id].join(':');
813
+ index.set(key, params.projection.resources.filter(resource => matchesRequirementGroup(requirementGroup, resource, product, params.partySize)).filter(resource => !resourceIdSet || resourceIdSet.has(String(resource.id))));
905
814
  });
906
815
  });
907
816
  return index;
908
817
  }
909
818
  function getSessionRangesForProduct(projection, product, dateRange) {
910
- const fixedOffsetMinutes = (0, import_localClock.extractFixedOffsetMinutes)(projection.meta.now) ?? (0, import_localClock.captureRuntimeOffsetMinutes)();
911
- const ranges = (product.sessionRanges || []).map((range) => {
912
- var _a;
819
+ const fixedOffsetMinutes = (0, _localClock.extractFixedOffsetMinutes)(projection.meta.now) ?? (0, _localClock.captureRuntimeOffsetMinutes)();
820
+ const ranges = (product.sessionRanges || []).map(range => {
913
821
  const normalized = normalizeAbsoluteRange(range, projection.meta.timezone);
914
- if (!normalized)
915
- return null;
822
+ if (!normalized) return null;
916
823
  const startDate = formatDate(normalized.start, projection.meta.timezone);
917
824
  if (startDate < dateRange.startDate || startDate > dateRange.endDate) {
918
825
  return null;
@@ -920,7 +827,7 @@ function getSessionRangesForProduct(projection, product, dateRange) {
920
827
  return {
921
828
  key: `${String(product.id)}:${normalized.start}:${normalized.end}`,
922
829
  productId: product.id,
923
- source: "session_schedule",
830
+ source: 'session_schedule',
924
831
  startAt: formatDateTime(normalized.start, projection.meta.timezone, fixedOffsetMinutes),
925
832
  endAt: formatDateTime(normalized.end, projection.meta.timezone, fixedOffsetMinutes),
926
833
  bookingStartAt: formatDateTime(normalized.start, projection.meta.timezone, fixedOffsetMinutes),
@@ -930,16 +837,11 @@ function getSessionRangesForProduct(projection, product, dateRange) {
930
837
  endTime: formatTime(normalized.end, projection.meta.timezone),
931
838
  timezone: projection.meta.timezone,
932
839
  scheduleRefs: range.scheduleRefs || [],
933
- primaryScheduleRef: (_a = range.scheduleRefs) == null ? void 0 : _a[0]
840
+ primaryScheduleRef: range.scheduleRefs?.[0]
934
841
  };
935
842
  }).filter(Boolean);
936
- const ruleRanges = materializeScheduleRules(
937
- product.sessionRules || [],
938
- dateRange,
939
- projection.meta.timezone,
940
- fixedOffsetMinutes
941
- );
942
- return dedupeProductRanges([...ranges, ...ruleRanges.map((range) => ({
843
+ const ruleRanges = materializeScheduleRules(product.sessionRules || [], dateRange, projection.meta.timezone, fixedOffsetMinutes);
844
+ return dedupeProductRanges([...ranges, ...ruleRanges.map(range => ({
943
845
  ...range,
944
846
  key: `${String(product.id)}:${range.startAt}:${range.endAt}`,
945
847
  productId: product.id,
@@ -947,29 +849,30 @@ function getSessionRangesForProduct(projection, product, dateRange) {
947
849
  }))]);
948
850
  }
949
851
  function dedupeProductRanges(ranges) {
950
- const map = /* @__PURE__ */ new Map();
951
- ranges.forEach((range) => {
852
+ const map = new Map();
853
+ ranges.forEach(range => {
952
854
  const key = getProductTimeRangeIdentity(range);
953
855
  if (!map.has(key)) {
954
- map.set(key, { ...range, key });
856
+ map.set(key, {
857
+ ...range,
858
+ key
859
+ });
955
860
  return;
956
861
  }
957
862
  const existing = map.get(key);
958
863
  existing.scheduleRefs = mergeScheduleRefs(existing.scheduleRefs, range.scheduleRefs || []);
959
- if (!existing.primaryScheduleRef)
960
- existing.primaryScheduleRef = range.primaryScheduleRef;
864
+ if (!existing.primaryScheduleRef) existing.primaryScheduleRef = range.primaryScheduleRef;
961
865
  });
962
866
  return Array.from(map.values()).sort((left, right) => left.startAt.localeCompare(right.startAt));
963
867
  }
964
868
  function getProductTimeRanges(projection, request = {}) {
965
- var _a;
966
869
  const normalizedPartySize = normalizePartySize(request.partySize);
967
870
  const dateRange = resolveProjectionDateRange(projection, request.dateRange);
968
- const fixedOffsetMinutes = (0, import_localClock.extractFixedOffsetMinutes)(projection.meta.now) ?? (0, import_localClock.captureRuntimeOffsetMinutes)();
871
+ const fixedOffsetMinutes = (0, _localClock.extractFixedOffsetMinutes)(projection.meta.now) ?? (0, _localClock.captureRuntimeOffsetMinutes)();
969
872
  const resourceWindowIndex = buildResourceWindowIndex(projection);
970
- const filteredProductIdSet = ((_a = request.productIds) == null ? void 0 : _a.length) ? new Set(request.productIds.map(String)) : null;
971
- return projection.products.filter((product) => !filteredProductIdSet || filteredProductIdSet.has(String(product.id))).map((product) => {
972
- if (product.kind === "normal") {
873
+ const filteredProductIdSet = request.productIds?.length ? new Set(request.productIds.map(String)) : null;
874
+ return projection.products.filter(product => !filteredProductIdSet || filteredProductIdSet.has(String(product.id))).map(product => {
875
+ if (product.kind === 'normal') {
973
876
  return {
974
877
  productId: product.id,
975
878
  title: product.title,
@@ -977,7 +880,7 @@ function getProductTimeRanges(projection, request = {}) {
977
880
  ranges: []
978
881
  };
979
882
  }
980
- if (product.kind === "session" || product.kind === "venue_slot") {
883
+ if (product.kind === 'session' || product.kind === 'venue_slot') {
981
884
  return {
982
885
  productId: product.id,
983
886
  title: product.title,
@@ -985,9 +888,9 @@ function getProductTimeRanges(projection, request = {}) {
985
888
  ranges: getSessionRangesForProduct(projection, product, dateRange)
986
889
  };
987
890
  }
988
- const requiredGroups = (product.requirementGroups || []).filter((group) => group.required !== false);
891
+ const requiredGroups = (product.requirementGroups || []).filter(group => group.required !== false);
989
892
  const operatingRanges = buildOperatingRanges(projection, dateRange);
990
- const resourceRanges = requiredGroups.length ? requiredGroups.map((group) => buildGroupWorkingRanges({
893
+ const resourceRanges = requiredGroups.length ? requiredGroups.map(group => buildGroupWorkingRanges({
991
894
  projection,
992
895
  product,
993
896
  requirementGroup: group,
@@ -995,32 +898,27 @@ function getProductTimeRanges(projection, request = {}) {
995
898
  resourceIds: request.resourceIds,
996
899
  partySize: normalizedPartySize,
997
900
  resourceWindowIndex
998
- })).reduce((current, ranges2) => current === null ? ranges2 : intersectRangeLists(current, ranges2), null) || [] : null;
901
+ })).reduce((current, ranges) => current === null ? ranges : intersectRangeLists(current, ranges), null) || [] : null;
999
902
  const baseRanges = resourceRanges === null ? operatingRanges : intersectRangeLists(resourceRanges, operatingRanges);
1000
903
  const durationMinutes = Math.max(1, Number(product.durationMinutes || 30) || 30);
1001
904
  const timeSliceMinutes = projection.meta.defaults.slotStepMinutes;
1002
- const durationAnchor = ceilToTenMinutes(
1003
- toDateTime(projection.meta.durationCandidateAnchorAt, projection.meta.timezone),
1004
- projection.meta.timezone
1005
- );
905
+ const durationAnchor = ceilToTenMinutes(toDateTime(projection.meta.durationCandidateAnchorAt, projection.meta.timezone), projection.meta.timezone);
1006
906
  const ranges = [];
1007
- baseRanges.forEach((range) => {
1008
- if (range.end <= durationAnchor)
1009
- return;
907
+ baseRanges.forEach(range => {
908
+ if (range.end <= durationAnchor) return;
1010
909
  let cursor = range.start;
1011
910
  if (cursor < durationAnchor) {
1012
911
  cursor = durationAnchor;
1013
912
  }
1014
913
  while (true) {
1015
914
  const bookingEnd = addMinutesZoned(cursor, durationMinutes, projection.meta.timezone);
1016
- if (bookingEnd > range.end)
1017
- break;
915
+ if (bookingEnd > range.end) break;
1018
916
  const bookingDate = formatDate(cursor);
1019
917
  if (bookingDate >= dateRange.startDate && bookingDate <= dateRange.endDate) {
1020
918
  ranges.push({
1021
919
  key: `${String(product.id)}:${cursor}:${bookingEnd}`,
1022
920
  productId: product.id,
1023
- source: "duration_window",
921
+ source: 'duration_window',
1024
922
  startAt: formatDateTime(cursor, projection.meta.timezone, fixedOffsetMinutes),
1025
923
  endAt: formatDateTime(bookingEnd, projection.meta.timezone, fixedOffsetMinutes),
1026
924
  bookingStartAt: formatDateTime(cursor, projection.meta.timezone, fixedOffsetMinutes),
@@ -1058,61 +956,42 @@ function aggregateMatchedCells(productId, requirementGroupId, resourceId, candid
1058
956
  }
1059
957
  const overlapped = [];
1060
958
  for (let index = Math.max(0, left - 1); index < indexedCells.length; index += 1) {
1061
- const { cell, range } = indexedCells[index];
1062
- if (range.startMs >= candidateEndMs)
1063
- break;
1064
- if (range.endMs <= candidateStartMs)
1065
- continue;
959
+ const {
960
+ cell,
961
+ range
962
+ } = indexedCells[index];
963
+ if (range.startMs >= candidateEndMs) break;
964
+ if (range.endMs <= candidateStartMs) continue;
1066
965
  const clipped = clipRange(range, candidateRange);
1067
- if (clipped)
1068
- overlapped.push({ cell, range: clipped });
966
+ if (clipped) overlapped.push({
967
+ cell,
968
+ range: clipped
969
+ });
1069
970
  }
1070
- if (!overlapped.length)
1071
- return void 0;
971
+ if (!overlapped.length) return undefined;
1072
972
  let cursor = candidateRange.start;
1073
973
  for (const item of overlapped) {
1074
- if (item.range.start > cursor)
1075
- return void 0;
974
+ if (item.range.start > cursor) return undefined;
1076
975
  if (item.range.end > cursor) {
1077
976
  cursor = item.range.end;
1078
977
  }
1079
- if (cursor >= candidateRange.end)
1080
- break;
978
+ if (cursor >= candidateRange.end) break;
1081
979
  }
1082
- if (cursor < candidateRange.end)
1083
- return void 0;
1084
- const cells = overlapped.map((item) => item.cell);
1085
- const statuses = cells.map((cell) => cell.status);
1086
- const nonPastStatuses = statuses.filter((status) => status !== "past");
980
+ if (cursor < candidateRange.end) return undefined;
981
+ const cells = overlapped.map(item => item.cell);
982
+ const statuses = cells.map(cell => cell.status);
983
+ const nonPastStatuses = statuses.filter(status => status !== 'past');
1087
984
  const aggregatedStatus = (() => {
1088
- if (nonPastStatuses.some((status) => status === "unavailable"))
1089
- return "unavailable";
1090
- if (nonPastStatuses.some((status) => status === "full"))
1091
- return "full";
1092
- if (nonPastStatuses.some((status) => status === "limited"))
1093
- return "limited";
1094
- if (nonPastStatuses.every((status) => status === "available"))
1095
- return "available";
1096
- if (statuses.every((status) => status === "past"))
1097
- return "past";
1098
- return "available";
985
+ if (nonPastStatuses.some(status => status === 'unavailable')) return 'unavailable';
986
+ if (nonPastStatuses.some(status => status === 'full')) return 'full';
987
+ if (nonPastStatuses.some(status => status === 'limited')) return 'limited';
988
+ if (nonPastStatuses.every(status => status === 'available')) return 'available';
989
+ if (statuses.every(status => status === 'past')) return 'past';
990
+ return 'available';
1099
991
  })();
1100
- const capacityCells = cells.filter((cell) => cell.remainingCapacity !== void 0 || cell.maxCapacity !== void 0);
1101
- const reasonCodes = Array.from(new Set(cells.flatMap((cell) => cell.reasonCodes)));
1102
- const occupancyRefs = Array.from(new Map(
1103
- cells.flatMap((cell) => cell.occupancyRefs || []).map((ref) => [
1104
- [
1105
- ref.id ?? "",
1106
- ref.resourceId ?? "",
1107
- ref.serverEventId ?? "",
1108
- ref.bookingUid ?? "",
1109
- ref.productUid ?? "",
1110
- ref.scheduleEventId ?? "",
1111
- ref.source ?? ""
1112
- ].join(":"),
1113
- ref
1114
- ])
1115
- ).values());
992
+ const capacityCells = cells.filter(cell => cell.remainingCapacity !== undefined || cell.maxCapacity !== undefined);
993
+ const reasonCodes = Array.from(new Set(cells.flatMap(cell => cell.reasonCodes)));
994
+ const occupancyRefs = Array.from(new Map(cells.flatMap(cell => cell.occupancyRefs || []).map(ref => [[ref.id ?? '', ref.resourceId ?? '', ref.serverEventId ?? '', ref.bookingUid ?? '', ref.productUid ?? '', ref.scheduleEventId ?? '', ref.source ?? ''].join(':'), ref])).values());
1116
995
  const firstCell = cells[0];
1117
996
  return {
1118
997
  ...firstCell,
@@ -1123,22 +1002,14 @@ function aggregateMatchedCells(productId, requirementGroupId, resourceId, candid
1123
1002
  start: candidateRange.start,
1124
1003
  end: candidateRange.end
1125
1004
  }),
1126
- startAt: formatDateTime(
1127
- candidateRange.start,
1128
- firstCell.timezone,
1129
- (0, import_localClock.extractFixedOffsetMinutes)(firstCell.startAt) ?? (0, import_localClock.captureRuntimeOffsetMinutes)()
1130
- ),
1131
- endAt: formatDateTime(
1132
- candidateRange.end,
1133
- firstCell.timezone,
1134
- (0, import_localClock.extractFixedOffsetMinutes)(firstCell.startAt) ?? (0, import_localClock.captureRuntimeOffsetMinutes)()
1135
- ),
1005
+ startAt: formatDateTime(candidateRange.start, firstCell.timezone, (0, _localClock.extractFixedOffsetMinutes)(firstCell.startAt) ?? (0, _localClock.captureRuntimeOffsetMinutes)()),
1006
+ endAt: formatDateTime(candidateRange.end, firstCell.timezone, (0, _localClock.extractFixedOffsetMinutes)(firstCell.startAt) ?? (0, _localClock.captureRuntimeOffsetMinutes)()),
1136
1007
  date: formatDate(candidateRange.start, firstCell.timezone),
1137
1008
  startTime: formatTime(candidateRange.start, firstCell.timezone),
1138
1009
  endTime: formatTime(candidateRange.end, firstCell.timezone),
1139
1010
  status: aggregatedStatus,
1140
- remainingCapacity: capacityCells.length ? Math.min(...capacityCells.map((cell) => cell.remainingCapacity ?? Number.MAX_SAFE_INTEGER)) : void 0,
1141
- maxCapacity: capacityCells.length ? Math.min(...capacityCells.map((cell) => cell.maxCapacity ?? Number.MAX_SAFE_INTEGER)) : void 0,
1011
+ remainingCapacity: capacityCells.length ? Math.min(...capacityCells.map(cell => cell.remainingCapacity ?? Number.MAX_SAFE_INTEGER)) : undefined,
1012
+ maxCapacity: capacityCells.length ? Math.min(...capacityCells.map(cell => cell.maxCapacity ?? Number.MAX_SAFE_INTEGER)) : undefined,
1142
1013
  reasonCodes,
1143
1014
  occupancyRefs
1144
1015
  };
@@ -1159,54 +1030,44 @@ function aggregateMatchedCellSummary(candidateRange, indexedCells) {
1159
1030
  const cells = [];
1160
1031
  let cursorMs = candidateStartMs;
1161
1032
  for (let index = Math.max(0, left - 1); index < indexedCells.length; index += 1) {
1162
- const { cell, range } = indexedCells[index];
1163
- if (range.startMs >= candidateEndMs)
1164
- break;
1165
- if (range.endMs <= candidateStartMs)
1166
- continue;
1033
+ const {
1034
+ cell,
1035
+ range
1036
+ } = indexedCells[index];
1037
+ if (range.startMs >= candidateEndMs) break;
1038
+ if (range.endMs <= candidateStartMs) continue;
1167
1039
  const clippedStartMs = Math.max(range.startMs, candidateStartMs);
1168
1040
  const clippedEndMs = Math.min(range.endMs, candidateEndMs);
1169
- if (clippedEndMs <= clippedStartMs)
1170
- continue;
1171
- if (clippedStartMs > cursorMs)
1172
- return void 0;
1041
+ if (clippedEndMs <= clippedStartMs) continue;
1042
+ if (clippedStartMs > cursorMs) return undefined;
1173
1043
  cells.push(cell);
1174
- if (clippedEndMs > cursorMs)
1175
- cursorMs = clippedEndMs;
1176
- if (cursorMs >= candidateEndMs)
1177
- break;
1044
+ if (clippedEndMs > cursorMs) cursorMs = clippedEndMs;
1045
+ if (cursorMs >= candidateEndMs) break;
1178
1046
  }
1179
- if (!cells.length || cursorMs < candidateEndMs)
1180
- return void 0;
1181
- const statuses = cells.map((cell) => cell.status);
1182
- const nonPastStatuses = statuses.filter((status2) => status2 !== "past");
1047
+ if (!cells.length || cursorMs < candidateEndMs) return undefined;
1048
+ const statuses = cells.map(cell => cell.status);
1049
+ const nonPastStatuses = statuses.filter(status => status !== 'past');
1183
1050
  const status = (() => {
1184
- if (nonPastStatuses.some((cellStatus) => cellStatus === "unavailable"))
1185
- return "unavailable";
1186
- if (nonPastStatuses.some((cellStatus) => cellStatus === "full"))
1187
- return "full";
1188
- if (nonPastStatuses.some((cellStatus) => cellStatus === "limited"))
1189
- return "limited";
1190
- if (nonPastStatuses.every((cellStatus) => cellStatus === "available"))
1191
- return "available";
1192
- if (statuses.every((cellStatus) => cellStatus === "past"))
1193
- return "past";
1194
- return "available";
1051
+ if (nonPastStatuses.some(cellStatus => cellStatus === 'unavailable')) return 'unavailable';
1052
+ if (nonPastStatuses.some(cellStatus => cellStatus === 'full')) return 'full';
1053
+ if (nonPastStatuses.some(cellStatus => cellStatus === 'limited')) return 'limited';
1054
+ if (nonPastStatuses.every(cellStatus => cellStatus === 'available')) return 'available';
1055
+ if (statuses.every(cellStatus => cellStatus === 'past')) return 'past';
1056
+ return 'available';
1195
1057
  })();
1196
- const capacityCells = cells.filter((cell) => cell.remainingCapacity !== void 0 || cell.maxCapacity !== void 0);
1058
+ const capacityCells = cells.filter(cell => cell.remainingCapacity !== undefined || cell.maxCapacity !== undefined);
1197
1059
  return {
1198
1060
  status,
1199
- remainingCapacity: capacityCells.length ? Math.min(...capacityCells.map((cell) => cell.remainingCapacity ?? Number.MAX_SAFE_INTEGER)) : void 0,
1200
- maxCapacity: capacityCells.length ? Math.min(...capacityCells.map((cell) => cell.maxCapacity ?? Number.MAX_SAFE_INTEGER)) : void 0,
1201
- reasonCodes: Array.from(new Set(cells.flatMap((cell) => cell.reasonCodes)))
1061
+ remainingCapacity: capacityCells.length ? Math.min(...capacityCells.map(cell => cell.remainingCapacity ?? Number.MAX_SAFE_INTEGER)) : undefined,
1062
+ maxCapacity: capacityCells.length ? Math.min(...capacityCells.map(cell => cell.maxCapacity ?? Number.MAX_SAFE_INTEGER)) : undefined,
1063
+ reasonCodes: Array.from(new Set(cells.flatMap(cell => cell.reasonCodes)))
1202
1064
  };
1203
1065
  }
1204
1066
  function buildAvailabilityCellIndex(projection) {
1205
- const index = /* @__PURE__ */ new Map();
1206
- projection.cells.forEach((cell) => {
1207
- if (cell.requirementGroupId == null || cell.resourceId == null)
1208
- return;
1209
- const key = [String(cell.productId), cell.requirementGroupId, String(cell.resourceId)].join(":");
1067
+ const index = new Map();
1068
+ projection.cells.forEach(cell => {
1069
+ if (cell.requirementGroupId == null || cell.resourceId == null) return;
1070
+ const key = [String(cell.productId), cell.requirementGroupId, String(cell.resourceId)].join(':');
1210
1071
  const entries = index.get(key) || [];
1211
1072
  entries.push({
1212
1073
  cell,
@@ -1223,35 +1084,27 @@ function buildAvailabilityCellIndex(projection) {
1223
1084
  return index;
1224
1085
  }
1225
1086
  function buildEvaluationContext(params) {
1226
- var _a;
1227
1087
  const evaluatedAt = toDateTime(params.evaluatedAt || params.projection.meta.now, params.projection.meta.timezone);
1228
- const selectionsByGroupKey = /* @__PURE__ */ new Map();
1229
- (params.requirementSelections || []).forEach((selection) => {
1230
- const key = selection.requirementGroupId || String(selection.formId ?? "");
1088
+ const selectionsByGroupKey = new Map();
1089
+ (params.requirementSelections || []).forEach(selection => {
1090
+ const key = selection.requirementGroupId || String(selection.formId ?? '');
1231
1091
  selectionsByGroupKey.set(key, selection);
1232
1092
  });
1233
1093
  return {
1234
1094
  evaluatedAt,
1235
1095
  evaluatedAtMs: evaluatedAt,
1236
- resourceIdSet: ((_a = params.resourceIds) == null ? void 0 : _a.length) ? new Set(params.resourceIds.map(String)) : null,
1096
+ resourceIdSet: params.resourceIds?.length ? new Set(params.resourceIds.map(String)) : null,
1237
1097
  selectionsByGroupKey
1238
1098
  };
1239
1099
  }
1240
1100
  function summarizeStatuses(statuses) {
1241
- if (!statuses.length)
1242
- return "unavailable";
1243
- if (statuses.every((status) => status === "past"))
1244
- return "past";
1245
- if (statuses.some((status) => status === "unavailable"))
1246
- return "unavailable";
1247
- if (statuses.some((status) => status === "full"))
1248
- return "full";
1249
- if (statuses.some((status) => status === "limited"))
1250
- return "limited";
1251
- if (statuses.every((status) => status === "available"))
1252
- return "available";
1253
- if (statuses.some((status) => status === "past"))
1254
- return "past";
1101
+ if (!statuses.length) return 'unavailable';
1102
+ if (statuses.every(status => status === 'past')) return 'past';
1103
+ if (statuses.some(status => status === 'unavailable')) return 'unavailable';
1104
+ if (statuses.some(status => status === 'full')) return 'full';
1105
+ if (statuses.some(status => status === 'limited')) return 'limited';
1106
+ if (statuses.every(status => status === 'available')) return 'available';
1107
+ if (statuses.some(status => status === 'past')) return 'past';
1255
1108
  return statuses[0];
1256
1109
  }
1257
1110
  function createStatusAggregate() {
@@ -1265,83 +1118,72 @@ function createStatusAggregate() {
1265
1118
  }
1266
1119
  function pushStatusAggregate(aggregate, status) {
1267
1120
  aggregate.count += 1;
1268
- if (status !== "past")
1269
- aggregate.allPast = false;
1270
- if (status === "available")
1271
- aggregate.hasAvailable = true;
1272
- if (status === "limited")
1273
- aggregate.hasLimited = true;
1274
- if (status === "full")
1275
- aggregate.hasFull = true;
1121
+ if (status !== 'past') aggregate.allPast = false;
1122
+ if (status === 'available') aggregate.hasAvailable = true;
1123
+ if (status === 'limited') aggregate.hasLimited = true;
1124
+ if (status === 'full') aggregate.hasFull = true;
1276
1125
  }
1277
1126
  function summarizeStatusAggregate(aggregate) {
1278
- if (aggregate.count === 0)
1279
- return "unavailable";
1280
- if (aggregate.hasAvailable)
1281
- return "available";
1282
- if (aggregate.hasLimited)
1283
- return "limited";
1284
- if (aggregate.hasFull)
1285
- return "full";
1286
- if (aggregate.allPast)
1287
- return "past";
1288
- return "unavailable";
1127
+ if (aggregate.count === 0) return 'unavailable';
1128
+ if (aggregate.hasAvailable) return 'available';
1129
+ if (aggregate.hasLimited) return 'limited';
1130
+ if (aggregate.hasFull) return 'full';
1131
+ if (aggregate.allPast) return 'past';
1132
+ return 'unavailable';
1289
1133
  }
1290
1134
  function evaluateMatchedResourceSummary(params) {
1291
1135
  const isPast = params.candidateRange.endMs <= params.evaluationContext.evaluatedAtMs;
1292
1136
  if (!params.matchedCell) {
1293
1137
  return {
1294
- status: isPast ? "past" : "unavailable",
1138
+ status: isPast ? 'past' : 'unavailable',
1295
1139
  maxPartySize: 0,
1296
1140
  remainingCapacity: 0,
1297
1141
  maxCapacity: 0,
1298
- reasonCodes: isPast ? ["past"] : ["outside_resource_window"]
1142
+ reasonCodes: isPast ? ['past'] : ['outside_resource_window']
1299
1143
  };
1300
1144
  }
1301
1145
  const remainingCapacity = Math.max(0, params.matchedCell.remainingCapacity || 0);
1302
1146
  const maxCapacity = Math.max(0, params.matchedCell.maxCapacity ?? params.resourceCapacity ?? 0);
1303
1147
  const maxPartySize = isPast ? 0 : remainingCapacity;
1304
1148
  const status = (() => {
1305
- if (isPast)
1306
- return "past";
1307
- if (params.matchedCell.status === "unavailable")
1308
- return "unavailable";
1309
- if (remainingCapacity < params.requiredCapacity)
1310
- return "full";
1311
- if (remainingCapacity < maxCapacity)
1312
- return "limited";
1313
- return "available";
1149
+ if (isPast) return 'past';
1150
+ if (params.matchedCell.status === 'unavailable') return 'unavailable';
1151
+ if (remainingCapacity < params.requiredCapacity) return 'full';
1152
+ if (remainingCapacity < maxCapacity) return 'limited';
1153
+ return 'available';
1314
1154
  })();
1315
1155
  return {
1316
1156
  status,
1317
1157
  remainingCapacity,
1318
1158
  maxCapacity,
1319
1159
  maxPartySize,
1320
- reasonCodes: isPast ? Array.from(/* @__PURE__ */ new Set([...params.matchedCell.reasonCodes || [], "past"])) : params.matchedCell.reasonCodes
1160
+ reasonCodes: isPast ? Array.from(new Set([...(params.matchedCell.reasonCodes || []), 'past'])) : params.matchedCell.reasonCodes
1321
1161
  };
1322
1162
  }
1323
1163
  function evaluateRequirementGroupSummary(params) {
1324
- var _a;
1325
- const { projection, product, requirementGroup, candidateRange, requestedPartySize, evaluationContext } = params;
1164
+ const {
1165
+ projection,
1166
+ product,
1167
+ requirementGroup,
1168
+ candidateRange,
1169
+ requestedPartySize,
1170
+ evaluationContext
1171
+ } = params;
1326
1172
  const required = requirementGroup.required !== false;
1327
1173
  const minimum = Math.max(required ? 1 : 0, Number(requirementGroup.min ?? (required ? 1 : 0)) || 0);
1328
1174
  const maximum = Math.max(minimum, Number(requirementGroup.max ?? Math.max(minimum, 1)) || Math.max(minimum, 1));
1329
- const matchedResources = ((_a = params.groupResourceIndex) == null ? void 0 : _a.get([String(product.id), requirementGroup.id].join(":"))) || projection.resources.filter((resource) => matchesRequirementGroup(requirementGroup, resource, product, requestedPartySize ?? 1)).filter((resource) => !evaluationContext.resourceIdSet || evaluationContext.resourceIdSet.has(String(resource.id)));
1175
+ const matchedResources = params.groupResourceIndex?.get([String(product.id), requirementGroup.id].join(':')) || projection.resources.filter(resource => matchesRequirementGroup(requirementGroup, resource, product, requestedPartySize ?? 1)).filter(resource => !evaluationContext.resourceIdSet || evaluationContext.resourceIdSet.has(String(resource.id)));
1330
1176
  const requiredCapacity = resolveRequiredCapacity(requirementGroup, product, requestedPartySize);
1331
1177
  const optionStatusAggregate = createStatusAggregate();
1332
- const reasonCodeSet = /* @__PURE__ */ new Set();
1178
+ const reasonCodeSet = new Set();
1333
1179
  let totalResourceCount = 0;
1334
1180
  let availableResourceCount = 0;
1335
1181
  let groupMaxPartySize = 0;
1336
1182
  let groupRemainingCapacity = 0;
1337
1183
  let groupMaxCapacity = 0;
1338
- matchedResources.forEach((resource) => {
1339
- var _a2;
1184
+ matchedResources.forEach(resource => {
1340
1185
  totalResourceCount += 1;
1341
- const matchedCell = aggregateMatchedCellSummary(
1342
- candidateRange,
1343
- ((_a2 = params.cellIndex) == null ? void 0 : _a2.get([String(product.id), requirementGroup.id, String(resource.id)].join(":"))) || []
1344
- );
1186
+ const matchedCell = aggregateMatchedCellSummary(candidateRange, params.cellIndex?.get([String(product.id), requirementGroup.id, String(resource.id)].join(':')) || []);
1345
1187
  const option = evaluateMatchedResourceSummary({
1346
1188
  matchedCell,
1347
1189
  resourceCapacity: resource.capacity,
@@ -1350,23 +1192,23 @@ function evaluateRequirementGroupSummary(params) {
1350
1192
  evaluationContext
1351
1193
  });
1352
1194
  pushStatusAggregate(optionStatusAggregate, option.status);
1353
- option.reasonCodes.forEach((reasonCode) => reasonCodeSet.add(reasonCode));
1195
+ option.reasonCodes.forEach(reasonCode => reasonCodeSet.add(reasonCode));
1354
1196
  groupRemainingCapacity = Math.max(groupRemainingCapacity, option.remainingCapacity);
1355
1197
  groupMaxCapacity = Math.max(groupMaxCapacity, option.maxCapacity);
1356
- if (option.status === "available" || option.status === "limited") {
1198
+ if (option.status === 'available' || option.status === 'limited') {
1357
1199
  availableResourceCount += 1;
1358
1200
  groupMaxPartySize = Math.max(groupMaxPartySize, option.maxPartySize);
1359
1201
  }
1360
1202
  });
1361
- let status = "available";
1203
+ let status = 'available';
1362
1204
  if (candidateRange.endMs <= evaluationContext.evaluatedAtMs) {
1363
- status = "past";
1205
+ status = 'past';
1364
1206
  } else if (availableResourceCount < minimum) {
1365
- status = optionStatusAggregate.hasAvailable || optionStatusAggregate.hasLimited || optionStatusAggregate.hasFull ? "full" : "unavailable";
1207
+ status = optionStatusAggregate.hasAvailable || optionStatusAggregate.hasLimited || optionStatusAggregate.hasFull ? 'full' : 'unavailable';
1366
1208
  } else if (availableResourceCount === totalResourceCount && !optionStatusAggregate.hasLimited && !optionStatusAggregate.hasFull && !optionStatusAggregate.allPast) {
1367
- status = "available";
1209
+ status = 'available';
1368
1210
  } else {
1369
- status = "limited";
1211
+ status = 'limited';
1370
1212
  }
1371
1213
  return {
1372
1214
  required,
@@ -1380,20 +1222,19 @@ function evaluateRequirementGroupSummary(params) {
1380
1222
  };
1381
1223
  }
1382
1224
  function evaluateCandidateSummary(projection, candidate, request) {
1383
- var _a, _b;
1384
- const product = ((_a = request.productIndex) == null ? void 0 : _a.get(String(candidate.productId))) || projection.products.find((item) => sameId(item.id, candidate.productId));
1225
+ const product = request.productIndex?.get(String(candidate.productId)) || projection.products.find(item => sameId(item.id, candidate.productId));
1385
1226
  if (!product) {
1386
- throw availabilityError("INVALID_TARGET_FILTER", "candidate.productId 不存在于 projection.products", {
1227
+ throw availabilityError('INVALID_TARGET_FILTER', 'candidate.productId 不存在于 projection.products', {
1387
1228
  candidate
1388
1229
  });
1389
1230
  }
1390
1231
  const candidateRangeCacheKey = getCandidateRangeCacheKey(candidate);
1391
- const candidateRange = ((_b = request.candidateRangeCache) == null ? void 0 : _b.get(candidateRangeCacheKey)) || (() => {
1232
+ const candidateRange = request.candidateRangeCache?.get(candidateRangeCacheKey) || (() => {
1392
1233
  const normalized = normalizeAbsoluteRange(getCandidateBookingRange(candidate), projection.meta.timezone);
1393
1234
  return normalized ? toIndexedRange(normalized) : null;
1394
1235
  })();
1395
1236
  if (!candidateRange) {
1396
- throw availabilityError("INVALID_TARGET_FILTER", "candidate 时间范围非法", {
1237
+ throw availabilityError('INVALID_TARGET_FILTER', 'candidate 时间范围非法', {
1397
1238
  candidate
1398
1239
  });
1399
1240
  }
@@ -1406,7 +1247,7 @@ function evaluateCandidateSummary(projection, candidate, request) {
1406
1247
  resourceIds: request.resourceIds
1407
1248
  });
1408
1249
  const requestedPartySize = normalizePartySize(request.partySize);
1409
- const requiredGroups = (product.requirementGroups || []).filter((group) => group.required !== false).map((requirementGroup) => evaluateRequirementGroupSummary({
1250
+ const requiredGroups = (product.requirementGroups || []).filter(group => group.required !== false).map(requirementGroup => evaluateRequirementGroupSummary({
1410
1251
  projection,
1411
1252
  product,
1412
1253
  requirementGroup,
@@ -1422,34 +1263,33 @@ function evaluateCandidateSummary(projection, candidate, request) {
1422
1263
  evaluationContext,
1423
1264
  timezoneName: projection.meta.timezone
1424
1265
  });
1425
- const candidateStatus = requiredGroups.length ? summarizeStatuses(requiredGroups.map((group) => group.status)) : candidateRange.endMs <= evaluationContext.evaluatedAtMs ? "past" : "available";
1266
+ const candidateStatus = requiredGroups.length ? summarizeStatuses(requiredGroups.map(group => group.status)) : candidateRange.endMs <= evaluationContext.evaluatedAtMs ? 'past' : 'available';
1426
1267
  const maxPartySize = requiredGroups.length ? requiredGroups.reduce((min, group) => Math.min(min, group.maxPartySize), Number.MAX_SAFE_INTEGER) : 0;
1427
1268
  const remainingCapacity = requiredGroups.length ? requiredGroups.reduce((min, group) => Math.min(min, group.remainingCapacity), Number.MAX_SAFE_INTEGER) : 0;
1428
1269
  const maxCapacity = requiredGroups.length ? requiredGroups.reduce((min, group) => Math.min(min, group.maxCapacity), Number.MAX_SAFE_INTEGER) : 0;
1429
- const status = (gate == null ? void 0 : gate.status) || candidateStatus;
1270
+ const status = gate?.status || candidateStatus;
1430
1271
  return {
1431
1272
  status,
1432
1273
  maxPartySize: gate ? 0 : Number.isFinite(maxPartySize) ? maxPartySize : 0,
1433
- remainingCapacity: status === "past" || status === "unavailable" ? 0 : Number.isFinite(remainingCapacity) ? remainingCapacity : 0,
1274
+ remainingCapacity: status === 'past' || status === 'unavailable' ? 0 : Number.isFinite(remainingCapacity) ? remainingCapacity : 0,
1434
1275
  maxCapacity: Number.isFinite(maxCapacity) ? maxCapacity : 0,
1435
- reasonCodes: (gate == null ? void 0 : gate.reasonCodes) || Array.from(new Set(requiredGroups.flatMap((group) => group.reasonCodes)))
1276
+ reasonCodes: gate?.reasonCodes || Array.from(new Set(requiredGroups.flatMap(group => group.reasonCodes)))
1436
1277
  };
1437
1278
  }
1438
1279
  function evaluateCandidate(projection, candidate, request) {
1439
- var _a, _b;
1440
- const product = ((_a = request.productIndex) == null ? void 0 : _a.get(String(candidate.productId))) || projection.products.find((item) => sameId(item.id, candidate.productId));
1280
+ const product = request.productIndex?.get(String(candidate.productId)) || projection.products.find(item => sameId(item.id, candidate.productId));
1441
1281
  if (!product) {
1442
- throw availabilityError("INVALID_TARGET_FILTER", "candidate.productId 不存在于 projection.products", {
1282
+ throw availabilityError('INVALID_TARGET_FILTER', 'candidate.productId 不存在于 projection.products', {
1443
1283
  candidate
1444
1284
  });
1445
1285
  }
1446
1286
  const candidateRangeCacheKey = getCandidateRangeCacheKey(candidate);
1447
- const candidateRange = ((_b = request.candidateRangeCache) == null ? void 0 : _b.get(candidateRangeCacheKey)) || (() => {
1287
+ const candidateRange = request.candidateRangeCache?.get(candidateRangeCacheKey) || (() => {
1448
1288
  const normalized = normalizeAbsoluteRange(getCandidateBookingRange(candidate), projection.meta.timezone);
1449
1289
  return normalized ? toIndexedRange(normalized) : null;
1450
1290
  })();
1451
1291
  if (!candidateRange) {
1452
- throw availabilityError("INVALID_TARGET_FILTER", "candidate 时间范围非法", {
1292
+ throw availabilityError('INVALID_TARGET_FILTER', 'candidate 时间范围非法', {
1453
1293
  candidate
1454
1294
  });
1455
1295
  }
@@ -1465,81 +1305,66 @@ function evaluateCandidate(projection, candidate, request) {
1465
1305
  const requestedPartySize = normalizePartySize(request.partySize);
1466
1306
  const partySize = requestedPartySize ?? 1;
1467
1307
  const selectionsByGroupKey = evaluationContext.selectionsByGroupKey;
1468
- const requirementGroups = (product.requirementGroups || []).map((requirementGroup) => {
1469
- var _a2;
1308
+ const requirementGroups = (product.requirementGroups || []).map(requirementGroup => {
1470
1309
  const required = requirementGroup.required !== false;
1471
1310
  const minimum = Math.max(required ? 1 : 0, Number(requirementGroup.min ?? (required ? 1 : 0)) || 0);
1472
1311
  const maximum = Math.max(minimum, Number(requirementGroup.max ?? Math.max(minimum, 1)) || Math.max(minimum, 1));
1473
- const selection = selectionsByGroupKey.get(requirementGroup.id) || selectionsByGroupKey.get(String(requirementGroup.formId ?? ""));
1474
- const matchedResources = (((_a2 = request.groupResourceIndex) == null ? void 0 : _a2.get([String(product.id), requirementGroup.id].join(":"))) || projection.resources.filter((resource) => matchesRequirementGroup(requirementGroup, resource, product, partySize)).filter((resource) => !evaluationContext.resourceIdSet || evaluationContext.resourceIdSet.has(String(resource.id)))).filter((resource) => !selection || selection.resourceIds.some((resourceId) => sameId(resourceId, resource.id)));
1475
- const options = matchedResources.map((resource) => {
1476
- var _a3, _b2, _c;
1477
- const matchedCell = aggregateMatchedCells(
1478
- product.id,
1479
- requirementGroup.id,
1480
- resource.id,
1481
- candidateRange,
1482
- ((_a3 = request.cellIndex) == null ? void 0 : _a3.get([String(product.id), requirementGroup.id, String(resource.id)].join(":"))) || []
1483
- );
1312
+ const selection = selectionsByGroupKey.get(requirementGroup.id) || selectionsByGroupKey.get(String(requirementGroup.formId ?? ''));
1313
+ const matchedResources = (request.groupResourceIndex?.get([String(product.id), requirementGroup.id].join(':')) || projection.resources.filter(resource => matchesRequirementGroup(requirementGroup, resource, product, partySize)).filter(resource => !evaluationContext.resourceIdSet || evaluationContext.resourceIdSet.has(String(resource.id)))).filter(resource => !selection || selection.resourceIds.some(resourceId => sameId(resourceId, resource.id)));
1314
+ const options = matchedResources.map(resource => {
1315
+ const matchedCell = aggregateMatchedCells(product.id, requirementGroup.id, resource.id, candidateRange, request.cellIndex?.get([String(product.id), requirementGroup.id, String(resource.id)].join(':')) || []);
1484
1316
  if (!matchedCell) {
1485
- const isPast2 = candidateRange.endMs <= evaluationContext.evaluatedAtMs;
1317
+ const isPast = candidateRange.endMs <= evaluationContext.evaluatedAtMs;
1486
1318
  return {
1487
1319
  resourceId: resource.id,
1488
1320
  resourceName: resource.name,
1489
1321
  resourceFormId: resource.formId,
1490
- policyId: (_b2 = resource.raw) == null ? void 0 : _b2.policy_id,
1322
+ policyId: resource.raw?.policy_id,
1491
1323
  resourceType: resource.resourceType,
1492
- status: isPast2 ? "past" : "unavailable",
1324
+ status: isPast ? 'past' : 'unavailable',
1493
1325
  maxPartySize: 0,
1494
- reasonCodes: isPast2 ? ["past"] : ["outside_resource_window"]
1326
+ reasonCodes: isPast ? ['past'] : ['outside_resource_window']
1495
1327
  };
1496
1328
  }
1497
1329
  const remainingCapacity = matchedCell.remainingCapacity;
1498
1330
  const maxCapacity = matchedCell.maxCapacity;
1499
1331
  const isPast = candidateRange.endMs <= evaluationContext.evaluatedAtMs;
1500
- const maxPartySize2 = isPast ? 0 : Math.max(0, remainingCapacity || 0);
1332
+ const maxPartySize = isPast ? 0 : Math.max(0, remainingCapacity || 0);
1501
1333
  const requiredCapacity = resolveRequiredCapacity(requirementGroup, product, requestedPartySize);
1502
- const status2 = (() => {
1503
- if (isPast)
1504
- return "past";
1505
- if (matchedCell.status === "unavailable")
1506
- return "unavailable";
1507
- if ((remainingCapacity || 0) < requiredCapacity)
1508
- return "full";
1509
- if ((remainingCapacity || 0) < (maxCapacity || resource.capacity))
1510
- return "limited";
1511
- return "available";
1334
+ const status = (() => {
1335
+ if (isPast) return 'past';
1336
+ if (matchedCell.status === 'unavailable') return 'unavailable';
1337
+ if ((remainingCapacity || 0) < requiredCapacity) return 'full';
1338
+ if ((remainingCapacity || 0) < (maxCapacity || resource.capacity)) return 'limited';
1339
+ return 'available';
1512
1340
  })();
1513
1341
  return {
1514
1342
  resourceId: resource.id,
1515
1343
  resourceName: resource.name,
1516
1344
  resourceFormId: resource.formId,
1517
- policyId: (_c = resource.raw) == null ? void 0 : _c.policy_id,
1345
+ policyId: resource.raw?.policy_id,
1518
1346
  resourceType: resource.resourceType,
1519
- status: status2,
1347
+ status,
1520
1348
  remainingCapacity,
1521
1349
  maxCapacity,
1522
- maxPartySize: maxPartySize2,
1523
- reasonCodes: isPast ? Array.from(/* @__PURE__ */ new Set([...matchedCell.reasonCodes, "past"])) : matchedCell.reasonCodes,
1350
+ maxPartySize,
1351
+ reasonCodes: isPast ? Array.from(new Set([...matchedCell.reasonCodes, 'past'])) : matchedCell.reasonCodes,
1524
1352
  matchedCell
1525
1353
  };
1526
1354
  });
1527
- const availableOptions = options.filter((option) => option.status === "available" || option.status === "limited");
1355
+ const availableOptions = options.filter(option => option.status === 'available' || option.status === 'limited');
1528
1356
  const totalResourceCount = options.length;
1529
1357
  const availableResourceCount = availableOptions.length;
1530
- const groupMaxPartySize = availableOptions.reduce(
1531
- (max, option) => Math.max(max, option.maxPartySize),
1532
- 0
1533
- );
1534
- let status = "available";
1358
+ const groupMaxPartySize = availableOptions.reduce((max, option) => Math.max(max, option.maxPartySize), 0);
1359
+ let status = 'available';
1535
1360
  if (candidateRange.endMs <= evaluationContext.evaluatedAtMs) {
1536
- status = "past";
1361
+ status = 'past';
1537
1362
  } else if (availableResourceCount < minimum) {
1538
- status = options.some((option) => option.status === "full" || option.status === "limited" || option.status === "available") ? "full" : "unavailable";
1539
- } else if (options.every((option) => option.status === "available")) {
1540
- status = "available";
1363
+ status = options.some(option => option.status === 'full' || option.status === 'limited' || option.status === 'available') ? 'full' : 'unavailable';
1364
+ } else if (options.every(option => option.status === 'available')) {
1365
+ status = 'available';
1541
1366
  } else {
1542
- status = "limited";
1367
+ status = 'limited';
1543
1368
  }
1544
1369
  return {
1545
1370
  groupId: requirementGroup.id,
@@ -1554,70 +1379,66 @@ function evaluateCandidate(projection, candidate, request) {
1554
1379
  availableResourceCount,
1555
1380
  totalResourceCount,
1556
1381
  maxPartySize: groupMaxPartySize,
1557
- remainingCapacity: Math.max(...availableOptions.map((option) => option.remainingCapacity || 0), 0),
1558
- maxCapacity: Math.max(...availableOptions.map((option) => option.maxCapacity || 0), 0),
1559
- reasonCodes: Array.from(new Set(options.flatMap((option) => option.reasonCodes))),
1382
+ remainingCapacity: Math.max(...availableOptions.map(option => option.remainingCapacity || 0), 0),
1383
+ maxCapacity: Math.max(...availableOptions.map(option => option.maxCapacity || 0), 0),
1384
+ reasonCodes: Array.from(new Set(options.flatMap(option => option.reasonCodes))),
1560
1385
  options
1561
1386
  };
1562
1387
  });
1563
- const requiredGroups = requirementGroups.filter((group) => group.required);
1564
- const requiredStatuses = requiredGroups.map((group) => group.status);
1388
+ const requiredGroups = requirementGroups.filter(group => group.required);
1389
+ const requiredStatuses = requiredGroups.map(group => group.status);
1565
1390
  const gate = evaluateCandidateTimeGate({
1566
1391
  product,
1567
1392
  candidateRange,
1568
1393
  evaluationContext,
1569
1394
  timezoneName: projection.meta.timezone
1570
1395
  });
1571
- const candidateStatus = requiredGroups.length ? summarizeStatuses(requiredStatuses) : candidateRange.endMs <= evaluationContext.evaluatedAtMs ? "past" : "available";
1396
+ const candidateStatus = requiredGroups.length ? summarizeStatuses(requiredStatuses) : candidateRange.endMs <= evaluationContext.evaluatedAtMs ? 'past' : 'available';
1572
1397
  const maxPartySize = requiredGroups.length ? requiredGroups.reduce((min, group) => Math.min(min, group.maxPartySize), Number.MAX_SAFE_INTEGER) : 0;
1573
- const selectedAssignments = requirementGroups.flatMap((group) => {
1574
- const selection = selectionsByGroupKey.get(group.groupId) || selectionsByGroupKey.get(String(group.formId ?? ""));
1575
- if (!selection && !(request.allowImplicitSelection && group.required))
1576
- return [];
1577
- const availableOptions = group.options.filter((option) => option.status === "available" || option.status === "limited");
1398
+ const selectedAssignments = requirementGroups.flatMap(group => {
1399
+ const selection = selectionsByGroupKey.get(group.groupId) || selectionsByGroupKey.get(String(group.formId ?? ''));
1400
+ if (!selection && !(request.allowImplicitSelection && group.required)) return [];
1401
+ const availableOptions = group.options.filter(option => option.status === 'available' || option.status === 'limited');
1578
1402
  const selectedOptions = selection ? availableOptions.slice(0, group.max) : availableOptions.slice(0, Math.max(group.min, 1));
1579
- const originalGroup = (product.requirementGroups || []).find((item) => item.id === group.groupId) || {
1403
+ const originalGroup = (product.requirementGroups || []).find(item => item.id === group.groupId) || {
1580
1404
  id: group.groupId,
1581
1405
  resourceType: group.resourceType,
1582
1406
  min: group.min,
1583
1407
  max: group.max
1584
1408
  };
1585
- return selectedOptions.map((selectedOption) => {
1586
- var _a2, _b2;
1587
- return {
1588
- productId: product.id,
1589
- resourceId: selectedOption.resourceId,
1590
- formId: group.formId,
1591
- startAt: candidate.bookingStartAt || candidate.startAt,
1592
- endAt: candidate.bookingEndAt || candidate.endAt,
1593
- capacityRequired: resolveRequiredCapacity(originalGroup, product, requestedPartySize),
1594
- scheduleId: (_a2 = candidate.primaryScheduleRef) == null ? void 0 : _a2.scheduleId,
1595
- timeSlotId: (_b2 = candidate.primaryScheduleRef) == null ? void 0 : _b2.timeSlotId
1596
- };
1597
- });
1409
+ return selectedOptions.map(selectedOption => ({
1410
+ productId: product.id,
1411
+ resourceId: selectedOption.resourceId,
1412
+ formId: group.formId,
1413
+ startAt: candidate.bookingStartAt || candidate.startAt,
1414
+ endAt: candidate.bookingEndAt || candidate.endAt,
1415
+ capacityRequired: resolveRequiredCapacity(originalGroup, product, requestedPartySize),
1416
+ scheduleId: candidate.primaryScheduleRef?.scheduleId,
1417
+ timeSlotId: candidate.primaryScheduleRef?.timeSlotId
1418
+ }));
1598
1419
  });
1599
1420
  return {
1600
1421
  item: {
1601
1422
  ...candidate,
1602
- status: (gate == null ? void 0 : gate.status) || candidateStatus,
1423
+ status: gate?.status || candidateStatus,
1603
1424
  maxPartySize: gate ? 0 : Number.isFinite(maxPartySize) ? maxPartySize : 0,
1604
- reasonCodes: (gate == null ? void 0 : gate.reasonCodes) || Array.from(new Set(requiredGroups.flatMap((group) => group.reasonCodes))),
1425
+ reasonCodes: gate?.reasonCodes || Array.from(new Set(requiredGroups.flatMap(group => group.reasonCodes))),
1605
1426
  requirementGroups
1606
1427
  },
1607
1428
  assignments: gate ? [] : selectedAssignments,
1608
- gate: gate || void 0
1429
+ gate: gate || undefined
1609
1430
  };
1610
1431
  }
1611
1432
  function assertTimeCandidates(request) {
1612
- if (request.target === "time" && (!Array.isArray(request.candidates) || request.candidates.length === 0)) {
1613
- throw availabilityError("MISSING_TIME_CANDIDATES", "target=time 必须传非空 candidates", {
1433
+ if (request.target === 'time' && (!Array.isArray(request.candidates) || request.candidates.length === 0)) {
1434
+ throw availabilityError('MISSING_TIME_CANDIDATES', 'target=time 必须传非空 candidates', {
1614
1435
  request
1615
1436
  });
1616
1437
  }
1617
1438
  }
1618
1439
  function assertSelectedRanges(request) {
1619
- if (request.target === "resource" && (!Array.isArray(request.selectedRanges) || request.selectedRanges.length === 0)) {
1620
- throw availabilityError("MISSING_SELECTED_RANGE", "target=resource 必须传非空 selectedRanges", {
1440
+ if (request.target === 'resource' && (!Array.isArray(request.selectedRanges) || request.selectedRanges.length === 0)) {
1441
+ throw availabilityError('MISSING_SELECTED_RANGE', 'target=resource 必须传非空 selectedRanges', {
1621
1442
  request
1622
1443
  });
1623
1444
  }
@@ -1627,14 +1448,16 @@ function assertRangesWithinProjection(projection, ranges) {
1627
1448
  start: toDateTime(projection.meta.coverage.startAt, projection.meta.timezone),
1628
1449
  end: toDateTime(projection.meta.coverage.endAt, projection.meta.timezone)
1629
1450
  };
1630
- ranges.forEach((range) => {
1451
+ ranges.forEach(range => {
1631
1452
  const displayRange = normalizeAbsoluteRange(range, projection.meta.timezone);
1632
1453
  const bookingRange = normalizeAbsoluteRange(getCandidateBookingRange(range), projection.meta.timezone);
1633
1454
  if (!displayRange || !bookingRange) {
1634
- throw availabilityError("INVALID_TARGET_FILTER", "候选时间范围非法", { range });
1455
+ throw availabilityError('INVALID_TARGET_FILTER', '候选时间范围非法', {
1456
+ range
1457
+ });
1635
1458
  }
1636
1459
  if (!contains(coverage, displayRange) || !contains(coverage, bookingRange)) {
1637
- throw availabilityError("OUT_OF_PROJECTION_COVERAGE", "候选时间范围超出 projection.coverage", {
1460
+ throw availabilityError('OUT_OF_PROJECTION_COVERAGE', '候选时间范围超出 projection.coverage', {
1638
1461
  range,
1639
1462
  coverage: projection.meta.coverage
1640
1463
  });
@@ -1642,22 +1465,17 @@ function assertRangesWithinProjection(projection, ranges) {
1642
1465
  });
1643
1466
  }
1644
1467
  function queryAvailabilityProjection(projection, request) {
1645
- var _a;
1646
1468
  const normalizedPartySize = normalizePartySize(request.partySize);
1647
- const evaluatedAt = formatDateTime(
1648
- (0, import_localClock.localDateToScalar)(/* @__PURE__ */ new Date()),
1649
- projection.meta.timezone,
1650
- (0, import_localClock.extractFixedOffsetMinutes)(projection.meta.now) ?? (0, import_localClock.captureRuntimeOffsetMinutes)()
1651
- );
1652
- if (request.target === "time") {
1469
+ const evaluatedAt = formatDateTime((0, _localClock.localDateToScalar)(new Date()), projection.meta.timezone, (0, _localClock.extractFixedOffsetMinutes)(projection.meta.now) ?? (0, _localClock.captureRuntimeOffsetMinutes)());
1470
+ if (request.target === 'time') {
1653
1471
  assertTimeCandidates(request);
1654
1472
  assertRangesWithinProjection(projection, request.candidates);
1655
1473
  }
1656
- if (request.target === "resource") {
1474
+ if (request.target === 'resource') {
1657
1475
  assertSelectedRanges(request);
1658
1476
  assertRangesWithinProjection(projection, request.selectedRanges);
1659
1477
  }
1660
- const productIdSet = ((_a = request.productIds) == null ? void 0 : _a.length) ? new Set(request.productIds.map(String)) : null;
1478
+ const productIdSet = request.productIds?.length ? new Set(request.productIds.map(String)) : null;
1661
1479
  const productIndex = buildProductIndex(projection);
1662
1480
  const cellIndex = buildAvailabilityCellIndex(projection);
1663
1481
  const groupResourceIndex = buildGroupResourceIndex({
@@ -1665,23 +1483,23 @@ function queryAvailabilityProjection(projection, request) {
1665
1483
  resourceIds: request.resourceIds,
1666
1484
  partySize: normalizedPartySize
1667
1485
  });
1668
- const candidateRangeCache = /* @__PURE__ */ new Map();
1486
+ const candidateRangeCache = new Map();
1669
1487
  const evaluationContext = buildEvaluationContext({
1670
1488
  projection,
1671
1489
  evaluatedAt,
1672
1490
  resourceIds: request.resourceIds
1673
1491
  });
1674
- if (request.target === "time") {
1492
+ if (request.target === 'time') {
1675
1493
  const candidates = dedupeProductRanges(request.candidates);
1676
- const candidatesByProduct = /* @__PURE__ */ new Map();
1677
- candidates.forEach((candidate) => {
1494
+ const candidatesByProduct = new Map();
1495
+ candidates.forEach(candidate => {
1678
1496
  const key = String(candidate.productId);
1679
- candidatesByProduct.set(key, [...candidatesByProduct.get(key) || [], candidate]);
1497
+ candidatesByProduct.set(key, [...(candidatesByProduct.get(key) || []), candidate]);
1680
1498
  });
1681
- const data2 = projection.products.filter((product) => !productIdSet || productIdSet.has(String(product.id))).map((product) => ({
1499
+ const data = projection.products.filter(product => !productIdSet || productIdSet.has(String(product.id))).map(product => ({
1682
1500
  productId: product.id,
1683
1501
  title: product.title,
1684
- times: (candidatesByProduct.get(String(product.id)) || []).map((candidate) => evaluateCandidate(projection, candidate, {
1502
+ times: (candidatesByProduct.get(String(product.id)) || []).map(candidate => evaluateCandidate(projection, candidate, {
1685
1503
  partySize: normalizedPartySize,
1686
1504
  resourceIds: request.resourceIds,
1687
1505
  evaluatedAt,
@@ -1691,25 +1509,25 @@ function queryAvailabilityProjection(projection, request) {
1691
1509
  candidateRangeCache,
1692
1510
  evaluationContext
1693
1511
  }).item)
1694
- })).filter((group) => group.times.length > 0);
1512
+ })).filter(group => group.times.length > 0);
1695
1513
  return {
1696
- target: "time",
1514
+ target: 'time',
1697
1515
  evaluatedAt,
1698
1516
  snapshotId: projection.meta.snapshotId,
1699
- data: data2
1517
+ data
1700
1518
  };
1701
1519
  }
1702
- if (request.target === "resource") {
1520
+ if (request.target === 'resource') {
1703
1521
  const selectedRanges = dedupeProductRanges(request.selectedRanges);
1704
- const rangesByProduct = /* @__PURE__ */ new Map();
1705
- selectedRanges.forEach((candidate) => {
1522
+ const rangesByProduct = new Map();
1523
+ selectedRanges.forEach(candidate => {
1706
1524
  const key = String(candidate.productId);
1707
- rangesByProduct.set(key, [...rangesByProduct.get(key) || [], candidate]);
1525
+ rangesByProduct.set(key, [...(rangesByProduct.get(key) || []), candidate]);
1708
1526
  });
1709
- const data2 = projection.products.filter((product) => !productIdSet || productIdSet.has(String(product.id))).map((product) => ({
1527
+ const data = projection.products.filter(product => !productIdSet || productIdSet.has(String(product.id))).map(product => ({
1710
1528
  productId: product.id,
1711
1529
  title: product.title,
1712
- ranges: (rangesByProduct.get(String(product.id)) || []).map((candidate) => {
1530
+ ranges: (rangesByProduct.get(String(product.id)) || []).map(candidate => {
1713
1531
  const evaluated = evaluateCandidate(projection, candidate, {
1714
1532
  partySize: normalizedPartySize,
1715
1533
  resourceIds: request.resourceIds,
@@ -1728,12 +1546,12 @@ function queryAvailabilityProjection(projection, request) {
1728
1546
  requirementGroups: evaluated.requirementGroups
1729
1547
  };
1730
1548
  })
1731
- })).filter((group) => group.ranges.length > 0);
1549
+ })).filter(group => group.ranges.length > 0);
1732
1550
  return {
1733
- target: "resource",
1551
+ target: 'resource',
1734
1552
  evaluatedAt,
1735
1553
  snapshotId: projection.meta.snapshotId,
1736
- data: data2
1554
+ data
1737
1555
  };
1738
1556
  }
1739
1557
  const dateRange = resolveProjectionDateRange(projection, request.dateRange);
@@ -1743,17 +1561,17 @@ function queryAvailabilityProjection(projection, request) {
1743
1561
  resourceIds: request.resourceIds,
1744
1562
  partySize: normalizedPartySize
1745
1563
  });
1746
- const candidateGroupMap = new Map(candidateGroups.map((group) => [String(group.productId), group]));
1747
- if (request.target === "date") {
1748
- const data2 = projection.products.filter((product) => !productIdSet || productIdSet.has(String(product.id))).map((product) => {
1564
+ const candidateGroupMap = new Map(candidateGroups.map(group => [String(group.productId), group]));
1565
+ if (request.target === 'date') {
1566
+ const data = projection.products.filter(product => !productIdSet || productIdSet.has(String(product.id))).map(product => {
1749
1567
  const group = candidateGroupMap.get(String(product.id)) || {
1750
1568
  productId: product.id,
1751
1569
  title: product.title,
1752
1570
  kind: product.kind,
1753
1571
  ranges: []
1754
1572
  };
1755
- const byDate = /* @__PURE__ */ new Map();
1756
- group.ranges.forEach((candidate) => {
1573
+ const byDate = new Map();
1574
+ group.ranges.forEach(candidate => {
1757
1575
  const evaluated = evaluateCandidateSummary(projection, candidate, {
1758
1576
  partySize: normalizedPartySize,
1759
1577
  resourceIds: request.resourceIds,
@@ -1770,30 +1588,30 @@ function queryAvailabilityProjection(projection, request) {
1770
1588
  maxPartySize: 0,
1771
1589
  remainingCapacity: 0,
1772
1590
  maxCapacity: 0,
1773
- reasonCodeSet: /* @__PURE__ */ new Set(),
1591
+ reasonCodeSet: new Set(),
1774
1592
  statusAggregate: createStatusAggregate()
1775
1593
  };
1776
1594
  aggregate.totalCount += 1;
1777
- if (evaluated.status === "available" || evaluated.status === "limited") {
1595
+ if (evaluated.status === 'available' || evaluated.status === 'limited') {
1778
1596
  aggregate.availableCount += 1;
1779
1597
  }
1780
1598
  aggregate.maxPartySize = Math.max(aggregate.maxPartySize, evaluated.maxPartySize);
1781
1599
  aggregate.remainingCapacity += evaluated.remainingCapacity;
1782
1600
  aggregate.maxCapacity += evaluated.maxCapacity;
1783
1601
  pushStatusAggregate(aggregate.statusAggregate, evaluated.status);
1784
- evaluated.reasonCodes.forEach((reasonCode) => aggregate.reasonCodeSet.add(reasonCode));
1602
+ evaluated.reasonCodes.forEach(reasonCode => aggregate.reasonCodeSet.add(reasonCode));
1785
1603
  byDate.set(candidate.date, aggregate);
1786
1604
  });
1787
- const dates = enumerateDates(dateRange.startDate, dateRange.endDate).map((date) => {
1605
+ const dates = enumerateDates(dateRange.startDate, dateRange.endDate).map(date => {
1788
1606
  const aggregate = byDate.get(date);
1789
1607
  return {
1790
1608
  date,
1791
- status: aggregate ? summarizeStatusAggregate(aggregate.statusAggregate) : "unavailable",
1792
- availableCount: (aggregate == null ? void 0 : aggregate.availableCount) || 0,
1793
- totalCount: (aggregate == null ? void 0 : aggregate.totalCount) || 0,
1794
- maxPartySize: (aggregate == null ? void 0 : aggregate.maxPartySize) || 0,
1795
- remainingCapacity: (aggregate == null ? void 0 : aggregate.remainingCapacity) || 0,
1796
- maxCapacity: (aggregate == null ? void 0 : aggregate.maxCapacity) || 0,
1609
+ status: aggregate ? summarizeStatusAggregate(aggregate.statusAggregate) : 'unavailable',
1610
+ availableCount: aggregate?.availableCount || 0,
1611
+ totalCount: aggregate?.totalCount || 0,
1612
+ maxPartySize: aggregate?.maxPartySize || 0,
1613
+ remainingCapacity: aggregate?.remainingCapacity || 0,
1614
+ maxCapacity: aggregate?.maxCapacity || 0,
1797
1615
  reasonCodes: aggregate ? Array.from(aggregate.reasonCodeSet) : []
1798
1616
  };
1799
1617
  });
@@ -1804,13 +1622,13 @@ function queryAvailabilityProjection(projection, request) {
1804
1622
  };
1805
1623
  });
1806
1624
  return {
1807
- target: "date",
1625
+ target: 'date',
1808
1626
  evaluatedAt,
1809
1627
  snapshotId: projection.meta.snapshotId,
1810
- data: data2
1628
+ data
1811
1629
  };
1812
1630
  }
1813
- const data = projection.products.filter((product) => !productIdSet || productIdSet.has(String(product.id))).map((product) => {
1631
+ const data = projection.products.filter(product => !productIdSet || productIdSet.has(String(product.id))).map(product => {
1814
1632
  const group = candidateGroupMap.get(String(product.id)) || {
1815
1633
  productId: product.id,
1816
1634
  title: product.title,
@@ -1818,12 +1636,12 @@ function queryAvailabilityProjection(projection, request) {
1818
1636
  ranges: []
1819
1637
  };
1820
1638
  const productStatusAggregate = createStatusAggregate();
1821
- const productReasonCodeSet = /* @__PURE__ */ new Set();
1822
- const dateStatusMap = /* @__PURE__ */ new Map();
1639
+ const productReasonCodeSet = new Set();
1640
+ const dateStatusMap = new Map();
1823
1641
  let availableTimeCount = 0;
1824
1642
  let totalTimeCount = 0;
1825
1643
  let maxPartySize = 0;
1826
- group.ranges.forEach((candidate) => {
1644
+ group.ranges.forEach(candidate => {
1827
1645
  const evaluated = evaluateCandidateSummary(projection, candidate, {
1828
1646
  partySize: normalizedPartySize,
1829
1647
  resourceIds: request.resourceIds,
@@ -1835,19 +1653,19 @@ function queryAvailabilityProjection(projection, request) {
1835
1653
  evaluationContext
1836
1654
  });
1837
1655
  totalTimeCount += 1;
1838
- if (evaluated.status === "available" || evaluated.status === "limited") {
1656
+ if (evaluated.status === 'available' || evaluated.status === 'limited') {
1839
1657
  availableTimeCount += 1;
1840
1658
  }
1841
1659
  maxPartySize = Math.max(maxPartySize, evaluated.maxPartySize);
1842
1660
  pushStatusAggregate(productStatusAggregate, evaluated.status);
1843
- evaluated.reasonCodes.forEach((reasonCode) => productReasonCodeSet.add(reasonCode));
1661
+ evaluated.reasonCodes.forEach(reasonCode => productReasonCodeSet.add(reasonCode));
1844
1662
  const dateAggregate = dateStatusMap.get(candidate.date) || createStatusAggregate();
1845
1663
  pushStatusAggregate(dateAggregate, evaluated.status);
1846
1664
  dateStatusMap.set(candidate.date, dateAggregate);
1847
1665
  });
1848
- const availableDateCount = Array.from(dateStatusMap.values()).filter((aggregate) => {
1849
- const status2 = summarizeStatusAggregate(aggregate);
1850
- return status2 === "available" || status2 === "limited";
1666
+ const availableDateCount = Array.from(dateStatusMap.values()).filter(aggregate => {
1667
+ const status = summarizeStatusAggregate(aggregate);
1668
+ return status === 'available' || status === 'limited';
1851
1669
  }).length;
1852
1670
  const totalDateCount = dateStatusMap.size;
1853
1671
  const status = summarizeStatusAggregate(productStatusAggregate);
@@ -1869,22 +1687,34 @@ function queryAvailabilityProjection(projection, request) {
1869
1687
  };
1870
1688
  });
1871
1689
  return {
1872
- target: "product",
1690
+ target: 'product',
1873
1691
  evaluatedAt,
1874
1692
  snapshotId: projection.meta.snapshotId,
1875
1693
  data
1876
1694
  };
1877
1695
  }
1696
+ function summarizeDateStatus(statuses) {
1697
+ if (!statuses.length) return 'unavailable';
1698
+ if (statuses.some(status => status === 'available')) return 'available';
1699
+ if (statuses.some(status => status === 'limited')) return 'limited';
1700
+ if (statuses.some(status => status === 'full')) return 'full';
1701
+ if (statuses.every(status => status === 'past')) return 'past';
1702
+ return 'unavailable';
1703
+ }
1704
+ function summarizeProductStatus(statuses) {
1705
+ if (!statuses.length) return 'unavailable';
1706
+ if (statuses.some(status => status === 'available')) return 'available';
1707
+ if (statuses.some(status => status === 'limited')) return 'limited';
1708
+ if (statuses.some(status => status === 'full')) return 'full';
1709
+ if (statuses.every(status => status === 'past')) return 'past';
1710
+ return 'unavailable';
1711
+ }
1878
1712
  function validateAvailabilitySelection(projection, params) {
1879
1713
  const normalizedPartySize = normalizePartySize(params.partySize);
1880
1714
  const normalizedBookingCount = normalizeBookingCount(params.bookingCount);
1881
1715
  assertRangesWithinProjection(projection, [params.candidate]);
1882
1716
  const productIndex = buildProductIndex(projection);
1883
- const evaluatedAt = formatDateTime(
1884
- (0, import_localClock.localDateToScalar)(/* @__PURE__ */ new Date()),
1885
- projection.meta.timezone,
1886
- (0, import_localClock.extractFixedOffsetMinutes)(projection.meta.now) ?? (0, import_localClock.captureRuntimeOffsetMinutes)()
1887
- );
1717
+ const evaluatedAt = formatDateTime((0, _localClock.localDateToScalar)(new Date()), projection.meta.timezone, (0, _localClock.extractFixedOffsetMinutes)(projection.meta.now) ?? (0, _localClock.captureRuntimeOffsetMinutes)());
1888
1718
  const evaluated = evaluateCandidate(projection, params.candidate, {
1889
1719
  partySize: normalizedPartySize,
1890
1720
  evaluatedAt,
@@ -1896,7 +1726,7 @@ function validateAvailabilitySelection(projection, params) {
1896
1726
  projection,
1897
1727
  partySize: normalizedPartySize
1898
1728
  }),
1899
- candidateRangeCache: /* @__PURE__ */ new Map(),
1729
+ candidateRangeCache: new Map(),
1900
1730
  evaluationContext: buildEvaluationContext({
1901
1731
  projection,
1902
1732
  evaluatedAt,
@@ -1907,27 +1737,27 @@ function validateAvailabilitySelection(projection, params) {
1907
1737
  if (evaluated.gate) {
1908
1738
  conflicts.push({
1909
1739
  code: evaluated.gate.conflictCode,
1910
- message: evaluated.gate.status === "past" ? "候选时间已过期" : "候选时间不满足预订时间门禁",
1740
+ message: evaluated.gate.status === 'past' ? '候选时间已过期' : '候选时间不满足预订时间门禁',
1911
1741
  reasonCodes: evaluated.gate.reasonCodes,
1912
1742
  productId: params.candidate.productId,
1913
1743
  candidateKey: params.candidate.key
1914
1744
  });
1915
1745
  return {
1916
1746
  ok: false,
1917
- status: evaluated.gate.status === "past" ? "past" : "conflict",
1747
+ status: evaluated.gate.status === 'past' ? 'past' : 'conflict',
1918
1748
  assignments: [],
1919
1749
  conflicts,
1920
1750
  availability: evaluated.item
1921
1751
  };
1922
1752
  }
1923
- const requiredGroups = evaluated.item.requirementGroups.filter((group) => group.required);
1924
- const findSelectionForGroup = (group) => params.requirementSelections.find((selection) => selection.requirementGroupId && selection.requirementGroupId === group.groupId || selection.formId !== void 0 && sameId(selection.formId, group.formId));
1925
- requiredGroups.forEach((group) => {
1753
+ const requiredGroups = evaluated.item.requirementGroups.filter(group => group.required);
1754
+ const findSelectionForGroup = group => params.requirementSelections.find(selection => selection.requirementGroupId && selection.requirementGroupId === group.groupId || selection.formId !== undefined && sameId(selection.formId, group.formId));
1755
+ requiredGroups.forEach(group => {
1926
1756
  const selection = findSelectionForGroup(group);
1927
- const selectedCount = new Set(((selection == null ? void 0 : selection.resourceIds) || []).map(String)).size;
1757
+ const selectedCount = new Set((selection?.resourceIds || []).map(String)).size;
1928
1758
  if (selectedCount < group.min) {
1929
1759
  conflicts.push({
1930
- code: "missing_resource",
1760
+ code: 'missing_resource',
1931
1761
  message: `资源组 ${group.groupId} 缺少已选资源`,
1932
1762
  productId: params.candidate.productId,
1933
1763
  requirementGroupId: group.groupId,
@@ -1938,7 +1768,7 @@ function validateAvailabilitySelection(projection, params) {
1938
1768
  }
1939
1769
  if (selectedCount > group.max) {
1940
1770
  conflicts.push({
1941
- code: "invalid_selection_count",
1771
+ code: 'invalid_selection_count',
1942
1772
  message: `资源组 ${group.groupId} 已选资源数量超出上限`,
1943
1773
  productId: params.candidate.productId,
1944
1774
  requirementGroupId: group.groupId,
@@ -1947,10 +1777,9 @@ function validateAvailabilitySelection(projection, params) {
1947
1777
  });
1948
1778
  return;
1949
1779
  }
1950
- if (group.status === "available" || group.status === "limited")
1951
- return;
1780
+ if (group.status === 'available' || group.status === 'limited') return;
1952
1781
  conflicts.push({
1953
- code: group.status === "past" ? "past" : group.status === "full" ? "resource_full" : "missing_resource",
1782
+ code: group.status === 'past' ? 'past' : group.status === 'full' ? 'resource_full' : 'missing_resource',
1954
1783
  message: `资源组 ${group.groupId} 当前不可提交`,
1955
1784
  productId: params.candidate.productId,
1956
1785
  requirementGroupId: group.groupId,
@@ -1958,12 +1787,12 @@ function validateAvailabilitySelection(projection, params) {
1958
1787
  candidateKey: params.candidate.key
1959
1788
  });
1960
1789
  });
1961
- params.requirementSelections.forEach((selection) => {
1962
- const matchedGroup = evaluated.item.requirementGroups.find((group) => selection.requirementGroupId ? group.groupId === selection.requirementGroupId : sameId(group.formId, selection.formId));
1790
+ params.requirementSelections.forEach(selection => {
1791
+ const matchedGroup = evaluated.item.requirementGroups.find(group => selection.requirementGroupId ? group.groupId === selection.requirementGroupId : sameId(group.formId, selection.formId));
1963
1792
  if (!matchedGroup) {
1964
1793
  conflicts.push({
1965
- code: "invalid_requirement_group",
1966
- message: "选择的资源组不属于当前商品",
1794
+ code: 'invalid_requirement_group',
1795
+ message: '选择的资源组不属于当前商品',
1967
1796
  productId: params.candidate.productId,
1968
1797
  requirementGroupId: selection.requirementGroupId,
1969
1798
  formId: selection.formId,
@@ -1974,7 +1803,7 @@ function validateAvailabilitySelection(projection, params) {
1974
1803
  const selectedCount = new Set(selection.resourceIds.map(String)).size;
1975
1804
  if (!matchedGroup.required && (selectedCount < matchedGroup.min || selectedCount > matchedGroup.max)) {
1976
1805
  conflicts.push({
1977
- code: "invalid_selection_count",
1806
+ code: 'invalid_selection_count',
1978
1807
  message: `资源组 ${matchedGroup.groupId} 已选资源数量不满足 ${matchedGroup.min}-${matchedGroup.max}`,
1979
1808
  productId: params.candidate.productId,
1980
1809
  requirementGroupId: matchedGroup.groupId,
@@ -1982,23 +1811,20 @@ function validateAvailabilitySelection(projection, params) {
1982
1811
  candidateKey: params.candidate.key
1983
1812
  });
1984
1813
  }
1985
- selection.resourceIds.forEach((resourceId) => {
1986
- const option = matchedGroup == null ? void 0 : matchedGroup.options.find((item) => sameId(item.resourceId, resourceId));
1987
- const matchedCell = option == null ? void 0 : option.matchedCell;
1988
- const remainingCapacity = matchedCell == null ? void 0 : matchedCell.remainingCapacity;
1989
- if (option && (option.status === "available" || option.status === "limited") && remainingCapacity !== void 0) {
1814
+ selection.resourceIds.forEach(resourceId => {
1815
+ const option = matchedGroup?.options.find(item => sameId(item.resourceId, resourceId));
1816
+ const matchedCell = option?.matchedCell;
1817
+ const remainingCapacity = matchedCell?.remainingCapacity;
1818
+ if (option && (option.status === 'available' || option.status === 'limited') && remainingCapacity !== undefined) {
1990
1819
  const capacityPerBooking = normalizedPartySize ?? 1;
1991
- const availableQuantity = Math.max(
1992
- 0,
1993
- Math.floor(remainingCapacity / capacityPerBooking)
1994
- );
1820
+ const availableQuantity = Math.max(0, Math.floor(remainingCapacity / capacityPerBooking));
1995
1821
  if (normalizedBookingCount > availableQuantity) {
1996
1822
  conflicts.push({
1997
- code: "resource_capacity_exceeded",
1823
+ code: 'resource_capacity_exceeded',
1998
1824
  message: `所选资源「${option.resourceName || String(resourceId)}」当前最多可预订 ${availableQuantity} 个`,
1999
1825
  productId: params.candidate.productId,
2000
- requirementGroupId: matchedGroup == null ? void 0 : matchedGroup.groupId,
2001
- formId: matchedGroup == null ? void 0 : matchedGroup.formId,
1826
+ requirementGroupId: matchedGroup?.groupId,
1827
+ formId: matchedGroup?.formId,
2002
1828
  resourceId,
2003
1829
  resourceName: option.resourceName,
2004
1830
  candidateKey: params.candidate.key,
@@ -2010,13 +1836,13 @@ function validateAvailabilitySelection(projection, params) {
2010
1836
  return;
2011
1837
  }
2012
1838
  }
2013
- if (!option || option.status !== "available" && option.status !== "limited") {
1839
+ if (!option || option.status !== 'available' && option.status !== 'limited') {
2014
1840
  conflicts.push({
2015
- code: "resource_unavailable",
1841
+ code: 'resource_unavailable',
2016
1842
  message: `资源 ${String(resourceId)} 当前不可提交`,
2017
1843
  productId: params.candidate.productId,
2018
- requirementGroupId: matchedGroup == null ? void 0 : matchedGroup.groupId,
2019
- formId: matchedGroup == null ? void 0 : matchedGroup.formId,
1844
+ requirementGroupId: matchedGroup?.groupId,
1845
+ formId: matchedGroup?.formId,
2020
1846
  resourceId,
2021
1847
  candidateKey: params.candidate.key
2022
1848
  });
@@ -2024,17 +1850,10 @@ function validateAvailabilitySelection(projection, params) {
2024
1850
  });
2025
1851
  });
2026
1852
  return {
2027
- ok: conflicts.length === 0 && (evaluated.item.status === "available" || evaluated.item.status === "limited"),
2028
- status: conflicts.length === 0 ? evaluated.item.status : "conflict",
1853
+ ok: conflicts.length === 0 && (evaluated.item.status === 'available' || evaluated.item.status === 'limited'),
1854
+ status: conflicts.length === 0 ? evaluated.item.status : 'conflict',
2029
1855
  assignments: evaluated.assignments,
2030
1856
  conflicts,
2031
1857
  availability: evaluated.item
2032
1858
  };
2033
- }
2034
- // Annotate the CommonJS export names for ESM import in node:
2035
- 0 && (module.exports = {
2036
- createAvailabilityProjection,
2037
- getProductTimeRanges,
2038
- queryAvailabilityProjection,
2039
- validateAvailabilitySelection
2040
- });
1859
+ }