nexus-shared 1.1.21 → 3.0.0

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 (376) hide show
  1. package/CHANGELOG.md +262 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1000 -0
  4. package/dist/Client.Index.d.ts +55 -0
  5. package/dist/Client.Index.js +60 -0
  6. package/dist/Components/Chats/Chat.d.ts +28 -0
  7. package/dist/Components/Chats/Chat.js +19 -0
  8. package/dist/Components/Chats/ChatButton.d.ts +26 -0
  9. package/dist/Components/Chats/ChatButton.js +41 -0
  10. package/dist/Components/Chats/ChatComposer.d.ts +41 -0
  11. package/dist/Components/Chats/ChatComposer.js +177 -0
  12. package/dist/Components/Chats/ChatConversations.d.ts +35 -0
  13. package/dist/Components/Chats/ChatConversations.js +38 -0
  14. package/dist/Components/Chats/ChatPanel.d.ts +66 -0
  15. package/dist/Components/Chats/ChatPanel.js +94 -0
  16. package/dist/Components/Chats/ChatParts.d.ts +87 -0
  17. package/dist/Components/Chats/ChatParts.js +100 -0
  18. package/dist/Components/Chats/ChatThread.d.ts +22 -0
  19. package/dist/Components/Chats/ChatThread.js +96 -0
  20. package/dist/Components/Documents/Button.d.ts +29 -0
  21. package/dist/Components/Documents/Button.js +11 -0
  22. package/dist/Components/Documents/Menu.d.ts +102 -0
  23. package/dist/Components/Documents/Menu.js +242 -0
  24. package/dist/Components/Documents/SplitButton.d.ts +64 -0
  25. package/dist/Components/Documents/SplitButton.js +129 -0
  26. package/dist/Components/Documents/TabButtons.d.ts +80 -0
  27. package/dist/Components/Documents/TabButtons.js +38 -0
  28. package/dist/Components/Forms/ApiForm.d.ts +23 -0
  29. package/dist/Components/Forms/ApiForm.js +100 -0
  30. package/dist/Components/Forms/Crud.d.ts +17 -0
  31. package/dist/Components/Forms/Crud.js +708 -0
  32. package/dist/Components/Forms/CrudParts.d.ts +15 -0
  33. package/dist/Components/Forms/CrudParts.js +36 -0
  34. package/dist/Components/Forms/ExcelImport.d.ts +42 -0
  35. package/dist/Components/Forms/ExcelImport.js +190 -0
  36. package/dist/Components/Forms/Form.d.ts +52 -0
  37. package/dist/Components/Forms/Form.js +323 -0
  38. package/dist/Components/Forms/SubmitForm.d.ts +65 -0
  39. package/dist/Components/Forms/SubmitForm.js +198 -0
  40. package/dist/Components/Inputs/Calendar.d.ts +69 -0
  41. package/dist/Components/Inputs/Calendar.js +258 -0
  42. package/dist/Components/Inputs/CheckBox.d.ts +53 -0
  43. package/dist/Components/Inputs/CheckBox.js +87 -0
  44. package/dist/Components/Inputs/CheckBoxGroup.d.ts +20 -0
  45. package/dist/Components/Inputs/CheckBoxGroup.js +80 -0
  46. package/dist/Components/Inputs/ChoiceField.d.ts +122 -0
  47. package/dist/Components/Inputs/ChoiceField.js +169 -0
  48. package/dist/Components/Inputs/DatePicker.d.ts +54 -0
  49. package/dist/Components/Inputs/DatePicker.js +246 -0
  50. package/dist/Components/Inputs/DateRangePicker.d.ts +62 -0
  51. package/dist/Components/Inputs/DateRangePicker.js +283 -0
  52. package/dist/Components/Inputs/DateTimePicker.d.ts +75 -0
  53. package/dist/Components/Inputs/DateTimePicker.js +400 -0
  54. package/dist/Components/Inputs/Dropdown.d.ts +117 -0
  55. package/dist/Components/Inputs/Dropdown.js +839 -0
  56. package/dist/Components/Inputs/DropdownParts.d.ts +84 -0
  57. package/dist/Components/Inputs/DropdownParts.js +102 -0
  58. package/dist/Components/Inputs/EditorShell.d.ts +45 -0
  59. package/dist/Components/Inputs/EditorShell.js +214 -0
  60. package/dist/Components/Inputs/Field.d.ts +53 -0
  61. package/dist/Components/Inputs/Field.js +16 -0
  62. package/dist/Components/Inputs/GroupForm.d.ts +20 -0
  63. package/dist/Components/Inputs/GroupForm.js +172 -0
  64. package/dist/Components/Inputs/InputField.d.ts +59 -0
  65. package/dist/Components/Inputs/InputField.js +169 -0
  66. package/dist/Components/Inputs/InputRenderer.d.ts +37 -0
  67. package/dist/Components/Inputs/InputRenderer.js +99 -0
  68. package/dist/Components/Inputs/MarkdownEditor.d.ts +67 -0
  69. package/dist/Components/Inputs/MarkdownEditor.js +289 -0
  70. package/dist/Components/Inputs/NumberBox.d.ts +53 -0
  71. package/dist/Components/Inputs/NumberBox.js +228 -0
  72. package/dist/Components/Inputs/RadioGroup.d.ts +20 -0
  73. package/dist/Components/Inputs/RadioGroup.js +43 -0
  74. package/dist/Components/Inputs/ReadOnlyNotice.d.ts +36 -0
  75. package/dist/Components/Inputs/ReadOnlyNotice.js +117 -0
  76. package/dist/Components/Inputs/RichTextEditor.d.ts +103 -0
  77. package/dist/Components/Inputs/RichTextEditor.js +1170 -0
  78. package/dist/Components/Inputs/RichTextParts.d.ts +63 -0
  79. package/dist/Components/Inputs/RichTextParts.js +157 -0
  80. package/dist/Components/Inputs/RowsInput.d.ts +115 -0
  81. package/dist/Components/Inputs/RowsInput.js +308 -0
  82. package/dist/Components/Inputs/Slider.d.ts +87 -0
  83. package/dist/Components/Inputs/Slider.js +216 -0
  84. package/dist/Components/Inputs/TabularForm.d.ts +27 -0
  85. package/dist/Components/Inputs/TabularForm.js +348 -0
  86. package/dist/Components/Inputs/TextArea.d.ts +43 -0
  87. package/dist/Components/Inputs/TextArea.js +31 -0
  88. package/dist/Components/Inputs/TextBox.d.ts +52 -0
  89. package/dist/Components/Inputs/TextBox.js +58 -0
  90. package/dist/Components/Inputs/TextField.d.ts +31 -0
  91. package/dist/Components/Inputs/TextField.js +35 -0
  92. package/dist/Components/Inputs/TimePanel.d.ts +34 -0
  93. package/dist/Components/Inputs/TimePanel.js +288 -0
  94. package/dist/Components/Inputs/TimePicker.d.ts +54 -0
  95. package/dist/Components/Inputs/TimePicker.js +240 -0
  96. package/dist/Components/Inputs/TreeView.d.ts +160 -0
  97. package/dist/Components/Inputs/TreeView.js +947 -0
  98. package/dist/Components/Layouts/MessageBox.d.ts +71 -0
  99. package/dist/Components/Layouts/MessageBox.js +104 -0
  100. package/dist/Components/Layouts/Popup.d.ts +92 -0
  101. package/dist/Components/Layouts/Popup.js +376 -0
  102. package/dist/Components/Layouts/PopupContext.d.ts +2 -0
  103. package/dist/Components/Layouts/PopupContext.js +4 -0
  104. package/dist/Components/Layouts/PopupDock.d.ts +22 -0
  105. package/dist/Components/Layouts/PopupDock.js +121 -0
  106. package/dist/Components/Layouts/PopupHost.d.ts +56 -0
  107. package/dist/Components/Layouts/PopupHost.js +163 -0
  108. package/dist/Components/Layouts/PopupRules.d.ts +30 -0
  109. package/dist/Components/Layouts/PopupRules.js +63 -0
  110. package/dist/Components/Layouts/ThemePicker.d.ts +13 -0
  111. package/dist/Components/Layouts/ThemePicker.js +22 -0
  112. package/dist/Components/Layouts/ThemeScript.d.ts +14 -0
  113. package/dist/Components/Layouts/ThemeScript.js +13 -0
  114. package/dist/Components/Layouts/ThemeSwitcher.d.ts +13 -0
  115. package/dist/Components/Layouts/ThemeSwitcher.js +49 -0
  116. package/dist/Components/Layouts/Toaster.d.ts +76 -0
  117. package/dist/Components/Layouts/Toaster.js +172 -0
  118. package/dist/Components/Viewers/DataTable.d.ts +25 -0
  119. package/dist/Components/Viewers/DataTable.js +596 -0
  120. package/dist/Components/Viewers/DataTableColumns.d.ts +28 -0
  121. package/dist/Components/Viewers/DataTableColumns.js +243 -0
  122. package/dist/Components/Viewers/DataTableParts.d.ts +138 -0
  123. package/dist/Components/Viewers/DataTableParts.js +157 -0
  124. package/dist/Components/Viewers/MarkdownView.d.ts +26 -0
  125. package/dist/Components/Viewers/MarkdownView.js +79 -0
  126. package/dist/Components/Viewers/RichTextView.d.ts +18 -0
  127. package/dist/Components/Viewers/RichTextView.js +17 -0
  128. package/dist/Helpers/AnimationHelpers.d.ts +6 -0
  129. package/dist/Helpers/AnimationHelpers.js +35 -0
  130. package/dist/Helpers/ApiClient.d.ts +96 -0
  131. package/dist/Helpers/ApiClient.js +200 -0
  132. package/dist/Helpers/ApiFormHelpers.d.ts +49 -0
  133. package/dist/Helpers/ApiFormHelpers.js +120 -0
  134. package/dist/Helpers/ApiModules.d.ts +131 -0
  135. package/dist/Helpers/ApiModules.js +195 -0
  136. package/dist/Helpers/ApiResponses.d.ts +51 -0
  137. package/dist/Helpers/ApiResponses.js +250 -0
  138. package/dist/Helpers/ApiRoutes.d.ts +140 -0
  139. package/dist/Helpers/ApiRoutes.js +183 -0
  140. package/dist/Helpers/ApiToasts.d.ts +21 -0
  141. package/dist/Helpers/ApiToasts.js +22 -0
  142. package/dist/Helpers/ChatBackend.d.ts +134 -0
  143. package/dist/Helpers/ChatBackend.js +332 -0
  144. package/dist/Helpers/ChatHelpers.d.ts +189 -0
  145. package/dist/Helpers/ChatHelpers.js +486 -0
  146. package/dist/Helpers/ChatHooks.d.ts +81 -0
  147. package/dist/Helpers/ChatHooks.js +175 -0
  148. package/dist/Helpers/ChatStore.d.ts +132 -0
  149. package/dist/Helpers/ChatStore.js +394 -0
  150. package/dist/Helpers/ChatSummaries.d.ts +39 -0
  151. package/dist/Helpers/ChatSummaries.js +118 -0
  152. package/dist/Helpers/ClassHelpers.d.ts +3 -0
  153. package/dist/Helpers/ClassHelpers.js +4 -0
  154. package/dist/Helpers/CrudBackend.d.ts +98 -0
  155. package/dist/Helpers/CrudBackend.js +214 -0
  156. package/dist/Helpers/CrudHelpers.d.ts +111 -0
  157. package/dist/Helpers/CrudHelpers.js +267 -0
  158. package/dist/Helpers/DateHelpers.d.ts +88 -0
  159. package/dist/Helpers/DateHelpers.js +363 -0
  160. package/dist/Helpers/DatePreferences.d.ts +14 -0
  161. package/dist/Helpers/DatePreferences.js +94 -0
  162. package/dist/Helpers/DateTimeHelpers.d.ts +87 -0
  163. package/dist/Helpers/DateTimeHelpers.js +286 -0
  164. package/dist/Helpers/DragHelpers.d.ts +129 -0
  165. package/dist/Helpers/DragHelpers.js +281 -0
  166. package/dist/Helpers/DropdownHelpers.d.ts +56 -0
  167. package/dist/Helpers/DropdownHelpers.js +180 -0
  168. package/dist/Helpers/ExcelBackend.d.ts +106 -0
  169. package/dist/Helpers/ExcelBackend.js +290 -0
  170. package/dist/Helpers/ExcelHelpers.d.ts +98 -0
  171. package/dist/Helpers/ExcelHelpers.js +302 -0
  172. package/dist/Helpers/FormDrafts.d.ts +18 -0
  173. package/dist/Helpers/FormDrafts.js +76 -0
  174. package/dist/Helpers/FormStore.d.ts +233 -0
  175. package/dist/Helpers/FormStore.js +752 -0
  176. package/dist/Helpers/InputParamsHelpers.d.ts +79 -0
  177. package/dist/Helpers/InputParamsHelpers.js +388 -0
  178. package/dist/Helpers/MarkdownEditing.d.ts +27 -0
  179. package/dist/Helpers/MarkdownEditing.js +250 -0
  180. package/dist/Helpers/MarkdownHelpers.d.ts +82 -0
  181. package/dist/Helpers/MarkdownHelpers.js +775 -0
  182. package/dist/Helpers/MessageBuilder.d.ts +74 -0
  183. package/dist/Helpers/MessageBuilder.js +252 -0
  184. package/dist/Helpers/NepaliCalendar.d.ts +25 -0
  185. package/dist/Helpers/NepaliCalendar.js +107 -0
  186. package/dist/Helpers/NumberHelpers.d.ts +71 -0
  187. package/dist/Helpers/NumberHelpers.js +202 -0
  188. package/dist/Helpers/Permissions.d.ts +62 -0
  189. package/dist/Helpers/Permissions.js +133 -0
  190. package/dist/Helpers/PopoverHelpers.d.ts +68 -0
  191. package/dist/Helpers/PopoverHelpers.js +195 -0
  192. package/dist/Helpers/RichTextHelpers.d.ts +77 -0
  193. package/dist/Helpers/RichTextHelpers.js +1072 -0
  194. package/dist/Helpers/RowsStore.d.ts +166 -0
  195. package/dist/Helpers/RowsStore.js +606 -0
  196. package/dist/Helpers/TableColumns.d.ts +47 -0
  197. package/dist/Helpers/TableColumns.js +282 -0
  198. package/dist/Helpers/TableExport.d.ts +30 -0
  199. package/dist/Helpers/TableExport.js +97 -0
  200. package/dist/Helpers/TableHelpers.d.ts +110 -0
  201. package/dist/Helpers/TableHelpers.js +301 -0
  202. package/dist/Helpers/TableStore.d.ts +142 -0
  203. package/dist/Helpers/TableStore.js +530 -0
  204. package/dist/Helpers/ThemeHelpers.d.ts +15 -0
  205. package/dist/Helpers/ThemeHelpers.js +16 -0
  206. package/dist/Helpers/TimeHelpers.d.ts +73 -0
  207. package/dist/Helpers/TimeHelpers.js +253 -0
  208. package/dist/Helpers/TreeStore.d.ts +188 -0
  209. package/dist/Helpers/TreeStore.js +577 -0
  210. package/dist/Helpers/ValidationHelpers.d.ts +124 -0
  211. package/dist/Helpers/ValidationHelpers.js +301 -0
  212. package/dist/Interfaces/ApiInterfaces.d.ts +174 -0
  213. package/dist/Interfaces/ApiInterfaces.js +30 -0
  214. package/dist/Interfaces/ChatInterfaces.d.ts +240 -0
  215. package/{src/sso-client.tsx → dist/Interfaces/ChatInterfaces.js} +1 -1
  216. package/dist/Interfaces/CrudInterfaces.d.ts +232 -0
  217. package/dist/Interfaces/CrudInterfaces.js +1 -0
  218. package/dist/Interfaces/DateInterfaces.d.ts +154 -0
  219. package/dist/Interfaces/DateInterfaces.js +29 -0
  220. package/dist/Interfaces/ExcelInterfaces.d.ts +215 -0
  221. package/dist/Interfaces/ExcelInterfaces.js +45 -0
  222. package/dist/Interfaces/FormInterfaces.d.ts +402 -0
  223. package/dist/Interfaces/FormInterfaces.js +1 -0
  224. package/dist/Interfaces/InputInterfaces.d.ts +141 -0
  225. package/dist/Interfaces/InputInterfaces.js +1 -0
  226. package/dist/Interfaces/MessageInterfaces.d.ts +111 -0
  227. package/dist/Interfaces/MessageInterfaces.js +5 -0
  228. package/dist/Interfaces/TableInterfaces.d.ts +351 -0
  229. package/dist/Interfaces/TableInterfaces.js +1 -0
  230. package/dist/Interfaces/ThemeInterfaces.d.ts +17 -0
  231. package/dist/Interfaces/ThemeInterfaces.js +17 -0
  232. package/dist/Interfaces/TypeInterfaces.d.ts +4 -0
  233. package/dist/Interfaces/TypeInterfaces.js +1 -0
  234. package/dist/Server.Index.d.ts +2 -0
  235. package/dist/Server.Index.js +5 -0
  236. package/dist/Services/ApiProxy.d.ts +32 -0
  237. package/dist/Services/ApiProxy.js +111 -0
  238. package/dist/Services/BrowserApi.d.ts +95 -0
  239. package/dist/Services/BrowserApi.js +129 -0
  240. package/dist/Services/ChatApi.d.ts +37 -0
  241. package/dist/Services/ChatApi.js +98 -0
  242. package/dist/Services/ExcelApi.d.ts +46 -0
  243. package/dist/Services/ExcelApi.js +196 -0
  244. package/dist/Services/ServerApi.d.ts +54 -0
  245. package/dist/Services/ServerApi.js +78 -0
  246. package/dist/Services/ThemeService.d.ts +17 -0
  247. package/dist/Services/ThemeService.js +83 -0
  248. package/dist/Shared.Index.d.ts +51 -0
  249. package/dist/Shared.Index.js +55 -0
  250. package/package.json +85 -33
  251. package/src/Styles/Nexus.Base.css +70 -0
  252. package/src/Styles/Nexus.Button.css +153 -0
  253. package/src/Styles/Nexus.Calendar.css +275 -0
  254. package/src/Styles/Nexus.Chat.css +1489 -0
  255. package/src/Styles/Nexus.Choice.css +345 -0
  256. package/src/Styles/Nexus.Crud.css +161 -0
  257. package/src/Styles/Nexus.DateTime.css +81 -0
  258. package/src/Styles/Nexus.Dropdown.css +774 -0
  259. package/src/Styles/Nexus.Editor.css +297 -0
  260. package/src/Styles/Nexus.Excel.css +370 -0
  261. package/src/Styles/Nexus.Form.css +321 -0
  262. package/src/Styles/Nexus.Index.css +36 -0
  263. package/src/Styles/Nexus.Input.css +637 -0
  264. package/src/Styles/Nexus.Markdown.css +265 -0
  265. package/src/Styles/Nexus.Menu.css +259 -0
  266. package/src/Styles/Nexus.Popup.css +731 -0
  267. package/src/Styles/Nexus.RichText.css +463 -0
  268. package/src/Styles/Nexus.Rows.css +596 -0
  269. package/src/Styles/Nexus.Slider.css +278 -0
  270. package/src/Styles/Nexus.Tab.Buttons.css +419 -0
  271. package/src/Styles/Nexus.Table.css +1296 -0
  272. package/src/Styles/Nexus.Theme.Picker.css +232 -0
  273. package/src/Styles/Nexus.Themes.css +194 -0
  274. package/src/Styles/Nexus.Time.css +147 -0
  275. package/src/Styles/Nexus.Toast.css +265 -0
  276. package/src/Styles/Nexus.Tokens.css +72 -0
  277. package/src/Styles/Nexus.Tree.css +435 -0
  278. package/src/api-services/authentication-service.tsx +0 -23
  279. package/src/api-services/preference-service.tsx +0 -5
  280. package/src/api-services/system-service.tsx +0 -29
  281. package/src/client.ts +0 -31
  282. package/src/components/documents/button.tsx +0 -133
  283. package/src/components/documents/icon-box.tsx +0 -93
  284. package/src/components/documents/page-title.tsx +0 -7
  285. package/src/components/documents/tab-button.tsx +0 -172
  286. package/src/components/documents/tag.tsx +0 -30
  287. package/src/components/index.js +0 -0
  288. package/src/components/inputs/checkbox-input.tsx +0 -66
  289. package/src/components/inputs/input-box.tsx +0 -48
  290. package/src/components/inputs/input-element.tsx +0 -65
  291. package/src/components/inputs/input-form.tsx +0 -164
  292. package/src/components/inputs/input.tsx +0 -181
  293. package/src/components/inputs/number-input.tsx +0 -108
  294. package/src/components/inputs/radiobox-input.tsx +0 -53
  295. package/src/components/inputs/textarea-input.tsx +0 -47
  296. package/src/components/inputs/textbox-input.tsx +0 -45
  297. package/src/components/layouts/global-dialogbox.tsx +0 -431
  298. package/src/components/layouts/global-layout.tsx +0 -64
  299. package/src/components/layouts/layout-helpers.tsx +0 -20
  300. package/src/components/layouts/panels/user-panel.tsx +0 -112
  301. package/src/components/layouts/utility-menu.tsx +0 -49
  302. package/src/components/panels/theme-panel.tsx +0 -46
  303. package/src/helpers/bitwise-helpers.tsx +0 -11
  304. package/src/helpers/browser-helpers.tsx +0 -156
  305. package/src/helpers/datasource-helpers.tsx +0 -99
  306. package/src/helpers/element-helpers.tsx +0 -123
  307. package/src/helpers/input-helpers.tsx +0 -74
  308. package/src/helpers/string-helpers.tsx +0 -28
  309. package/src/helpers/utility-helpers.tsx +0 -45
  310. package/src/helpers/validation-helpers.tsx +0 -302
  311. package/src/index.ts +0 -26
  312. package/src/interface.ts +0 -19
  313. package/src/interfaces/auth-token-interfaces.tsx +0 -6
  314. package/src/interfaces/browser-interfaces.tsx +0 -36
  315. package/src/interfaces/button-interfaces.tsx +0 -63
  316. package/src/interfaces/datasource-interfaces.tsx +0 -22
  317. package/src/interfaces/datatable-interfaces.tsx +0 -25
  318. package/src/interfaces/dialogbox-interfaces.tsx +0 -5
  319. package/src/interfaces/exception-interfaces.tsx +0 -99
  320. package/src/interfaces/http-interfaces.tsx +0 -129
  321. package/src/interfaces/icon-interfaces.tsx +0 -129
  322. package/src/interfaces/input-interfaces.tsx +0 -410
  323. package/src/interfaces/layout-interfaces.tsx +0 -191
  324. package/src/interfaces/menu-interfaces.tsx +0 -53
  325. package/src/interfaces/message-interfaces.tsx +0 -32
  326. package/src/interfaces/permission-interfaces.tsx +0 -9
  327. package/src/interfaces/storage-interfaces.tsx +0 -5
  328. package/src/interfaces/system-interfaces.tsx +0 -22
  329. package/src/interfaces/theme-interfaces.tsx +0 -123
  330. package/src/interfaces/type-interfaces.tsx +0 -28
  331. package/src/interfaces/user-interfaces.tsx +0 -47
  332. package/src/nexus-client.tsx +0 -21
  333. package/src/nexus.environments.tsx +0 -66
  334. package/src/proxy-api/proxy-backend.tsx +0 -60
  335. package/src/proxy-api/proxy-constants.tsx +0 -28
  336. package/src/proxy-api/proxy-helpers.tsx +0 -91
  337. package/src/proxy-api-client.tsx +0 -1
  338. package/src/proxy-api-server.tsx +0 -2
  339. package/src/services/http-client-services.tsx +0 -75
  340. package/src/services/http-services.tsx +0 -158
  341. package/src/services/loader-service.tsx +0 -185
  342. package/src/services/localstorage-service.tsx +0 -119
  343. package/src/services/message-services.tsx +0 -383
  344. package/src/services/theme-service.tsx +0 -163
  345. package/src/services/user-services.tsx +0 -10
  346. package/src/sso-config/auth-token-validation.ts +0 -16
  347. package/src/sso-config/callback-route.tsx +0 -35
  348. package/src/sso-config/cookie-encryption.ts +0 -51
  349. package/src/sso-config/cookie-helpers.tsx +0 -102
  350. package/src/sso-config/forgot-password-route.tsx +0 -36
  351. package/src/sso-config/oauth-callback-state.ts +0 -61
  352. package/src/sso-config/pkce-helpers.ts +0 -9
  353. package/src/sso-config/provider-complete-route.tsx +0 -18
  354. package/src/sso-config/redirect-context-actions.tsx +0 -45
  355. package/src/sso-config/redirect-context-helpers.tsx +0 -159
  356. package/src/sso-config/redirect-context-persist.tsx +0 -18
  357. package/src/sso-config/redirect-context-route.tsx +0 -45
  358. package/src/sso-config/refresh-route.tsx +0 -23
  359. package/src/sso-config/sign-in-route.tsx +0 -74
  360. package/src/sso-config/sign-out-route.tsx +0 -57
  361. package/src/sso-config/sign-up-route.tsx +0 -57
  362. package/src/sso-config/sso-backend-flow.ts +0 -38
  363. package/src/sso-config/sso-complete-route.tsx +0 -50
  364. package/src/sso-config/sso-config.ts +0 -59
  365. package/src/sso-config/sso-interfaces.tsx +0 -99
  366. package/src/sso-config/sso-response-helpers.tsx +0 -20
  367. package/src/sso-server.tsx +0 -21
  368. package/src/styles/nexus.animation.css +0 -269
  369. package/src/styles/nexus.core.css +0 -119
  370. package/src/styles/nexus.dialog.css +0 -144
  371. package/src/styles/nexus.grid-view.css +0 -42
  372. package/src/styles/nexus.icon.css +0 -51
  373. package/src/styles/nexus.input.css +0 -207
  374. package/src/styles/nexus.loader.css +0 -11
  375. package/src/styles/nexus.logic.css +0 -47
  376. package/src/styles/nexus.utility.css +0 -347
@@ -0,0 +1,79 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../../Helpers/ClassHelpers.js";
3
+ import { parseMarkdown } from "../../Helpers/MarkdownHelpers.js";
4
+ /**
5
+ * Shows Markdown as formatted text: headings, lists, tasks, tables, code, quotes, links, and images. Raw HTML stays
6
+ * text and unsafe addresses are dropped, so it is safe for text from any user. It has no state, so it renders on the
7
+ * server as well.
8
+ *
9
+ * @example <MarkdownView value={room.description} />
10
+ */
11
+ export function MarkdownView({ value, breaks = true, newTab = "external", placeholder, onTaskToggle, className, ...rest }) {
12
+ const blocks = parseMarkdown(value, { breaks });
13
+ return (_jsx("div", { ...rest, className: cx("nx-markdown", className), children: blocks.length > 0 ? renderBlocks(blocks, { newTab, onTaskToggle }) : placeholder && _jsx("p", { className: "nx-markdown__empty", children: placeholder }) }));
14
+ }
15
+ function renderBlocks(blocks, options, tight = false) {
16
+ return blocks.map((block, key) => {
17
+ switch (block.type) {
18
+ case "heading": {
19
+ const Heading = `h${block.level}`;
20
+ return _jsx(Heading, { children: renderInlines(block.children, options) }, key);
21
+ }
22
+ case "paragraph":
23
+ // Items of a tight list hold their text without paragraph spacing.
24
+ return tight ? _jsx("span", { children: renderInlines(block.children, options) }, key) : _jsx("p", { children: renderInlines(block.children, options) }, key);
25
+ case "code":
26
+ return (_jsx("pre", { children: _jsx("code", { className: block.language ? `language-${block.language.replace(/[^\w#+.-]/g, "")}` : undefined, children: block.text }) }, key));
27
+ case "quote":
28
+ return _jsx("blockquote", { children: renderBlocks(block.children, options) }, key);
29
+ case "list": {
30
+ const List = block.ordered ? "ol" : "ul";
31
+ return (_jsx(List, { start: block.ordered && block.start !== 1 ? block.start : undefined, children: block.items.map((item, index) => renderItem(item, index, options, block.tight)) }, key));
32
+ }
33
+ case "table":
34
+ return (_jsx("div", { className: "nx-markdown__table", children: _jsxs("table", { children: [_jsx("thead", { children: _jsx("tr", { children: block.head.map((cell, index) => (_jsx("th", { scope: "col", style: block.align[index] ? { textAlign: block.align[index] } : undefined, children: renderInlines(cell.children, options) }, index))) }) }), block.rows.length > 0 && (_jsx("tbody", { children: block.rows.map((row, rowIndex) => (_jsx("tr", { children: row.map((cell, index) => (_jsx("td", { style: block.align[index] ? { textAlign: block.align[index] } : undefined, children: renderInlines(cell.children, options) }, index))) }, rowIndex))) }))] }) }, key));
35
+ case "rule":
36
+ return _jsx("hr", {}, key);
37
+ }
38
+ });
39
+ }
40
+ function renderItem(item, key, options, tight) {
41
+ if (item.checked === null)
42
+ return _jsx("li", { children: renderBlocks(item.children, options, tight) }, key);
43
+ const { onTaskToggle } = options;
44
+ const first = item.children[0];
45
+ const label = first && (first.type === "paragraph" || first.type === "heading") ? plainText(first.children) : undefined;
46
+ return (_jsxs("li", { className: "nx-markdown__task", children: [_jsx("input", { type: "checkbox", checked: item.checked, disabled: !onTaskToggle, "aria-label": label || "Task", onChange: onTaskToggle ? () => onTaskToggle(item.line, !item.checked) : undefined }), renderBlocks(item.children, options, tight)] }, key));
47
+ }
48
+ function renderInlines(nodes, options) {
49
+ return nodes.map((node, key) => {
50
+ switch (node.type) {
51
+ case "text":
52
+ return node.text;
53
+ case "strong":
54
+ return _jsx("strong", { children: renderInlines(node.children, options) }, key);
55
+ case "emphasis":
56
+ return _jsx("em", { children: renderInlines(node.children, options) }, key);
57
+ case "strike":
58
+ return _jsx("del", { children: renderInlines(node.children, options) }, key);
59
+ case "code":
60
+ return _jsx("code", { children: node.text }, key);
61
+ case "break":
62
+ return _jsx("br", {}, key);
63
+ case "image":
64
+ return node.src === null ? (_jsx("span", { children: node.alt }, key)) : (_jsx("img", { src: node.src, alt: node.alt, title: node.title, loading: "lazy", decoding: "async" }, key));
65
+ case "link": {
66
+ const children = renderInlines(node.children, options);
67
+ if (node.href === null)
68
+ return _jsx("span", { children: children }, key);
69
+ // Links to other sites carry no referrer and no endorsement for search engines, since any user may write them.
70
+ const external = /^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(node.href);
71
+ const newTab = options.newTab === "all" || (options.newTab === "external" && external);
72
+ return (_jsx("a", { href: node.href, title: node.title, target: newTab ? "_blank" : undefined, rel: external ? "nofollow noopener noreferrer" : newTab ? "noopener noreferrer" : undefined, children: children }, key));
73
+ }
74
+ }
75
+ });
76
+ }
77
+ function plainText(nodes) {
78
+ return nodes.map(node => (node.type === "text" || node.type === "code" ? node.text : node.type === "image" ? node.alt : node.type === "break" ? " " : plainText(node.children))).join("");
79
+ }
@@ -0,0 +1,18 @@
1
+ import type { ComponentPropsWithoutRef, ReactNode } from "react";
2
+ import { type RichTextRenderOptions } from "../../Helpers/RichTextHelpers.ts";
3
+ export interface RichTextViewProps extends Omit<ComponentPropsWithoutRef<"div">, "children" | "dangerouslySetInnerHTML">, Omit<RichTextRenderOptions, "newTab"> {
4
+ /** The HTML, as a rich text editor stored it. It is sanitized again here, so HTML from anywhere is safe to show. */
5
+ value: string | null | undefined;
6
+ /** Links to other sites open in a new tab. Default true. */
7
+ newTab?: boolean;
8
+ /** Shown when there is nothing to show. */
9
+ placeholder?: ReactNode;
10
+ }
11
+ /**
12
+ * Shows rich text: the HTML a `RichTextEditor` made, sanitized again and styled like the editor. Tokens are filled
13
+ * from `values` when given, e.g. to preview a template with sample data. No state and no effects, so it renders in
14
+ * server components too.
15
+ *
16
+ * @example <RichTextView value={room.description} />
17
+ */
18
+ export declare function RichTextView({ value, newTab, placeholder, images, tokenFormat, values, className, ...rest }: RichTextViewProps): import("react").JSX.Element | null;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cx } from "../../Helpers/ClassHelpers.js";
3
+ import { renderRichTextHtml } from "../../Helpers/RichTextHelpers.js";
4
+ /**
5
+ * Shows rich text: the HTML a `RichTextEditor` made, sanitized again and styled like the editor. Tokens are filled
6
+ * from `values` when given, e.g. to preview a template with sample data. No state and no effects, so it renders in
7
+ * server components too.
8
+ *
9
+ * @example <RichTextView value={room.description} />
10
+ */
11
+ export function RichTextView({ value, newTab = true, placeholder, images, tokenFormat, values, className, ...rest }) {
12
+ const html = renderRichTextHtml(value, { newTab, images, tokenFormat, values });
13
+ if (!html) {
14
+ return placeholder ? (_jsx("div", { ...rest, className: cx("nx-richtext", className), children: _jsx("p", { className: "nx-richtext__empty", children: placeholder }) })) : null;
15
+ }
16
+ return _jsx("div", { ...rest, className: cx("nx-richtext", className), dangerouslySetInnerHTML: { __html: html } });
17
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Calls `done` once the element's CSS animation `name` has finished, or at once when the element runs no animation of
3
+ * that name. Call it after the class that starts the animation is on the element, e.g. in a layout effect.
4
+ * Returns a function that cancels the wait.
5
+ */
6
+ export declare function afterAnimation(element: Element, name: string, done: () => void): () => void;
@@ -0,0 +1,35 @@
1
+ /*
2
+ * Exit animations. A closing element gets a class that starts a CSS animation, and the next step (closing a dialog,
3
+ * removing a toast) waits for it. When no animation runs, the step must not wait: with reduced motion (which Windows
4
+ * reports when its animations are turned off) or when a theme sets `animation: none`.
5
+ */
6
+ /**
7
+ * Calls `done` once the element's CSS animation `name` has finished, or at once when the element runs no animation of
8
+ * that name. Call it after the class that starts the animation is on the element, e.g. in a layout effect.
9
+ * Returns a function that cancels the wait.
10
+ */
11
+ export function afterAnimation(element, name, done) {
12
+ const animation = typeof element.getAnimations === "function" ? element.getAnimations().find(item => item.animationName === name) : undefined;
13
+ if (!animation) {
14
+ done();
15
+ return () => undefined;
16
+ }
17
+ let waiting = true;
18
+ let timer = 0;
19
+ const finish = () => {
20
+ if (!waiting)
21
+ return;
22
+ waiting = false;
23
+ window.clearTimeout(timer);
24
+ done();
25
+ };
26
+ // A safety net for a timeline that stops, as in a hidden tab: the animation's own length, and a little more.
27
+ const end = Number(animation.effect?.getComputedTiming().endTime);
28
+ timer = window.setTimeout(finish, (Number.isFinite(end) ? end : 0) + 100);
29
+ // Rejects when the animation is cancelled, e.g. the popup opens again; the caller cancels the wait then.
30
+ animation.finished.then(finish, () => undefined);
31
+ return () => {
32
+ waiting = false;
33
+ window.clearTimeout(timer);
34
+ };
35
+ }
@@ -0,0 +1,96 @@
1
+ import type { ApiCallOptions, ApiEndpoint, ApiResult, ApiTarget, HttpMethod } from "../Interfaces/ApiInterfaces.ts";
2
+ import type { MessageActionName, MessageContext } from "../Interfaces/MessageInterfaces.ts";
3
+ /** One call, as a client's hooks see it. */
4
+ export interface ApiCall<TOptions = object> {
5
+ target: ApiTarget;
6
+ /** The endpoint, when the target is one. */
7
+ endpoint?: ApiEndpoint;
8
+ method: HttpMethod;
9
+ /** The full URL; empty while it is not known yet, or when it could not be built. */
10
+ url: string;
11
+ options: ApiCallOptions & TOptions;
12
+ /** What the call does and acts on, for its messages. */
13
+ context: MessageContext;
14
+ }
15
+ /**
16
+ * Reads a server's answer as an `ApiResult`. A client's `read` is the app's default for every call that does not bring
17
+ * its own (`ApiCallOptions.read`); `call` says what was asked for, and is left out for an answer that came from
18
+ * somewhere other than a call (an app's own send function).
19
+ *
20
+ * @example
21
+ * // A backend that answers { success, message, data, error: { code, message, fields } }
22
+ * configureApi({
23
+ * read: (body, status) => {
24
+ * const answer = body as { success?: boolean; message?: string; data?: unknown; error?: { code: string; fields?: Record<string, string[]> } };
25
+ * if (answer?.success) return { ok: true, status, result: answer.data, message: answer.message };
26
+ * return { ok: false, status, message: answer?.message, messageCode: answer?.error?.code, errors: … };
27
+ * },
28
+ * });
29
+ */
30
+ export type ApiResponseReader<TOptions = object> = (body: unknown, status: number | undefined, call?: ApiCall<TOptions>) => ApiResult;
31
+ export interface ApiClientConfig<TOptions = object> {
32
+ /** The full URL of an endpoint, given its filled path: through the proxy in the browser, the module's root on a server. */
33
+ resolveUrl: (endpoint: ApiEndpoint, path: string, call: ApiCall<TOptions>) => string | Promise<string>;
34
+ /** The full URL of a plain URL target. Default: as it is. */
35
+ resolveTarget?: (url: string, call: ApiCall<TOptions>) => string | Promise<string>;
36
+ /** Headers on every call, such as the tenant or branch. */
37
+ headers?: (call: ApiCall<TOptions>) => Record<string, string> | undefined | Promise<Record<string, string> | undefined>;
38
+ /** A bearer token for every call. */
39
+ token?: (call: ApiCall<TOptions>) => string | null | undefined | Promise<string | null | undefined>;
40
+ /** Milliseconds to wait for an answer. Default 60,000. */
41
+ timeout?: number;
42
+ credentials?: RequestCredentials;
43
+ /**
44
+ * Reads every answer the call itself did not bring a reader for. Default `readApiResponse`: Nexus API responses,
45
+ * ASP.NET Core problem details, and any JSON by its status. An app on another backend registers its own once, so
46
+ * components that call inside themselves (`Crud`, `ApiForm`) read that backend's envelope too.
47
+ */
48
+ read?: ApiResponseReader<TOptions>;
49
+ /** Changes the request just before it is sent, such as Next.js caching on a server. */
50
+ prepare?: (init: RequestInit, call: ApiCall<TOptions>) => RequestInit;
51
+ /**
52
+ * Sends a GET only once while the same one runs, and gives every caller its answer. Only where every call is the same
53
+ * user's (the browser): on a server, callers are different users.
54
+ */
55
+ dedupe?: boolean;
56
+ /** Before a call is sent, such as a loading toast. May return a function that gets the answer. */
57
+ onStart?: (call: ApiCall<TOptions>) => ((result: ApiResult) => void) | void;
58
+ /** After every answer: toasts in the browser, logs on a server. */
59
+ onResult?: (result: ApiResult, call: ApiCall<TOptions>) => void;
60
+ /** The `fetch` to use. Default: the global one, looked up on each call. */
61
+ fetch?: typeof fetch;
62
+ }
63
+ type Options<TOptions> = ApiCallOptions & TOptions;
64
+ export interface ApiClient<TOptions = object> {
65
+ /** Sends a call with the endpoint's method (or `options.method`). */
66
+ send<T = unknown>(target: ApiTarget, options?: Options<TOptions>): Promise<ApiResult<T>>;
67
+ get<T = unknown>(target: ApiTarget, options?: Omit<Options<TOptions>, "body" | "method">): Promise<ApiResult<T>>;
68
+ post<T = unknown>(target: ApiTarget, body?: unknown, options?: Omit<Options<TOptions>, "body" | "method">): Promise<ApiResult<T>>;
69
+ put<T = unknown>(target: ApiTarget, body?: unknown, options?: Omit<Options<TOptions>, "body" | "method">): Promise<ApiResult<T>>;
70
+ patch<T = unknown>(target: ApiTarget, body?: unknown, options?: Omit<Options<TOptions>, "body" | "method">): Promise<ApiResult<T>>;
71
+ /** A DELETE; ids for a batch go in `options.body`. */
72
+ delete<T = unknown>(target: ApiTarget, options?: Omit<Options<TOptions>, "method">): Promise<ApiResult<T>>;
73
+ /** The URL a call would go to, for links and downloads. Throws when it cannot be built. */
74
+ url(target: ApiTarget, options?: Pick<ApiCallOptions, "params" | "query" | "body">): Promise<string>;
75
+ }
76
+ export declare const DEFAULT_API_TIMEOUT = 60000;
77
+ /** What a call does when it names nothing: GET loads, DELETE deletes, the rest save. */
78
+ export declare function defaultAction(method: HttpMethod): MessageActionName;
79
+ /** What a call does and acts on: its endpoint, its method, and the context its messages are built from. */
80
+ export declare function apiCallContext(target: ApiTarget, options?: Pick<ApiCallOptions, "method" | "action" | "subject" | "count" | "plural">): {
81
+ endpoint?: ApiEndpoint;
82
+ method: HttpMethod;
83
+ context: MessageContext;
84
+ };
85
+ /**
86
+ * Whether an error is a framework's own signal passing through a call, never a failed request: Next.js throws one from
87
+ * `headers()`, `cookies()`, or a `no-store` fetch while it prerenders, to learn that a page renders per request (and
88
+ * for `redirect()` and `notFound()`). Each carries a `digest`, or React's `$typeof`. The engine throws them on.
89
+ */
90
+ export declare function isFrameworkError(error: unknown): boolean;
91
+ /**
92
+ * Makes an API client. Apps use the two made here, `api` (browser) and `serverApi` (server); make another for an API
93
+ * that is not a Nexus module.
94
+ */
95
+ export declare function createApiClient<TOptions extends object = object>(config: ApiClientConfig<TOptions>): ApiClient<TOptions>;
96
+ export {};
@@ -0,0 +1,200 @@
1
+ import { isAbortError, readApiResponse, readFetchResponse, toFormData } from "./ApiResponses.js";
2
+ import { appendQuery, fillApiPath, isApiEndpoint } from "./ApiRoutes.js";
3
+ import { buildMessage } from "./MessageBuilder.js";
4
+ export const DEFAULT_API_TIMEOUT = 60_000;
5
+ /** Statuses worth one more try for a GET: the server or a gateway was briefly unavailable. */
6
+ const RETRY_STATUSES = new Set([502, 503, 504]);
7
+ /** What a call does when it names nothing: GET loads, DELETE deletes, the rest save. */
8
+ export function defaultAction(method) {
9
+ return method === "GET" ? "load" : method === "DELETE" ? "delete" : "save";
10
+ }
11
+ /** What a call does and acts on: its endpoint, its method, and the context its messages are built from. */
12
+ export function apiCallContext(target, options = {}) {
13
+ const endpoint = isApiEndpoint(target) ? target : undefined;
14
+ const method = options.method ?? endpoint?.method ?? "GET";
15
+ const context = { action: options.action ?? endpoint?.action ?? defaultAction(method), subject: options.subject ?? endpoint?.subject };
16
+ if (options.count !== undefined)
17
+ context.count = options.count;
18
+ if (options.plural)
19
+ context.plural = options.plural;
20
+ return { endpoint, method, context };
21
+ }
22
+ function isRawBody(body) {
23
+ return ((typeof Blob !== "undefined" && body instanceof Blob) ||
24
+ (typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams) ||
25
+ body instanceof ArrayBuffer ||
26
+ ArrayBuffer.isView(body) ||
27
+ (typeof ReadableStream !== "undefined" && body instanceof ReadableStream));
28
+ }
29
+ function pause(ms, signal) {
30
+ return new Promise(resolve => {
31
+ if (signal.aborted)
32
+ return resolve();
33
+ const done = () => {
34
+ clearTimeout(timer);
35
+ signal.removeEventListener("abort", done);
36
+ resolve();
37
+ };
38
+ const timer = setTimeout(done, ms);
39
+ signal.addEventListener("abort", done, { once: true });
40
+ });
41
+ }
42
+ const aborted = () => ({ ok: false, aborted: true });
43
+ /**
44
+ * Whether an error is a framework's own signal passing through a call, never a failed request: Next.js throws one from
45
+ * `headers()`, `cookies()`, or a `no-store` fetch while it prerenders, to learn that a page renders per request (and
46
+ * for `redirect()` and `notFound()`). Each carries a `digest`, or React's `$typeof`. The engine throws them on.
47
+ */
48
+ export function isFrameworkError(error) {
49
+ const value = error;
50
+ return typeof value === "object" && value !== null && (typeof value.digest === "string" || value.$typeof !== undefined);
51
+ }
52
+ /**
53
+ * Makes an API client. Apps use the two made here, `api` (browser) and `serverApi` (server); make another for an API
54
+ * that is not a Nexus module.
55
+ */
56
+ export function createApiClient(config) {
57
+ const inflight = new Map();
58
+ function describe(target, options) {
59
+ return { target, ...apiCallContext(target, options), url: "", options };
60
+ }
61
+ async function resolve(call) {
62
+ const { endpoint, options } = call;
63
+ if (endpoint) {
64
+ const path = fillApiPath(endpoint.path, options.params, options.body);
65
+ return appendQuery(await config.resolveUrl(endpoint, path, call), endpoint.query, options.query);
66
+ }
67
+ const url = call.target;
68
+ return appendQuery(config.resolveTarget ? await config.resolveTarget(url, call) : url, options.query);
69
+ }
70
+ /** Sends the request and reads its answer. */
71
+ async function request(call) {
72
+ const { method, options, endpoint } = call;
73
+ const headers = { Accept: "application/json", ...(await config.headers?.(call)) };
74
+ const token = await config.token?.(call);
75
+ if (token)
76
+ headers.Authorization = /^bearer /i.test(token) ? token : `Bearer ${token}`;
77
+ let body;
78
+ if (method !== "GET" && options.body !== undefined) {
79
+ const bodyType = options.bodyType ?? endpoint?.bodyType ?? (typeof FormData !== "undefined" && options.body instanceof FormData ? "form-data" : "json");
80
+ if (isRawBody(options.body))
81
+ body = options.body;
82
+ else if (bodyType === "form-data")
83
+ body = toFormData(options.body);
84
+ else {
85
+ body = JSON.stringify(options.body);
86
+ headers["Content-Type"] = "application/json";
87
+ }
88
+ }
89
+ Object.assign(headers, options.headers);
90
+ const controller = new AbortController();
91
+ const caller = options.signal;
92
+ const stop = () => controller.abort();
93
+ caller?.addEventListener("abort", stop, { once: true });
94
+ let timedOut = false;
95
+ const timeout = options.timeout ?? config.timeout ?? DEFAULT_API_TIMEOUT;
96
+ const timer = timeout > 0
97
+ ? setTimeout(() => {
98
+ timedOut = true;
99
+ controller.abort();
100
+ }, timeout)
101
+ : undefined;
102
+ // The call's own reader, else the app's (`configureApi({ read })`), else the built-in one. The app's is looked up
103
+ // on every call, so registering it after a module was imported still takes effect.
104
+ const appRead = config.read;
105
+ const read = options.read ?? (appRead ? (answer, status) => appRead(answer, status, call) : (answer, status) => readApiResponse(answer, status, call.context));
106
+ const stopped = () => caller?.aborted ? aborted() : timedOut ? { ok: false, timeout: true, message: buildMessage("reason", { ...call.context, timeout: true }) } : undefined;
107
+ let init = { method, headers, body, credentials: options.credentials ?? config.credentials, signal: controller.signal };
108
+ if (config.prepare)
109
+ init = config.prepare(init, call);
110
+ const send = config.fetch ?? globalThis.fetch;
111
+ const retries = options.retries ?? (method === "GET" ? 1 : 0);
112
+ try {
113
+ for (let attempt = 0;; attempt++) {
114
+ if (caller?.aborted)
115
+ return aborted();
116
+ let response;
117
+ try {
118
+ response = await send(call.url, init);
119
+ }
120
+ catch (error) {
121
+ if (isFrameworkError(error))
122
+ throw error;
123
+ const early = stopped();
124
+ if (early)
125
+ return early;
126
+ // Aborted by a signal the call does not know, such as the page closing: nothing to show either.
127
+ if (isAbortError(error))
128
+ return aborted();
129
+ if (attempt < retries) {
130
+ await pause(300 * 2 ** attempt, controller.signal);
131
+ continue;
132
+ }
133
+ return { ok: false, network: true, message: buildMessage("reason", { ...call.context, network: true }) };
134
+ }
135
+ if (RETRY_STATUSES.has(response.status) && attempt < retries) {
136
+ await response.body?.cancel().catch(() => undefined);
137
+ await pause(300 * 2 ** attempt, controller.signal);
138
+ continue;
139
+ }
140
+ const result = await readFetchResponse(response, read);
141
+ // A body cut off by an abort or the timeout reads as empty: say what happened instead.
142
+ return stopped() ?? result;
143
+ }
144
+ }
145
+ finally {
146
+ clearTimeout(timer);
147
+ caller?.removeEventListener("abort", stop);
148
+ }
149
+ }
150
+ async function send(target, options = {}) {
151
+ const call = describe(target, options);
152
+ const done = config.onStart?.(call);
153
+ let result;
154
+ try {
155
+ call.url = await resolve(call);
156
+ }
157
+ catch (error) {
158
+ if (isFrameworkError(error)) {
159
+ done?.(aborted());
160
+ throw error;
161
+ }
162
+ // A module without a root URL, or a path that names a value the call did not give: nothing is sent.
163
+ result = { ok: false, message: error instanceof Error ? error.message : String(error) };
164
+ }
165
+ if (!result) {
166
+ try {
167
+ result = await run(call);
168
+ }
169
+ catch (error) {
170
+ // A framework's signal (or a throw in the app's own headers or token): a loading toast closes, the error goes on.
171
+ done?.(aborted());
172
+ throw error;
173
+ }
174
+ }
175
+ config.onResult?.(result, call);
176
+ done?.(result);
177
+ return result;
178
+ }
179
+ async function run(call) {
180
+ if (!config.dedupe || call.method !== "GET" || call.options.signal)
181
+ return request(call);
182
+ let shared = inflight.get(call.url);
183
+ if (!shared) {
184
+ shared = request(call).finally(() => inflight.delete(call.url));
185
+ inflight.set(call.url, shared);
186
+ }
187
+ // Each caller gets its own copy, so one cannot change another's answer.
188
+ return { ...(await shared) };
189
+ }
190
+ const withBody = (method) => (target, body, options) => send(target, { ...options, body, method });
191
+ return {
192
+ send,
193
+ get: (target, options) => send(target, { ...options, method: "GET" }),
194
+ post: withBody("POST"),
195
+ put: withBody("PUT"),
196
+ patch: withBody("PATCH"),
197
+ delete: (target, options) => send(target, { ...options, method: "DELETE" }),
198
+ url: (target, options = {}) => resolve(describe(target, options)),
199
+ };
200
+ }
@@ -0,0 +1,49 @@
1
+ import type { ApiCallOptions, ApiResult } from "../Interfaces/ApiInterfaces.ts";
2
+ import type { ApiRequest, FormErrors } from "../Interfaces/FormInterfaces.ts";
3
+ export interface SendApiOptions {
4
+ signal?: AbortSignal;
5
+ read?: (body: unknown, status: number | undefined) => ApiResult;
6
+ }
7
+ /**
8
+ * The call options of a form's `{ url, method, headers, bodyType, credentials }` request with its values. Without a
9
+ * `read` the client's own reader is used: the app's (`configureApi({ read })`), else the built-in one.
10
+ */
11
+ export declare function apiRequestOptions(request: ApiRequest, body: unknown, { signal, read }?: SendApiOptions): ApiCallOptions;
12
+ /** An abort as the error `fetch` throws, for code that expects a stopped request to reject. */
13
+ export declare function abortError(): Error;
14
+ /**
15
+ * Sends a body with `fetch` and reads the answer. Resolves with a failed result for a network error or a timeout;
16
+ * rejects only when the request is aborted. `ApiForm` sends through the app's `api` instead, with its headers.
17
+ */
18
+ export declare function sendApiRequest(request: ApiRequest, body: unknown, options?: SendApiOptions): Promise<ApiResult>;
19
+ /** What an `ApiSend` function answered, read the same way: a `Response`, an `ApiResult`, or a body. */
20
+ export declare function toApiResult(answer: unknown, read?: (body: unknown, status: number | undefined) => ApiResult): Promise<ApiResult>;
21
+ /**
22
+ * The input a server's field name belongs to, or `undefined` when the form has no such input. Names match without case,
23
+ * and a path matches its first part that is an input ("Addresses[1].City" and "$.addresses" go to "addresses";
24
+ * "dto.Email" to "email"). The one rule every server message is matched by.
25
+ */
26
+ export declare function matchInputName(name: string, inputNames: readonly string[]): string | undefined;
27
+ /**
28
+ * Puts a server's messages on the inputs they name, by `matchInputName`. Messages that match no input are returned in
29
+ * `others`, for the form's message.
30
+ */
31
+ export declare function matchFieldErrors(errors: Record<string, string> | undefined, inputNames: readonly string[]): {
32
+ fields: FormErrors;
33
+ others: string[];
34
+ };
35
+ /**
36
+ * A failed answer as what a form shows, through the one path a 400's field errors already take: every message the server
37
+ * put on an input (`inputName`, several named with "|") goes to that input, every other message - including one naming an
38
+ * input this form does not have - goes under the form, and nothing is shown twice. `message` is `null` when the inputs
39
+ * carry it all. An answer that explains nothing falls back to a message for its HTTP status; that is the only time our
40
+ * own wording is shown.
41
+ *
42
+ * @example
43
+ * const { errors, message } = formErrorsFromResult(answer, store.inputs.map(params => params.name));
44
+ * store.setErrors(errors, message);
45
+ */
46
+ export declare function formErrorsFromResult(answer: ApiResult, inputNames: readonly string[]): {
47
+ errors: FormErrors;
48
+ message: string | null;
49
+ };
@@ -0,0 +1,120 @@
1
+ import { createApiClient } from "./ApiClient.js";
2
+ import { apiStatusMessage, isApiResult, readApiResponse, readFetchResponse } from "./ApiResponses.js";
3
+ /**
4
+ * The call options of a form's `{ url, method, headers, bodyType, credentials }` request with its values. Without a
5
+ * `read` the client's own reader is used: the app's (`configureApi({ read })`), else the built-in one.
6
+ */
7
+ export function apiRequestOptions(request, body, { signal, read } = {}) {
8
+ const { method = "POST", headers, bodyType = "json", credentials } = request;
9
+ return { method, headers, bodyType, credentials, signal, read, body: body ?? {} };
10
+ }
11
+ /** An abort as the error `fetch` throws, for code that expects a stopped request to reject. */
12
+ export function abortError() {
13
+ return typeof DOMException === "function" ? new DOMException("The request was aborted.", "AbortError") : Object.assign(new Error("The request was aborted."), { name: "AbortError" });
14
+ }
15
+ /** Sends URLs as they are, with none of an app's settings. */
16
+ const plainClient = createApiClient({
17
+ resolveUrl: () => {
18
+ throw new Error("sendApiRequest takes a URL; call an endpoint with api or serverApi.");
19
+ },
20
+ });
21
+ /**
22
+ * Sends a body with `fetch` and reads the answer. Resolves with a failed result for a network error or a timeout;
23
+ * rejects only when the request is aborted. `ApiForm` sends through the app's `api` instead, with its headers.
24
+ */
25
+ export async function sendApiRequest(request, body, options = {}) {
26
+ const result = await plainClient.send(request.url, apiRequestOptions(request, body, options));
27
+ if (result.aborted)
28
+ throw abortError();
29
+ return result;
30
+ }
31
+ /** What an `ApiSend` function answered, read the same way: a `Response`, an `ApiResult`, or a body. */
32
+ export async function toApiResult(answer, read = readApiResponse) {
33
+ if (typeof Response !== "undefined" && answer instanceof Response)
34
+ return readFetchResponse(answer, read);
35
+ if (isApiResult(answer))
36
+ return answer;
37
+ return read(answer, undefined);
38
+ }
39
+ function inputsByName(inputNames) {
40
+ return new Map(inputNames.map(name => [name.toLowerCase(), name]));
41
+ }
42
+ function matchIn(key, byName) {
43
+ const parts = key.replace(/^\$\.?/, "").split(/[.[\]]+/).filter(Boolean);
44
+ return parts.slice(0, 2).map(part => byName.get(part.toLowerCase())).find(Boolean);
45
+ }
46
+ /**
47
+ * The input a server's field name belongs to, or `undefined` when the form has no such input. Names match without case,
48
+ * and a path matches its first part that is an input ("Addresses[1].City" and "$.addresses" go to "addresses";
49
+ * "dto.Email" to "email"). The one rule every server message is matched by.
50
+ */
51
+ export function matchInputName(name, inputNames) {
52
+ return matchIn(name, inputsByName(inputNames));
53
+ }
54
+ /**
55
+ * Puts a server's messages on the inputs they name, by `matchInputName`. Messages that match no input are returned in
56
+ * `others`, for the form's message.
57
+ */
58
+ export function matchFieldErrors(errors, inputNames) {
59
+ const byName = inputsByName(inputNames);
60
+ const fields = {};
61
+ const others = [];
62
+ for (const [key, message] of Object.entries(errors ?? {})) {
63
+ if (!message)
64
+ continue;
65
+ const name = matchIn(key, byName);
66
+ if (name)
67
+ fields[name] = fields[name] && fields[name] !== message ? `${fields[name]} ${message}` : message;
68
+ else if (!others.includes(message))
69
+ others.push(message);
70
+ }
71
+ return { fields, others };
72
+ }
73
+ /**
74
+ * A failed answer as what a form shows, through the one path a 400's field errors already take: every message the server
75
+ * put on an input (`inputName`, several named with "|") goes to that input, every other message - including one naming an
76
+ * input this form does not have - goes under the form, and nothing is shown twice. `message` is `null` when the inputs
77
+ * carry it all. An answer that explains nothing falls back to a message for its HTTP status; that is the only time our
78
+ * own wording is shown.
79
+ *
80
+ * @example
81
+ * const { errors, message } = formErrorsFromResult(answer, store.inputs.map(params => params.name));
82
+ * store.setErrors(errors, message);
83
+ */
84
+ export function formErrorsFromResult(answer, inputNames) {
85
+ const byName = inputsByName(inputNames);
86
+ const fields = {};
87
+ const others = [];
88
+ const place = (message, key) => {
89
+ const sentence = message?.trim();
90
+ if (!sentence)
91
+ return;
92
+ const name = key ? matchIn(key, byName) : undefined;
93
+ if (name)
94
+ fields[name] = fields[name] && fields[name] !== sentence ? `${fields[name]} ${sentence}` : sentence;
95
+ else if (!others.includes(sentence))
96
+ others.push(sentence);
97
+ };
98
+ if (answer.errorMessages?.length) {
99
+ for (const problem of answer.errorMessages) {
100
+ const named = (problem.inputName?.split("|") ?? []).map(part => part.trim()).filter(name => name && matchIn(name, byName));
101
+ if (named.length)
102
+ for (const name of named)
103
+ place(problem.message, name);
104
+ // No input of this form: shown with the others, never swallowed.
105
+ else
106
+ place(problem.message);
107
+ }
108
+ }
109
+ else {
110
+ for (const [key, message] of Object.entries(answer.errors ?? {}))
111
+ place(message, key);
112
+ // The answer's own message, when an input does not already show it.
113
+ if (!Object.values(fields).includes(answer.message ?? ""))
114
+ place(answer.message);
115
+ }
116
+ const message = others.join(" ");
117
+ if (message)
118
+ return { errors: fields, message };
119
+ return { errors: fields, message: Object.keys(fields).length ? null : apiStatusMessage(answer.status) };
120
+ }