@ryuzaki13/react-foundation-ui 1.8.11 → 1.8.13

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 (281) hide show
  1. package/dist/chunks/defaultRenderers-u_Vep6Pj.js.map +1 -1
  2. package/dist/chunks/multi-select-DtmRtX6v.js.map +1 -1
  3. package/dist/chunks/select-MN6zDPKM.js.map +1 -1
  4. package/dist/chunks/useODataSelectBase-DR4PrY1j.js.map +1 -1
  5. package/dist/multi-select/model/useODataMultiSelect.d.ts +1 -1
  6. package/dist/multi-select/model/useODataMultiSelect.d.ts.map +1 -1
  7. package/dist/multi-select/ui/MultiSelect.d.ts +1 -1
  8. package/dist/multi-select/ui/MultiSelect.d.ts.map +1 -1
  9. package/dist/multi-select/ui/SerializableMultiSelect.d.ts.map +1 -1
  10. package/dist/multi-select/ui/defaultRenderers.d.ts +1 -1
  11. package/dist/multi-select/ui/defaultRenderers.d.ts.map +1 -1
  12. package/dist/select/model/useODataSelect.d.ts +1 -1
  13. package/dist/select/model/useODataSelect.d.ts.map +1 -1
  14. package/dist/select/model/useODataSelectBase.d.ts +1 -1
  15. package/dist/select/model/useODataSelectBase.d.ts.map +1 -1
  16. package/dist/tree-select/index.js.map +1 -1
  17. package/dist/tree-select/model/buildODataTreeNodes.d.ts +1 -1
  18. package/dist/tree-select/model/buildODataTreeNodes.d.ts.map +1 -1
  19. package/package.json +3 -3
  20. package/storybook-static/assets/{Badge-BojMuOws.js → Badge-BFhBl7yM.js} +1 -1
  21. package/storybook-static/assets/{Badge.stories-C9gJRuIO.js → Badge.stories-BEu55X4s.js} +1 -1
  22. package/storybook-static/assets/{BadgeList-dNEWI7-O.js → BadgeList-mqmnGtRR.js} +1 -1
  23. package/storybook-static/assets/{Button-Bbt3JzIY.js → Button-CESdnWrO.js} +1 -1
  24. package/storybook-static/assets/{Button-CrTHXWYk.js → Button-_YPAu9sd.js} +1 -1
  25. package/storybook-static/assets/{Button.stories-BaYXOBeF.js → Button.stories-B0Y_T17B.js} +1 -1
  26. package/storybook-static/assets/{CalendarView-Dp0zus1o.js → CalendarView-DLuO2Ywz.js} +1 -1
  27. package/storybook-static/assets/{CheckBox-BiK9yWNt.js → CheckBox-CGiNmAPT.js} +1 -1
  28. package/storybook-static/assets/{CheckBox-UzYHaD6S.js → CheckBox-l0pbBm9z.js} +1 -1
  29. package/storybook-static/assets/{CheckBox.stories-C2BOiWZj.js → CheckBox.stories-ROPAnqh8.js} +1 -1
  30. package/storybook-static/assets/{Color-6AOWCN3U-C5S4wUlD.js → Color-6AOWCN3U-kPaRN0HS.js} +1 -1
  31. package/storybook-static/assets/{ContextMenu-D9EZxb7d.js → ContextMenu-Bob4DtRn.js} +1 -1
  32. package/storybook-static/assets/{ContextMenu-BWQln_tb.js → ContextMenu-DxfUAeJS.js} +1 -1
  33. package/storybook-static/assets/{ContextMenu.stories-U38zPh9_.js → ContextMenu.stories-DCJmWy5n.js} +1 -1
  34. package/storybook-static/assets/{CustomOptionButton-COIOrxQn.js → CustomOptionButton-DuEz9CXt.js} +1 -1
  35. package/storybook-static/assets/DateInput-BQ8SoeLE.js +1 -0
  36. package/storybook-static/assets/{DateInput-C-entb-r.js → DateInput-DmNLD4Dz.js} +1 -1
  37. package/storybook-static/assets/{DateInput.stories-NmUXQLDU.js → DateInput.stories-sTpYf7Ob.js} +1 -1
  38. package/storybook-static/assets/{DateRangePresetSelect-BM1L1KOo.js → DateRangePresetSelect-DRq4eJ2n.js} +1 -1
  39. package/storybook-static/assets/{DateRangePresetSelect-CAJzxaq0.js → DateRangePresetSelect-fgJfLaLO.js} +1 -1
  40. package/storybook-static/assets/{DateRangePresetSelect.stories-CC47XLnD.js → DateRangePresetSelect.stories-ib7Qh_BB.js} +1 -1
  41. package/storybook-static/assets/{Detail-CDluXtts.js → Detail-CJniLn1R.js} +1 -1
  42. package/storybook-static/assets/{Detail.stories-DjniLC4n.js → Detail.stories-BXjdNaL-.js} +1 -1
  43. package/storybook-static/assets/{Dialog-bzRSgGU7.js → Dialog-D6a_qzr3.js} +1 -1
  44. package/storybook-static/assets/{Dialog-bw6cHUsz.js → Dialog-DOYnnXlU.js} +1 -1
  45. package/storybook-static/assets/{Dialog.stories-C6Rs6s_p.js → Dialog.stories-B1cZLRK3.js} +1 -1
  46. package/storybook-static/assets/{Disclosure-DelA6FsQ.js → Disclosure-Chh5o_0s.js} +1 -1
  47. package/storybook-static/assets/{Disclosure.stories-D7yUinyb.js → Disclosure.stories-Cvdvf7V8.js} +1 -1
  48. package/storybook-static/assets/{DisclosureGroup-DbK7mU_Q.js → DisclosureGroup-CRnQOgVj.js} +1 -1
  49. package/storybook-static/assets/{DocsRenderer-JROSPFPF-v3z0zVN1.js → DocsRenderer-JROSPFPF-BBiErkeC.js} +1 -1
  50. package/storybook-static/assets/{DropZone-CI3NW4AC.js → DropZone-Bm5gqdi9.js} +1 -1
  51. package/storybook-static/assets/{DropZone-Cyy-ttEO.js → DropZone-C4L7JAlq.js} +1 -1
  52. package/storybook-static/assets/{DropZone.stories-S0HSpGTB.js → DropZone.stories-BhA9q8Qv.js} +1 -1
  53. package/storybook-static/assets/{DropdownMenu-Ct1bSZsE.js → DropdownMenu-DtZNSUHF.js} +1 -1
  54. package/storybook-static/assets/{ExpandableActionPanel-Oj3xlasj.js → ExpandableActionPanel-0ugXlJy_.js} +1 -1
  55. package/storybook-static/assets/{ExpandableActionPanel.stories-Ch7CLkpR.js → ExpandableActionPanel.stories-CLgMlA9k.js} +1 -1
  56. package/storybook-static/assets/{Flex-UiFm8Tkc.js → Flex-CJlia-8f.js} +1 -1
  57. package/storybook-static/assets/{Flex.stories-CaDiVlBD.js → Flex.stories-DCvfi1EY.js} +1 -1
  58. package/storybook-static/assets/{FloatingPopover-CTYV9W7T.js → FloatingPopover-CsnfPiwV.js} +1 -1
  59. package/storybook-static/assets/{FormattedTableCell-D-ysSR3p.js → FormattedTableCell-Dz5xWVYO.js} +1 -1
  60. package/storybook-static/assets/{Grid-CigBXPe2.js → Grid-C-KwTRAM.js} +1 -1
  61. package/storybook-static/assets/{Grid.stories-ay3z9DQZ.js → Grid.stories-CzbqknNR.js} +1 -1
  62. package/storybook-static/assets/{ImageCropper.stories-Dkce_ugb.js → ImageCropper.stories-DaE-gm-b.js} +1 -1
  63. package/storybook-static/assets/{ImageRenderer-C9_EmGBW.js → ImageRenderer-IsNCXCrZ.js} +1 -1
  64. package/storybook-static/assets/{ImageRenderer.stories-C7bXaNBI.js → ImageRenderer.stories-BbsjUEfJ.js} +1 -1
  65. package/storybook-static/assets/{ImageView.stories-D-ZzGlk8.js → ImageView.stories-DY0UITYz.js} +1 -1
  66. package/storybook-static/assets/{ImageViewer.stories-ItcRfxyy.js → ImageViewer.stories--9q9b9nv.js} +2 -2
  67. package/storybook-static/assets/{ImageViewerRuntime-eaupID36.js → ImageViewerRuntime-CNEH3XGN.js} +1 -1
  68. package/storybook-static/assets/{Input-op4j7Beh.js → Input-DLubbE7P.js} +1 -1
  69. package/storybook-static/assets/{Input-BUDNR96c.js → Input-Dw2JblYt.js} +1 -1
  70. package/storybook-static/assets/{Input.stories-DwulvJIg.js → Input.stories-DQeyDULG.js} +1 -1
  71. package/storybook-static/assets/{InputDateTime-B_cxOp6k.js → InputDateTime-BRzl8Vrt.js} +1 -1
  72. package/storybook-static/assets/{InputDateTime.stories-BueLF4HC.js → InputDateTime.stories-BJTj5v8J.js} +1 -1
  73. package/storybook-static/assets/{InputFile-DSg7sugm.js → InputFile-B8UroZji.js} +1 -1
  74. package/storybook-static/assets/{InputFile-C8z6TPc1.js → InputFile-DPftxjGw.js} +1 -1
  75. package/storybook-static/assets/{InputFile.stories-C6XSbIua.js → InputFile.stories-C7yyqHqc.js} +1 -1
  76. package/storybook-static/assets/{InputFiles-DoatLq3q.js → InputFiles-BQVXHVLy.js} +1 -1
  77. package/storybook-static/assets/{InputFiles-DeZ84Uu_.js → InputFiles-UD_9S7-N.js} +1 -1
  78. package/storybook-static/assets/{InputFiles.stories-BMbE8SJU.js → InputFiles.stories-DjjJS5pZ.js} +1 -1
  79. package/storybook-static/assets/{InputFilesList-CsfDDqaJ.js → InputFilesList-BNjijD3Y.js} +1 -1
  80. package/storybook-static/assets/{InputImage-yJSq98EH.js → InputImage-C-pHr-Sx.js} +1 -1
  81. package/storybook-static/assets/{InputImage-D_YnB2Tw.js → InputImage-DbcbqIhY.js} +1 -1
  82. package/storybook-static/assets/{InputImage.stories-nivUG307.js → InputImage.stories-D-TC9r9t.js} +1 -1
  83. package/storybook-static/assets/{InteractiveComponents-IPkC_Rkg.js → InteractiveComponents-DbhWgM9y.js} +1 -1
  84. package/storybook-static/assets/{InteractiveComponents.stories-D4VzVKZJ.js → InteractiveComponents.stories-B4mkVSea.js} +1 -1
  85. package/storybook-static/assets/{LayoutPicker-ZHkMi-LM.js → LayoutPicker-BoV_E3qt.js} +1 -1
  86. package/storybook-static/assets/{LayoutPicker-aMzPN5Do.js → LayoutPicker-w1EsRpYP.js} +1 -1
  87. package/storybook-static/assets/{LayoutPicker.stories-DAzpzxn5.js → LayoutPicker.stories--ChGnGxf.js} +1 -1
  88. package/storybook-static/assets/{Listbox-CR5nXC9J.js → Listbox-CzZRsr6a.js} +1 -1
  89. package/storybook-static/assets/{Listbox-BmfQOnrb.js → Listbox-qZdTi11b.js} +1 -1
  90. package/storybook-static/assets/{Listbox.stories-CeOpsKxB.js → Listbox.stories-C9k_1850.js} +1 -1
  91. package/storybook-static/assets/{Modal-BStp52Zo.js → Modal-DCpul8QM.js} +1 -1
  92. package/storybook-static/assets/{Modal.stories-CwGm5GYE.js → Modal.stories-Cy0Xd4LF.js} +1 -1
  93. package/storybook-static/assets/{ModalManagerProvider-B5uUXWF9.js → ModalManagerProvider-BrPVMcHV.js} +1 -1
  94. package/storybook-static/assets/{MultiSelect-308npb27.js → MultiSelect-BEQdl-F4.js} +1 -1
  95. package/storybook-static/assets/{MultiSelect-BDs02MZh.js → MultiSelect-Dy4X94E2.js} +1 -1
  96. package/storybook-static/assets/{MultiSelect.stories-DRi_iTp4.js → MultiSelect.stories-FuzejJmz.js} +1 -1
  97. package/storybook-static/assets/{NoData-ovaQEE9W.js → NoData-BsQ9EfkA.js} +1 -1
  98. package/storybook-static/assets/{NotificationsHost-BpeMWhLY.js → NotificationsHost-BSrODvw7.js} +1 -1
  99. package/storybook-static/assets/NotificationsHost.stories-DeiWspcY.js +29 -0
  100. package/storybook-static/assets/{ODataMultiSelect-qlft9L9L.js → ODataMultiSelect-BEz9gcd2.js} +1 -1
  101. package/storybook-static/assets/{ODataMultiSelect.stories-CMPIoPiy.js → ODataMultiSelect.stories-BwMgAkbE.js} +2 -2
  102. package/storybook-static/assets/{ODataSelect-DKBQArAK.js → ODataSelect-BgC7GHOv.js} +1 -1
  103. package/storybook-static/assets/{ODataSelect.stories-DxYIhtwe.js → ODataSelect.stories-DEIRAB-s.js} +2 -2
  104. package/storybook-static/assets/{ODataTreeMultiSelect-DAt6THP3.js → ODataTreeMultiSelect-WyZZuX0_.js} +1 -1
  105. package/storybook-static/assets/{ODataTreeMultiSelect.stories-CBNJP0mP.js → ODataTreeMultiSelect.stories-B6cxuagy.js} +1 -1
  106. package/storybook-static/assets/{ODataTreeSelect-Bzzt4CQC.js → ODataTreeSelect-OJGi3-S7.js} +1 -1
  107. package/storybook-static/assets/{ODataTreeSelect.stories-CWVvuqOt.js → ODataTreeSelect.stories-wGRGErCH.js} +1 -1
  108. package/storybook-static/assets/{OneStepScroller-BY5lz8qi.js → OneStepScroller-DycQ3RuL.js} +1 -1
  109. package/storybook-static/assets/{Option-Rw8xwrPg.js → Option-taxbFfjo.js} +1 -1
  110. package/storybook-static/assets/{OptionButton-Br48w_Zg.js → OptionButton-Q0QaPCII.js} +1 -1
  111. package/storybook-static/assets/{PeriodSelect-8-wpXz6w.js → PeriodSelect-DUJ2nF3F.js} +1 -1
  112. package/storybook-static/assets/{PeriodSelect-DxSRwKw-.js → PeriodSelect-DnM7x-uC.js} +1 -1
  113. package/storybook-static/assets/{PeriodSelect.stories-B-qgUzlS.js → PeriodSelect.stories-Db0bqe3J.js} +1 -1
  114. package/storybook-static/assets/{PickerTriggerActions-C5xmysFh.js → PickerTriggerActions-CGJd7xmR.js} +1 -1
  115. package/storybook-static/assets/{Popover-NJwt_vZZ.js → Popover-B6oidz5p.js} +1 -1
  116. package/storybook-static/assets/{Popover-Ds8PUigp.js → Popover-Tbhf8T81.js} +1 -1
  117. package/storybook-static/assets/{Popover.stories-CUOFA_Xf.js → Popover.stories-DE1LxZvv.js} +1 -1
  118. package/storybook-static/assets/{PresetRangeDateInput-DyOpbkON.js → PresetRangeDateInput-CQ5FfmrL.js} +1 -1
  119. package/storybook-static/assets/{PresetRangeDateInput-Xk0bOy3S.js → PresetRangeDateInput-DfgoR0My.js} +1 -1
  120. package/storybook-static/assets/{PresetRangeDateInput.stories-CwjUlXj0.js → PresetRangeDateInput.stories-CgoYAYBG.js} +1 -1
  121. package/storybook-static/assets/{PresetSelect-Ddmr0niY.js → PresetSelect-BPtAknAr.js} +1 -1
  122. package/storybook-static/assets/README--TpGcM95.js +110 -0
  123. package/storybook-static/assets/{README-MzcO5Z_P.js → README-4iBHWAP5.js} +1 -1
  124. package/storybook-static/assets/README-98KIgy5G.js +75 -0
  125. package/storybook-static/assets/README-B6II8p0W.js +154 -0
  126. package/storybook-static/assets/{README-CkWglKbS.js → README-B6vU5_7I.js} +1 -1
  127. package/storybook-static/assets/README-BFJVjjZR.js +325 -0
  128. package/storybook-static/assets/README-BIT4HlFP.js +441 -0
  129. package/storybook-static/assets/{README-DqQDgJN-.js → README-BK6Epio1.js} +1 -1
  130. package/storybook-static/assets/{README-DRnlD7y8.js → README-BRlPB5_k.js} +1 -1
  131. package/storybook-static/assets/README-BTFpEaXG.js +64 -0
  132. package/storybook-static/assets/README-BWhXgC_o.js +226 -0
  133. package/storybook-static/assets/README-BbNHvOoR.js +101 -0
  134. package/storybook-static/assets/README-BbunUo8b.js +118 -0
  135. package/storybook-static/assets/README-BmYpfZKr.js +1844 -0
  136. package/storybook-static/assets/README-Bu5uvhAT.js +230 -0
  137. package/storybook-static/assets/README-Bx6fvSBE.js +1282 -0
  138. package/storybook-static/assets/README-Bx6gIGiA.js +69 -0
  139. package/storybook-static/assets/README-ByhxsX44.js +324 -0
  140. package/storybook-static/assets/README-Byi1FFpw.js +98 -0
  141. package/storybook-static/assets/README-C3Is3a3B.js +208 -0
  142. package/storybook-static/assets/{README-C0BwTe5N.js → README-C6prwAXw.js} +1 -1
  143. package/storybook-static/assets/{README-BzVni2ve.js → README-C9S83J_G.js} +1 -1
  144. package/storybook-static/assets/{README-BHw6MVjD.js → README-CGgxP5Zo.js} +1 -1
  145. package/storybook-static/assets/README-CJ2ZVG2U.js +2124 -0
  146. package/storybook-static/assets/README-CNGCAm5d.js +95 -0
  147. package/storybook-static/assets/README-Cb_q420B.js +90 -0
  148. package/storybook-static/assets/README-CtIDPNJ3.js +186 -0
  149. package/storybook-static/assets/README-CzO3Dchr.js +64 -0
  150. package/storybook-static/assets/README-D0rzQthv.js +264 -0
  151. package/storybook-static/assets/README-DEQu7djz.js +1704 -0
  152. package/storybook-static/assets/README-DFihT_sm.js +296 -0
  153. package/storybook-static/assets/{README-CTIBG4zH.js → README-DMiWsyv_.js} +1 -1
  154. package/storybook-static/assets/README-DOx8lXXk.js +82 -0
  155. package/storybook-static/assets/{README-DzTc6XmY.js → README-DPhDjr43.js} +1 -1
  156. package/storybook-static/assets/{README-Dki94Nf2.js → README-D_NqWPa9.js} +1 -1
  157. package/storybook-static/assets/README-DhJ5Rb1w.js +355 -0
  158. package/storybook-static/assets/README-DlwhWCvQ.js +1146 -0
  159. package/storybook-static/assets/README-Dxi73a2Q.js +740 -0
  160. package/storybook-static/assets/README-Dycov6NP.js +583 -0
  161. package/storybook-static/assets/README-GTBkSG4_.js +3591 -0
  162. package/storybook-static/assets/README-Gfd9tLTw.js +672 -0
  163. package/storybook-static/assets/{README-B4zYJg6u.js → README-Kfawetke.js} +1 -1
  164. package/storybook-static/assets/{README-BzVHwvgo.js → README-UDBM7ZMT.js} +1 -1
  165. package/storybook-static/assets/{README-D-XjRtAE.js → README-VTWQxLm_.js} +1 -1
  166. package/storybook-static/assets/README-YHNWrTZv.js +400 -0
  167. package/storybook-static/assets/README-_0Otw0eD.js +448 -0
  168. package/storybook-static/assets/README-aw2Xu9xu.js +158 -0
  169. package/storybook-static/assets/README-eg1emlll.js +192 -0
  170. package/storybook-static/assets/README-iqQHdhZ4.js +416 -0
  171. package/storybook-static/assets/{README-BdPlV5nA.js → README-llxZAtCj.js} +1 -1
  172. package/storybook-static/assets/README-wFnEIGl4.js +95 -0
  173. package/storybook-static/assets/{README-Dx3cpFMr.js → README-webB1yH0.js} +1 -1
  174. package/storybook-static/assets/README-wtreGRCe.js +208 -0
  175. package/storybook-static/assets/{RadioButton-DWbkCY8u.js → RadioButton-CNOLDk_o.js} +1 -1
  176. package/storybook-static/assets/{RadioButton-oSvQ-gQh.js → RadioButton-Ce05_q7s.js} +1 -1
  177. package/storybook-static/assets/{RadioButton.stories-GNQ9XJZT.js → RadioButton.stories-CPd4lTl0.js} +1 -1
  178. package/storybook-static/assets/{RadioGroup-CvXjGXcV.js → RadioGroup-BtfD5Szd.js} +1 -1
  179. package/storybook-static/assets/{RadioGroup-eBYggurL.js → RadioGroup-Ccm7G-dv.js} +1 -1
  180. package/storybook-static/assets/{RadioGroup.stories-C77j_cPv.js → RadioGroup.stories-D-RRPHwi.js} +1 -1
  181. package/storybook-static/assets/{Scrollable-De6SdhI3.js → Scrollable-DvdBIeig.js} +1 -1
  182. package/storybook-static/assets/{Select-DQMoVjn4.js → Select-8PV9PBKF.js} +1 -1
  183. package/storybook-static/assets/{Select-Bwc0bEpt.js → Select-G04B41FM.js} +1 -1
  184. package/storybook-static/assets/{Select.stories-BSIJKJnM.js → Select.stories-Cf6CBhV8.js} +1 -1
  185. package/storybook-static/assets/{SerializableSelect-B73irNFR.js → SerializableSelect-CdQdHIVp.js} +1 -1
  186. package/storybook-static/assets/{SerializableSelect.stories-Z9q6g5v2.js → SerializableSelect.stories-0AKUGpbS.js} +1 -1
  187. package/storybook-static/assets/{SingleDateTimeInput-Dq9LcjnS.js → SingleDateTimeInput-DWT1ORhL.js} +1 -1
  188. package/storybook-static/assets/{SingleDateTimeInput-BHl6srqA.js → SingleDateTimeInput-DattLt6w.js} +1 -1
  189. package/storybook-static/assets/SingleDateTimeInput.stories-BLJV3jzV.js +48 -0
  190. package/storybook-static/assets/{Slider-CvM0N_kc.js → Slider-DKEI4cDd.js} +1 -1
  191. package/storybook-static/assets/{Slider-BiyKexKa.js → Slider-Dci-mJt_.js} +1 -1
  192. package/storybook-static/assets/{Slider.stories-CdHAMDmY.js → Slider.stories-Dp7oQueF.js} +1 -1
  193. package/storybook-static/assets/{SliderInput-DqHotbhB.js → SliderInput-BGPQmlgY.js} +1 -1
  194. package/storybook-static/assets/{SliderInput-Bx_oHUCV.js → SliderInput-BhjqGOKt.js} +1 -1
  195. package/storybook-static/assets/{SliderInput.stories-R5yDhghM.js → SliderInput.stories-DXNhRU3X.js} +1 -1
  196. package/storybook-static/assets/{Sortable-D7pjTHO0.js → Sortable-CMd9l1Gd.js} +1 -1
  197. package/storybook-static/assets/{Sortable.stories-CVLG6pSH.js → Sortable.stories-BN1cFEFJ.js} +1 -1
  198. package/storybook-static/assets/{Splitter-B1Du6XI2.js → Splitter-GL65TlI9.js} +1 -1
  199. package/storybook-static/assets/{Splitter.stories-D-FRLMCc.js → Splitter.stories-q5PlI6rB.js} +1 -1
  200. package/storybook-static/assets/{StateSelect-zl-yZibY.js → StateSelect-Byik5-iZ.js} +1 -1
  201. package/storybook-static/assets/{StateSelect-BJdUEUBc.js → StateSelect-CT0JF2V6.js} +1 -1
  202. package/storybook-static/assets/{StateSelect.stories-DgidR5zR.js → StateSelect.stories-pPEPmgnN.js} +1 -1
  203. package/storybook-static/assets/{StringToggle.stories-IucGrtIi.js → StringToggle.stories-BQlFmCE7.js} +1 -1
  204. package/storybook-static/assets/{Switch-BJrn2lIN.js → Switch--xGPiYk-.js} +1 -1
  205. package/storybook-static/assets/{Switch-5dCshvVo.js → Switch-DhaLI9CG.js} +1 -1
  206. package/storybook-static/assets/{Switch.stories-Dp_l3_JL.js → Switch.stories-5DisgIyj.js} +1 -1
  207. package/storybook-static/assets/{Table-BFAEcUde.js → Table-Bf6MicdG.js} +1 -1
  208. package/storybook-static/assets/{Table.stories-CohejRQv.js → Table.stories-CS7rQgj9.js} +2 -2
  209. package/storybook-static/assets/{Tabs-CI32QiHf.js → Tabs-B4y4y8Ap.js} +1 -1
  210. package/storybook-static/assets/{Tabs.stories-Cr9d9ipA.js → Tabs.stories-C0SpdGkh.js} +1 -1
  211. package/storybook-static/assets/{TabsBox-BlDOUDMF.js → TabsBox-C5LLLhnj.js} +1 -1
  212. package/storybook-static/assets/{TagInput-YZ7eSv8W.js → TagInput-D4BpuCoH.js} +1 -1
  213. package/storybook-static/assets/{TagInput.stories-DrOvsiI5.js → TagInput.stories-rn2wtZP_.js} +1 -1
  214. package/storybook-static/assets/{Text-DK4jN26J.js → Text-BhBt9e7C.js} +1 -1
  215. package/storybook-static/assets/{Textarea-CFh5f_lC.js → Textarea-B-Hz7ujq.js} +1 -1
  216. package/storybook-static/assets/{Textarea-DxPuWBZj.js → Textarea-BHHxY5sd.js} +1 -1
  217. package/storybook-static/assets/{Textarea.stories-CUeOGKYc.js → Textarea.stories-CZ2kWz20.js} +1 -1
  218. package/storybook-static/assets/{TimePanel-BUhFtwlb.js → TimePanel-Cg8fgFsn.js} +1 -1
  219. package/storybook-static/assets/{TimePanel.stories-CpXpMBDN.js → TimePanel.stories-BxdwtK2e.js} +1 -1
  220. package/storybook-static/assets/{Toggle-l2AvDbRn.js → Toggle-BWwfk9fi.js} +1 -1
  221. package/storybook-static/assets/{Toggle-BtApzBNH.js → Toggle-Ce9rbjBc.js} +1 -1
  222. package/storybook-static/assets/{Toggle.stories-Ca8otmAa.js → Toggle.stories-0fzcmzKy.js} +1 -1
  223. package/storybook-static/assets/{TreeMultiSelect-C9QDV63L.js → TreeMultiSelect-B06Y0lIc.js} +1 -1
  224. package/storybook-static/assets/{TreeMultiSelect-nXC4ZB5b.js → TreeMultiSelect-CrAdR_jM.js} +1 -1
  225. package/storybook-static/assets/{TreeMultiSelect.stories-6STRGJbF.js → TreeMultiSelect.stories-CAVoj56E.js} +1 -1
  226. package/storybook-static/assets/{TreePickerBase-Cq7fSw3x.js → TreePickerBase-qU2xPlar.js} +1 -1
  227. package/storybook-static/assets/{TreeSelect-CITef9rp.js → TreeSelect-BIHydJFy.js} +1 -1
  228. package/storybook-static/assets/{TreeSelect-rMNyu_KT.js → TreeSelect-BKnWu0C7.js} +1 -1
  229. package/storybook-static/assets/{TreeSelect.stories-B-Emz9yW.js → TreeSelect.stories-Dp_8ybAf.js} +1 -1
  230. package/storybook-static/assets/{TreeTable-CLiP0td3.js → TreeTable-pVXjU9dG.js} +1 -1
  231. package/storybook-static/assets/{TreeTable.stories-DJb9qt8k.js → TreeTable.stories-CV_8Ui6R.js} +2 -2
  232. package/storybook-static/assets/{blocks-DndFRxxW.js → blocks-BY40djCc.js} +3 -3
  233. package/storybook-static/assets/{check-CLkTJVXe.js → check-CVkPTDzp.js} +1 -1
  234. package/storybook-static/assets/{chevron-down-miCejSEn.js → chevron-down-BtNO4Vhe.js} +1 -1
  235. package/storybook-static/assets/{chevron-left-DhQIPej2.js → chevron-left-kceSK3sZ.js} +1 -1
  236. package/storybook-static/assets/{chevron-right-DfBBx-fr.js → chevron-right-BA_LO2El.js} +1 -1
  237. package/storybook-static/assets/{chunk-W22LQPXL-IdwKKJUw.js → chunk-W22LQPXL-yJiFPv6e.js} +1 -1
  238. package/storybook-static/assets/{circle-alert-DOmVf8am.js → circle-alert-B2m4a1zz.js} +1 -1
  239. package/storybook-static/assets/{circle-check-e7eSQ2VD.js → circle-check-Cfjhotlm.js} +1 -1
  240. package/storybook-static/assets/{components-Bm4Cw8Ap.js → components-D6yV8PLX.js} +3 -3
  241. package/storybook-static/assets/{contextMenu-DpIKeJju.js → contextMenu-B0pQBIW0.js} +1 -1
  242. package/storybook-static/assets/{defaultRenderers-m21aWJXz.js → defaultRenderers-DfmnIuod.js} +1 -1
  243. package/storybook-static/assets/{download-eAKjms6q.js → download-Lba8elK9.js} +1 -1
  244. package/storybook-static/assets/{floating-ui.react-DmilONq6.js → floating-ui.react-BFNvfMpU.js} +1 -1
  245. package/storybook-static/assets/formatters-D33Uh3si-CxrG0jsB.js +1 -0
  246. package/storybook-static/assets/foundationApi-DZhnQ2cC.js +59 -0
  247. package/storybook-static/assets/foundationLib-CoVTo22U.js +30 -0
  248. package/storybook-static/assets/{hooks-RI5_2AXI.js → hooks-Cc6eXN6U.js} +1 -1
  249. package/storybook-static/assets/iframe-BXM-JBuH.js +1 -0
  250. package/storybook-static/assets/{iframe-Sr0umHM6.js → iframe-o7sH2IXC.js} +4 -4
  251. package/storybook-static/assets/{info-DhJTzwcL.js → info-BpsVP_dM.js} +1 -1
  252. package/storybook-static/assets/odata-CXsbxsRY.js +1 -0
  253. package/storybook-static/assets/odata-service-CWkHASdw-DlmSiioz.js +3 -0
  254. package/storybook-static/assets/{odataStoryFixtures-lgnWIUp7.js → odataStoryFixtures-Dj_bzRqh.js} +1 -1
  255. package/storybook-static/assets/{react-YFbUKK0m.js → react-DeJ0JTHb.js} +1 -1
  256. package/storybook-static/assets/{refresh-cw-C7WnxX9M.js → refresh-cw-BvGyjEXe.js} +1 -1
  257. package/storybook-static/assets/{settings-2-0NVvyKQl.js → settings-2-BdXxnHos.js} +1 -1
  258. package/storybook-static/assets/{sortable-DDGG-Zfs.js → sortable-XkyN-9r5.js} +1 -1
  259. package/storybook-static/assets/{syntaxhighlighter-QEQV7K2Y-CxWm8nc8.js → syntaxhighlighter-QEQV7K2Y-Cafn3LV1.js} +1 -1
  260. package/storybook-static/assets/{trash-2-BnceCuhg.js → trash-2-uPH9-fND.js} +1 -1
  261. package/storybook-static/assets/{triangle-alert-MPi3GVgt.js → triangle-alert-tT01vSMw.js} +1 -1
  262. package/storybook-static/assets/{useODataSelectBase-hmjK6chS.js → useODataSelectBase-B2LlOwPZ.js} +1 -1
  263. package/storybook-static/assets/useODataTreeData-CU7Rqcb9.js +1 -0
  264. package/storybook-static/assets/{usePickerQuery-CtsGhieD.js → usePickerQuery-D8GHQ6KR.js} +1 -1
  265. package/storybook-static/assets/{usePickerTriggerController-EaI8W2G0.js → usePickerTriggerController-CcK1jmDy.js} +1 -1
  266. package/storybook-static/assets/{x-CqPSvWfo.js → x-BlAURbAH.js} +1 -1
  267. package/storybook-static/iframe.html +2 -2
  268. package/storybook-static/index.json +1 -1
  269. package/storybook-static/project.json +1 -1
  270. package/storybook-static/assets/DateInput-05Iy69_a.js +0 -1
  271. package/storybook-static/assets/NotificationsHost.stories-CgbFboVD.js +0 -29
  272. package/storybook-static/assets/SingleDateTimeInput.stories-C2RuBRAF.js +0 -48
  273. package/storybook-static/assets/async-SjiCjIjO.js +0 -625
  274. package/storybook-static/assets/formatters-CBgrBeSA-CqB1pZxS.js +0 -1
  275. package/storybook-static/assets/foundationLib-DRrox3tN.js +0 -25
  276. package/storybook-static/assets/iframe-DQo-3eW2.js +0 -1
  277. package/storybook-static/assets/odata-DhISMZkE.js +0 -1
  278. package/storybook-static/assets/odata-service-B6TxBSLm-Bq8Ya67E.js +0 -3
  279. package/storybook-static/assets/odataFetchFn-Dv6Xi5CI.js +0 -763
  280. package/storybook-static/assets/persisted-CUFiw53N.js +0 -600
  281. package/storybook-static/assets/useODataTreeData-CKuavbWc.js +0 -1
@@ -0,0 +1,1844 @@
1
+ import{n as e}from"./rolldown-runtime-DkW27tQK.js";import{n as t}from"./iframe-o7sH2IXC.js";import{i as n,r}from"./react-Bl2r1tuC.js";import{l as i,m as a}from"./blocks-BY40djCc.js";function o(e){let t={a:`a`,blockquote:`blockquote`,code:`code`,h1:`h1`,h2:`h2`,h3:`h3`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,table:`table`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,...n(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(i,{title:`Foundation Lib/API/OData Service`}),`
2
+ `,(0,c.jsx)(t.h1,{id:`odata-service`,children:`OData Service`}),`
3
+ `,(0,c.jsxs)(t.p,{children:[`Модуль `,(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/odata-service`}),` — единая библиотечная точка входа для работы с техническими контрактами OData V2:`]}),`
4
+ `,(0,c.jsxs)(t.ul,{children:[`
5
+ `,(0,c.jsx)(t.li,{children:`чтения XML metadata сервиса;`}),`
6
+ `,(0,c.jsx)(t.li,{children:`описания сущностей, колонок, параметров и Function Import;`}),`
7
+ `,(0,c.jsx)(t.li,{children:`преобразования JavaScript-значений в OData literals и обратно;`}),`
8
+ `,(0,c.jsx)(t.li,{children:`построения path для EntitySet и Function Import;`}),`
9
+ `,(0,c.jsxs)(t.li,{children:[`построения `,(0,c.jsx)(t.code,{children:`$filter`}),` и `,(0,c.jsx)(t.code,{children:`$orderby`}),`;`]}),`
10
+ `,(0,c.jsx)(t.li,{children:`нормализации конфигурации пользовательских фильтров;`}),`
11
+ `,(0,c.jsx)(t.li,{children:`поиска пар «код + текст» в справочных коллекциях;`}),`
12
+ `,(0,c.jsx)(t.li,{children:`хранения общих настроек OData-справочников.`}),`
13
+ `]}),`
14
+ `,(0,c.jsx)(t.p,{children:`README рассчитан на разработчика без доступа к исходному коду и с минимальным знанием JavaScript/TypeScript. В примерах объясняется не только вызов функции, но и смысл входных данных, результат, ограничения и типичные ошибки.`}),`
15
+ `,(0,c.jsxs)(t.blockquote,{children:[`
16
+ `,(0,c.jsxs)(t.p,{children:[`Ранее helpers справочных коллекций публиковались через отдельный subpath `,(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/odata`}),`. Теперь этот код входит в `,(0,c.jsx)(t.code,{children:`odata-service`}),`. Старый subpath удалён без compatibility alias.`]}),`
17
+ `]}),`
18
+ `,(0,c.jsx)(t.h2,{id:`содержание`,children:`Содержание`}),`
19
+ `,(0,c.jsxs)(t.ul,{children:[`
20
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D1%87%D1%82%D0%BE-%D0%B4%D0%B5%D0%BB%D0%B0%D0%B5%D1%82-%D0%B8-%D1%87%D0%B5%D0%B3%D0%BE-%D0%BD%D0%B5-%D0%B4%D0%B5%D0%BB%D0%B0%D0%B5%D1%82-%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C`,children:`Что делает и чего не делает модуль`})}),`
21
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-%D0%B8-%D0%B8%D0%BC%D0%BF%D0%BE%D1%80%D1%82`,children:`Установка и импорт`})}),`
22
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8-%D0%B8-%D1%81%D1%80%D0%B5%D0%B4%D0%B0-%D0%B2%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F`,children:`Зависимости и среда выполнения`})}),`
23
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#odata-v2-%D0%BF%D1%80%D0%BE%D1%81%D1%82%D1%8B%D0%BC%D0%B8-%D1%81%D0%BB%D0%BE%D0%B2%D0%B0%D0%BC%D0%B8`,children:`OData V2 простыми словами`})}),`
24
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%B1%D1%8B%D1%81%D1%82%D1%80%D1%8B%D0%B9-%D1%81%D1%82%D0%B0%D1%80%D1%82`,children:`Быстрый старт`})}),`
25
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BE%D0%B1%D1%89%D0%B8%D0%B9-%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85`,children:`Общий маршрут данных`})}),`
26
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%B1%D0%B0%D0%B7%D0%BE%D0%B2%D1%8B%D0%B5-%D1%82%D0%B8%D0%BF%D1%8B`,children:`Базовые типы`})}),`
27
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D1%87%D1%82%D0%B5%D0%BD%D0%B8%D0%B5-metadata`,children:`Чтение metadata`})}),`
28
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D1%82%D0%B8%D0%BF%D1%8B-%D1%80%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%D0%B0-metadata`,children:`Типы результата metadata`})}),`
29
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-odata-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B9`,children:`Форматирование OData-значений`})}),`
30
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BF%D0%B0%D1%80%D1%81%D0%B8%D0%BD%D0%B3-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B9`,children:`Парсинг значений`})}),`
31
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F-%D0%BF%D0%BE-%D1%83%D0%BC%D0%BE%D0%BB%D1%87%D0%B0%D0%BD%D0%B8%D1%8E-%D0%B8-formatter-registry`,children:`Значения по умолчанию и formatter registry`})}),`
32
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#wrapped-%D0%B8-unwrapped-%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D1%8B`,children:`Wrapped и unwrapped параметры`})}),`
33
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BF%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D0%BE%D0%B2-%D0%B8-path`,children:`Построение параметров и path`})}),`
34
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BF%D1%80%D0%BE%D1%81%D1%82%D1%8B%D0%B5-odata-%D1%84%D0%B8%D0%BB%D1%8C%D1%82%D1%80%D1%8B`,children:`Простые OData-фильтры`})}),`
35
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#compiled-filter-definitions`,children:`Compiled filter definitions`})}),`
36
+ `,(0,c.jsx)(t.li,{children:(0,c.jsxs)(t.a,{href:`#%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0-%D0%B8-orderby`,children:[`Сортировка и `,(0,c.jsx)(t.code,{children:`$orderby`})]})}),`
37
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#sql-like-%D1%84%D0%B8%D0%BB%D1%8C%D1%82%D1%80%D1%8B`,children:`SQL-like фильтры`})}),`
38
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BF%D0%B0%D1%80%D1%8B-%D0%BA%D0%BE%D0%B4--%D1%82%D0%B5%D0%BA%D1%81%D1%82`,children:`Пары «код + текст»`})}),`
39
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B8-odata-%D0%BA%D0%BE%D0%BB%D0%BB%D0%B5%D0%BA%D1%86%D0%B8%D0%B9`,children:`Настройки OData-коллекций`})}),`
40
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BC%D0%B0%D0%BB%D0%B5%D0%BD%D1%8C%D0%BA%D0%B8%D0%B5-helpers`,children:`Маленькие helpers`})}),`
41
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8-%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C-%D0%B8-%D0%BE%D0%B3%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D0%BD%D0%B8%D1%8F`,children:`Ошибки, безопасность и ограничения`})}),`
42
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#ssr-%D0%B1%D1%80%D0%B0%D1%83%D0%B7%D0%B5%D1%80-%D0%B8-react`,children:`SSR, браузер и React`})}),`
43
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-consumer-%D0%BA%D0%BE%D0%B4%D0%B0`,children:`Тестирование consumer-кода`})}),`
44
+ `,(0,c.jsx)(t.li,{children:(0,c.jsxs)(t.a,{href:`#%D0%BC%D0%B8%D0%B3%D1%80%D0%B0%D1%86%D0%B8%D1%8F-%D1%81-odata`,children:[`Миграция с `,(0,c.jsx)(t.code,{children:`/odata`})]})}),`
45
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D0%BF%D0%BE%D0%BB%D0%BD%D0%B0%D1%8F-%D0%BA%D0%B0%D1%80%D1%82%D0%B0-api`,children:`Полная карта API`})}),`
46
+ `,(0,c.jsx)(t.li,{children:(0,c.jsx)(t.a,{href:`#%D1%87%D0%B0%D1%81%D1%82%D1%8B%D0%B5-%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81%D1%8B`,children:`Частые вопросы`})}),`
47
+ `]}),`
48
+ `,(0,c.jsx)(t.h2,{id:`что-делает-и-чего-не-делает-модуль`,children:`Что делает и чего не делает модуль`}),`
49
+ `,(0,c.jsx)(t.h3,{id:`делает`,children:`Делает`}),`
50
+ `,(0,c.jsx)(t.p,{children:`Модуль выполняет локальные, предсказуемые преобразования:`}),`
51
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`XML metadata ──> ServiceMetadata
52
+ JS value ──> OData literal
53
+ params ──> operation path
54
+ filter tree ──> строка $filter
55
+ sort state ──> строка $orderby
56
+ rows ──> пары code/text
57
+ `})}),`
58
+ `,(0,c.jsx)(t.p,{children:`Большая часть функций не обращается к сети и не знает адрес backend.`}),`
59
+ `,(0,c.jsx)(t.h3,{id:`не-делает`,children:`Не делает`}),`
60
+ `,(0,c.jsx)(t.p,{children:`Модуль не выполняет HTTP-запросы и не управляет транспортом:`}),`
61
+ `,(0,c.jsxs)(t.ul,{children:[`
62
+ `,(0,c.jsxs)(t.li,{children:[`не вызывает `,(0,c.jsx)(t.code,{children:`fetch`}),`;`]}),`
63
+ `,(0,c.jsx)(t.li,{children:`не добавляет SAP SSO credentials;`}),`
64
+ `,(0,c.jsx)(t.li,{children:`не получает и не обновляет X-CSRF token;`}),`
65
+ `,(0,c.jsx)(t.li,{children:`не формирует TanStack Query keys;`}),`
66
+ `,(0,c.jsx)(t.li,{children:`не хранит server response cache;`}),`
67
+ `,(0,c.jsx)(t.li,{children:`не выполняет mutations;`}),`
68
+ `,(0,c.jsx)(t.li,{children:`не знает бизнес-правила конкретной таблицы или feature.`}),`
69
+ `]}),`
70
+ `,(0,c.jsx)(t.p,{children:`Для транспорта используйте отдельный пакет:`}),`
71
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import {} from /* transport API */ "@ryuzaki13/react-foundation-api/odata";
72
+ `})}),`
73
+ `,(0,c.jsx)(t.p,{children:`Граница пакетов выглядит так:`}),`
74
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`react-foundation-lib/odata-service
75
+ metadata, types, values, path, filter, sort
76
+
77
+
78
+ react-foundation-api/odata
79
+ HTTP, SAP Gateway, SSO, X-CSRF, Query/Mutation
80
+
81
+
82
+ host entity / feature / widget
83
+ бизнес-правила, экран, пользовательский сценарий
84
+ `})}),`
85
+ `,(0,c.jsx)(t.h2,{id:`установка-и-импорт`,children:`Установка и импорт`}),`
86
+ `,(0,c.jsx)(t.p,{children:`Установите пакет:`}),`
87
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-bash`,children:`npm install @ryuzaki13/react-foundation-lib
88
+ `})}),`
89
+ `,(0,c.jsx)(t.p,{children:`Для полного entrypoint также разрешите совместимые peer dependencies, если package manager не сделал этого автоматически:`}),`
90
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-bash`,children:`npm install zod zustand immer @tanstack/react-query
91
+ `})}),`
92
+ `,(0,c.jsx)(t.p,{children:`Импортируйте API только из опубликованного subpath:`}),`
93
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import {
94
+ buildEntityOperationPath,
95
+ buildODataFilter,
96
+ buildODataOrderBy,
97
+ odataFormatValue,
98
+ parseServiceMetadata
99
+ } from "@ryuzaki13/react-foundation-lib/odata-service";
100
+
101
+ import type {
102
+ EntityMetadata,
103
+ FilterExpression,
104
+ ServiceMetadata,
105
+ WrappedODataParameters
106
+ } from "@ryuzaki13/react-foundation-lib/odata-service";
107
+ `})}),`
108
+ `,(0,c.jsx)(t.p,{children:`Не используйте корневой или приватный импорт:`}),`
109
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`// Неверно: корневой entrypoint не опубликован.
110
+ import { buildODataFilter } from "@ryuzaki13/react-foundation-lib";
111
+
112
+ // Неверно: внутреннее расположение файлов не является публичным контрактом.
113
+ import { buildODataFilter } from "@ryuzaki13/react-foundation-lib/dist/odata-service/filters";
114
+
115
+ // Неверно: прежний subpath удалён после объединения модулей.
116
+ import { findCollectionPairs } from "@ryuzaki13/react-foundation-lib/odata-service";
117
+ `})}),`
118
+ `,(0,c.jsxs)(t.h3,{id:`import-type-для-typescript-типов`,children:[(0,c.jsx)(t.code,{children:`import type`}),` для TypeScript-типов`]}),`
119
+ `,(0,c.jsxs)(t.p,{children:[`TypeScript-типы исчезают из JavaScript после сборки. Для них предпочтителен `,(0,c.jsx)(t.code,{children:`import type`}),`:`]}),`
120
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import type { ServiceMetadata } from "@ryuzaki13/react-foundation-lib/odata-service";
121
+ `})}),`
122
+ `,(0,c.jsxs)(t.p,{children:[`Обычные функции и constants требуют обычного `,(0,c.jsx)(t.code,{children:`import`}),`:`]}),`
123
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import { parseServiceMetadata } from "@ryuzaki13/react-foundation-lib/odata-service";
124
+ `})}),`
125
+ `,(0,c.jsx)(t.h2,{id:`зависимости-и-среда-выполнения`,children:`Зависимости и среда выполнения`}),`
126
+ `,(0,c.jsxs)(t.p,{children:[`После объединения один entrypoint содержит и pure helpers, и Zustand store. Поэтому host, который импортирует `,(0,c.jsx)(t.code,{children:`odata-service`}),`, должен уметь разрешить runtime dependencies entrypoint.`]}),`
127
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Зависимость`}),(0,c.jsx)(t.th,{children:`Зачем нужна`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`zod`})}),(0,c.jsx)(t.td,{children:`Runtime-проверка OData primitive values и аргументов formatter factories.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`react`})}),(0,c.jsx)(t.td,{children:`Runtime для React-oriented Zustand hook API.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`zustand`})}),(0,c.jsx)(t.td,{children:`Глобальный store настроек OData-коллекций.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`immer`})}),(0,c.jsx)(t.td,{children:`Middleware для обновления Zustand store.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`@tanstack/react-query`})}),(0,c.jsxs)(t.td,{children:[`Тип и helper `,(0,c.jsx)(t.code,{children:`unwrapODataQueryResult`}),`.`]})]})]})]}),`
128
+ `,(0,c.jsx)(t.p,{children:`Среда выполнения различается по API:`}),`
129
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Группа`}),(0,c.jsx)(t.th,{children:`Где можно использовать`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Форматирование, filters, sorts, builders`}),(0,c.jsx)(t.td,{children:`Browser и Node.js, если не вызывается browser-only API.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`parseServiceMetadata`})}),(0,c.jsxs)(t.td,{children:[`Среда с глобальным `,(0,c.jsx)(t.code,{children:`DOMParser`}),`; обычно browser или DOM-полифил в Node.js.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`useODataCollectionStore`}),`, `,(0,c.jsx)(t.code,{children:`odataCollectionConfig`})]}),(0,c.jsx)(t.td,{children:`React/browser runtime; selector hooks вызываются по правилам React hooks.`})]})]})]}),`
130
+ `,(0,c.jsxs)(t.p,{children:[`Пакет распространяется как ESM. Используйте `,(0,c.jsx)(t.code,{children:`import`}),`, а не CommonJS `,(0,c.jsx)(t.code,{children:`require`}),`.`]}),`
131
+ `,(0,c.jsx)(t.h2,{id:`odata-v2-простыми-словами`,children:`OData V2 простыми словами`}),`
132
+ `,(0,c.jsx)(t.h3,{id:`сервис`,children:`Сервис`}),`
133
+ `,(0,c.jsx)(t.p,{children:`OData service — набор HTTP endpoints, объединённых общей схемой. Например:`}),`
134
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`/sap/opu/odata/sap/REPORT_SRV
135
+ `})}),`
136
+ `,(0,c.jsxs)(t.p,{children:[`В `,(0,c.jsx)(t.code,{children:`ODataServiceConfig`}),` имя сервиса хранится без предположений о полном origin:`]}),`
137
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const config = {
138
+ service: "REPORT_SRV",
139
+ target: "Sales"
140
+ };
141
+ `})}),`
142
+ `,(0,c.jsx)(t.h3,{id:`metadata`,children:`Metadata`}),`
143
+ `,(0,c.jsx)(t.p,{children:`Metadata — XML-документ, описывающий структуру сервиса:`}),`
144
+ `,(0,c.jsxs)(t.ul,{children:[`
145
+ `,(0,c.jsx)(t.li,{children:`какие сущности существуют;`}),`
146
+ `,(0,c.jsx)(t.li,{children:`какие поля есть у сущности;`}),`
147
+ `,(0,c.jsx)(t.li,{children:`какой тип у каждого поля;`}),`
148
+ `,(0,c.jsx)(t.li,{children:`какие параметры обязательны;`}),`
149
+ `,(0,c.jsx)(t.li,{children:`какие Function Import доступны;`}),`
150
+ `,(0,c.jsx)(t.li,{children:`какое поле является текстом для code-поля.`}),`
151
+ `]}),`
152
+ `,(0,c.jsxs)(t.p,{children:[`Обычно metadata получает transport-слой, а `,(0,c.jsx)(t.code,{children:`parseServiceMetadata`}),` превращает XML в удобный JavaScript-объект.`]}),`
153
+ `,(0,c.jsx)(t.h3,{id:`entityset-и-entitytype`,children:`EntitySet и EntityType`}),`
154
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`EntityType`}),` описывает форму записи, например поля `,(0,c.jsx)(t.code,{children:`ID`}),`, `,(0,c.jsx)(t.code,{children:`NAME`}),`, `,(0,c.jsx)(t.code,{children:`AMOUNT`}),`.`]}),`
155
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`EntitySet`}),` — адресуемая коллекция записей этого типа, например `,(0,c.jsx)(t.code,{children:`Sales`}),`.`]}),`
156
+ `,(0,c.jsxs)(t.p,{children:[`В config свойство `,(0,c.jsx)(t.code,{children:`target`}),` обычно содержит имя EntitySet.`]}),`
157
+ `,(0,c.jsx)(t.h3,{id:`function-import`,children:`Function Import`}),`
158
+ `,(0,c.jsx)(t.p,{children:`Function Import — операция OData V2, похожая на именованный backend-метод. Она может принимать query parameters и иметь HTTP method из metadata.`}),`
159
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`/REPORT_SRV/Recalculate?p_year=2026
160
+ `})}),`
161
+ `,(0,c.jsx)(t.h3,{id:`параметр-и-literal`,children:`Параметр и literal`}),`
162
+ `,(0,c.jsx)(t.p,{children:`JavaScript-строку нельзя бездумно вставлять в OData URL. Backend ожидает OData literal:`}),`
163
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`JavaScript: O'Reilly
164
+ OData: 'O''Reilly'
165
+
166
+ JavaScript: true
167
+ OData ABAP: 'X'
168
+
169
+ JavaScript: 12.5
170
+ Edm.Decimal: 12.5M
171
+ `})}),`
172
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`odataFormatValue`}),` знает эти правила, а builder применяет их в metadata-порядке.`]}),`
173
+ `,(0,c.jsxs)(t.h3,{id:`filter-и-orderby`,children:[(0,c.jsx)(t.code,{children:`$filter`}),` и `,(0,c.jsx)(t.code,{children:`$orderby`})]}),`
174
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`$filter`}),` ограничивает записи:`]}),`
175
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`STATUS eq 'A' and AMOUNT ge 100M
176
+ `})}),`
177
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`$orderby`}),` задаёт сортировку:`]}),`
178
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`YEAR asc,AMOUNT desc
179
+ `})}),`
180
+ `,(0,c.jsx)(t.p,{children:`Модуль строит только значение query option. Передачу в HTTP request выполняет transport.`}),`
181
+ `,(0,c.jsx)(t.h3,{id:`codetext-pair`,children:`Code/text pair`}),`
182
+ `,(0,c.jsx)(t.p,{children:`SAP-сервисы часто возвращают технический код и читаемый текст разными полями:`}),`
183
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
184
+ COMPANY: "1000",
185
+ COMPANY_txt: "Основная организация"
186
+ }
187
+ `})}),`
188
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`findCollectionPairs`}),` находит такие пары, а `,(0,c.jsx)(t.code,{children:`buildSeparatedArrays`}),` создаёт из rows отдельные справочные массивы.`]}),`
189
+ `,(0,c.jsx)(t.h2,{id:`быстрый-старт`,children:`Быстрый старт`}),`
190
+ `,(0,c.jsx)(t.p,{children:`Представим, что transport уже получил XML metadata как строку.`}),`
191
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import {
192
+ buildEntityOperationPath,
193
+ buildODataFilter,
194
+ buildODataOrderBy,
195
+ createFilterEqual,
196
+ parseServiceMetadata,
197
+ wrapODataParams
198
+ } from "@ryuzaki13/react-foundation-lib/odata-service";
199
+
200
+ type SalesRow = {
201
+ COMPANY: string;
202
+ YEAR: number;
203
+ AMOUNT: number;
204
+ };
205
+
206
+ const serviceMetadata = parseServiceMetadata(metadataXml);
207
+ const salesMetadata = serviceMetadata.entities.Sales;
208
+
209
+ if (!salesMetadata) {
210
+ throw new Error("В metadata отсутствует EntitySet Sales");
211
+ }
212
+
213
+ const params = wrapODataParams({
214
+ p_company: "1000",
215
+ p_year: 2026
216
+ });
217
+
218
+ const path = buildEntityOperationPath(salesMetadata, { service: "REPORT_SRV", target: "Sales" }, params, "query");
219
+
220
+ const filter = buildODataFilter(createFilterEqual<SalesRow, string>("COMPANY", "1000"));
221
+
222
+ const orderBy = buildODataOrderBy<SalesRow>([], [{ key: "AMOUNT", desc: true }]);
223
+
224
+ console.log(path);
225
+ console.log(filter); // COMPANY eq '1000'
226
+ console.log(orderBy); // AMOUNT desc
227
+ `})}),`
228
+ `,(0,c.jsxs)(t.p,{children:[`Transport затем использует `,(0,c.jsx)(t.code,{children:`path`}),`, `,(0,c.jsx)(t.code,{children:`filter`}),` и `,(0,c.jsx)(t.code,{children:`orderBy`}),` при создании request. Сам `,(0,c.jsx)(t.code,{children:`odata-service`}),` request не отправляет.`]}),`
229
+ `,(0,c.jsx)(t.h2,{id:`общий-маршрут-данных`,children:`Общий маршрут данных`}),`
230
+ `,(0,c.jsx)(t.p,{children:`Для большинства экранов полезен такой порядок:`}),`
231
+ `,(0,c.jsxs)(t.ol,{children:[`
232
+ `,(0,c.jsx)(t.li,{children:`Transport загружает metadata XML.`}),`
233
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`parseServiceMetadata`}),` создаёт `,(0,c.jsx)(t.code,{children:`ServiceMetadata`}),`.`]}),`
234
+ `,(0,c.jsxs)(t.li,{children:[`Consumer выбирает `,(0,c.jsx)(t.code,{children:`entities[target]`}),` или `,(0,c.jsx)(t.code,{children:`functionImports[target]`}),`.`]}),`
235
+ `,(0,c.jsxs)(t.li,{children:[`Значения формы оборачиваются через `,(0,c.jsx)(t.code,{children:`wrapODataParams`}),` или сразу создаются как `,(0,c.jsx)(t.code,{children:`{ value }`}),`.`]}),`
236
+ `,(0,c.jsx)(t.li,{children:`Builder проверяет параметры по metadata и создаёт path.`}),`
237
+ `,(0,c.jsxs)(t.li,{children:[`Filter helpers создают `,(0,c.jsx)(t.code,{children:`$filter`}),`.`]}),`
238
+ `,(0,c.jsxs)(t.li,{children:[`Sort helpers создают `,(0,c.jsx)(t.code,{children:`$orderby`}),`.`]}),`
239
+ `,(0,c.jsx)(t.li,{children:`API-пакет выполняет HTTP request.`}),`
240
+ `,(0,c.jsxs)(t.li,{children:[`UI форматирует ответ через `,(0,c.jsx)(t.code,{children:`formatters`}),`/table pipeline, а не через path builder.`]}),`
241
+ `]}),`
242
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`metadata XML
243
+ │ parseServiceMetadata
244
+
245
+ ServiceMetadata ──────────────┐
246
+
247
+ form values ─ wrap params ───┼─> path builder ─┐
248
+ │ │
249
+ filter values ─ compile ─────┼─> $filter ──────┼─> API transport
250
+ │ │
251
+ sort state ──────────────────┴─> $orderby ─────┘
252
+ `})}),`
253
+ `,(0,c.jsx)(t.h2,{id:`базовые-типы`,children:`Базовые типы`}),`
254
+ `,(0,c.jsx)(t.h3,{id:`basemetatype`,children:(0,c.jsx)(t.code,{children:`BaseMetaType`})}),`
255
+ `,(0,c.jsx)(t.p,{children:`UI работает с четырьмя укрупнёнными типами:`}),`
256
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type BaseMetaType = "string" | "number" | "boolean" | "date";
257
+ `})}),`
258
+ `,(0,c.jsx)(t.p,{children:`Constants и helpers:`}),`
259
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import {
260
+ BASE_META_TYPES,
261
+ BASE_META_TYPE_LABELS,
262
+ isBaseMetaType,
263
+ resolveBaseTypeLabel
264
+ } from "@ryuzaki13/react-foundation-lib/odata-service";
265
+
266
+ console.log(BASE_META_TYPES);
267
+ // ["string", "number", "boolean", "date"]
268
+
269
+ console.log(BASE_META_TYPE_LABELS.number); // "число"
270
+ console.log(isBaseMetaType("date")); // true
271
+ console.log(isBaseMetaType("datetime")); // false
272
+ console.log(resolveBaseTypeLabel("boolean")); // "boolean"
273
+ `})}),`
274
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`datetime`}),` — OData type, а `,(0,c.jsx)(t.code,{children:`date`}),` — укрупнённый UI type. Это разные уровни модели.`]}),`
275
+ `,(0,c.jsx)(t.h3,{id:`odatametatype`,children:(0,c.jsx)(t.code,{children:`ODataMetaType`})}),`
276
+ `,(0,c.jsx)(t.p,{children:`Поддерживаемые нормализованные OData-типы:`}),`
277
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Группа`}),(0,c.jsx)(t.th,{children:`Значения TypeScript`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`String-like`}),(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`"string"`}),`, `,(0,c.jsx)(t.code,{children:`"guid"`}),`, `,(0,c.jsx)(t.code,{children:`"binary"`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Numeric`}),(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`"byte"`}),`, `,(0,c.jsx)(t.code,{children:`"int"`}),`, `,(0,c.jsx)(t.code,{children:`"long"`}),`, `,(0,c.jsx)(t.code,{children:`"float"`}),`, `,(0,c.jsx)(t.code,{children:`"decimal"`}),`, `,(0,c.jsx)(t.code,{children:`"double"`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Boolean`}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`"boolean"`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`Date/time`}),(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`"datetime"`}),`, `,(0,c.jsx)(t.code,{children:`"datetimeOffset"`}),`, `,(0,c.jsx)(t.code,{children:`"time"`})]})]})]})]}),`
278
+ `,(0,c.jsx)(t.p,{children:`Связанные типы:`}),`
279
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type ODataBooleanType = "boolean";
280
+ type ODataNumericType = "byte" | "int" | "long" | "float" | "decimal" | "double";
281
+ type ODataIntegerType = "byte" | "int" | "long";
282
+ type ODataDateType = "datetime" | "datetimeOffset" | "time";
283
+ type ODataStringType = "string" | "guid" | "binary";
284
+ `})}),`
285
+ `,(0,c.jsx)(t.p,{children:`Type guards не проверяют значение поля. Они проверяют только строку типа:`}),`
286
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`isODataBooleanType("boolean"); // true
287
+ isODataNumericType("decimal"); // true
288
+ isODataIntegerType("float"); // false
289
+ isODataDateType("datetime"); // true
290
+ `})}),`
291
+ `,(0,c.jsx)(t.h3,{id:`getbasetypefromodatatype`,children:(0,c.jsx)(t.code,{children:`getBaseTypeFromODataType`})}),`
292
+ `,(0,c.jsx)(t.p,{children:`Преобразует подробный OData type в укрупнённый UI type:`}),`
293
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`getBaseTypeFromODataType("guid"); // "string"
294
+ getBaseTypeFromODataType("decimal"); // "number"
295
+ getBaseTypeFromODataType("boolean"); // "boolean"
296
+ getBaseTypeFromODataType("datetimeOffset"); // "date"
297
+ `})}),`
298
+ `,(0,c.jsx)(t.h3,{id:`http-и-operation-types`,children:`HTTP и operation types`}),`
299
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type BaseMethod = "GET" | "POST" | "PUT" | "DELETE";
300
+
301
+ type ODataOperationMethod = "create" | "update" | "delete" | "read" | "query" | "fi";
302
+ `})}),`
303
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`ODataOperationMethod`}),` описывает намерение consumer-кода. Это не готовый HTTP method. Например, Function Import отдельно хранит `,(0,c.jsx)(t.code,{children:`httpMethod`}),` из metadata.`]}),`
304
+ `,(0,c.jsx)(t.h2,{id:`чтение-metadata`,children:`Чтение metadata`}),`
305
+ `,(0,c.jsx)(t.h3,{id:`parseservicemetadata`,children:(0,c.jsx)(t.code,{children:`parseServiceMetadata`})}),`
306
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`function parseServiceMetadata(xmlText: string): ServiceMetadata;
307
+ `})}),`
308
+ `,(0,c.jsx)(t.p,{children:`Функция принимает полный EDMX/XML текст и возвращает:`}),`
309
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type ServiceMetadata = {
310
+ entities: Record<string, EntityMetadata>;
311
+ functionImports: Record<string, FunctionImportMetadata>;
312
+ };
313
+ `})}),`
314
+ `,(0,c.jsx)(t.p,{children:`Пример:`}),`
315
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const metadata = parseServiceMetadata(xmlText);
316
+
317
+ const entity = metadata.entities.Sales;
318
+ const action = metadata.functionImports.Recalculate;
319
+ `})}),`
320
+ `,(0,c.jsxs)(t.p,{children:[`Если имя неизвестно, результат обращения по ключу будет `,(0,c.jsx)(t.code,{children:`undefined`}),`. Проверяйте это явно до использования:`]}),`
321
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const entity = metadata.entities[target];
322
+
323
+ if (!entity) {
324
+ throw new Error(\`EntitySet \${target} отсутствует в metadata\`);
325
+ }
326
+ `})}),`
327
+ `,(0,c.jsx)(t.h3,{id:`какие-xml-типы-поддерживаются`,children:`Какие XML-типы поддерживаются`}),`
328
+ `,(0,c.jsx)(t.p,{children:`Parser нормализует следующие EDM-типы:`}),`
329
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`EDMX`}),(0,c.jsx)(t.th,{children:(0,c.jsx)(t.code,{children:`ODataMetaType`})})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.String`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`string`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.Guid`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`guid`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.Boolean`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`boolean`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.Byte`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`byte`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`Edm.Int16`}),`, `,(0,c.jsx)(t.code,{children:`Edm.Int32`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`int`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.Int64`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`long`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.Single`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`float`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.Decimal`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`decimal`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.Double`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`double`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.DateTime`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`datetime`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.DateTimeOffset`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`datetimeOffset`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.Time`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`time`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Edm.Binary`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`binary`})})]})]})]}),`
330
+ `,(0,c.jsx)(t.p,{children:`Неподдерживаемый scalar EDM type приводит к ошибке. Это намеренное поведение: неизвестный тип нельзя безопасно сериализовать как строку по догадке.`}),`
331
+ `,(0,c.jsx)(t.h3,{id:`nullable-mandatory-и-параметры`,children:`Nullable, mandatory и параметры`}),`
332
+ `,(0,c.jsx)(t.p,{children:`Parser учитывает несколько источников обязательности:`}),`
333
+ `,(0,c.jsxs)(t.ul,{children:[`
334
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`Nullable="false"`}),`;`]}),`
335
+ `,(0,c.jsxs)(t.li,{children:[`SAP attribute `,(0,c.jsx)(t.code,{children:`sap:parameter="mandatory"`}),`;`]}),`
336
+ `,(0,c.jsx)(t.li,{children:`key-поля параметризованной сущности;`}),`
337
+ `,(0,c.jsx)(t.li,{children:`action-for key для Function Import.`}),`
338
+ `]}),`
339
+ `,(0,c.jsx)(t.p,{children:`Для Function Import поддерживаются входные scalar параметры. Параметры с неподдерживаемым mode или не-scalar типом отклоняются.`}),`
340
+ `,(0,c.jsx)(t.h3,{id:`abap-boolean-like-string`,children:`ABAP boolean-like string`}),`
341
+ `,(0,c.jsxs)(t.p,{children:[`Строковое поле `,(0,c.jsx)(t.code,{children:`Edm.String`}),` с `,(0,c.jsx)(t.code,{children:`MaxLength="1"`}),` помечается:`]}),`
342
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`abapBooleanLike: true;
343
+ `})}),`
344
+ `,(0,c.jsxs)(t.p,{children:[`Это не превращает metadata type в `,(0,c.jsx)(t.code,{children:`boolean`}),`. Маркер нужен `,(0,c.jsx)(t.code,{children:`odataParseValueByMetadata`}),`, чтобы значения `,(0,c.jsx)(t.code,{children:`"X"`}),`, `,(0,c.jsx)(t.code,{children:`" "`}),` и `,(0,c.jsx)(t.code,{children:`""`}),` читать как boolean.`]}),`
345
+ `,(0,c.jsx)(t.h3,{id:`колонки-роли-и-codetext`,children:`Колонки, роли и code/text`}),`
346
+ `,(0,c.jsx)(t.p,{children:`Parser определяет:`}),`
347
+ `,(0,c.jsxs)(t.ul,{children:[`
348
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`label`}),` из SAP metadata с fallback на id;`]}),`
349
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`sortable`}),` и `,(0,c.jsx)(t.code,{children:`filterable`}),`;`]}),`
350
+ `,(0,c.jsxs)(t.li,{children:[`аналитическую роль `,(0,c.jsx)(t.code,{children:`measure`}),` или `,(0,c.jsx)(t.code,{children:`dimension`}),`;`]}),`
351
+ `,(0,c.jsxs)(t.li,{children:[`семантику `,(0,c.jsx)(t.code,{children:`none`}),`, `,(0,c.jsx)(t.code,{children:`code`}),` или `,(0,c.jsx)(t.code,{children:`text`}),`;`]}),`
352
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`linkedColumnId`}),` для пары code/text.`]}),`
353
+ `]}),`
354
+ `,(0,c.jsxs)(t.p,{children:[`Связь code/text ищется по SAP attribute и распространённым suffixes `,(0,c.jsx)(t.code,{children:`_txt`}),`, `,(0,c.jsx)(t.code,{children:`_text`}),`, `,(0,c.jsx)(t.code,{children:`_t`}),`. Дополнительно существуют проектные forced ids:`]}),`
355
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`FORCED_CODE_TEXT_IDS; // Set с "ZDIV" и "ZCFO1"
356
+ `})}),`
357
+ `,(0,c.jsx)(t.p,{children:`Helpers:`}),`
358
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`resolveMetadataColumnLabel({ id: "BUKRS", label: "" }); // "BUKRS"
359
+ isForcedCodeTextId("ZDIV"); // true
360
+ isForcedCodeTextFamilyId("zdiv_txt"); // true
361
+ `})}),`
362
+ `,(0,c.jsx)(t.p,{children:`Сравнение forced family выполняется без учёта регистра.`}),`
363
+ `,(0,c.jsx)(t.h3,{id:`filterable-columns`,children:`Filterable columns`}),`
364
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const columns = collectFilterableColumns(entity.columns);
365
+ const ids = collectFilterableColumnsIds(entity.columns);
366
+
367
+ console.log(ids.has("COMPANY"));
368
+ `})}),`
369
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`collectFilterableColumns`}),` возвращает новый массив только с `,(0,c.jsx)(t.code,{children:`filterable: true`}),`.`]}),`
370
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`collectFilterableColumnsIds`}),` возвращает readonly `,(0,c.jsx)(t.code,{children:`Set<string>`}),`, удобный для быстрых проверок.`]}),`
371
+ `,(0,c.jsx)(t.h3,{id:`важные-ограничения-parser`,children:`Важные ограничения parser`}),`
372
+ `,(0,c.jsxs)(t.ul,{children:[`
373
+ `,(0,c.jsxs)(t.li,{children:[`Нужен глобальный `,(0,c.jsx)(t.code,{children:`DOMParser`}),`.`]}),`
374
+ `,(0,c.jsx)(t.li,{children:`Функция не загружает metadata по URL.`}),`
375
+ `,(0,c.jsx)(t.li,{children:`Функция не кеширует результат.`}),`
376
+ `,(0,c.jsx)(t.li,{children:`Функция не проверяет server data rows по metadata.`}),`
377
+ `,(0,c.jsx)(t.li,{children:`Complex types не превращаются в обычные filterable scalar columns.`}),`
378
+ `,(0,c.jsx)(t.li,{children:`Служебные result-wrapper сущности могут быть скрыты из итоговой карты, а их columns перенесены к публичной parameterized entity.`}),`
379
+ `,(0,c.jsxs)(t.li,{children:[`Entity type с именем, заканчивающимся на `,(0,c.jsx)(t.code,{children:`Type`}),`, без result navigation не считается автоматически параметризованным.`]}),`
380
+ `]}),`
381
+ `,(0,c.jsx)(t.h2,{id:`типы-результата-metadata`,children:`Типы результата metadata`}),`
382
+ `,(0,c.jsx)(t.h3,{id:`entityparameterproperty`,children:(0,c.jsx)(t.code,{children:`EntityParameterProperty`})}),`
383
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type EntityParameterProperty = {
384
+ id: string;
385
+ label: string;
386
+ type: ODataMetaType;
387
+ originalType: string;
388
+ abapBooleanLike?: true;
389
+ nullable?: true;
390
+ mandatory?: true;
391
+ maxLength?: number;
392
+ precision?: number;
393
+ scale?: number;
394
+ parameter?: string;
395
+ };
396
+ `})}),`
397
+ `,(0,c.jsxs)(t.p,{children:[`Обратите внимание на optional flags: отсутствие `,(0,c.jsx)(t.code,{children:`mandatory`}),` означает «не отмечен обязательным», а не обязательно явное `,(0,c.jsx)(t.code,{children:`false`}),`.`]}),`
398
+ `,(0,c.jsx)(t.h3,{id:`entitycolumnproperty`,children:(0,c.jsx)(t.code,{children:`EntityColumnProperty`})}),`
399
+ `,(0,c.jsx)(t.p,{children:`Колонка содержит основную информацию параметра и UI/runtime metadata:`}),`
400
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type EntityColumnProperty = {
401
+ id: string;
402
+ label: string;
403
+ type: ODataMetaType;
404
+ originalType: string;
405
+ abapBooleanLike?: true;
406
+ maxLength?: number;
407
+ precision?: number;
408
+ scale?: number;
409
+ parameter?: string;
410
+ linkedColumnId?: string;
411
+ semanticType: "none" | "code" | "text";
412
+ sortable: boolean;
413
+ filterable: boolean;
414
+ role: "measure" | "dimension";
415
+ };
416
+ `})}),`
417
+ `,(0,c.jsx)(t.h3,{id:`entitymetadata`,children:(0,c.jsx)(t.code,{children:`EntityMetadata`})}),`
418
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type EntityMetadata = {
419
+ title: string;
420
+ columns: EntityColumnProperty[];
421
+ parameters?: EntityParameterProperty[];
422
+ result?: "Set" | "Results";
423
+ };
424
+ `})}),`
425
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`result`}),` задаёт navigation segment для параметризованной сущности.`]}),`
426
+ `,(0,c.jsx)(t.h3,{id:`functionimportmetadata`,children:(0,c.jsx)(t.code,{children:`FunctionImportMetadata`})}),`
427
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type FunctionImportMetadata = {
428
+ name: string;
429
+ title: string;
430
+ httpMethod?: "GET" | "POST" | "PUT" | "DELETE";
431
+ parameters?: EntityParameterProperty[];
432
+ returnType: string;
433
+ entitySet?: string;
434
+ actionFor?: string;
435
+ resultEntity?: string;
436
+ };
437
+ `})}),`
438
+ `,(0,c.jsx)(t.h3,{id:`остальные-metadata-типы`,children:`Остальные metadata-типы`}),`
439
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type ServiceEntities = Record<string, EntityMetadata>;
440
+ type ServiceFunctionImports = Record<string, FunctionImportMetadata>;
441
+ type ODataTargetMetadata = EntityMetadata | FunctionImportMetadata;
442
+
443
+ type EntityPropertyBase = {
444
+ id: string;
445
+ label: string;
446
+ };
447
+
448
+ type EntityPropertyBaseWithType = EntityPropertyBase & {
449
+ type: ODataMetaType;
450
+ };
451
+ `})}),`
452
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`ODataTargetMetadata`}),` удобен для generic UI, который умеет показывать как entity, так и Function Import.`]}),`
453
+ `,(0,c.jsx)(t.h2,{id:`форматирование-odata-значений`,children:`Форматирование OData-значений`}),`
454
+ `,(0,c.jsx)(t.h3,{id:`odataformatvalue`,children:(0,c.jsx)(t.code,{children:`odataFormatValue`})}),`
455
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`function odataFormatValue(type: ODataMetaType, value: unknown): string;
456
+ `})}),`
457
+ `,(0,c.jsx)(t.p,{children:`Функция сначала проверяет значение через Zod schema соответствующего типа, затем создаёт OData literal.`}),`
458
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`odataFormatValue("string", "O'Reilly");
459
+ // 'O''Reilly'
460
+
461
+ odataFormatValue("boolean", true);
462
+ // 'X'
463
+
464
+ odataFormatValue("decimal", 12.5);
465
+ // 12.5M
466
+ `})}),`
467
+ `,(0,c.jsx)(t.h3,{id:`таблица-сериализации`,children:`Таблица сериализации`}),`
468
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Тип`}),(0,c.jsx)(t.th,{children:`Допустимое JavaScript-значение`}),(0,c.jsx)(t.th,{children:`Пример результата`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`string`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`string`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`'Текст'`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`guid`})}),(0,c.jsxs)(t.td,{children:[`UUID или строка из hex-символов и `,(0,c.jsx)(t.code,{children:`-`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`guid'550e8400-e29b-41d4-a716-446655440000'`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`boolean`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`boolean`})}),(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`true`}),` → `,(0,c.jsx)(t.code,{children:`'X'`}),`, `,(0,c.jsx)(t.code,{children:`false`}),` → `,(0,c.jsx)(t.code,{children:`' '`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`byte`})}),(0,c.jsxs)(t.td,{children:[`integer `,(0,c.jsx)(t.code,{children:`0..255`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`255`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`int`})}),(0,c.jsx)(t.td,{children:`integer`}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`42`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`long`})}),(0,c.jsx)(t.td,{children:`integer`}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`42L`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`float`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`number`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`12.5F`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`decimal`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`number`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`12.5M`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`double`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`number`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`12.5D`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`datetime`})}),(0,c.jsxs)(t.td,{children:[`valid `,(0,c.jsx)(t.code,{children:`Date`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`datetime'2026-08-18T10:30:00'`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`datetimeOffset`})}),(0,c.jsxs)(t.td,{children:[`valid `,(0,c.jsx)(t.code,{children:`Date`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`datetimeoffset'2026-08-18T05:30:00.000Z'`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`time`})}),(0,c.jsxs)(t.td,{children:[`valid `,(0,c.jsx)(t.code,{children:`Date`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`time'PT10H30M00S'`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`binary`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`string`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`binary'BASE64...'`})})]})]})]}),`
469
+ `,(0,c.jsx)(t.h3,{id:`строки-и-одинарные-кавычки`,children:`Строки и одинарные кавычки`}),`
470
+ `,(0,c.jsx)(t.p,{children:`Одинарная кавычка внутри OData string literal удваивается:`}),`
471
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`odataFormatValue("string", "Иванов 'Тест'");
472
+ // 'Иванов ''Тест'''
473
+ `})}),`
474
+ `,(0,c.jsx)(t.p,{children:`Не экранируйте строку вручную перед вызовом. Иначе кавычки будут удвоены повторно.`}),`
475
+ `,(0,c.jsx)(t.h3,{id:`boolean-в-sapabap-форме`,children:`Boolean в SAP/ABAP форме`}),`
476
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`boolean`}),` сериализуется не как `,(0,c.jsx)(t.code,{children:`true`}),`/`,(0,c.jsx)(t.code,{children:`false`}),`, а как ABAP-like char:`]}),`
477
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`odataFormatValue("boolean", true); // "'X'"
478
+ odataFormatValue("boolean", false); // "' '"
479
+ `})}),`
480
+ `,(0,c.jsxs)(t.p,{children:[`Если конкретный endpoint ждёт `,(0,c.jsx)(t.code,{children:`0`}),`/`,(0,c.jsx)(t.code,{children:`1`}),`, выберите custom formatter из registry или передайте собственный formatter в wrapped parameter.`]}),`
481
+ `,(0,c.jsx)(t.h3,{id:`даты-и-timezone`,children:`Даты и timezone`}),`
482
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`ODataDateFormat`}),` предоставляет три formatter-а:`]}),`
483
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`ODataDateFormat.datetime(date);
484
+ ODataDateFormat.datetimeOffset(date);
485
+ ODataDateFormat.time(date);
486
+ `})}),`
487
+ `,(0,c.jsx)(t.p,{children:`Их семантика различается:`}),`
488
+ `,(0,c.jsxs)(t.ul,{children:[`
489
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`datetime`}),` использует видимые локальные календарные компоненты `,(0,c.jsx)(t.code,{children:`Date`}),` без suffix timezone;`]}),`
490
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`datetimeOffset`}),` вызывает `,(0,c.jsx)(t.code,{children:`date.toISOString()`}),` и поэтому сериализует UTC-время с `,(0,c.jsx)(t.code,{children:`Z`}),`;`]}),`
491
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`time`}),` использует локальные компоненты времени как ISO duration `,(0,c.jsx)(t.code,{children:`PT...`}),`.`]}),`
492
+ `]}),`
493
+ `,(0,c.jsxs)(t.p,{children:[`Не заменяйте один тип другим только потому, что оба принимают `,(0,c.jsx)(t.code,{children:`Date`}),`. Выбирайте тип из metadata endpoint.`]}),`
494
+ `,(0,c.jsx)(t.h3,{id:`runtime-validation`,children:`Runtime validation`}),`
495
+ `,(0,c.jsxs)(t.p,{children:[`Некорректное значение приводит к `,(0,c.jsx)(t.code,{children:`Error`}),`:`]}),`
496
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`odataFormatValue("int", 1.5);
497
+ // Error: значение не является integer
498
+
499
+ odataFormatValue("byte", 300);
500
+ // Error: значение больше 255
501
+
502
+ odataFormatValue("datetime", "2026-08-18");
503
+ // Error: ожидался Date, а не string
504
+ `})}),`
505
+ `,(0,c.jsx)(t.p,{children:`Основные schemas доступны напрямую:`}),`
506
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const result = odataTypeSchemas.decimal.safeParse(12.5);
507
+
508
+ if (!result.success) {
509
+ console.error(result.error);
510
+ }
511
+ `})}),`
512
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`guid`}),` принимает canonical UUID и более мягкую строку, содержащую только hex-символы и дефисы. Не считайте этот schema полной бизнес-проверкой UUID, если backend требует конкретную версию/форму.`]}),`
513
+ `,(0,c.jsx)(t.h3,{id:`primitive-guards`,children:`Primitive guards`}),`
514
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`isStringSafe("A"); // true
515
+ isNumberSafe(10); // true
516
+ isNumberSafe("10"); // false
517
+ isBooleanSafe(false); // true
518
+ isDateSafe(new Date()); // true для valid Date
519
+ `})}),`
520
+ `,(0,c.jsxs)(t.p,{children:[`Guards ничего не преобразуют. Например, `,(0,c.jsx)(t.code,{children:`isNumberSafe("10")`}),` не парсит строку.`]}),`
521
+ `,(0,c.jsx)(t.h2,{id:`парсинг-значений`,children:`Парсинг значений`}),`
522
+ `,(0,c.jsx)(t.h3,{id:`odataparsevalue`,children:(0,c.jsx)(t.code,{children:`odataParseValue`})}),`
523
+ `,(0,c.jsx)(t.p,{children:`Преобразует raw server/control value в JavaScript-представление по нормализованному OData type.`}),`
524
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`odataParseValue("boolean", "X"); // true
525
+ odataParseValue("boolean", " "); // false
526
+ odataParseValue("int", "1 234"); // 1234
527
+ odataParseValue("datetime", "2026-08-18"); // Date или null по правилам date parser
528
+ odataParseValue("string", 123); // "123"
529
+ `})}),`
530
+ `,(0,c.jsx)(t.h3,{id:`boolean-parsing`,children:`Boolean parsing`}),`
531
+ `,(0,c.jsx)(t.p,{children:`Распознаются:`}),`
532
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Input`}),(0,c.jsx)(t.th,{children:`Result`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`true`}),`, `,(0,c.jsx)(t.code,{children:`false`})]}),(0,c.jsx)(t.td,{children:`то же boolean`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`1`}),`, `,(0,c.jsx)(t.code,{children:`"1"`}),`, `,(0,c.jsx)(t.code,{children:`"true"`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`true`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`0`}),`, `,(0,c.jsx)(t.code,{children:`"0"`}),`, `,(0,c.jsx)(t.code,{children:`"false"`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`false`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`"X"`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`true`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`" "`}),`, `,(0,c.jsx)(t.code,{children:`""`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`false`})})]})]})]}),`
533
+ `,(0,c.jsxs)(t.p,{children:[`Регистр учитывается только для ABAP `,(0,c.jsx)(t.code,{children:`"X"`}),`: именно uppercase `,(0,c.jsx)(t.code,{children:`X`}),` распознаётся напрямую. Обычные `,(0,c.jsx)(t.code,{children:`true`}),`/`,(0,c.jsx)(t.code,{children:`false`}),` приводятся к lower case после `,(0,c.jsx)(t.code,{children:`trim`}),`.`]}),`
534
+ `,(0,c.jsx)(t.p,{children:`Если значение не распознано, функция возвращает исходное значение:`}),`
535
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`odataParseValue("boolean", "maybe"); // "maybe"
536
+ odataParseValue("boolean", 2); // 2
537
+ `})}),`
538
+ `,(0,c.jsxs)(t.p,{children:[`Поэтому результат нельзя без проверки безусловно присвоить переменной `,(0,c.jsx)(t.code,{children:`boolean`}),`.`]}),`
539
+ `,(0,c.jsx)(t.h3,{id:`numbers-и-dates`,children:`Numbers и dates`}),`
540
+ `,(0,c.jsxs)(t.p,{children:[`Numeric types делегируют parsing общему `,(0,c.jsx)(t.code,{children:`parseNumber`}),`, а date/time types — общему `,(0,c.jsx)(t.code,{children:`parseDate`}),` из `,(0,c.jsx)(t.code,{children:`formatters`}),`.`]}),`
541
+ `,(0,c.jsx)(t.p,{children:`Это означает:`}),`
542
+ `,(0,c.jsxs)(t.ul,{children:[`
543
+ `,(0,c.jsx)(t.li,{children:`строковые числа могут быть преобразованы;`}),`
544
+ `,(0,c.jsx)(t.li,{children:`невалидное число не обязано приводить к exception;`}),`
545
+ `,(0,c.jsxs)(t.li,{children:[`результат даты может быть `,(0,c.jsx)(t.code,{children:`Date`}),`, `,(0,c.jsx)(t.code,{children:`null`}),` или исходным/промежуточным значением согласно date parser contract.`]}),`
546
+ `]}),`
547
+ `,(0,c.jsxs)(t.p,{children:[`Для форматирования данных в UI используйте `,(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/formatters`}),`, а не `,(0,c.jsx)(t.code,{children:`odataParseValue`}),` как display formatter.`]}),`
548
+ `,(0,c.jsx)(t.h3,{id:`string-like-types`,children:`String-like types`}),`
549
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`string`}),`, `,(0,c.jsx)(t.code,{children:`guid`}),` и `,(0,c.jsx)(t.code,{children:`binary`}),` используют `,(0,c.jsx)(t.code,{children:`String(value)`}),`:`]}),`
550
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`odataParseValue("string", null); // "null"
551
+ odataParseValue("string", undefined); // "undefined"
552
+ `})}),`
553
+ `,(0,c.jsxs)(t.p,{children:[`Если `,(0,c.jsx)(t.code,{children:`null`}),` должен остаться `,(0,c.jsx)(t.code,{children:`null`}),`, отфильтруйте его до вызова.`]}),`
554
+ `,(0,c.jsx)(t.h3,{id:`odataparsevaluebymetadata`,children:(0,c.jsx)(t.code,{children:`odataParseValueByMetadata`})}),`
555
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`odataParseValueByMetadata({ type: "string", abapBooleanLike: true }, "X"); // true
556
+ `})}),`
557
+ `,(0,c.jsxs)(t.p,{children:[`Дополнительные правила для `,(0,c.jsx)(t.code,{children:`abapBooleanLike`}),`:`]}),`
558
+ `,(0,c.jsxs)(t.ul,{children:[`
559
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`null`}),` и `,(0,c.jsx)(t.code,{children:`undefined`}),` → `,(0,c.jsx)(t.code,{children:`false`}),`;`]}),`
560
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`"X"`}),` → `,(0,c.jsx)(t.code,{children:`true`}),`;`]}),`
561
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`" "`}),` и `,(0,c.jsx)(t.code,{children:`""`}),` → `,(0,c.jsx)(t.code,{children:`false`}),`;`]}),`
562
+ `,(0,c.jsx)(t.li,{children:`другое строковое значение проходит обычный parsing типа;`}),`
563
+ `,(0,c.jsx)(t.li,{children:`non-string значение не считается ABAP char и тоже проходит обычный parsing.`}),`
564
+ `]}),`
565
+ `,(0,c.jsxs)(t.p,{children:[`Например, boolean `,(0,c.jsx)(t.code,{children:`true`}),` при descriptor type `,(0,c.jsx)(t.code,{children:`string`}),` в итоге станет строкой `,(0,c.jsx)(t.code,{children:`"true"`}),`.`]}),`
566
+ `,(0,c.jsx)(t.h2,{id:`значения-по-умолчанию-и-formatter-registry`,children:`Значения по умолчанию и formatter registry`}),`
567
+ `,(0,c.jsx)(t.h3,{id:`defaultodatatypevalue`,children:(0,c.jsx)(t.code,{children:`defaultODataTypeValue`})}),`
568
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`defaultODataTypeValue("boolean"); // false
569
+ defaultODataTypeValue("decimal"); // 0
570
+ defaultODataTypeValue("datetime"); // новый Date
571
+ defaultODataTypeValue("string"); // ""
572
+ `})}),`
573
+ `,(0,c.jsxs)(t.p,{children:[`Для каждого вызова date/time создаётся новый `,(0,c.jsx)(t.code,{children:`Date`}),` с текущим моментом. Не используйте функцию как стабильную constant.`]}),`
574
+ `,(0,c.jsx)(t.h3,{id:`defaultcontroltypevalue`,children:(0,c.jsx)(t.code,{children:`defaultControlTypeValue`})}),`
575
+ `,(0,c.jsxs)(t.p,{children:[`То же правило на уровне `,(0,c.jsx)(t.code,{children:`BaseMetaType`}),`:`]}),`
576
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`defaultControlTypeValue("boolean"); // false
577
+ defaultControlTypeValue("number"); // 0
578
+ defaultControlTypeValue("date"); // новый Date
579
+ defaultControlTypeValue("string"); // ""
580
+ `})}),`
581
+ `,(0,c.jsxs)(t.p,{children:[`Это технический default, а не утверждение, что пустое поле пользователя должно иметь `,(0,c.jsx)(t.code,{children:`0`}),`, `,(0,c.jsx)(t.code,{children:`false`}),` или текущую дату. Решение о null/empty принадлежит форме и бизнес-сценарию.`]}),`
582
+ `,(0,c.jsx)(t.h3,{id:`formatter-description`,children:`Formatter description`}),`
583
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type ODataFormatterDescription = {
584
+ id: string;
585
+ label: string;
586
+ description: string;
587
+ fn: (...args: unknown[]) => string;
588
+ };
589
+ `})}),`
590
+ `,(0,c.jsx)(t.p,{children:`Registry нужен UI-редактору mapping, где пользователь выбирает способ сериализации.`}),`
591
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const items = getFormattersFor("datetime");
592
+
593
+ for (const item of items) {
594
+ console.log(item.id, item.label);
595
+ }
596
+ `})}),`
597
+ `,(0,c.jsx)(t.p,{children:`Доступные ids:`}),`
598
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Base type`}),(0,c.jsx)(t.th,{children:`Id`}),(0,c.jsx)(t.th,{children:`Назначение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`date`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`base`})}),(0,c.jsxs)(t.td,{children:[`OData `,(0,c.jsx)(t.code,{children:`datetime`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`date`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`datetimeOffset`})}),(0,c.jsxs)(t.td,{children:[`OData `,(0,c.jsx)(t.code,{children:`datetimeoffset`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`date`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`time`})}),(0,c.jsxs)(t.td,{children:[`OData `,(0,c.jsx)(t.code,{children:`time`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`date`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`stringAbapDate`})}),(0,c.jsxs)(t.td,{children:[`Строка ABAP `,(0,c.jsx)(t.code,{children:`YYYYMMDD`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`boolean`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`base`})}),(0,c.jsxs)(t.td,{children:[`ABAP boolean `,(0,c.jsx)(t.code,{children:`'X'`}),`/`,(0,c.jsx)(t.code,{children:`' '`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`boolean`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`asChar`})}),(0,c.jsxs)(t.td,{children:[`Строка `,(0,c.jsx)(t.code,{children:`'0'`}),`/`,(0,c.jsx)(t.code,{children:`'1'`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`boolean`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`asByte`})}),(0,c.jsxs)(t.td,{children:[`Число `,(0,c.jsx)(t.code,{children:`0`}),`/`,(0,c.jsx)(t.code,{children:`1`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`string`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`base`})}),(0,c.jsx)(t.td,{children:`Экранированная OData string`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`number`})}),(0,c.jsx)(t.td,{children:`—`}),(0,c.jsx)(t.td,{children:`Встроенных registry formatter-ов нет`})]})]})]}),`
599
+ `,(0,c.jsx)(t.p,{children:`API поиска:`}),`
600
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`getFormattersFor("datetime");
601
+ getFormattersForBaseType("date");
602
+ getFormatter("boolean", "asByte");
603
+ getFormatterForBaseType("boolean", "asChar");
604
+ `})}),`
605
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`getFormattersFor*`}),` возвращает массив с `,(0,c.jsx)(t.code,{children:`id`}),`.`]}),`
606
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`getFormatter*`}),` возвращает внутреннее описание конкретного элемента без добавленного поля `,(0,c.jsx)(t.code,{children:`id`}),` либо `,(0,c.jsx)(t.code,{children:`undefined`}),`, если id отсутствует.`]}),`
607
+ `,(0,c.jsx)(t.h3,{id:`formatter-factories`,children:`Formatter factories`}),`
608
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const joinStrings = createStringFormatter((...values) => {
609
+ return \`'\${values.join("-")}'\`;
610
+ });
611
+
612
+ joinStrings("A", "B"); // 'A-B'
613
+ joinStrings("A", 10); // Error validation
614
+ `})}),`
615
+ `,(0,c.jsx)(t.p,{children:`Фабрики:`}),`
616
+ `,(0,c.jsxs)(t.ul,{children:[`
617
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`createFormatter(schema, fn)`}),` — общий Zod-validated wrapper;`]}),`
618
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`createBooleanFormatter(fn)`}),` — все аргументы обязаны быть boolean;`]}),`
619
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`createStringFormatter(fn)`}),` — все аргументы обязаны быть string;`]}),`
620
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`createNumberFormatter(fn)`}),` — все аргументы обязаны быть number;`]}),`
621
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`createDateFormatter(fn)`}),` — все аргументы обязаны быть valid `,(0,c.jsx)(t.code,{children:`Date`}),`.`]}),`
622
+ `]}),`
623
+ `,(0,c.jsxs)(t.p,{children:[`Фабрика проверяет весь массив аргументов при каждом вызове. Она не проверяет, что возвращённая строка действительно является корректным OData literal; это ответственность функции `,(0,c.jsx)(t.code,{children:`fn`}),`.`]}),`
624
+ `,(0,c.jsx)(t.h2,{id:`wrapped-и-unwrapped-параметры`,children:`Wrapped и unwrapped параметры`}),`
625
+ `,(0,c.jsxs)(t.h3,{id:`зачем-нужна-обёртка--value-`,children:[`Зачем нужна обёртка `,(0,c.jsx)(t.code,{children:`{ value }`})]}),`
626
+ `,(0,c.jsx)(t.p,{children:`Плоские параметры удобны для формы:`}),`
627
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const flat = {
628
+ p_company: "1000",
629
+ p_year: 2026
630
+ };
631
+ `})}),`
632
+ `,(0,c.jsx)(t.p,{children:`Builder принимает wrapped форму, потому что каждому параметру можно назначить formatter:`}),`
633
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const wrapped = {
634
+ p_company: { value: "1000" },
635
+ p_year: { value: 2026 },
636
+ p_date: {
637
+ value: selectedDate,
638
+ formatter: ODataDateFormat.datetime
639
+ }
640
+ };
641
+ `})}),`
642
+ `,(0,c.jsx)(t.p,{children:`Основные типы:`}),`
643
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type ODataParameterValue = BaseType | BaseType[];
644
+
645
+ type ODataValue<T extends ODataParameterValue = ODataParameterValue> = {
646
+ value: T;
647
+ formatter?: (...args: unknown[]) => string;
648
+ };
649
+
650
+ type UnwrappedODataParameters = Record<string, ODataParameterValue>;
651
+ type WrappedODataParameters = Record<string, ODataValue>;
652
+ `})}),`
653
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`InputValue`}),`, `,(0,c.jsx)(t.code,{children:`UnwrappedInputParameters`}),` и `,(0,c.jsx)(t.code,{children:`WrappedInputParameters`}),` — аналогичные generic UI-контракты поверх `,(0,c.jsx)(t.code,{children:`InputType`}),`.`]}),`
654
+ `,(0,c.jsx)(t.h3,{id:`wrapodataparams`,children:(0,c.jsx)(t.code,{children:`wrapODataParams`})}),`
655
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const wrapped = wrapODataParams({
656
+ p_zero: 0,
657
+ p_enabled: false,
658
+ p_empty: ""
659
+ });
660
+ `})}),`
661
+ `,(0,c.jsx)(t.p,{children:`Результат:`}),`
662
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
663
+ p_zero: { value: 0 },
664
+ p_enabled: { value: false },
665
+ p_empty: { value: "" }
666
+ }
667
+ `})}),`
668
+ `,(0,c.jsxs)(t.p,{children:[`Функция сохраняет `,(0,c.jsx)(t.code,{children:`0`}),`, `,(0,c.jsx)(t.code,{children:`false`}),`, пустую строку и `,(0,c.jsx)(t.code,{children:`null`}),` буквально. Она не форматирует и не нормализует значения.`]}),`
669
+ `,(0,c.jsxs)(t.p,{children:[`Для `,(0,c.jsx)(t.code,{children:`null`}),` или `,(0,c.jsx)(t.code,{children:`undefined`}),` вместо всего объекта возвращается пустой объект.`]}),`
670
+ `,(0,c.jsx)(t.h3,{id:`unwrapodataparams`,children:(0,c.jsx)(t.code,{children:`unwrapODataParams`})}),`
671
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`unwrapODataParams({
672
+ z: { value: false },
673
+ a: { value: undefined }
674
+ });
675
+ `})}),`
676
+ `,(0,c.jsx)(t.p,{children:`Результат:`}),`
677
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
678
+ a: null,
679
+ z: false
680
+ }
681
+ `})}),`
682
+ `,(0,c.jsx)(t.p,{children:`Правила:`}),`
683
+ `,(0,c.jsxs)(t.ul,{children:[`
684
+ `,(0,c.jsx)(t.li,{children:`ключи сортируются по имени;`}),`
685
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`undefined`}),` внутри параметра превращается в `,(0,c.jsx)(t.code,{children:`null`}),`;`]}),`
686
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`formatter`}),` отбрасывается;`]}),`
687
+ `,(0,c.jsxs)(t.li,{children:[`отсутствующий аргумент даёт `,(0,c.jsx)(t.code,{children:`undefined`}),`;`]}),`
688
+ `,(0,c.jsx)(t.li,{children:`преобразование shallow: вложенные значения не клонируются.`}),`
689
+ `]}),`
690
+ `,(0,c.jsxs)(t.p,{children:[`Generic types `,(0,c.jsx)(t.code,{children:`WrapODataParameters<T>`}),` и `,(0,c.jsx)(t.code,{children:`UnwrapODataParameters<T>`}),` помогают сохранить имена и типы keys на этапе TypeScript.`]}),`
691
+ `,(0,c.jsx)(t.h2,{id:`построение-параметров-и-path`,children:`Построение параметров и path`}),`
692
+ `,(0,c.jsx)(t.h3,{id:`buildparameterentries`,children:(0,c.jsx)(t.code,{children:`buildParameterEntries`})}),`
693
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`function buildParameterEntries(
694
+ metadata: { parameters?: EntityParameterProperty[] },
695
+ params: WrappedODataParameters
696
+ ): Array<[string, string]>;
697
+ `})}),`
698
+ `,(0,c.jsxs)(t.p,{children:[`Функция идёт по `,(0,c.jsx)(t.code,{children:`metadata.parameters`}),`, а не по keys объекта `,(0,c.jsx)(t.code,{children:`params`}),`. Поэтому:`]}),`
699
+ `,(0,c.jsxs)(t.ul,{children:[`
700
+ `,(0,c.jsx)(t.li,{children:`порядок результата совпадает с metadata;`}),`
701
+ `,(0,c.jsxs)(t.li,{children:[`лишние keys в `,(0,c.jsx)(t.code,{children:`params`}),` игнорируются;`]}),`
702
+ `,(0,c.jsx)(t.li,{children:`отсутствующий optional параметр пропускается;`}),`
703
+ `,(0,c.jsxs)(t.li,{children:[`отсутствующий mandatory параметр приводит к `,(0,c.jsx)(t.code,{children:`Error`}),`.`]}),`
704
+ `]}),`
705
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const entries = buildParameterEntries(
706
+ {
707
+ parameters: [
708
+ { id: "p_company", label: "Компания", type: "string", originalType: "Edm.String", mandatory: true },
709
+ { id: "p_year", label: "Год", type: "int", originalType: "Edm.Int32" }
710
+ ]
711
+ },
712
+ {
713
+ p_company: { value: "1000" },
714
+ p_year: { value: 2026 }
715
+ }
716
+ );
717
+
718
+ // [["p_company", "'1000'"], ["p_year", "2026"]]
719
+ `})}),`
720
+ `,(0,c.jsx)(t.h3,{id:`mandatory-и-optional`,children:`Mandatory и optional`}),`
721
+ `,(0,c.jsxs)(t.p,{children:[`Только `,(0,c.jsx)(t.code,{children:`null`}),` и `,(0,c.jsx)(t.code,{children:`undefined`}),` считаются отсутствием:`]}),`
722
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
723
+ value: 0;
724
+ } // присутствует
725
+ {
726
+ value: false;
727
+ } // присутствует
728
+ {
729
+ value: "";
730
+ } // присутствует и будет отформатировано как ''
731
+ `})}),`
732
+ `,(0,c.jsx)(t.h3,{id:`массивы`,children:`Массивы`}),`
733
+ `,(0,c.jsx)(t.p,{children:`Обычный OData parameter является scalar. Поэтому массив разрешён только вместе с custom formatter:`}),`
734
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const params = {
735
+ p_ids: {
736
+ value: ["A", "B"],
737
+ formatter: (value: unknown) => {
738
+ const values = value as string[];
739
+ return \`'\${values.join(",")}'\`;
740
+ }
741
+ }
742
+ };
743
+ `})}),`
744
+ `,(0,c.jsx)(t.p,{children:`Без formatter builder выбросит ошибку «Массив значений OData-параметра требует custom formatter».`}),`
745
+ `,(0,c.jsx)(t.h3,{id:`maxlength`,children:(0,c.jsx)(t.code,{children:`maxLength`})}),`
746
+ `,(0,c.jsxs)(t.p,{children:[`Если parameter metadata содержит `,(0,c.jsx)(t.code,{children:`maxLength`}),`, builder ожидает literal в одинарных кавычках и проверяет число символов внутри них.`]}),`
747
+ `,(0,c.jsx)(t.p,{children:`Это важно:`}),`
748
+ `,(0,c.jsxs)(t.ul,{children:[`
749
+ `,(0,c.jsx)(t.li,{children:`string literal проверяется;`}),`
750
+ `,(0,c.jsxs)(t.li,{children:[`custom formatter тоже обязан вернуть `,(0,c.jsx)(t.code,{children:`'...'`}),`;`]}),`
751
+ `,(0,c.jsxs)(t.li,{children:[`numeric literal при наличии `,(0,c.jsx)(t.code,{children:`maxLength`}),` не пройдёт проверку, потому что не заключён в кавычки.`]}),`
752
+ `]}),`
753
+ `,(0,c.jsx)(t.p,{children:`Сообщение любой formatting-ошибки дополняется id параметра.`}),`
754
+ `,(0,c.jsx)(t.h3,{id:`buildentityparameters`,children:(0,c.jsx)(t.code,{children:`buildEntityParameters`})}),`
755
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const value = buildEntityParameters(entityMetadata, params);
756
+ // (p_company='1000',p_year=2026)
757
+ `})}),`
758
+ `,(0,c.jsxs)(t.p,{children:[`Каждый уже отформатированный literal проходит через `,(0,c.jsx)(t.code,{children:`encodeURIComponent`}),`. Не применяйте `,(0,c.jsx)(t.code,{children:`encodeURIComponent`}),` к value заранее.`]}),`
759
+ `,(0,c.jsx)(t.p,{children:`Если параметров нет, функция возвращает пустую строку.`}),`
760
+ `,(0,c.jsx)(t.h3,{id:`buildfunctionimportparameters`,children:(0,c.jsx)(t.code,{children:`buildFunctionImportParameters`})}),`
761
+ `,(0,c.jsxs)(t.p,{children:[`Возвращает стандартный `,(0,c.jsx)(t.code,{children:`URLSearchParams`}),`:`]}),`
762
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const searchParams = buildFunctionImportParameters(functionMetadata, params);
763
+ console.log(searchParams.toString());
764
+ `})}),`
765
+ `,(0,c.jsxs)(t.p,{children:[`Values сначала форматируются как OData literals, затем передаются в `,(0,c.jsx)(t.code,{children:`URLSearchParams`}),`. Не сравнивайте вручную raw query string, если порядок/encoding можно проверить через `,(0,c.jsx)(t.code,{children:`searchParams.get`}),`.`]}),`
766
+ `,(0,c.jsx)(t.h3,{id:`odataserviceconfig`,children:(0,c.jsx)(t.code,{children:`ODataServiceConfig`})}),`
767
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type ODataServiceConfig = {
768
+ service: string;
769
+ target: string;
770
+ };
771
+ `})}),`
772
+ `,(0,c.jsxs)(t.p,{children:[`Функции не очищают `,(0,c.jsx)(t.code,{children:`/`}),` и не валидируют имена. Передавайте именно ожидаемые segments без query string.`]}),`
773
+ `,(0,c.jsx)(t.h3,{id:`buildentitypath`,children:(0,c.jsx)(t.code,{children:`buildEntityPath`})}),`
774
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`buildEntityPath(metadata, config, params);
775
+ `})}),`
776
+ `,(0,c.jsx)(t.p,{children:`Формат:`}),`
777
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`/{service}/{target}{parameters}/{result}
778
+ `})}),`
779
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`/{result}`}),` добавляется только если metadata содержит `,(0,c.jsx)(t.code,{children:`result: "Set" | "Results"`}),`.`]}),`
780
+ `,(0,c.jsx)(t.h3,{id:`buildentityoperationpath`,children:(0,c.jsx)(t.code,{children:`buildEntityOperationPath`})}),`
781
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`buildEntityOperationPath(metadata, config, params, method);
782
+ `})}),`
783
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Method`}),(0,c.jsx)(t.th,{children:`Поведение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`create`})}),(0,c.jsxs)(t.td,{children:[`Возвращает только `,(0,c.jsx)(t.code,{children:`/{service}/{target}`}),`; params игнорируются.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`query`}),` для обычной entity`]}),(0,c.jsx)(t.td,{children:`Возвращает base path; params игнорируются.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`query`}),` для parameterized entity`]}),(0,c.jsxs)(t.td,{children:[`Добавляет параметры и `,(0,c.jsx)(t.code,{children:`/Set`}),` либо `,(0,c.jsx)(t.code,{children:`/Results`}),`.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`read`}),`, `,(0,c.jsx)(t.code,{children:`update`}),`, `,(0,c.jsx)(t.code,{children:`delete`})]}),(0,c.jsx)(t.td,{children:`Добавляет entity parameters, но не result navigation.`})]})]})]}),`
784
+ `,(0,c.jsxs)(t.p,{children:[`В development-сборке `,(0,c.jsx)(t.code,{children:`query`}),` с params для обычной entity пишет `,(0,c.jsx)(t.code,{children:`console.warn`}),`, потому что target не поддерживает parameterized query.`]}),`
785
+ `,(0,c.jsx)(t.h3,{id:`buildfunctionimportpath`,children:(0,c.jsx)(t.code,{children:`buildFunctionImportPath`})}),`
786
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const path = buildFunctionImportPath(functionMetadata, config, params);
787
+ // /REPORT_SRV/Recalculate?p_year=2026
788
+ `})}),`
789
+ `,(0,c.jsxs)(t.p,{children:[`Если serializable parameters нет, `,(0,c.jsx)(t.code,{children:`?`}),` не добавляется.`]}),`
790
+ `,(0,c.jsx)(t.h3,{id:`path-не-является-полным-url`,children:`Path не является полным URL`}),`
791
+ `,(0,c.jsxs)(t.p,{children:[`Результат начинается с `,(0,c.jsx)(t.code,{children:`/`}),`, но не содержит:`]}),`
792
+ `,(0,c.jsxs)(t.ul,{children:[`
793
+ `,(0,c.jsx)(t.li,{children:`origin;`}),`
794
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`/sap/opu/odata/sap`}),`;`]}),`
795
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`$filter`}),`, `,(0,c.jsx)(t.code,{children:`$orderby`}),`, `,(0,c.jsx)(t.code,{children:`$select`}),`, `,(0,c.jsx)(t.code,{children:`$expand`}),`;`]}),`
796
+ `,(0,c.jsx)(t.li,{children:`HTTP method и body;`}),`
797
+ `,(0,c.jsx)(t.li,{children:`authentication headers.`}),`
798
+ `]}),`
799
+ `,(0,c.jsx)(t.p,{children:`Соединять path с transport base URL должен API-слой.`}),`
800
+ `,(0,c.jsx)(t.h2,{id:`простые-odata-фильтры`,children:`Простые OData-фильтры`}),`
801
+ `,(0,c.jsx)(t.h3,{id:`два-этапа`,children:`Два этапа`}),`
802
+ `,(0,c.jsx)(t.p,{children:`Фильтр строится в два этапа:`}),`
803
+ `,(0,c.jsxs)(t.ol,{children:[`
804
+ `,(0,c.jsxs)(t.li,{children:[`Создать типизированное дерево `,(0,c.jsx)(t.code,{children:`FilterExpression`}),`.`]}),`
805
+ `,(0,c.jsxs)(t.li,{children:[`Сериализовать дерево через `,(0,c.jsx)(t.code,{children:`buildODataFilter`}),`.`]}),`
806
+ `]}),`
807
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type Row = {
808
+ STATUS: string;
809
+ AMOUNT: number;
810
+ };
811
+
812
+ const expression = createFilterEqual<Row, string>("STATUS", "A");
813
+ const filter = buildODataFilter(expression);
814
+
815
+ console.log(filter); // STATUS eq 'A'
816
+ `})}),`
817
+ `,(0,c.jsxs)(t.p,{children:[`Не собирайте `,(0,c.jsx)(t.code,{children:`$filter`}),` конкатенацией пользовательских строк. Formatter должен экранировать values.`]}),`
818
+ `,(0,c.jsx)(t.h3,{id:`операции`,children:`Операции`}),`
819
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`FilterOperation.eq; // "eq"
820
+ FilterOperation.ne; // "ne"
821
+ FilterOperation.gt; // "gt"
822
+ FilterOperation.ge; // "ge"
823
+ FilterOperation.lt; // "lt"
824
+ FilterOperation.le; // "le"
825
+ FilterOperation.startswith; // "startswith"
826
+ FilterOperation.endswith; // "endswith"
827
+ FilterOperation.contains; // "contains"
828
+ `})}),`
829
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`FILTER_OPERATIONS`}),` — readonly массив всех значений.`]}),`
830
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`isFilterOperation("contains"); // true
831
+ isFilterOperation("in"); // false
832
+
833
+ resolveOperationLabel("ge"); // "больше или равно"
834
+ `})}),`
835
+ `,(0,c.jsx)(t.h3,{id:`структура-выражения`,children:`Структура выражения`}),`
836
+ `,(0,c.jsx)(t.p,{children:`Leaf содержит conditions:`}),`
837
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type FilterCondition<T> = {
838
+ key: keyof T;
839
+ value: BaseType;
840
+ operation?: FilterOperation;
841
+ type?: ODataMetaType;
842
+ };
843
+
844
+ type FilterConditionExpression<T> = {
845
+ and?: boolean;
846
+ conditions: FilterCondition<T>[];
847
+ };
848
+ `})}),`
849
+ `,(0,c.jsx)(t.p,{children:`Nested node содержит другие filters:`}),`
850
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type FilterNestedExpression<T> = {
851
+ and?: boolean;
852
+ filters: FilterExpression<T>[];
853
+ };
854
+ `})}),`
855
+ `,(0,c.jsxs)(t.p,{children:[`Один node не может одновременно содержать `,(0,c.jsx)(t.code,{children:`conditions`}),` и `,(0,c.jsx)(t.code,{children:`filters`}),`.`]}),`
856
+ `,(0,c.jsxs)(t.h3,{id:`значение-and`,children:[`Значение `,(0,c.jsx)(t.code,{children:`and`})]}),`
857
+ `,(0,c.jsxs)(t.p,{children:[`По умолчанию `,(0,c.jsx)(t.code,{children:`and`}),` отсутствует или равно `,(0,c.jsx)(t.code,{children:`false`}),`. Это означает OR:`]}),`
858
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
859
+ conditions: [
860
+ { key: "STATUS", value: "A" },
861
+ { key: "STATUS", value: "B" }
862
+ ];
863
+ }
864
+ // (STATUS eq 'A' or STATUS eq 'B')
865
+ `})}),`
866
+ `,(0,c.jsxs)(t.p,{children:[`Для AND укажите `,(0,c.jsx)(t.code,{children:`and: true`}),`:`]}),`
867
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
868
+ and: true,
869
+ conditions: [
870
+ { key: "AMOUNT", operation: "ge", value: 100, type: "decimal" },
871
+ { key: "AMOUNT", operation: "le", value: 500, type: "decimal" }
872
+ ]
873
+ }
874
+ // (AMOUNT ge 100M and AMOUNT le 500M)
875
+ `})}),`
876
+ `,(0,c.jsx)(t.h3,{id:`вывод-odata-type-из-value`,children:`Вывод OData type из value`}),`
877
+ `,(0,c.jsxs)(t.p,{children:[`Если `,(0,c.jsx)(t.code,{children:`condition.type`}),` отсутствует, используется простое правило:`]}),`
878
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`JavaScript value`}),(0,c.jsx)(t.th,{children:`Выведенный OData type`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`number`}),` или `,(0,c.jsx)(t.code,{children:`bigint`})]}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`int`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`boolean`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`boolean`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Date`})}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`datetime`})})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:`остальное`}),(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`string`})})]})]})]}),`
879
+ `,(0,c.jsxs)(t.p,{children:[`Для decimal, long, datetimeOffset и других точных типов всегда задавайте `,(0,c.jsx)(t.code,{children:`type`}),` явно:`]}),`
880
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
881
+ key: "AMOUNT",
882
+ value: 12.5,
883
+ operation: "ge",
884
+ type: "decimal"
885
+ }
886
+ `})}),`
887
+ `,(0,c.jsxs)(t.p,{children:[`Иначе `,(0,c.jsx)(t.code,{children:`12.5`}),` будет проверено как `,(0,c.jsx)(t.code,{children:`int`}),` и formatting завершится ошибкой.`]}),`
888
+ `,(0,c.jsx)(t.h3,{id:`строковые-операции`,children:`Строковые операции`}),`
889
+ `,(0,c.jsx)(t.p,{children:`Результат соответствует OData V2 syntax:`}),`
890
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`buildODataFilter(createFilterContains<Row, string>("STATUS", "A"));
891
+ // substringof('A',STATUS)
892
+ `})}),`
893
+ `,(0,c.jsx)(t.p,{children:`Для starts/ends:`}),`
894
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`startswith(STATUS, 'A')
895
+ endswith(STATUS, 'A')
896
+ `})}),`
897
+ `,(0,c.jsx)(t.h3,{id:`createfilter`,children:(0,c.jsx)(t.code,{children:`createFilter`})}),`
898
+ `,(0,c.jsx)(t.p,{children:`Общая фабрика:`}),`
899
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`createFilter<Row, number>("AMOUNT", 100, "gt");
900
+ `})}),`
901
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`null`}),` и `,(0,c.jsx)(t.code,{children:`undefined`}),` создают пустой leaf `,(0,c.jsx)(t.code,{children:`{ conditions: [] }`}),`.`]}),`
902
+ `,(0,c.jsx)(t.p,{children:`Массив создаёт несколько conditions с OR по умолчанию:`}),`
903
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`createFilter<Row, string>("STATUS", ["A", "B"], "eq");
904
+ // после serialization: (STATUS eq 'A' or STATUS eq 'B')
905
+ `})}),`
906
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`null`}),`/`,(0,c.jsx)(t.code,{children:`undefined`}),` элементы массива удаляются. Остальные falsy values сохраняются.`]}),`
907
+ `,(0,c.jsx)(t.h3,{id:`фабрики`,children:`Фабрики`}),`
908
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`createFilterEqual("STATUS", "A");
909
+ createFilterContains("NAME", "сталь");
910
+ createFilterBetween("AMOUNT", [100, 500]);
911
+ `})}),`
912
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`createFilterBetween`}),` создаёт `,(0,c.jsx)(t.code,{children:`ge`}),` + `,(0,c.jsx)(t.code,{children:`le`}),` с AND.`]}),`
913
+ `,(0,c.jsxs)(t.p,{children:[`При одной пустой границе создаётся одностороннее условие. Но текущая реализация проверяет частичную границу по truthiness. Поэтому значения `,(0,c.jsx)(t.code,{children:`0`}),`, `,(0,c.jsx)(t.code,{children:`false`}),` и `,(0,c.jsx)(t.code,{children:`""`}),` как единственная граница будут потеряны:`]}),`
914
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`createFilterBetween("AMOUNT", [0, null]);
915
+ // пустое выражение, а не AMOUNT ge 0
916
+ `})}),`
917
+ `,(0,c.jsxs)(t.p,{children:[`Если нулевая открытая граница важна, создайте condition вручную через `,(0,c.jsx)(t.code,{children:`createFilter`}),`.`]}),`
918
+ `,(0,c.jsx)(t.h3,{id:`createfilterequalfalsy`,children:(0,c.jsx)(t.code,{children:`createFilterEqualFalsy`})}),`
919
+ `,(0,c.jsx)(t.p,{children:`Эта фабрика намеренно удаляет любые falsy values:`}),`
920
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`createFilterEqualFalsy("AMOUNT", 0); // пусто
921
+ createFilterEqualFalsy("ACTIVE", false); // пусто
922
+ createFilterEqualFalsy("TEXT", ""); // пусто
923
+ `})}),`
924
+ `,(0,c.jsxs)(t.p,{children:[`Используйте её только когда `,(0,c.jsx)(t.code,{children:`0`}),`, `,(0,c.jsx)(t.code,{children:`false`}),` и `,(0,c.jsx)(t.code,{children:`""`}),` действительно означают «фильтр не задан». В обычном случае нужен `,(0,c.jsx)(t.code,{children:`createFilterEqual`}),`.`]}),`
925
+ `,(0,c.jsx)(t.h3,{id:`mergefilterexpressions`,children:(0,c.jsx)(t.code,{children:`mergeFilterExpressions`})}),`
926
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const merged = mergeFilterExpressions(left, right);
927
+ `})}),`
928
+ `,(0,c.jsxs)(t.ul,{children:[`
929
+ `,(0,c.jsx)(t.li,{children:`если left отсутствует — возвращается right;`}),`
930
+ `,(0,c.jsx)(t.li,{children:`если right отсутствует — возвращается left;`}),`
931
+ `,(0,c.jsx)(t.li,{children:`если есть оба — создаётся nested AND;`}),`
932
+ `,(0,c.jsxs)(t.li,{children:[`если отсутствуют оба — результат `,(0,c.jsx)(t.code,{children:`undefined`}),`.`]}),`
933
+ `]}),`
934
+ `,(0,c.jsx)(t.h3,{id:`buildodatafilter`,children:(0,c.jsx)(t.code,{children:`buildODataFilter`})}),`
935
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const filterText = buildODataFilter(expression);
936
+ `})}),`
937
+ `,(0,c.jsx)(t.p,{children:`Правила:`}),`
938
+ `,(0,c.jsxs)(t.ul,{children:[`
939
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`undefined`}),` → `,(0,c.jsx)(t.code,{children:`""`}),`;`]}),`
940
+ `,(0,c.jsxs)(t.li,{children:[`пустой leaf/node → `,(0,c.jsx)(t.code,{children:`""`}),`;`]}),`
941
+ `,(0,c.jsx)(t.li,{children:`один child выводится без лишней групповой обёртки;`}),`
942
+ `,(0,c.jsx)(t.li,{children:`несколько children заключаются в скобки;`}),`
943
+ `,(0,c.jsxs)(t.li,{children:[`некорректная runtime-форма expression приводит к `,(0,c.jsx)(t.code,{children:`Error`}),`.`]}),`
944
+ `]}),`
945
+ `,(0,c.jsx)(t.h3,{id:`buildodatafilterrecursive`,children:(0,c.jsx)(t.code,{children:`buildODataFilterRecursive`})}),`
946
+ `,(0,c.jsx)(t.p,{children:`Альтернативный serializer использует более агрессивные скобки для вложенных выражений. Логический смысл тот же, но строка может отличаться:`}),`
947
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const normal = buildODataFilter(expression);
948
+ const explicit = buildODataFilterRecursive(expression);
949
+ `})}),`
950
+ `,(0,c.jsx)(t.p,{children:`Если backend/tests завязаны на точную форму строки, выберите один serializer и используйте его последовательно.`}),`
951
+ `,(0,c.jsx)(t.h3,{id:`typescript-не-валидирует-field-id-в-runtime`,children:`TypeScript не валидирует field id в runtime`}),`
952
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`keyof T`}),` помогает только во время компиляции:`]}),`
953
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`createFilterEqual<Row, string>("STATUS", "A");
954
+ `})}),`
955
+ `,(0,c.jsxs)(t.p,{children:[`После `,(0,c.jsx)(t.code,{children:`as`}),`, JSON parse или plain JavaScript в key может попасть произвольная строка. OData filter builder не имеет runtime allowlist field ids. Если definition приходит извне, сверяйте field id с metadata columns.`]}),`
956
+ `,(0,c.jsx)(t.h2,{id:`compiled-filter-definitions`,children:`Compiled filter definitions`}),`
957
+ `,(0,c.jsx)(t.p,{children:`Этот слой нужен сложным фильтрам, когда UI-конфигурация и текущее значение хранятся отдельно.`}),`
958
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`definitions + values
959
+
960
+ ├─ sanitize
961
+
962
+ compileFiltersToExpression
963
+
964
+
965
+ FilterExpression
966
+
967
+
968
+ buildODataFilter
969
+ `})}),`
970
+ `,(0,c.jsxs)(t.p,{children:[`Для простого программного фильтра этот слой не обязателен. Используйте `,(0,c.jsx)(t.code,{children:`createFilterEqual`}),` напрямую.`]}),`
971
+ `,(0,c.jsx)(t.h3,{id:`значения`,children:`Значения`}),`
972
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type ODataFilterValue = InputType | Record<string, string[]>;
973
+ type ODataFilterValues = Record<string, ODataFilterValue>;
974
+ `})}),`
975
+ `,(0,c.jsxs)(t.p,{children:[`Объект `,(0,c.jsx)(t.code,{children:`Record<string, string[]>`}),` представляет выбранные значения разных уровней tree filter:`]}),`
976
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const values = {
977
+ productTree: {
978
+ CATEGORY: ["A"],
979
+ GROUP: ["A1", "A2"]
980
+ }
981
+ };
982
+ `})}),`
983
+ `,(0,c.jsx)(t.h3,{id:`виды-definitions`,children:`Виды definitions`}),`
984
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type FilterDefinitionKind = "segment" | "tree" | "advanced" | "local" | "registered" | "column";
985
+ `})}),`
986
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Kind`}),(0,c.jsx)(t.th,{children:`Смысл`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`segment`})}),(0,c.jsx)(t.td,{children:`Select/multi-select, обычно загружаемый OData-справочник.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`tree`})}),(0,c.jsx)(t.td,{children:`Значения распределены по нескольким физическим колонкам уровней.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`local`})}),(0,c.jsx)(t.td,{children:`Локальный control с декларативным binding.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`registered`})}),(0,c.jsx)(t.td,{children:`Control предоставлен внешним runtime registry; owner column может отсутствовать.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`advanced`})}),(0,c.jsx)(t.td,{children:`Advanced-search выбирает массив значений owner column.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`column`})}),(0,c.jsx)(t.td,{children:`Обычное scalar/multiple значение колонки.`})]})]})]}),`
987
+ `,(0,c.jsx)(t.p,{children:`Component ids:`}),`
988
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type FilterSegmentComponentId = "select" | "multi-select";
989
+ type FilterTreeComponentId = "tree-select" | "tree-multi-select";
990
+ type FilterLocalComponentId = "select" | "multi-select" | "checkbox" | "text-input" | "number-input";
991
+ type FilterAdvancedComponentId = "advanced-search-select";
992
+ type FilterRegisteredValueMode = "single" | "multiple";
993
+ `})}),`
994
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`isFilterLocalComponentId(value)`}),` проверяет только local component ids.`]}),`
995
+ `,(0,c.jsx)(t.h3,{id:`segment`,children:`Segment`}),`
996
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const segmentDefinition: ODataCompiledFilterDefinition = {
997
+ id: "company",
998
+ ownerColumnId: "BUKRS",
999
+ columnIds: ["BUKRS"],
1000
+ kind: "segment",
1001
+ componentId: "multi-select",
1002
+ controlType: "string",
1003
+ dictionaryCodeKey: "BUKRS"
1004
+ };
1005
+ `})}),`
1006
+ `,(0,c.jsxs)(t.p,{children:[`Для `,(0,c.jsx)(t.code,{children:`select`}),` ожидается scalar. Для `,(0,c.jsx)(t.code,{children:`multi-select`}),` ожидается массив. Массив компилируется в OR из `,(0,c.jsx)(t.code,{children:`eq`}),` conditions.`]}),`
1007
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`dictionaryCodeKey`}),` задаёт key dependency при загрузке связанного справочника. Если он совпадает с `,(0,c.jsx)(t.code,{children:`ownerColumnId`}),`, sanitizer удаляет избыточное поле.`]}),`
1008
+ `,(0,c.jsx)(t.h3,{id:`tree`,children:`Tree`}),`
1009
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const treeDefinition: ODataCompiledFilterDefinition = {
1010
+ id: "productTree",
1011
+ ownerColumnId: "CATEGORY",
1012
+ columnIds: ["CATEGORY", "GROUP"],
1013
+ kind: "tree",
1014
+ componentId: "tree-multi-select",
1015
+ controlType: "string",
1016
+ levelOperator: "and"
1017
+ };
1018
+ `})}),`
1019
+ `,(0,c.jsxs)(t.p,{children:[`Внутри каждого уровня значения объединяются OR. Между уровнями по умолчанию используется AND. Укажите `,(0,c.jsx)(t.code,{children:`levelOperator: "or"`}),`, чтобы уровни объединялись OR.`]}),`
1020
+ `,(0,c.jsxs)(t.p,{children:[`Если `,(0,c.jsx)(t.code,{children:`ownerColumnId`}),` не входит в нормализованный `,(0,c.jsx)(t.code,{children:`columnIds`}),`, sanitizer заменяет owner на первый column id.`]}),`
1021
+ `,(0,c.jsx)(t.h3,{id:`condition-binding`,children:`Condition binding`}),`
1022
+ `,(0,c.jsx)(t.p,{children:`Один local/registered control может создавать conditions по другой физической колонке:`}),`
1023
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type ODataFilterCondition = {
1024
+ columnId?: string;
1025
+ operation: FilterOperation;
1026
+ valueSource: "static" | "input";
1027
+ value?: BaseType;
1028
+ };
1029
+
1030
+ type ODataFilterConditionGroup = {
1031
+ and?: boolean;
1032
+ conditions: ODataFilterCondition[];
1033
+ };
1034
+ `})}),`
1035
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`valueSource: "static"`}),` берёт `,(0,c.jsx)(t.code,{children:`condition.value`}),`.`]}),`
1036
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`valueSource: "input"`}),` берёт текущее значение control.`]}),`
1037
+ `,(0,c.jsxs)(t.p,{children:[`Если `,(0,c.jsx)(t.code,{children:`columnId`}),` не указан, local definition использует `,(0,c.jsx)(t.code,{children:`ownerColumnId`}),` как fallback. У registered definition искусственного owner нет, поэтому condition должна явно назвать column id.`]}),`
1038
+ `,(0,c.jsx)(t.h3,{id:`list-binding`,children:`List binding`}),`
1039
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const binding: ODataFilterBinding = {
1040
+ kind: "list",
1041
+ and: false,
1042
+ options: [
1043
+ {
1044
+ key: "open",
1045
+ label: "Открытые",
1046
+ filter: {
1047
+ conditions: [
1048
+ {
1049
+ columnId: "STATUS",
1050
+ operation: "eq",
1051
+ valueSource: "static",
1052
+ value: "O"
1053
+ }
1054
+ ]
1055
+ }
1056
+ }
1057
+ ]
1058
+ };
1059
+ `})}),`
1060
+ `,(0,c.jsxs)(t.p,{children:[`Выбранные options объединяются OR, если `,(0,c.jsx)(t.code,{children:`and !== true`}),`.`]}),`
1061
+ `,(0,c.jsxs)(t.p,{children:[`Для local list option без explicit filter используется fallback `,(0,c.jsx)(t.code,{children:`ownerColumnId eq option.key`}),`.`]}),`
1062
+ `,(0,c.jsx)(t.p,{children:`Для registered list такого fallback нет: option без filter не создаёт server filter.`}),`
1063
+ `,(0,c.jsxs)(t.p,{children:[`Пустой string `,(0,c.jsx)(t.code,{children:`option.key`}),` разрешён и может означать отдельный пользовательский выбор. Дубли option keys sanitizer сейчас не удаляет.`]}),`
1064
+ `,(0,c.jsx)(t.h3,{id:`boolean-binding`,children:`Boolean binding`}),`
1065
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const binding: ODataFilterBinding = {
1066
+ kind: "boolean",
1067
+ trueFilter: {
1068
+ conditions: [{ columnId: "ACTIVE", operation: "eq", valueSource: "static", value: true }]
1069
+ }
1070
+ };
1071
+ `})}),`
1072
+ `,(0,c.jsxs)(t.p,{children:[`Filter включается только когда control value строго равно `,(0,c.jsx)(t.code,{children:`true`}),`.`]}),`
1073
+ `,(0,c.jsxs)(t.p,{children:[`Local boolean без `,(0,c.jsx)(t.code,{children:`trueFilter`}),` создаёт fallback `,(0,c.jsx)(t.code,{children:`ownerColumnId eq true`}),`.`]}),`
1074
+ `,(0,c.jsxs)(t.p,{children:[`Registered boolean без explicit `,(0,c.jsx)(t.code,{children:`trueFilter`}),` ничего не создаёт.`]}),`
1075
+ `,(0,c.jsx)(t.h3,{id:`value-binding`,children:`Value binding`}),`
1076
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const binding: ODataFilterBinding = {
1077
+ kind: "value",
1078
+ valueFilter: {
1079
+ conditions: [{ columnId: "AMOUNT", operation: "ge", valueSource: "input" }]
1080
+ }
1081
+ };
1082
+ `})}),`
1083
+ `,(0,c.jsx)(t.p,{children:`Local value без explicit group создаёт fallback equality по owner column.`}),`
1084
+ `,(0,c.jsx)(t.p,{children:`Registered value без explicit group ничего не создаёт.`}),`
1085
+ `,(0,c.jsx)(t.h3,{id:`registered-filter-без-binding`,children:`Registered filter без binding`}),`
1086
+ `,(0,c.jsx)(t.p,{children:`Registered definition может быть parameter-only:`}),`
1087
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const definition: ODataCompiledFilterDefinition = {
1088
+ id: "period",
1089
+ kind: "registered",
1090
+ componentId: "period-control",
1091
+ controlType: "date",
1092
+ valueMode: "single",
1093
+ columnIds: []
1094
+ };
1095
+ `})}),`
1096
+ `,(0,c.jsxs)(t.p,{children:[`Значение такого control может использоваться другим уровнем приложения, но `,(0,c.jsx)(t.code,{children:`compileFiltersToExpression`}),` не добавит его в `,(0,c.jsx)(t.code,{children:`$filter`}),`.`]}),`
1097
+ `,(0,c.jsx)(t.h3,{id:`sanitizefilterconditiongroup`,children:(0,c.jsx)(t.code,{children:`sanitizeFilterConditionGroup`})}),`
1098
+ `,(0,c.jsx)(t.p,{children:`Нормализует condition group:`}),`
1099
+ `,(0,c.jsxs)(t.ul,{children:[`
1100
+ `,(0,c.jsxs)(t.li,{children:[`trim для `,(0,c.jsx)(t.code,{children:`columnId`}),`;`]}),`
1101
+ `,(0,c.jsx)(t.li,{children:`невалидное/пустое static value удаляется;`}),`
1102
+ `,(0,c.jsx)(t.li,{children:`input condition может не иметь сохранённого value;`}),`
1103
+ `,(0,c.jsxs)(t.li,{children:[`пустая после очистки группа становится `,(0,c.jsx)(t.code,{children:`undefined`}),`;`]}),`
1104
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`and: false`}),` сохраняется как явный OR;`]}),`
1105
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`and: true`}),` сохраняется как AND.`]}),`
1106
+ `]}),`
1107
+ `,(0,c.jsx)(t.h3,{id:`sanitizefilterbinding`,children:(0,c.jsx)(t.code,{children:`sanitizeFilterBinding`})}),`
1108
+ `,(0,c.jsx)(t.p,{children:`Нормализует binding:`}),`
1109
+ `,(0,c.jsxs)(t.ul,{children:[`
1110
+ `,(0,c.jsx)(t.li,{children:`trim для option key и label;`}),`
1111
+ `,(0,c.jsx)(t.li,{children:`пустой key сохраняется;`}),`
1112
+ `,(0,c.jsx)(t.li,{children:`пустой label заменяется key;`}),`
1113
+ `,(0,c.jsx)(t.li,{children:`options не дедуплицируются;`}),`
1114
+ `,(0,c.jsxs)(t.li,{children:[`list без options становится `,(0,c.jsx)(t.code,{children:`undefined`}),`;`]}),`
1115
+ `,(0,c.jsx)(t.li,{children:`вложенные condition groups очищаются.`}),`
1116
+ `]}),`
1117
+ `,(0,c.jsx)(t.h3,{id:`sanitizefilterdefinitions`,children:(0,c.jsx)(t.code,{children:`sanitizeFilterDefinitions`})}),`
1118
+ `,(0,c.jsx)(t.p,{children:`Нормализует массив definitions без изменения входных объектов. Это важно для frozen Zustand/Immer snapshots.`}),`
1119
+ `,(0,c.jsx)(t.p,{children:`Основные правила:`}),`
1120
+ `,(0,c.jsxs)(t.ul,{children:[`
1121
+ `,(0,c.jsxs)(t.li,{children:[`trim для `,(0,c.jsx)(t.code,{children:`id`}),`, `,(0,c.jsx)(t.code,{children:`ownerColumnId`}),`, `,(0,c.jsx)(t.code,{children:`componentId`}),`, `,(0,c.jsx)(t.code,{children:`columnIds`}),`;`]}),`
1122
+ `,(0,c.jsx)(t.li,{children:`definitions без обязательного id удаляются;`}),`
1123
+ `,(0,c.jsx)(t.li,{children:`non-registered definition без owner удаляется;`}),`
1124
+ `,(0,c.jsxs)(t.li,{children:[`tree без физических `,(0,c.jsx)(t.code,{children:`columnIds`}),` удаляется;`]}),`
1125
+ `,(0,c.jsx)(t.li,{children:`local без валидного binding удаляется;`}),`
1126
+ `,(0,c.jsx)(t.li,{children:`column ids дедуплицируются;`}),`
1127
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`segment.columnIds`}),` пересчитывается как `,(0,c.jsx)(t.code,{children:`[ownerColumnId]`}),`;`]}),`
1128
+ `,(0,c.jsxs)(t.li,{children:[`local/registered `,(0,c.jsx)(t.code,{children:`columnIds`}),` выводятся из binding conditions;`]}),`
1129
+ `,(0,c.jsxs)(t.li,{children:[`legacy kinds `,(0,c.jsx)(t.code,{children:`odata-segment`}),` и `,(0,c.jsx)(t.code,{children:`odata-tree`}),` переводятся в `,(0,c.jsx)(t.code,{children:`segment`}),` и `,(0,c.jsx)(t.code,{children:`tree`}),`;`]}),`
1130
+ `,(0,c.jsxs)(t.li,{children:[`legacy `,(0,c.jsx)(t.code,{children:`treeSelect`}),` переводится в `,(0,c.jsx)(t.code,{children:`tree-select`}),`; остальное tree legacy значение становится `,(0,c.jsx)(t.code,{children:`tree-multi-select`}),`.`]}),`
1131
+ `]}),`
1132
+ `,(0,c.jsx)(t.p,{children:`Это migration sanitizer сохранённых конфигураций, а не разрешение продолжать создавать legacy definitions в новом коде.`}),`
1133
+ `,(0,c.jsx)(t.h3,{id:`sanitizefiltervalue`,children:(0,c.jsx)(t.code,{children:`sanitizeFilterValue`})}),`
1134
+ `,(0,c.jsx)(t.p,{children:`Scalar правила:`}),`
1135
+ `,(0,c.jsxs)(t.ul,{children:[`
1136
+ `,(0,c.jsxs)(t.li,{children:[`string обрезается через `,(0,c.jsx)(t.code,{children:`trim`}),`, но пустая строка сохраняется;`]}),`
1137
+ `,(0,c.jsx)(t.li,{children:`finite number сохраняется;`}),`
1138
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`NaN`}),`, `,(0,c.jsx)(t.code,{children:`Infinity`}),`, `,(0,c.jsx)(t.code,{children:`-Infinity`}),` удаляются;`]}),`
1139
+ `,(0,c.jsxs)(t.li,{children:[`boolean сохраняется, включая `,(0,c.jsx)(t.code,{children:`false`}),`;`]}),`
1140
+ `,(0,c.jsxs)(t.li,{children:[`valid `,(0,c.jsx)(t.code,{children:`Date`}),` сохраняется;`]}),`
1141
+ `,(0,c.jsxs)(t.li,{children:[`invalid `,(0,c.jsx)(t.code,{children:`Date`}),` удаляется.`]}),`
1142
+ `]}),`
1143
+ `,(0,c.jsx)(t.p,{children:`Array правила:`}),`
1144
+ `,(0,c.jsxs)(t.ul,{children:[`
1145
+ `,(0,c.jsx)(t.li,{children:`элементы очищаются как scalar;`}),`
1146
+ `,(0,c.jsx)(t.li,{children:`дубликаты удаляются с учётом типа;`}),`
1147
+ `,(0,c.jsx)(t.li,{children:`результат сортируется детерминированно;`}),`
1148
+ `,(0,c.jsxs)(t.li,{children:[`пустой массив становится `,(0,c.jsx)(t.code,{children:`undefined`}),`.`]}),`
1149
+ `]}),`
1150
+ `,(0,c.jsx)(t.p,{children:`Tree object правила:`}),`
1151
+ `,(0,c.jsxs)(t.ul,{children:[`
1152
+ `,(0,c.jsx)(t.li,{children:`keys колонок проходят trim;`}),`
1153
+ `,(0,c.jsx)(t.li,{children:`принимаются только string arrays;`}),`
1154
+ `,(0,c.jsx)(t.li,{children:`string values trim/dedupe;`}),`
1155
+ `,(0,c.jsx)(t.li,{children:`пустые строки удаляются;`}),`
1156
+ `,(0,c.jsx)(t.li,{children:`порядок значений tree array сохраняется, сортировка не выполняется;`}),`
1157
+ `,(0,c.jsx)(t.li,{children:`пустые уровни удаляются.`}),`
1158
+ `]}),`
1159
+ `,(0,c.jsx)(t.h3,{id:`sanitizefiltervalues`,children:(0,c.jsx)(t.code,{children:`sanitizeFilterValues`})}),`
1160
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const normalized = sanitizeFilterValues({
1161
+ " company ": [" 1000 ", "1000", "2000"],
1162
+ broken: Number.NaN
1163
+ });
1164
+
1165
+ // { company: ["1000", "2000"] }
1166
+ `})}),`
1167
+ `,(0,c.jsx)(t.p,{children:`Filter ids проходят trim. Пустые/невалидные значения удаляются.`}),`
1168
+ `,(0,c.jsx)(t.h3,{id:`mergefiltervaluepatch`,children:(0,c.jsx)(t.code,{children:`mergeFilterValuePatch`})}),`
1169
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const next = mergeFilterValuePatch(values, "company", ["1000"]);
1170
+ `})}),`
1171
+ `,(0,c.jsx)(t.p,{children:`Функция сначала нормализует весь snapshot, создаёт новый object и затем:`}),`
1172
+ `,(0,c.jsxs)(t.ul,{children:[`
1173
+ `,(0,c.jsx)(t.li,{children:`устанавливает нормализованное value;`}),`
1174
+ `,(0,c.jsxs)(t.li,{children:[`либо удаляет key, если новое value стало `,(0,c.jsx)(t.code,{children:`undefined`}),`.`]}),`
1175
+ `]}),`
1176
+ `,(0,c.jsx)(t.p,{children:`Входной object не мутируется.`}),`
1177
+ `,(0,c.jsx)(t.h3,{id:`potential-и-active-column-ids`,children:`Potential и active column ids`}),`
1178
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`resolveODataFilterDefinitionColumnIds(definition);
1179
+ resolveODataFilterDefinitionActiveColumnIds(definition, value);
1180
+ `})}),`
1181
+ `,(0,c.jsx)(t.p,{children:`Первая функция возвращает все физические колонки, которые definition потенциально может затронуть.`}),`
1182
+ `,(0,c.jsx)(t.p,{children:`Вторая учитывает текущее value. Например, у list binding options могут ссылаться на разные columns; active helper вернёт только columns выбранных options.`}),`
1183
+ `,(0,c.jsx)(t.p,{children:`Это полезно при проверке совместимости filter definition с конкретным OData target.`}),`
1184
+ `,(0,c.jsx)(t.h3,{id:`compilefilterstoexpression`,children:(0,c.jsx)(t.code,{children:`compileFiltersToExpression`})}),`
1185
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const expression = compileFiltersToExpression(definitions, values);
1186
+ const filter = buildODataFilter(expression);
1187
+ `})}),`
1188
+ `,(0,c.jsx)(t.p,{children:`Компилятор:`}),`
1189
+ `,(0,c.jsxs)(t.ol,{children:[`
1190
+ `,(0,c.jsx)(t.li,{children:`санитизирует definitions;`}),`
1191
+ `,(0,c.jsx)(t.li,{children:`санитизирует values;`}),`
1192
+ `,(0,c.jsxs)(t.li,{children:[`сопоставляет value по `,(0,c.jsx)(t.code,{children:`definition.id`}),`;`]}),`
1193
+ `,(0,c.jsx)(t.li,{children:`отбрасывает definitions без активного server expression;`}),`
1194
+ `,(0,c.jsx)(t.li,{children:`объединяет expressions разных definitions через AND.`}),`
1195
+ `]}),`
1196
+ `,(0,c.jsx)(t.p,{children:`Semantics по kind:`}),`
1197
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Kind`}),(0,c.jsx)(t.th,{children:`Как компилируется`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`segment/select`})}),(0,c.jsxs)(t.td,{children:[`scalar `,(0,c.jsx)(t.code,{children:`owner eq value`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`segment/multi-select`})}),(0,c.jsx)(t.td,{children:`массив equality через OR`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`tree`})}),(0,c.jsxs)(t.td,{children:[`values каждого уровня через OR, уровни через AND или `,(0,c.jsx)(t.code,{children:`levelOperator`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`local/list`})}),(0,c.jsx)(t.td,{children:`option filters либо fallback owner equality`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`local/boolean`})}),(0,c.jsxs)(t.td,{children:[`только `,(0,c.jsx)(t.code,{children:`true`}),`; explicit group либо owner `,(0,c.jsx)(t.code,{children:`eq true`})]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`local/value`})}),(0,c.jsx)(t.td,{children:`explicit input group либо owner equality`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`registered`})}),(0,c.jsx)(t.td,{children:`только explicit binding conditions; owner fallback отсутствует`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`column`})}),(0,c.jsx)(t.td,{children:`scalar/array owner equality`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`advanced`})}),(0,c.jsx)(t.td,{children:`только массив owner equality`})]})]})]}),`
1198
+ `,(0,c.jsxs)(t.p,{children:[`Если ничего не скомпилировалось, возвращается `,(0,c.jsx)(t.code,{children:`undefined`}),`, а `,(0,c.jsx)(t.code,{children:`buildODataFilter(undefined)`}),` даст пустую строку.`]}),`
1199
+ `,(0,c.jsx)(t.h3,{id:`dependencies-для-справочников`,children:`Dependencies для справочников`}),`
1200
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const dependencies = flattenFilterValuesToODataDependencies(definitions, values);
1201
+ `})}),`
1202
+ `,(0,c.jsxs)(t.p,{children:[`В dependencies попадают только `,(0,c.jsx)(t.code,{children:`segment`}),` и `,(0,c.jsx)(t.code,{children:`tree`}),`:`]}),`
1203
+ `,(0,c.jsxs)(t.ul,{children:[`
1204
+ `,(0,c.jsxs)(t.li,{children:[`segment записывается по `,(0,c.jsx)(t.code,{children:`dictionaryCodeKey`}),` либо `,(0,c.jsx)(t.code,{children:`ownerColumnId`}),`;`]}),`
1205
+ `,(0,c.jsxs)(t.li,{children:[`tree раскладывает выбранные значения по физическим `,(0,c.jsx)(t.code,{children:`columnIds`}),`;`]}),`
1206
+ `,(0,c.jsx)(t.li,{children:`local/registered/column/advanced пропускаются.`}),`
1207
+ `]}),`
1208
+ `,(0,c.jsx)(t.p,{children:`Результат:`}),`
1209
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
1210
+ BUKRS: ["1000"],
1211
+ CATEGORY: ["A"],
1212
+ GROUP: ["A1"]
1213
+ }
1214
+ `})}),`
1215
+ `,(0,c.jsx)(t.h3,{id:`цепочки-odata-сегментов`,children:`Цепочки OData-сегментов`}),`
1216
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type ODataChainItem = {
1217
+ codeKey: string;
1218
+ count: number;
1219
+ };
1220
+
1221
+ type ODataChainsMap = Record<string, ODataChainItem[]>;
1222
+ `})}),`
1223
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`collapseChainedODataSegmentFilterValues`}),` оставляет в каждой известной цепочке самый глубокий выбранный segment:`]}),`
1224
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const collapsed = collapseChainedODataSegmentFilterValues(
1225
+ {
1226
+ category: ["A"],
1227
+ group: ["A1"],
1228
+ localStatus: "active"
1229
+ },
1230
+ ["category", "group"],
1231
+ {
1232
+ product: [
1233
+ { codeKey: "category", count: 1 },
1234
+ { codeKey: "group", count: 2 }
1235
+ ]
1236
+ }
1237
+ );
1238
+
1239
+ // { group: ["A1"], localStatus: "active" }
1240
+ `})}),`
1241
+ `,(0,c.jsx)(t.p,{children:`Non-segment values и values без известной chain сохраняются. Функция не компилирует filter и не проверяет, действительно ли глубокое значение согласовано с родителем.`}),`
1242
+ `,(0,c.jsxs)(t.h2,{id:`сортировка-и-orderby`,children:[`Сортировка и `,(0,c.jsx)(t.code,{children:`$orderby`})]}),`
1243
+ `,(0,c.jsxs)(t.h3,{id:`тип-sort`,children:[`Тип `,(0,c.jsx)(t.code,{children:`Sort`})]}),`
1244
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`interface Sort<T> {
1245
+ key: T;
1246
+ desc?: true;
1247
+ }
1248
+ `})}),`
1249
+ `,(0,c.jsxs)(t.p,{children:[`Отсутствие `,(0,c.jsx)(t.code,{children:`desc`}),` означает ascending. Явное `,(0,c.jsx)(t.code,{children:`desc: false`}),` типом не предусмотрено.`]}),`
1250
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const sorting = [{ key: "YEAR" }, { key: "AMOUNT", desc: true }] satisfies Sort<"YEAR" | "AMOUNT">[];
1251
+ `})}),`
1252
+ `,(0,c.jsx)(t.h3,{id:`buildodataorder`,children:(0,c.jsx)(t.code,{children:`buildODataOrder`})}),`
1253
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`buildODataOrder(sorting);
1254
+ // YEAR asc,AMOUNT desc
1255
+ `})}),`
1256
+ `,(0,c.jsx)(t.p,{children:`Пустой массив даёт пустую строку.`}),`
1257
+ `,(0,c.jsx)(t.h3,{id:`getsortindex`,children:(0,c.jsx)(t.code,{children:`getSortIndex`})}),`
1258
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`getSortIndex(sorting, "AMOUNT"); // 1
1259
+ getSortIndex(sorting, "STATUS"); // -1
1260
+ `})}),`
1261
+ `,(0,c.jsx)(t.h3,{id:`togglesort`,children:(0,c.jsx)(t.code,{children:`toggleSort`})}),`
1262
+ `,(0,c.jsx)(t.p,{children:`Цикл одного поля:`}),`
1263
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`нет → asc → desc → нет
1264
+ `})}),`
1265
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`let sorting: Sort<keyof Row>[] = [];
1266
+
1267
+ sorting = toggleSort(sorting, "AMOUNT", false); // asc
1268
+ sorting = toggleSort(sorting, "AMOUNT", false); // desc
1269
+ sorting = toggleSort(sorting, "AMOUNT", false); // remove
1270
+ `})}),`
1271
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`multi: true`}),` сохраняет остальные fields.`]}),`
1272
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`multi: false`}),` оставляет только выбранный key. Это обычный single-sort UI.`]}),`
1273
+ `,(0,c.jsx)(t.p,{children:`Функция возвращает новый массив и не мутирует входной.`}),`
1274
+ `,(0,c.jsx)(t.h3,{id:`grouping-fallback`,children:`Grouping fallback`}),`
1275
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`resolveEffectiveSorts(grouping, sorting);
1276
+ `})}),`
1277
+ `,(0,c.jsx)(t.p,{children:`Правило:`}),`
1278
+ `,(0,c.jsxs)(t.ul,{children:[`
1279
+ `,(0,c.jsxs)(t.li,{children:[`если пользовательская `,(0,c.jsx)(t.code,{children:`sorting`}),` непустая — используется только она;`]}),`
1280
+ `,(0,c.jsxs)(t.li,{children:[`если пользовательской сортировки нет — fields `,(0,c.jsx)(t.code,{children:`grouping`}),` становятся ascending sorts.`]}),`
1281
+ `]}),`
1282
+ `,(0,c.jsx)(t.p,{children:`Grouping не дописывается к пользовательской sorting.`}),`
1283
+ `,(0,c.jsx)(t.h3,{id:`buildodataorderby`,children:(0,c.jsx)(t.code,{children:`buildODataOrderBy`})}),`
1284
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`buildODataOrderBy<Row>(["YEAR"], []); // "YEAR asc"
1285
+ buildODataOrderBy<Row>(["YEAR"], [{ key: "AMOUNT", desc: true }]);
1286
+ // "AMOUNT desc"
1287
+
1288
+ buildODataOrderBy<Row>([], []); // undefined
1289
+ `})}),`
1290
+ `,(0,c.jsxs)(t.p,{children:[`В отличие от `,(0,c.jsx)(t.code,{children:`buildODataOrder`}),`, пустой эффективный список даёт `,(0,c.jsx)(t.code,{children:`undefined`}),`. Это удобно при создании optional query option.`]}),`
1291
+ `,(0,c.jsx)(t.h3,{id:`getsortindicator`,children:(0,c.jsx)(t.code,{children:`getSortIndicator`})}),`
1292
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const indicator = getSortIndicator(grouping, sorting, "YEAR");
1293
+ `})}),`
1294
+ `,(0,c.jsx)(t.p,{children:`Результаты:`}),`
1295
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{ active: false }
1296
+
1297
+ {
1298
+ active: true,
1299
+ desc: true,
1300
+ order: 2
1301
+ }
1302
+
1303
+ {
1304
+ active: true,
1305
+ order: 1,
1306
+ lockedByGrouping: true
1307
+ }
1308
+ `})}),`
1309
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`order`}),` начинается с `,(0,c.jsx)(t.code,{children:`1`}),`, чтобы напрямую показывать номер при multi-sort.`]}),`
1310
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`lockedByGrouping`}),` появляется только для grouping fallback, когда пользовательской sorting ещё нет.`]}),`
1311
+ `,(0,c.jsx)(t.h2,{id:`sql-like-фильтры`,children:`SQL-like фильтры`}),`
1312
+ `,(0,c.jsxs)(t.blockquote,{children:[`
1313
+ `,(0,c.jsxs)(t.p,{children:[`Это не OData `,(0,c.jsx)(t.code,{children:`$filter`}),` и не query option SAP Gateway.`]}),`
1314
+ `]}),`
1315
+ `,(0,c.jsx)(t.p,{children:`SQL-like API предназначен только для legacy payload, где backend ожидает текст dynamic WHERE в request body.`}),`
1316
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const sql = buildSqlFilter([createSqlFilterEqual("STATUS", "A"), createSqlFilterIn("COMPANY", ["1000", "2000"])]);
1317
+
1318
+ // STATUS='A' AND COMPANY IN ('1000', '2000')
1319
+ `})}),`
1320
+ `,(0,c.jsx)(t.h3,{id:`типы`,children:`Типы`}),`
1321
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type SqlFilterScalarValue = string | number | boolean;
1322
+ type SqlFilterOperation = "eq" | "in";
1323
+ type SqlFilterConjunction = "and" | "or";
1324
+ type SqlFilterCondition = SqlFilterEqualCondition | SqlFilterInCondition;
1325
+ type SqlFilterExpression = SqlFilterCondition | SqlFilterGroup;
1326
+ `})}),`
1327
+ `,(0,c.jsx)(t.h3,{id:`factory-api`,children:`Factory API`}),`
1328
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`createSqlFilterEqual("STATUS", "A");
1329
+ createSqlFilterIn("COMPANY", ["1000", "2000"]);
1330
+ createSqlFilterGroup([createSqlFilterEqual("STATUS", "A"), createSqlFilterEqual("STATUS", "B")], "or");
1331
+ `})}),`
1332
+ `,(0,c.jsxs)(t.p,{children:[`Group всегда сериализуется в скобках. Выражения top-level всегда объединяются `,(0,c.jsx)(t.code,{children:`AND`}),`.`]}),`
1333
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`(STATUS='A' OR STATUS='B') AND COMPANY='1000'
1334
+ `})}),`
1335
+ `,(0,c.jsx)(t.h3,{id:`field-id-validation`,children:`Field id validation`}),`
1336
+ `,(0,c.jsx)(t.p,{children:`Разрешены только простые identifiers:`}),`
1337
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`^[A-Za-z_][A-Za-z0-9_]*$
1338
+ `})}),`
1339
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`assertSqlFilterFieldId("STATUS"); // ok
1340
+ assertSqlFilterFieldId("STATUS_01"); // ok
1341
+ assertSqlFilterFieldId("A; DROP"); // Error
1342
+ assertSqlFilterFieldId("TABLE.FIELD"); // Error
1343
+ `})}),`
1344
+ `,(0,c.jsxs)(t.p,{children:[`Validation выполняется при сериализации condition с usable value. Не используйте отсутствие ошибки для empty condition как отдельную проверку field id; для этого вызывайте `,(0,c.jsx)(t.code,{children:`assertSqlFilterFieldId`}),` прямо.`]}),`
1345
+ `,(0,c.jsx)(t.h3,{id:`values`,children:`Values`}),`
1346
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`isSqlFilterScalarValue("A"); // true
1347
+ isSqlFilterScalarValue(10); // true
1348
+ isSqlFilterScalarValue(false); // true
1349
+ isSqlFilterScalarValue(null); // false
1350
+ `})}),`
1351
+ `,(0,c.jsx)(t.p,{children:`Все values, включая numbers и booleans, сериализуются как quoted strings:`}),`
1352
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`COUNT='10'
1353
+ ACTIVE='true'
1354
+ `})}),`
1355
+ `,(0,c.jsx)(t.p,{children:`Одинарные кавычки внутри value удваиваются.`}),`
1356
+ `,(0,c.jsx)(t.p,{children:`Пропускаются:`}),`
1357
+ `,(0,c.jsxs)(t.ul,{children:[`
1358
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`null`}),` и `,(0,c.jsx)(t.code,{children:`undefined`}),`;`]}),`
1359
+ `,(0,c.jsx)(t.li,{children:`пустая/whitespace-only строка;`}),`
1360
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`NaN`}),` и infinity;`]}),`
1361
+ `,(0,c.jsx)(t.li,{children:`objects, arrays и другие неподдерживаемые values.`}),`
1362
+ `]}),`
1363
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`Date`}),` не пропускается молча, а приводит к ошибке. Caller обязан заранее выбрать формат даты и передать string.`]}),`
1364
+ `,(0,c.jsxs)(t.p,{children:[`Пустые groups и conditions не попадают в результат. Если всё пусто, `,(0,c.jsx)(t.code,{children:`buildSqlFilter`}),` возвращает `,(0,c.jsx)(t.code,{children:`""`}),`.`]}),`
1365
+ `,(0,c.jsx)(t.h3,{id:`почему-нельзя-применять-для-url`,children:`Почему нельзя применять для URL`}),`
1366
+ `,(0,c.jsxs)(t.p,{children:[`SQL-like syntax содержит `,(0,c.jsx)(t.code,{children:`IN`}),`, quoted payload values и field identifiers по собственному контракту backend. Это другой язык выражений. Для OData URL используйте `,(0,c.jsx)(t.code,{children:`createFilter*`}),` + `,(0,c.jsx)(t.code,{children:`buildODataFilter`}),`.`]}),`
1367
+ `,(0,c.jsx)(t.h2,{id:`пары-код--текст`,children:`Пары «код + текст»`}),`
1368
+ `,(0,c.jsx)(t.h3,{id:`типы-1`,children:`Типы`}),`
1369
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type CollectionItem = {
1370
+ [key: string]: string;
1371
+ };
1372
+
1373
+ interface CollectionPair {
1374
+ codeKey: string;
1375
+ textKey: string;
1376
+ }
1377
+
1378
+ interface DependencyFilter {
1379
+ key: string;
1380
+ values: CollectionItem[];
1381
+ }
1382
+ `})}),`
1383
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`CollectionItem`}),` типизирован как string record. Если transport возвращает `,(0,c.jsx)(t.code,{children:`null`}),`, number или missing key, сначала нормализуйте row. Runtime helper не проверяет соответствие типу TypeScript.`]}),`
1384
+ `,(0,c.jsx)(t.h3,{id:`findcollectionpairs`,children:(0,c.jsx)(t.code,{children:`findCollectionPairs`})}),`
1385
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const result = findCollectionPairs(
1386
+ {
1387
+ code: "A",
1388
+ code_TEXT: "Альфа",
1389
+ alone: "1"
1390
+ },
1391
+ { returnUnpaired: true }
1392
+ );
1393
+ `})}),`
1394
+ `,(0,c.jsx)(t.p,{children:`Результат:`}),`
1395
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
1396
+ pairs: [{ codeKey: "code", textKey: "code_TEXT" }],
1397
+ pairsMap: { code: "code_TEXT" },
1398
+ unpaired: ["alone"]
1399
+ }
1400
+ `})}),`
1401
+ `,(0,c.jsx)(t.p,{children:`Options:`}),`
1402
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type FindOptions = {
1403
+ textSuffixes?: string[];
1404
+ caseSensitive?: boolean;
1405
+ returnUnpaired?: boolean;
1406
+ };
1407
+ `})}),`
1408
+ `,(0,c.jsx)(t.p,{children:`Default suffixes:`}),`
1409
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`_txt, _text, _t, _Text, _TXT, _T
1410
+ `})}),`
1411
+ `,(0,c.jsxs)(t.p,{children:[`Поиск suffix по умолчанию нечувствителен к регистру. Custom suffixes экранируются как regexp text, поэтому `,(0,c.jsx)(t.code,{children:`".label"`}),` работает буквально:`]}),`
1412
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`findCollectionPairs({ material: "1", "material.label": "Материал" }, { textSuffixes: [".label"] });
1413
+ `})}),`
1414
+ `,(0,c.jsxs)(t.p,{children:[`Важная тонкость: после удаления suffix base field ищется через точное `,(0,c.jsx)(t.code,{children:`fields.includes(baseName)`}),`. Поэтому case-insensitive suffix не делает поиск base key полностью case-insensitive:`]}),`
1415
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
1416
+ CODE: "A",
1417
+ code_txt: "Альфа"
1418
+ }
1419
+ `})}),`
1420
+ `,(0,c.jsxs)(t.p,{children:[`Такая пара может не найтись из-за различия `,(0,c.jsx)(t.code,{children:`CODE`}),` и `,(0,c.jsx)(t.code,{children:`code`}),`. Лучше нормализовать имена полей или передавать согласованную metadata-пару.`]}),`
1421
+ `,(0,c.jsx)(t.p,{children:`Каждое поле используется максимум в одной найденной паре.`}),`
1422
+ `,(0,c.jsx)(t.h3,{id:`buildseparatedarrays`,children:(0,c.jsx)(t.code,{children:`buildSeparatedArrays`})}),`
1423
+ `,(0,c.jsx)(t.p,{children:`Функция из общей коллекции создаёт отдельный массив для каждой code/text pair:`}),`
1424
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const rows = [
1425
+ { code: "B", text: "Бета", group: "2", groupText: "Группа 2" },
1426
+ { code: "A", text: "Альфа", group: "1", groupText: "Группа 1" },
1427
+ { code: "A", text: "Альфа", group: "1", groupText: "Группа 1" }
1428
+ ];
1429
+
1430
+ const result = buildSeparatedArrays(rows, [
1431
+ { codeKey: "code", textKey: "text" },
1432
+ { codeKey: "group", textKey: "groupText" }
1433
+ ]);
1434
+ `})}),`
1435
+ `,(0,c.jsx)(t.p,{children:`Результат:`}),`
1436
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
1437
+ code: [
1438
+ { code: "A", text: "Альфа" },
1439
+ { code: "B", text: "Бета" }
1440
+ ],
1441
+ group: [
1442
+ { group: "1", groupText: "Группа 1" },
1443
+ { group: "2", groupText: "Группа 2" }
1444
+ ]
1445
+ }
1446
+ `})}),`
1447
+ `,(0,c.jsx)(t.p,{children:`Signature:`}),`
1448
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`buildSeparatedArrays(items, pairs, (excludeEmpty = true), (sortByCode = true));
1449
+ `})}),`
1450
+ `,(0,c.jsx)(t.p,{children:`Правила:`}),`
1451
+ `,(0,c.jsxs)(t.ul,{children:[`
1452
+ `,(0,c.jsx)(t.li,{children:`pair считается валидной, только если оба key есть в первом item;`}),`
1453
+ `,(0,c.jsx)(t.li,{children:`невалидная pair всё равно получает пустой массив в result;`}),`
1454
+ `,(0,c.jsx)(t.li,{children:`output row содержит только code и text fields;`}),`
1455
+ `,(0,c.jsxs)(t.li,{children:[`одинаковая комбинация `,(0,c.jsx)(t.code,{children:`code + "|" + text`}),` удаляется;`]}),`
1456
+ `,(0,c.jsx)(t.li,{children:`по умолчанию rows с пустой строкой text пропускаются;`}),`
1457
+ `,(0,c.jsx)(t.li,{children:`по умолчанию результат сортируется по code;`}),`
1458
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`sortByCode: false`}),` сортирует по text через общий `,(0,c.jsx)(t.code,{children:`compareStrings`}),`;`]}),`
1459
+ `,(0,c.jsxs)(t.li,{children:[`пустой `,(0,c.jsx)(t.code,{children:`items`}),` создаёт пустые массивы для всех pairs.`]}),`
1460
+ `]}),`
1461
+ `,(0,c.jsxs)(t.h3,{id:`ограничения-buildseparatedarrays`,children:[`Ограничения `,(0,c.jsx)(t.code,{children:`buildSeparatedArrays`})]}),`
1462
+ `,(0,c.jsxs)(t.p,{children:[`Функция приводит значения через `,(0,c.jsx)(t.code,{children:`String`}),` до проверки. Поэтому invalid runtime rows могут дать неожиданный текст:`]}),`
1463
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`String(undefined); // "undefined"
1464
+ String(null); // "null"
1465
+ `})}),`
1466
+ `,(0,c.jsxs)(t.p,{children:[`Из-за этого missing/null в последующих rows могут попасть в справочник как строки `,(0,c.jsx)(t.code,{children:`"undefined"`}),` или `,(0,c.jsx)(t.code,{children:`"null"`}),`. `,(0,c.jsx)(t.code,{children:`excludeEmpty`}),` удаляет только реально пустую строку после преобразования.`]}),`
1467
+ `,(0,c.jsx)(t.p,{children:`Кроме того, структура валидируется только по первому item. Если первый row не содержит поле, которое есть в остальных rows, pair будет считаться невалидной.`}),`
1468
+ `,(0,c.jsx)(t.p,{children:`Правильный путь для ненадёжного backend payload:`}),`
1469
+ `,(0,c.jsxs)(t.ol,{children:[`
1470
+ `,(0,c.jsx)(t.li,{children:`проверить/нормализовать rows;`}),`
1471
+ `,(0,c.jsx)(t.li,{children:`убедиться в одинаковой форме элементов;`}),`
1472
+ `,(0,c.jsxs)(t.li,{children:[`затем вызвать `,(0,c.jsx)(t.code,{children:`buildSeparatedArrays`}),`.`]}),`
1473
+ `]}),`
1474
+ `,(0,c.jsx)(t.h2,{id:`настройки-odata-коллекций`,children:`Настройки OData-коллекций`}),`
1475
+ `,(0,c.jsx)(t.h3,{id:`глобальный-zustand-store`,children:`Глобальный Zustand store`}),`
1476
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`useODataCollectionStore`}),` — один singleton на загруженный экземпляр модуля.`]}),`
1477
+ `,(0,c.jsx)(t.p,{children:`Default state:`}),`
1478
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
1479
+ defaultMaxVisibleItems: 200,
1480
+ defaultMinSearchCodeLength: 3,
1481
+ defaultMinSearchTextLength: 1,
1482
+ defaultSearchDebounceDelay: 100
1483
+ }
1484
+ `})}),`
1485
+ `,(0,c.jsx)(t.p,{children:`Значения обозначают:`}),`
1486
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Field`}),(0,c.jsx)(t.th,{children:`Смысл`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`defaultMaxVisibleItems`})}),(0,c.jsx)(t.td,{children:`Максимум показываемых элементов справочника по умолчанию.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`defaultMinSearchCodeLength`})}),(0,c.jsx)(t.td,{children:`Минимальная длина поиска по коду.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`defaultMinSearchTextLength`})}),(0,c.jsx)(t.td,{children:`Минимальная длина поиска по тексту.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`defaultSearchDebounceDelay`})}),(0,c.jsx)(t.td,{children:`Задержка поиска в миллисекундах.`})]})]})]}),`
1487
+ `,(0,c.jsx)(t.h3,{id:`чтение-и-изменение-вне-react`,children:`Чтение и изменение вне React`}),`
1488
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const state = useODataCollectionStore.getState();
1489
+
1490
+ state.setDefaultMaxVisibleItems(100);
1491
+ state.setDefaultMinSearchCodeLength(2);
1492
+ state.setDefaultMinSearchTextLength(2);
1493
+ state.setDefaultSearchDebounceDelay(250);
1494
+ `})}),`
1495
+ `,(0,c.jsxs)(t.p,{children:[`Setters не валидируют числа. Отрицательное значение, дробь или `,(0,c.jsx)(t.code,{children:`NaN`}),` будут сохранены. Host config обязан проверить значения до записи.`]}),`
1496
+ `,(0,c.jsx)(t.h3,{id:`использование-в-react`,children:`Использование в React`}),`
1497
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-tsx`,children:`function CollectionInfo() {
1498
+ const maxVisible = useODataCollectionStore((state) => state.defaultMaxVisibleItems);
1499
+
1500
+ return <span>Показываем не более {maxVisible}</span>;
1501
+ }
1502
+ `})}),`
1503
+ `,(0,c.jsx)(t.p,{children:`Convenience selectors:`}),`
1504
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`odataCollectionConfig.useMaxVisibleItems();
1505
+ odataCollectionConfig.useMinSearchCodeLength();
1506
+ odataCollectionConfig.useMinSearchTextLength();
1507
+ odataCollectionConfig.useSearchDebounce();
1508
+ `})}),`
1509
+ `,(0,c.jsx)(t.p,{children:`Это React hooks, несмотря на размещение внутри объекта. Вызывайте их только:`}),`
1510
+ `,(0,c.jsxs)(t.ul,{children:[`
1511
+ `,(0,c.jsx)(t.li,{children:`на верхнем уровне React component;`}),`
1512
+ `,(0,c.jsx)(t.li,{children:`внутри custom hook;`}),`
1513
+ `,(0,c.jsx)(t.li,{children:`не внутри condition, loop, event handler или обычной utility function.`}),`
1514
+ `]}),`
1515
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`odataCollectionConfig`}),` экспортирует только selectors. Для setters используйте `,(0,c.jsx)(t.code,{children:`useODataCollectionStore`}),`.`]}),`
1516
+ `,(0,c.jsx)(t.h3,{id:`глобальная-инициализация`,children:`Глобальная инициализация`}),`
1517
+ `,(0,c.jsx)(t.p,{children:`Если host меняет defaults, сделайте это один раз в application bootstrap, а не при каждом render:`}),`
1518
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`export function configureODataCollections() {
1519
+ const state = useODataCollectionStore.getState();
1520
+ state.setDefaultMaxVisibleItems(100);
1521
+ state.setDefaultSearchDebounceDelay(200);
1522
+ }
1523
+ `})}),`
1524
+ `,(0,c.jsx)(t.p,{children:`Store не сохраняется автоматически в localStorage/sessionStorage и не синхронизируется между browser tabs.`}),`
1525
+ `,(0,c.jsx)(t.h2,{id:`маленькие-helpers`,children:`Маленькие helpers`}),`
1526
+ `,(0,c.jsx)(t.h3,{id:`normalizerangevalue`,children:(0,c.jsx)(t.code,{children:`normalizeRangeValue`})}),`
1527
+ `,(0,c.jsxs)(t.p,{children:[`Преобразует `,(0,c.jsx)(t.code,{children:`InputType`}),` в двухэлементный range:`]}),`
1528
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`normalizeRangeValue([10, 20]); // [10, 20]
1529
+ normalizeRangeValue([10]); // [10, null]
1530
+ normalizeRangeValue(10); // [10, null]
1531
+ normalizeRangeValue(null); // [null, null]
1532
+ normalizeRangeValue({}); // [null, null]
1533
+ `})}),`
1534
+ `,(0,c.jsx)(t.p,{children:`Если массив длиннее двух элементов, используются только первые два. Nested objects не поддерживаются.`}),`
1535
+ `,(0,c.jsx)(t.h3,{id:`normalizebasevalue`,children:(0,c.jsx)(t.code,{children:`normalizeBaseValue`})}),`
1536
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`normalizeBaseValue("A"); // "A"
1537
+ normalizeBaseValue(0); // 0
1538
+ normalizeBaseValue(false); // false
1539
+ normalizeBaseValue(date); // тот же Date reference
1540
+ normalizeBaseValue([1, 2]); // null
1541
+ normalizeBaseValue({ a: 1 }); // null
1542
+ `})}),`
1543
+ `,(0,c.jsxs)(t.p,{children:[`Функция не клонирует `,(0,c.jsx)(t.code,{children:`Date`}),`.`]}),`
1544
+ `,(0,c.jsx)(t.h3,{id:`isbasevalue`,children:(0,c.jsx)(t.code,{children:`isBaseValue`})}),`
1545
+ `,(0,c.jsxs)(t.p,{children:[`Возвращает `,(0,c.jsx)(t.code,{children:`true`}),` для:`]}),`
1546
+ `,(0,c.jsxs)(t.ul,{children:[`
1547
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`null`}),` и `,(0,c.jsx)(t.code,{children:`undefined`}),`;`]}),`
1548
+ `,(0,c.jsx)(t.li,{children:`string, number, boolean;`}),`
1549
+ `,(0,c.jsxs)(t.li,{children:[`любого `,(0,c.jsx)(t.code,{children:`Date`}),` instance.`]}),`
1550
+ `]}),`
1551
+ `,(0,c.jsx)(t.p,{children:`Она не проверяет finite number и valid date:`}),`
1552
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`isBaseValue(Number.NaN); // true
1553
+ isBaseValue(new Date("bad")); // true
1554
+ `})}),`
1555
+ `,(0,c.jsx)(t.p,{children:`Для строгой проверки используйте schemas/guards конкретного OData type.`}),`
1556
+ `,(0,c.jsx)(t.h3,{id:`unwrapodataqueryresult`,children:(0,c.jsx)(t.code,{children:`unwrapODataQueryResult`})}),`
1557
+ `,(0,c.jsx)(t.p,{children:`Некоторые OData query functions возвращают envelope:`}),`
1558
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
1559
+ data: rows,
1560
+ totalCount: 123
1561
+ }
1562
+ `})}),`
1563
+ `,(0,c.jsxs)(t.p,{children:[`TanStack Query result тогда имеет `,(0,c.jsx)(t.code,{children:`query.data.data`}),`. Helper выносит fields наружу:`]}),`
1564
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const result = unwrapODataQueryResult(query);
1565
+
1566
+ result.data; // rows или undefined
1567
+ result.totalCount; // number или undefined
1568
+ result.isLoading; // остальные поля UseQueryResult сохранены
1569
+ `})}),`
1570
+ `,(0,c.jsx)(t.p,{children:`Тип:`}),`
1571
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`type UnwrappedODataQueryResult<T> = Omit<UseQueryResult<{ data: T; totalCount?: number }>, "data"> & {
1572
+ data: T | undefined;
1573
+ totalCount?: number;
1574
+ };
1575
+ `})}),`
1576
+ `,(0,c.jsx)(t.p,{children:`Функция делает shallow spread query result. Она не меняет cache и не создаёт новый query.`}),`
1577
+ `,(0,c.jsx)(t.h2,{id:`ошибки-безопасность-и-ограничения`,children:`Ошибки, безопасность и ограничения`}),`
1578
+ `,(0,c.jsx)(t.h3,{id:`какие-функции-бросают-ошибки`,children:`Какие функции бросают ошибки`}),`
1579
+ `,(0,c.jsx)(t.p,{children:`Основные error boundaries:`}),`
1580
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`API`}),(0,c.jsx)(t.th,{children:`Причина ошибки`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`parseServiceMetadata`})}),(0,c.jsx)(t.td,{children:`Неподдерживаемый EDM type, mode/shape параметра или неразбираемая структура metadata.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`odataFormatValue`})}),(0,c.jsx)(t.td,{children:`Значение не прошло Zod schema выбранного OData type.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`create*Formatter`}),` result`]}),(0,c.jsx)(t.td,{children:`Аргументы вызова не соответствуют schema фабрики.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildParameterEntries`})}),(0,c.jsxs)(t.td,{children:[`Нет mandatory value, массив без formatter, formatting error, нарушение `,(0,c.jsx)(t.code,{children:`maxLength`}),`.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`assertSqlFilterFieldId`})}),(0,c.jsx)(t.td,{children:`Field id не соответствует безопасному pattern.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildSqlFilter`})}),(0,c.jsxs)(t.td,{children:[`Попытка сериализовать `,(0,c.jsx)(t.code,{children:`Date`}),` без заранее выбранного формата.`]})]})]})]}),`
1581
+ `,(0,c.jsx)(t.p,{children:`Filter/sanitizer helpers чаще удаляют невалидные optional values и возвращают пустое выражение, а не бросают ошибку. Это разные контракты: builder параметров строгий, sanitizer сохранённого UI snapshot терпимый.`}),`
1582
+ `,(0,c.jsx)(t.h3,{id:`не-скрывайте-ошибку-пустой-строкой`,children:`Не скрывайте ошибку пустой строкой`}),`
1583
+ `,(0,c.jsx)(t.p,{children:`Плохой вариант:`}),`
1584
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`let path = "";
1585
+
1586
+ try {
1587
+ path = buildEntityPath(metadata, config, params);
1588
+ } catch {
1589
+ // Ошибка потеряна, request уйдёт по неправильному адресу.
1590
+ }
1591
+ `})}),`
1592
+ `,(0,c.jsx)(t.p,{children:`Лучше показать пользователю/разработчику контекст и не отправлять request:`}),`
1593
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`try {
1594
+ return buildEntityPath(metadata, config, params);
1595
+ } catch (error) {
1596
+ throw new Error(\`Не удалось подготовить OData target \${config.target}\`, {
1597
+ cause: error
1598
+ });
1599
+ }
1600
+ `})}),`
1601
+ `,(0,c.jsx)(t.h3,{id:`values-и-field-names-защищаются-по-разному`,children:`Values и field names защищаются по-разному`}),`
1602
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`odataFormatValue`}),` экранирует string values и проверяет primitive type.`]}),`
1603
+ `,(0,c.jsx)(t.p,{children:`Но имена полей/сервисов не проходят общий runtime allowlist:`}),`
1604
+ `,(0,c.jsxs)(t.ul,{children:[`
1605
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`FilterCondition.key`}),` вставляется как field id;`]}),`
1606
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`ODataServiceConfig.service`}),` и `,(0,c.jsx)(t.code,{children:`target`}),` вставляются как path segments;`]}),`
1607
+ `,(0,c.jsx)(t.li,{children:`metadata ids считаются доверенными server metadata;`}),`
1608
+ `,(0,c.jsx)(t.li,{children:`custom formatter может вернуть любую строку.`}),`
1609
+ `]}),`
1610
+ `,(0,c.jsx)(t.p,{children:`Если field/service/target приходит из пользовательского JSON, сверяйте его с заранее известным allowlist или разобранной metadata.`}),`
1611
+ `,(0,c.jsx)(t.h3,{id:`custom-formatter--доверенная-функция`,children:`Custom formatter — доверенная функция`}),`
1612
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`{
1613
+ value,
1614
+ formatter: (raw) => "..."
1615
+ }
1616
+ `})}),`
1617
+ `,(0,c.jsxs)(t.p,{children:[`Builder не понимает семантику строки custom formatter. Он не может гарантировать корректное escaping и тип literal. Не создавайте formatter из произвольного пользовательского JavaScript и не используйте `,(0,c.jsx)(t.code,{children:`eval`}),`.`]}),`
1618
+ `,(0,c.jsx)(t.p,{children:`Formatter должен быть заранее написан и протестирован.`}),`
1619
+ `,(0,c.jsx)(t.h3,{id:`не-кодируйте-всё-url-целиком`,children:`Не кодируйте всё URL целиком`}),`
1620
+ `,(0,c.jsxs)(t.p,{children:[`Entity parameters кодируются builder-ом на уровне отдельных values. Function Import использует `,(0,c.jsx)(t.code,{children:`URLSearchParams`}),`.`]}),`
1621
+ `,(0,c.jsxs)(t.p,{children:[`Если после этого вызвать `,(0,c.jsx)(t.code,{children:`encodeURIComponent`}),` на всём path, `,(0,c.jsx)(t.code,{children:`/`}),`, `,(0,c.jsx)(t.code,{children:`?`}),`, `,(0,c.jsx)(t.code,{children:`=`}),` и уже encoded проценты будут испорчены.`]}),`
1622
+ `,(0,c.jsx)(t.p,{children:`Transport должен соединять подготовленный path с base URL через свой проверенный URL builder.`}),`
1623
+ `,(0,c.jsx)(t.h3,{id:`metadata-не-является-validation-server-rows`,children:`Metadata не является validation server rows`}),`
1624
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`ServiceMetadata`}),` описывает ожидаемую структуру, но library не проверяет каждую запись ответа. TypeScript type assertion не заменяет runtime validation:`]}),`
1625
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const rows = response as SalesRow[]; // не проверяет данные
1626
+ `})}),`
1627
+ `,(0,c.jsx)(t.p,{children:`Если backend payload недоверенный, валидируйте его на transport/domain boundary.`}),`
1628
+ `,(0,c.jsx)(t.h3,{id:`sql-like-api-не-делает-dynamic-sql-безопасным-целиком`,children:`SQL-like API не делает dynamic SQL безопасным целиком`}),`
1629
+ `,(0,c.jsx)(t.p,{children:`Library ограничивает field id и экранирует quotes в values. Но окончательная безопасность зависит от backend:`}),`
1630
+ `,(0,c.jsxs)(t.ul,{children:[`
1631
+ `,(0,c.jsx)(t.li,{children:`backend не должен добавлять raw user fragments;`}),`
1632
+ `,(0,c.jsx)(t.li,{children:`backend должен ограничить доступные поля;`}),`
1633
+ `,(0,c.jsx)(t.li,{children:`лучше использовать parameterized database query;`}),`
1634
+ `,(0,c.jsx)(t.li,{children:`request body не должен давать пользователю произвольный WHERE.`}),`
1635
+ `]}),`
1636
+ `,(0,c.jsx)(t.h2,{id:`ssr-браузер-и-react`,children:`SSR, браузер и React`}),`
1637
+ `,(0,c.jsxs)(t.h3,{id:`domparser-и-nodejs`,children:[(0,c.jsx)(t.code,{children:`DOMParser`}),` и Node.js`]}),`
1638
+ `,(0,c.jsxs)(t.p,{children:[`В обычном browser `,(0,c.jsx)(t.code,{children:`DOMParser`}),` существует глобально. В чистом Node.js это не гарантируется.`]}),`
1639
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`if (typeof DOMParser === "undefined") {
1640
+ throw new Error("Для parseServiceMetadata требуется DOMParser");
1641
+ }
1642
+ `})}),`
1643
+ `,(0,c.jsx)(t.p,{children:`Для SSR есть три безопасных варианта:`}),`
1644
+ `,(0,c.jsxs)(t.ol,{children:[`
1645
+ `,(0,c.jsx)(t.li,{children:`парсить metadata только в browser;`}),`
1646
+ `,(0,c.jsx)(t.li,{children:`предоставить совместимый DOM implementation в server runtime;`}),`
1647
+ `,(0,c.jsxs)(t.li,{children:[`парсить metadata в transport/server layer и передавать сериализуемый `,(0,c.jsx)(t.code,{children:`ServiceMetadata`}),` дальше.`]}),`
1648
+ `]}),`
1649
+ `,(0,c.jsx)(t.p,{children:`Не добавляйте случайный DOM polyfill внутрь feature: настройте его один раз на границе runtime.`}),`
1650
+ `,(0,c.jsx)(t.h3,{id:`zustand-store-на-server`,children:`Zustand store на server`}),`
1651
+ `,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`useODataCollectionStore`}),` является singleton module state. В долгоживущем SSR process глобальное изменение может разделяться между requests.`]}),`
1652
+ `,(0,c.jsx)(t.p,{children:`Не храните в нём пользовательские или request-specific values. Текущий store предназначен только для общих defaults приложения.`}),`
1653
+ `,(0,c.jsx)(t.h3,{id:`rules-of-hooks`,children:`Rules of Hooks`}),`
1654
+ `,(0,c.jsx)(t.p,{children:`Следующий код неверен:`}),`
1655
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`function readConfig() {
1656
+ return odataCollectionConfig.useMaxVisibleItems();
1657
+ }
1658
+ `})}),`
1659
+ `,(0,c.jsx)(t.p,{children:`Обычная функция может быть вызвана вне React render.`}),`
1660
+ `,(0,c.jsx)(t.p,{children:`Для non-React чтения используйте:`}),`
1661
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const maxVisible = useODataCollectionStore.getState().defaultMaxVisibleItems;
1662
+ `})}),`
1663
+ `,(0,c.jsx)(t.h3,{id:`server-components`,children:`Server Components`}),`
1664
+ `,(0,c.jsxs)(t.p,{children:[`Pure helpers можно использовать в server code при доступности их runtime dependencies. Hook selectors нужно вызывать только в client component. Помечайте component согласно framework contract, например `,(0,c.jsx)(t.code,{children:`"use client"`}),`, когда это требуется.`]}),`
1665
+ `,(0,c.jsx)(t.h2,{id:`тестирование-consumer-кода`,children:`Тестирование consumer-кода`}),`
1666
+ `,(0,c.jsx)(t.h3,{id:`тест-value-formatting`,children:`Тест value formatting`}),`
1667
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import { describe, expect, it } from "vitest";
1668
+ import { odataFormatValue } from "@ryuzaki13/react-foundation-lib/odata-service";
1669
+
1670
+ describe("OData parameter values", () => {
1671
+ it("экранирует одинарную кавычку", () => {
1672
+ expect(odataFormatValue("string", "O'Reilly")).toBe("'O''Reilly'");
1673
+ });
1674
+ });
1675
+ `})}),`
1676
+ `,(0,c.jsx)(t.h3,{id:`тест-builder-по-metadata`,children:`Тест builder по metadata`}),`
1677
+ `,(0,c.jsx)(t.p,{children:`Создавайте минимальную metadata, которая важна сценарию:`}),`
1678
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`it("не позволяет пропустить обязательную компанию", () => {
1679
+ const metadata: EntityMetadata = {
1680
+ title: "Продажи",
1681
+ columns: [],
1682
+ parameters: [
1683
+ {
1684
+ id: "p_company",
1685
+ label: "Компания",
1686
+ type: "string",
1687
+ originalType: "Edm.String",
1688
+ mandatory: true
1689
+ }
1690
+ ],
1691
+ result: "Results"
1692
+ };
1693
+
1694
+ expect(() => buildEntityParameters(metadata, {})).toThrow("Отсутствует обязательный параметр: p_company");
1695
+ });
1696
+ `})}),`
1697
+ `,(0,c.jsx)(t.h3,{id:`тест-filters-как-структуры-и-строки`,children:`Тест filters как структуры и строки`}),`
1698
+ `,(0,c.jsx)(t.p,{children:`Полезно проверять оба уровня:`}),`
1699
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const expression = createFilterEqual<Row, string>("STATUS", ["A", "B"]);
1700
+
1701
+ expect(expression).toEqual({
1702
+ conditions: [
1703
+ { key: "STATUS", value: "A", operation: "eq" },
1704
+ { key: "STATUS", value: "B", operation: "eq" }
1705
+ ]
1706
+ });
1707
+
1708
+ expect(buildODataFilter(expression)).toBe("(STATUS eq 'A' or STATUS eq 'B')");
1709
+ `})}),`
1710
+ `,(0,c.jsx)(t.p,{children:`Structure test показывает ошибку compiler-а, string test — ошибку serializer-а.`}),`
1711
+ `,(0,c.jsx)(t.h3,{id:`тест-compiled-definitions`,children:`Тест compiled definitions`}),`
1712
+ `,(0,c.jsx)(t.p,{children:`Для каждого custom filter проверяйте минимум:`}),`
1713
+ `,(0,c.jsxs)(t.ul,{children:[`
1714
+ `,(0,c.jsx)(t.li,{children:`empty value;`}),`
1715
+ `,(0,c.jsx)(t.li,{children:`одно значение;`}),`
1716
+ `,(0,c.jsx)(t.li,{children:`несколько значений;`}),`
1717
+ `,(0,c.jsxs)(t.li,{children:[`falsy value `,(0,c.jsx)(t.code,{children:`0`}),`, `,(0,c.jsx)(t.code,{children:`false`}),`, `,(0,c.jsx)(t.code,{children:`""`}),`, если допустимо;`]}),`
1718
+ `,(0,c.jsx)(t.li,{children:`невалидный сохранённый snapshot;`}),`
1719
+ `,(0,c.jsx)(t.li,{children:`physical column ids;`}),`
1720
+ `,(0,c.jsxs)(t.li,{children:[`итоговую строку `,(0,c.jsx)(t.code,{children:`$filter`}),`.`]}),`
1721
+ `]}),`
1722
+ `,(0,c.jsx)(t.h3,{id:`тест-store`,children:`Тест store`}),`
1723
+ `,(0,c.jsx)(t.p,{children:`Singleton state сбрасывайте между tests:`}),`
1724
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`beforeEach(() => {
1725
+ useODataCollectionStore.setState({
1726
+ defaultMaxVisibleItems: 200,
1727
+ defaultMinSearchCodeLength: 3,
1728
+ defaultMinSearchTextLength: 1,
1729
+ defaultSearchDebounceDelay: 100
1730
+ });
1731
+ });
1732
+ `})}),`
1733
+ `,(0,c.jsx)(t.p,{children:`Иначе один test может влиять на следующий.`}),`
1734
+ `,(0,c.jsx)(t.h3,{id:`metadata-fixtures`,children:`Metadata fixtures`}),`
1735
+ `,(0,c.jsx)(t.p,{children:`Храните небольшие XML fixtures для отдельных особенностей:`}),`
1736
+ `,(0,c.jsxs)(t.ul,{children:[`
1737
+ `,(0,c.jsx)(t.li,{children:`parameterized entity;`}),`
1738
+ `,(0,c.jsx)(t.li,{children:`Function Import;`}),`
1739
+ `,(0,c.jsx)(t.li,{children:`code/text link;`}),`
1740
+ `,(0,c.jsx)(t.li,{children:`ABAP boolean-like field;`}),`
1741
+ `,(0,c.jsx)(t.li,{children:`unsupported type/error case.`}),`
1742
+ `]}),`
1743
+ `,(0,c.jsx)(t.p,{children:`Один огромный production EDMX делает test сложным для понимания и обновления.`}),`
1744
+ `,(0,c.jsxs)(t.h2,{id:`миграция-с-odata`,children:[`Миграция с `,(0,c.jsx)(t.code,{children:`/odata`})]}),`
1745
+ `,(0,c.jsx)(t.p,{children:`После объединения измените все импорты code/text helpers, date formatter и collection store.`}),`
1746
+ `,(0,c.jsx)(t.p,{children:`До:`}),`
1747
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import { buildSeparatedArrays, findCollectionPairs, ODataDateFormat, useODataCollectionStore } from "@ryuzaki13/react-foundation-lib/odata-service";
1748
+ `})}),`
1749
+ `,(0,c.jsx)(t.p,{children:`После:`}),`
1750
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import {
1751
+ buildSeparatedArrays,
1752
+ findCollectionPairs,
1753
+ ODataDateFormat,
1754
+ useODataCollectionStore
1755
+ } from "@ryuzaki13/react-foundation-lib/odata-service";
1756
+ `})}),`
1757
+ `,(0,c.jsx)(t.h3,{id:`что-именно-удалено`,children:`Что именно удалено`}),`
1758
+ `,(0,c.jsx)(t.p,{children:`Subpath export:`}),`
1759
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-text`,children:`@ryuzaki13/react-foundation-lib/odata
1760
+ `})}),`
1761
+ `,(0,c.jsx)(t.p,{children:`больше не является публичным контрактом. Compatibility re-export не оставлен.`}),`
1762
+ `,(0,c.jsx)(t.h3,{id:`порядок-миграции-нескольких-пакетов`,children:`Порядок миграции нескольких пакетов`}),`
1763
+ `,(0,c.jsxs)(t.p,{children:[`Если `,(0,c.jsx)(t.code,{children:`react-foundation-api`}),`, `,(0,c.jsx)(t.code,{children:`react-foundation-ui`}),` и host-приложение используют прежний subpath:`]}),`
1764
+ `,(0,c.jsxs)(t.ol,{children:[`
1765
+ `,(0,c.jsxs)(t.li,{children:[`опубликуйте новую breaking-версию `,(0,c.jsx)(t.code,{children:`react-foundation-lib`}),`;`]}),`
1766
+ `,(0,c.jsx)(t.li,{children:`обновите imports и dependency range в package-потребителе;`}),`
1767
+ `,(0,c.jsx)(t.li,{children:`проверьте typecheck/build этого потребителя;`}),`
1768
+ `,(0,c.jsx)(t.li,{children:`опубликуйте обновлённый зависимый package;`}),`
1769
+ `,(0,c.jsx)(t.li,{children:`только затем обновите host-приложение.`}),`
1770
+ `]}),`
1771
+ `,(0,c.jsx)(t.p,{children:`Нельзя отдельно переключить source consumer на новые exports, пока установленная версия library ещё не содержит перенесённые symbols.`}),`
1772
+ `,(0,c.jsx)(t.h3,{id:`что-не-нужно-менять`,children:`Что не нужно менять`}),`
1773
+ `,(0,c.jsxs)(t.p,{children:[`Imports, которые уже использовали `,(0,c.jsx)(t.code,{children:`/odata-service`}),`, остаются прежними:`]}),`
1774
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import { buildODataFilter } from "@ryuzaki13/react-foundation-lib/odata-service";
1775
+ `})}),`
1776
+ `,(0,c.jsx)(t.p,{children:`Не путайте с transport entrypoint:`}),`
1777
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`// Это другой пакет; его имя не меняется из-за объединения lib-модулей.
1778
+ import {} from /* transport API */ "@ryuzaki13/react-foundation-api/odata";
1779
+ `})}),`
1780
+ `,(0,c.jsx)(t.h2,{id:`полная-карта-api`,children:`Полная карта API`}),`
1781
+ `,(0,c.jsx)(t.p,{children:`Раздел перечисляет все публичные symbols единого entrypoint. Если symbol отсутствует в этой карте, не импортируйте его через глубокий путь.`}),`
1782
+ `,(0,c.jsx)(t.h3,{id:`base-types-и-labels`,children:`Base types и labels`}),`
1783
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Symbol`}),(0,c.jsx)(t.th,{children:`Вид`}),(0,c.jsx)(t.th,{children:`Назначение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`BaseMetaType`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`UI type: string/number/boolean/date.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`BASE_META_TYPES`})}),(0,c.jsx)(t.td,{children:`const`}),(0,c.jsx)(t.td,{children:`Readonly список base types.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`BASE_META_TYPE_LABELS`})}),(0,c.jsx)(t.td,{children:`const`}),(0,c.jsx)(t.td,{children:`Русские labels base types.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isBaseMetaType`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Runtime type guard base type.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`resolveBaseTypeLabel`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Label для base type.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataMetaType`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Union всех поддерживаемых OData types.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataBooleanType`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Boolean group.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataNumericType`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Numeric group.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataIntegerType`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Integer subset.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataDateType`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Date/time group.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataStringType`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`String/guid/binary group.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`BaseMethod`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`GET/POST/PUT/DELETE.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataOperationMethod`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`create/update/delete/read/query/fi.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isODataBooleanType`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Guard boolean type name.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isODataNumericType`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Guard numeric type name.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isODataIntegerType`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Guard integer type name.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isODataDateType`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Guard date type name.`})]})]})]}),`
1784
+ `,(0,c.jsx)(t.h3,{id:`metadata-contracts-и-parser`,children:`Metadata contracts и parser`}),`
1785
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Symbol`}),(0,c.jsx)(t.th,{children:`Вид`}),(0,c.jsx)(t.th,{children:`Назначение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`EntityPropertyBase`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Id + label.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`EntityPropertyBaseWithType`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Base property + OData type.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`EntityParameterProperty`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Parameter metadata.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ColumnRole`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`measure/dimension.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`EntityColumnProperty`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Column metadata и code/text semantics.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`EntityMetadata`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Entity columns, parameters и result navigation.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ServiceEntities`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Entity map.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FunctionImportMetadata`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Function Import metadata.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ServiceFunctionImports`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Function Import map.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataTargetMetadata`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Entity или Function Import.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ServiceMetadata`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Полный результат parser.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`parseServiceMetadata`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`EDMX XML → ServiceMetadata.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FORCED_CODE_TEXT_IDS`})}),(0,c.jsx)(t.td,{children:`const`}),(0,c.jsx)(t.td,{children:`Forced code/text base ids.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`resolveMetadataColumnLabel`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Непустой label с fallback на id.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isForcedCodeTextId`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Проверка точного forced id.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isForcedCodeTextFamilyId`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Проверка forced base/text family.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`collectFilterableColumns`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Filterable column objects.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`collectFilterableColumnsIds`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Set filterable column ids.`})]})]})]}),`
1786
+ `,(0,c.jsx)(t.h3,{id:`values-schemas-и-formatter-registry`,children:`Values, schemas и formatter registry`}),`
1787
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Symbol`}),(0,c.jsx)(t.th,{children:`Вид`}),(0,c.jsx)(t.th,{children:`Назначение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`odataTypeSchemas`})}),(0,c.jsx)(t.td,{children:`const`}),(0,c.jsx)(t.td,{children:`Zod schemas OData primitive types.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isStringSafe`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Strict string guard.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isNumberSafe`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Number guard.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isBooleanSafe`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Boolean guard.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isDateSafe`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Valid Date guard.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`odataFormatValue`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`JavaScript value → OData literal.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`odataParseValue`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Raw value → значение по OData type.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`odataParseValueByMetadata`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsxs)(t.td,{children:[`Parsing с `,(0,c.jsx)(t.code,{children:`abapBooleanLike`}),`.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`defaultODataTypeValue`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Default по OData type.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`defaultControlTypeValue`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Default по base UI type.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`getBaseTypeFromODataType`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`OData type → base type.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataDateFormat`})}),(0,c.jsx)(t.td,{children:`const`}),(0,c.jsx)(t.td,{children:`datetime/datetimeOffset/time literals.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataFormatterFn`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Formatter function contract.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataFormatterDescription`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Formatter registry item.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createFormatter`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Generic Zod-validated formatter factory.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createBooleanFormatter`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Boolean arguments factory.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createStringFormatter`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`String arguments factory.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createNumberFormatter`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Number arguments factory.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createDateFormatter`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Date arguments factory.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`getFormattersFor`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Registry items по OData type.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`getFormattersForBaseType`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Registry items по base type.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`getFormatter`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Один formatter по OData type/id.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`getFormatterForBaseType`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Один formatter по base type/id.`})]})]})]}),`
1788
+ `,(0,c.jsx)(t.h3,{id:`parameters-и-path`,children:`Parameters и path`}),`
1789
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Symbol`}),(0,c.jsx)(t.th,{children:`Вид`}),(0,c.jsx)(t.th,{children:`Назначение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataParameterValue`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Scalar или scalar array для formatter.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataValue`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`{ value, formatter? }`}),`.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`InputValue`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Wrapped generic input.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`UnwrappedODataParameters`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Flat OData parameters.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`UnwrappedInputParameters`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Flat input parameters.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`WrappedODataParameters`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Wrapped OData parameters.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`WrappedInputParameters`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Wrapped input parameters.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`WrapODataParameters`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Generic flat → wrapped mapping.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`UnwrapODataParameters`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Generic wrapped → flat mapping.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`wrapODataParams`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsxs)(t.td,{children:[`Flat object → `,(0,c.jsx)(t.code,{children:`{ value }`}),` object.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`unwrapODataParams`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Wrapped → sorted flat snapshot.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataServiceConfig`})}),(0,c.jsx)(t.td,{children:`interface`}),(0,c.jsx)(t.td,{children:`Service + target path segments.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildParameterEntries`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Metadata-ordered formatted entries.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildEntityParameters`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Entity parameter tuple.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildFunctionImportParameters`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Function Import URLSearchParams.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildEntityOperationPath`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Path по operation intent.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildFunctionImportPath`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Function Import path + query.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildEntityPath`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Parameterized entity path.`})]})]})]}),`
1790
+ `,(0,c.jsx)(t.h3,{id:`простые-odata-filters`,children:`Простые OData filters`}),`
1791
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Symbol`}),(0,c.jsx)(t.th,{children:`Вид`}),(0,c.jsx)(t.th,{children:`Назначение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterOperation`})}),(0,c.jsx)(t.td,{children:`const и type`}),(0,c.jsx)(t.td,{children:`Operations и их union type.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FILTER_OPERATIONS`})}),(0,c.jsx)(t.td,{children:`const`}),(0,c.jsx)(t.td,{children:`Readonly operations list.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isFilterOperation`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Runtime operation guard.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`resolveOperationLabel`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Русский operation label.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterCondition`})}),(0,c.jsx)(t.td,{children:`interface`}),(0,c.jsx)(t.td,{children:`Одно field condition.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`BaseFilterExpression`})}),(0,c.jsx)(t.td,{children:`interface`}),(0,c.jsxs)(t.td,{children:[`Общий `,(0,c.jsx)(t.code,{children:`and?`}),`.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterConditionExpression`})}),(0,c.jsx)(t.td,{children:`interface`}),(0,c.jsx)(t.td,{children:`Leaf conditions.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterNestedExpression`})}),(0,c.jsx)(t.td,{children:`interface`}),(0,c.jsx)(t.td,{children:`Nested filters.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterExpression`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Union leaf/nested node.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildODataFilter`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsxs)(t.td,{children:[`Expression → `,(0,c.jsx)(t.code,{children:`$filter`}),` value.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildODataFilterRecursive`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Вариант с дополнительными скобками.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createFilter`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Общая operation factory.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createFilterBetween`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsxs)(t.td,{children:[`Range `,(0,c.jsx)(t.code,{children:`ge`}),`/`,(0,c.jsx)(t.code,{children:`le`}),`.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createFilterEqual`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Equality scalar/array.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createFilterEqualFalsy`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Equality с удалением falsy values.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createFilterContains`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsxs)(t.td,{children:[(0,c.jsx)(t.code,{children:`substringof`}),` scalar/array.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`mergeFilterExpressions`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Два expressions через AND.`})]})]})]}),`
1792
+ `,(0,c.jsx)(t.h3,{id:`compiled-filter-definitions-1`,children:`Compiled filter definitions`}),`
1793
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Symbol`}),(0,c.jsx)(t.th,{children:`Вид`}),(0,c.jsx)(t.th,{children:`Назначение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataFilterValue`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Одно controlled filter value.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataFilterValues`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Values по filter id.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataFilterCondition`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Static/input condition.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataFilterConditionGroup`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Condition group.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataFilterOption`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`List option + filter.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataFilterBinding`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`List/boolean/value binding.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterDefinitionKind`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Supported definition kinds.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterRegisteredValueMode`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`single/multiple.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterSegmentComponentId`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Segment component ids.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterTreeComponentId`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Tree component ids.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterLocalComponentId`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Local component ids.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterAdvancedComponentId`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Advanced component id.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`FilterComponentId`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Union известных component ids.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataCompiledFilterDefinition`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Discriminated definition union.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isFilterLocalComponentId`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Local component guard.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`resolveODataFilterDefinitionColumnIds`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Potential physical columns.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`resolveODataFilterDefinitionActiveColumnIds`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Active physical columns.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`normalizeDictionaryCodeKey`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Trim/remove redundant dictionary key.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`sanitizeFilterConditionGroup`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Нормализация condition group.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`sanitizeFilterBinding`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Нормализация binding.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`sanitizeFilterDefinitions`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Нормализация definitions snapshot.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`sanitizeFilterValue`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Нормализация одного value.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`sanitizeFilterValues`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Нормализация values map.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`mergeFilterValuePatch`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Immutable normalized patch.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`collapseChainedODataSegmentFilterValues`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Самый глубокий segment chain.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`compileFiltersToExpression`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Definitions + values → expression.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`flattenFilterValuesToODataDependencies`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Segment/tree dependencies.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataChainItem`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Элемент dependency chain.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`ODataChainsMap`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Карта dependency chains.`})]})]})]}),`
1794
+ `,(0,c.jsx)(t.h3,{id:`sorts`,children:`Sorts`}),`
1795
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Symbol`}),(0,c.jsx)(t.th,{children:`Вид`}),(0,c.jsx)(t.th,{children:`Назначение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`Sort`})}),(0,c.jsx)(t.td,{children:`interface`}),(0,c.jsx)(t.td,{children:`Key + optional descending flag.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`getSortIndex`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Index key в sorting.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildODataOrder`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Sorting → order string.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`toggleSort`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Цикл none/asc/desc.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`resolveEffectiveSorts`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`User sorting или grouping fallback.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildODataOrderBy`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsxs)(t.td,{children:[`Effective `,(0,c.jsx)(t.code,{children:`$orderby`}),` либо undefined.`]})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`getSortIndicator`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`UI indicator state/order/lock.`})]})]})]}),`
1796
+ `,(0,c.jsx)(t.h3,{id:`sql-like-filters`,children:`SQL-like filters`}),`
1797
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Symbol`}),(0,c.jsx)(t.th,{children:`Вид`}),(0,c.jsx)(t.th,{children:`Назначение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`SqlFilterScalarValue`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`string/number/boolean.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`SqlFilterOperation`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`eq/in.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`SqlFilterEqualCondition`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Equality node.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`SqlFilterInCondition`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`IN node.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`SqlFilterCondition`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Condition union.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`SqlFilterConjunction`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`and/or.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`SqlFilterGroup`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Nested group.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`SqlFilterExpression`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`Condition/group union.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isSqlFilterScalarValue`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Scalar guard.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`assertSqlFilterFieldId`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Safe field identifier check.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createSqlFilterEqual`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Equality factory.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createSqlFilterIn`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`IN factory.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`createSqlFilterGroup`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Group factory.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildSqlFilter`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Legacy WHERE string.`})]})]})]}),`
1798
+ `,(0,c.jsx)(t.h3,{id:`collections-store-и-utilities`,children:`Collections, store и utilities`}),`
1799
+ `,(0,c.jsxs)(t.table,{children:[(0,c.jsx)(t.thead,{children:(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.th,{children:`Symbol`}),(0,c.jsx)(t.th,{children:`Вид`}),(0,c.jsx)(t.th,{children:`Назначение`})]})}),(0,c.jsxs)(t.tbody,{children:[(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`CollectionItem`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`String record справочника.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`CollectionPair`})}),(0,c.jsx)(t.td,{children:`interface`}),(0,c.jsx)(t.td,{children:`codeKey/textKey.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`DependencyFilter`})}),(0,c.jsx)(t.td,{children:`interface`}),(0,c.jsx)(t.td,{children:`Key + collection values.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`findCollectionPairs`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Поиск code/text pairs.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`buildSeparatedArrays`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Разделение/dedupe/sort справочника.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`useODataCollectionStore`})}),(0,c.jsx)(t.td,{children:`const/hook`}),(0,c.jsx)(t.td,{children:`Zustand state и setters defaults.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`odataCollectionConfig`})}),(0,c.jsx)(t.td,{children:`const`}),(0,c.jsx)(t.td,{children:`React selector hooks.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`normalizeRangeValue`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Input → двухэлементный range.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`normalizeBaseValue`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Input → scalar/null.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`isBaseValue`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Мягкий base value guard.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`UnwrappedODataQueryResult`})}),(0,c.jsx)(t.td,{children:`type`}),(0,c.jsx)(t.td,{children:`TanStack result с распакованным data.`})]}),(0,c.jsxs)(t.tr,{children:[(0,c.jsx)(t.td,{children:(0,c.jsx)(t.code,{children:`unwrapODataQueryResult`})}),(0,c.jsx)(t.td,{children:`function`}),(0,c.jsx)(t.td,{children:`Shallow unwrap query envelope.`})]})]})]}),`
1800
+ `,(0,c.jsx)(t.h2,{id:`частые-вопросы`,children:`Частые вопросы`}),`
1801
+ `,(0,c.jsx)(t.h3,{id:`какой-import-использовать-для-odata`,children:`Какой import использовать для OData?`}),`
1802
+ `,(0,c.jsx)(t.p,{children:`Для metadata, values, path, filters, sorts, code/text helpers и collection config:`}),`
1803
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`"@ryuzaki13/react-foundation-lib/odata-service";
1804
+ `})}),`
1805
+ `,(0,c.jsx)(t.p,{children:`Для HTTP/SAP Gateway/TanStack Query transport:`}),`
1806
+ `,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`"@ryuzaki13/react-foundation-api/odata";
1807
+ `})}),`
1808
+ `,(0,c.jsxs)(t.h3,{id:`почему-нельзя-импортировать-прежний-odata`,children:[`Почему нельзя импортировать прежний `,(0,c.jsx)(t.code,{children:`/odata`}),`?`]}),`
1809
+ `,(0,c.jsx)(t.p,{children:`Модули объединены. Два entrypoint создавали искусственное разделение одного технического OData-контракта и заставляли consumer угадывать расположение helper-а.`}),`
1810
+ `,(0,c.jsx)(t.h3,{id:`нужно-ли-парсить-metadata-перед-каждым-request`,children:`Нужно ли парсить metadata перед каждым request?`}),`
1811
+ `,(0,c.jsxs)(t.p,{children:[`Нет. Parser чисто преобразует XML, но может выполнять заметную работу. Получайте и кешируйте metadata на уровне API/query infrastructure, затем переиспользуйте `,(0,c.jsx)(t.code,{children:`ServiceMetadata`}),`.`]}),`
1812
+ `,(0,c.jsx)(t.h3,{id:`можно-ли-передать-обычный-object-params`,children:`Можно ли передать обычный object params?`}),`
1813
+ `,(0,c.jsxs)(t.p,{children:[`Builder ожидает wrapped values. Используйте `,(0,c.jsx)(t.code,{children:`wrapODataParams(flat)`}),` или создайте `,(0,c.jsx)(t.code,{children:`{ value, formatter? }`}),` вручную.`]}),`
1814
+ `,(0,c.jsx)(t.h3,{id:`почему-массив-параметра-требует-formatter`,children:`Почему массив параметра требует formatter?`}),`
1815
+ `,(0,c.jsx)(t.p,{children:`OData parameter metadata описывает scalar. Library не может угадать backend-specific способ сериализации массива: CSV, range, JSON, repeated query parameter или другое соглашение.`}),`
1816
+ `,(0,c.jsxs)(t.h3,{id:`почему-число-125-не-форматируется-в-фильтре-автоматически`,children:[`Почему число `,(0,c.jsx)(t.code,{children:`12.5`}),` не форматируется в фильтре автоматически?`]}),`
1817
+ `,(0,c.jsxs)(t.p,{children:[`Без `,(0,c.jsx)(t.code,{children:`condition.type`}),` number выводится как `,(0,c.jsx)(t.code,{children:`int`}),`. Укажите точный metadata type, например `,(0,c.jsx)(t.code,{children:`decimal`}),`.`]}),`
1818
+ `,(0,c.jsxs)(t.h3,{id:`почему-createfilterbetween0-null-пустой`,children:[`Почему `,(0,c.jsx)(t.code,{children:`createFilterBetween([0, null])`}),` пустой?`]}),`
1819
+ `,(0,c.jsxs)(t.p,{children:[`Текущая фабрика partial range использует truthy-проверку. Для нулевой границы создайте explicit `,(0,c.jsx)(t.code,{children:`createFilter(..., "ge")`}),`.`]}),`
1820
+ `,(0,c.jsx)(t.h3,{id:`почему-пустая-строка-иногда-сохраняется`,children:`Почему пустая строка иногда сохраняется?`}),`
1821
+ `,(0,c.jsxs)(t.p,{children:[`В compiled filters пустая строка является допустимым scalar и option key. Это нужно для SAP-состояний, где empty char имеет смысл. `,(0,c.jsx)(t.code,{children:`null`}),`/`,(0,c.jsx)(t.code,{children:`undefined`}),` означают отсутствие, а `,(0,c.jsx)(t.code,{children:`""`}),` может быть реальным условием.`]}),`
1822
+ `,(0,c.jsxs)(t.h3,{id:`можно-ли-использовать-sql-like-builder-для-filter`,children:[`Можно ли использовать SQL-like builder для `,(0,c.jsx)(t.code,{children:`$filter`}),`?`]}),`
1823
+ `,(0,c.jsx)(t.p,{children:`Нет. Это разные syntaxes и разные transport boundaries.`}),`
1824
+ `,(0,c.jsx)(t.h3,{id:`почему-codetext-pair-не-нашлась`,children:`Почему code/text pair не нашлась?`}),`
1825
+ `,(0,c.jsx)(t.p,{children:`Проверьте:`}),`
1826
+ `,(0,c.jsxs)(t.ul,{children:[`
1827
+ `,(0,c.jsx)(t.li,{children:`suffix text field;`}),`
1828
+ `,(0,c.jsx)(t.li,{children:`точное совпадение регистра base key;`}),`
1829
+ `,(0,c.jsx)(t.li,{children:`наличие обоих keys в первом row;`}),`
1830
+ `,(0,c.jsxs)(t.li,{children:[`custom `,(0,c.jsx)(t.code,{children:`textSuffixes`}),`;`]}),`
1831
+ `,(0,c.jsx)(t.li,{children:`не использовано ли поле другой pair.`}),`
1832
+ `]}),`
1833
+ `,(0,c.jsx)(t.h3,{id:`где-форматировать-server-value-для-отображения`,children:`Где форматировать server value для отображения?`}),`
1834
+ `,(0,c.jsxs)(t.p,{children:[`В `,(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/formatters`}),` или table formatter pipeline. OData literal formatter предназначен для request, а не для UI.`]}),`
1835
+ `,(0,c.jsx)(t.h3,{id:`где-выполнять-request`,children:`Где выполнять request?`}),`
1836
+ `,(0,c.jsxs)(t.p,{children:[`В `,(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-api/odata`}),` или через согласованный transport host-приложения. Этот модуль только подготавливает данные.`]}),`
1837
+ `,(0,c.jsx)(t.h2,{id:`связанные-модули`,children:`Связанные модули`}),`
1838
+ `,(0,c.jsxs)(t.ul,{children:[`
1839
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-api/odata`}),` — OData transport, queries и mutations.`]}),`
1840
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/formatters`}),` — отображение дат, чисел, строк и pipeline.`]}),`
1841
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/table`}),` — TanStack Table metadata adapter.`]}),`
1842
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/range-output`}),` — подстановка открытых границ диапазона перед внешней передачей.`]}),`
1843
+ `,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`@ryuzaki13/react-foundation-lib/xml`}),` — общие XML parsing/building helpers.`]}),`
1844
+ `]})]})}function s(e={}){let{wrapper:t}={...n(),...e.components};return t?(0,c.jsx)(t,{...e,children:(0,c.jsx)(o,{...e})}):o(e)}var c;function l(){return(l=e((()=>{c=t(),r(),a()})))()}l();export{s as default};