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,131 @@
1
+ import { NexusModule, type ApiEndpoint, type ApiModuleInfo, type ApiModuleValue } from "../Interfaces/ApiInterfaces.ts";
2
+ import { apiEndpoint, type ApiEndpointExtras, type ApiUrlParts } from "./ApiRoutes.ts";
3
+ /** Where the proxy route sits in an app: `app/api/app/[module]/[...path]/route.ts`. */
4
+ export declare const DEFAULT_PROXY_PATH = "/api/app";
5
+ /** An environment variable, read when it is needed (never inlined at build time), so one build serves every environment. */
6
+ export declare function readEnvironment(name: string | undefined): string | undefined;
7
+ export interface ApiModulesOptions<TModule extends ApiModuleValue> {
8
+ /** Browser calls go through the app's proxy route, which adds the user's token on the server. Default true. */
9
+ useProxy?: boolean;
10
+ /** Where the proxy route is. Default `/api/app`. */
11
+ proxyPath?: string;
12
+ /** Root URLs the browser calls when a module does not go through the proxy. Public: the browser sees them. */
13
+ browserRoots?: Partial<Record<TModule, string>>;
14
+ /** Root URLs the server calls, by module. They win over `serverRoot`. May be relative to the app's origin. */
15
+ serverRoots?: Partial<Record<TModule, string>>;
16
+ }
17
+ /**
18
+ * The API modules of an app. Extend it with the app's own module enum:
19
+ *
20
+ * - `modules`: every module, with its key for proxy URLs and its label for messages.
21
+ * - `serverRoot(module)`: the module's root URL for server calls and the proxy (from the environment, a settings
22
+ * service, anything; may be async).
23
+ *
24
+ * Then, if the defaults do not suit: `useProxy` to call modules straight from the browser, `browserRoot(module)` for
25
+ * those roots, and `proxies(module)` to send only some modules through the proxy.
26
+ *
27
+ * @example
28
+ * enum HrModule { Staff = "staff", Payroll = "payroll" }
29
+ *
30
+ * class HrApiModules extends ApiModules<HrModule> {
31
+ * readonly modules = [
32
+ * { module: HrModule.Staff, key: "staff", label: "Staff", env: "STAFF_API_URL" },
33
+ * { module: HrModule.Payroll, key: "payroll", label: "Payroll", env: "PAYROLL_API_URL" },
34
+ * ];
35
+ * serverRoot(module: HrModule) {
36
+ * return this.readEnv(this.info(module)?.env);
37
+ * }
38
+ * }
39
+ *
40
+ * export const MODULES = defineApiModules(new HrApiModules());
41
+ * export const EMPLOYEES = MODULES.controller(HrModule.Staff, "Employee", "employee");
42
+ */
43
+ export declare abstract class ApiModules<TModule extends ApiModuleValue = ApiModuleValue> {
44
+ /** Every module the app calls. */
45
+ abstract readonly modules: readonly ApiModuleInfo<TModule>[];
46
+ /** A module's root URL for server calls and the proxy: absolute, or relative to the app's origin. `undefined` when it is not set. */
47
+ abstract serverRoot(module: TModule): string | undefined | Promise<string | undefined>;
48
+ /** Browser calls go through the proxy route. Change it here, in the options, or in a subclass. */
49
+ useProxy: boolean;
50
+ /** Where the proxy route is. */
51
+ proxyPath: string;
52
+ protected readonly options: ApiModulesOptions<TModule>;
53
+ constructor(options?: ApiModulesOptions<TModule>);
54
+ /** A module's root URL for browser calls that skip the proxy. Default: `browserRoots` from the options. */
55
+ browserRoot(module: TModule): string | undefined;
56
+ /** Whether the browser calls a module through the proxy. Default: `useProxy`, for every module. */
57
+ proxies(_module: TModule): boolean;
58
+ /** A module by its value, its key (any case), or its value as text ("2048", as older proxy URLs have it). */
59
+ info(module: TModule | string): ApiModuleInfo<TModule> | undefined;
60
+ /** The root the browser calls for a module: the proxy's path for it, or its own root. Throws when neither is set. */
61
+ browserUrl(module: TModule): string;
62
+ /** A module's root for server calls, as set: `serverRoots`, else `serverRoot`. Throws when it is not set. May be relative. */
63
+ serverUrl(module: TModule): Promise<string>;
64
+ /** The URL the browser calls for an endpoint, for links, images, and downloads: `/api/app/reservation/Room/Detail/5`. */
65
+ browserApiUrl(endpoint: ApiEndpoint<TModule>, parts?: ApiUrlParts): string;
66
+ /** An environment variable, read at call time; for `serverRoot`. */
67
+ protected readEnv(name: string | undefined): string | undefined;
68
+ /** Endpoints typed to this app's modules: `MODULES.endpoint.get(HrModule.Staff, "Employee/Birthdays")`. */
69
+ readonly endpoint: Record<keyof typeof apiEndpoint, (module: TModule, path: string, extras?: ApiEndpointExtras) => ApiEndpoint<TModule>>;
70
+ /** A controller's standard endpoints, typed to this app's modules. See `apiController`. */
71
+ controller(module: TModule, controller: string, subject?: string): {
72
+ module: TModule;
73
+ controller: string;
74
+ listing: ApiEndpoint<TModule>;
75
+ activeListing: ApiEndpoint<TModule>;
76
+ findListing: ApiEndpoint<TModule>;
77
+ pagination: ApiEndpoint<TModule>;
78
+ lists: Record<import("../Shared.Index.ts").CrudMode, ApiEndpoint<TModule>>;
79
+ pages: Record<import("../Shared.Index.ts").CrudMode, ApiEndpoint<TModule>>;
80
+ pins: ApiEndpoint<TModule>;
81
+ details: ApiEndpoint<TModule>;
82
+ create: ApiEndpoint<TModule>;
83
+ update: ApiEndpoint<TModule>;
84
+ delete: ApiEndpoint<TModule>;
85
+ trash: ApiEndpoint<TModule>;
86
+ trashFromDelete: ApiEndpoint<TModule>;
87
+ recover: ApiEndpoint<TModule>;
88
+ flag: ApiEndpoint<TModule>;
89
+ pin: ApiEndpoint<TModule>;
90
+ audit: ApiEndpoint<TModule>;
91
+ accessActions: ApiEndpoint<TModule>;
92
+ mass: {
93
+ description: ApiEndpoint<TModule>;
94
+ trash: ApiEndpoint<TModule>;
95
+ trashFromDelete: ApiEndpoint<TModule>;
96
+ flag: ApiEndpoint<TModule>;
97
+ pin: ApiEndpoint<TModule>;
98
+ delete: ApiEndpoint<TModule>;
99
+ recover: ApiEndpoint<TModule>;
100
+ };
101
+ action: (method: import("../Shared.Index.ts").HttpMethod, action: string, extras?: Partial<Omit<ApiEndpoint<number>, "path" | "method" | "module">> | undefined) => ApiEndpoint<TModule>;
102
+ };
103
+ }
104
+ /** The Nexus modules, their names in proxy URLs, and the environment variables with their roots (as in the previous project). */
105
+ export declare const NEXUS_MODULES: readonly ApiModuleInfo<NexusModule>[];
106
+ /**
107
+ * The Nexus modules: each module's root from its environment variable on the server (`API_RESERVATION=…/App/Api`),
108
+ * browser calls through the proxy, and `NexusModule.None` for the app's own routes at `/api/…`. Extend it to change one
109
+ * thing, such as a module's root.
110
+ */
111
+ export declare class NexusApiModules extends ApiModules<NexusModule> {
112
+ readonly modules: readonly ApiModuleInfo<number>[];
113
+ serverRoot(module: NexusModule): string | undefined;
114
+ browserRoot(module: NexusModule): string | undefined;
115
+ proxies(module: NexusModule): boolean;
116
+ }
117
+ /**
118
+ * Makes an app's modules the ones every call uses, and returns them. Define endpoints through what it returns, so any
119
+ * file that calls an endpoint has registered the modules first, on the server and in the browser alike.
120
+ *
121
+ * @example
122
+ * export const MODULES = defineApiModules(new NexusApiModules({ useProxy: process.env.NEXT_PUBLIC_API_PROXY !== "false" }));
123
+ * export const ROOMS = MODULES.controller(NexusModule.Reservation, "Room", "room");
124
+ */
125
+ export declare function defineApiModules<T extends ApiModules<any>>(modules: T): T;
126
+ /** The app's modules: the ones given to `defineApiModules`, else `NexusApiModules`. */
127
+ export declare function getApiModules(): ApiModules<any>;
128
+ /** The URL the browser calls for an endpoint, through the app's modules. See `ApiModules.browserApiUrl`. */
129
+ export declare function browserApiUrl(endpoint: ApiEndpoint, parts?: ApiUrlParts): string;
130
+ /** Names a call for logs and messages: "reservation/Room/NormalListing". */
131
+ export declare function describeEndpoint(endpoint: ApiEndpoint): string;
@@ -0,0 +1,195 @@
1
+ import { NexusModule } from "../Interfaces/ApiInterfaces.js";
2
+ import { apiController, apiEndpoint, appendQuery, fillApiPath, joinUrl } from "./ApiRoutes.js";
3
+ import { notConfiguredMessage } from "./MessageBuilder.js";
4
+ /*
5
+ * An app's API modules: which modules it calls and where each one's root URL is. The package does not decide either.
6
+ * An app describes its own in a class that extends `ApiModules` (its module enum, its list, and how it finds a root)
7
+ * and registers it once with `defineApiModules`; the browser's `api`, the server's `serverApi`, and the proxy route all
8
+ * ask it. Nexus apps use `NexusApiModules`, which is also what runs when nothing is registered.
9
+ */
10
+ /** Where the proxy route sits in an app: `app/api/app/[module]/[...path]/route.ts`. */
11
+ export const DEFAULT_PROXY_PATH = "/api/app";
12
+ const KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
13
+ /** An environment variable, read when it is needed (never inlined at build time), so one build serves every environment. */
14
+ export function readEnvironment(name) {
15
+ if (!name)
16
+ return undefined;
17
+ const env = globalThis.process?.env;
18
+ return env?.[name]?.trim() || undefined;
19
+ }
20
+ /**
21
+ * The API modules of an app. Extend it with the app's own module enum:
22
+ *
23
+ * - `modules`: every module, with its key for proxy URLs and its label for messages.
24
+ * - `serverRoot(module)`: the module's root URL for server calls and the proxy (from the environment, a settings
25
+ * service, anything; may be async).
26
+ *
27
+ * Then, if the defaults do not suit: `useProxy` to call modules straight from the browser, `browserRoot(module)` for
28
+ * those roots, and `proxies(module)` to send only some modules through the proxy.
29
+ *
30
+ * @example
31
+ * enum HrModule { Staff = "staff", Payroll = "payroll" }
32
+ *
33
+ * class HrApiModules extends ApiModules<HrModule> {
34
+ * readonly modules = [
35
+ * { module: HrModule.Staff, key: "staff", label: "Staff", env: "STAFF_API_URL" },
36
+ * { module: HrModule.Payroll, key: "payroll", label: "Payroll", env: "PAYROLL_API_URL" },
37
+ * ];
38
+ * serverRoot(module: HrModule) {
39
+ * return this.readEnv(this.info(module)?.env);
40
+ * }
41
+ * }
42
+ *
43
+ * export const MODULES = defineApiModules(new HrApiModules());
44
+ * export const EMPLOYEES = MODULES.controller(HrModule.Staff, "Employee", "employee");
45
+ */
46
+ export class ApiModules {
47
+ /** Browser calls go through the proxy route. Change it here, in the options, or in a subclass. */
48
+ useProxy;
49
+ /** Where the proxy route is. */
50
+ proxyPath;
51
+ options;
52
+ constructor(options = {}) {
53
+ this.options = options;
54
+ this.useProxy = options.useProxy ?? true;
55
+ this.proxyPath = options.proxyPath ?? DEFAULT_PROXY_PATH;
56
+ }
57
+ /** A module's root URL for browser calls that skip the proxy. Default: `browserRoots` from the options. */
58
+ browserRoot(module) {
59
+ return this.options.browserRoots?.[module];
60
+ }
61
+ /** Whether the browser calls a module through the proxy. Default: `useProxy`, for every module. */
62
+ proxies(_module) {
63
+ return this.useProxy;
64
+ }
65
+ /** A module by its value, its key (any case), or its value as text ("2048", as older proxy URLs have it). */
66
+ info(module) {
67
+ const list = this.modules;
68
+ const exact = list.find(info => info.module === module);
69
+ if (exact || typeof module !== "string")
70
+ return exact;
71
+ const key = module.toLowerCase();
72
+ return list.find(info => info.key.toLowerCase() === key || String(info.module).toLowerCase() === key);
73
+ }
74
+ /** The root the browser calls for a module: the proxy's path for it, or its own root. Throws when neither is set. */
75
+ browserUrl(module) {
76
+ const info = this.info(module);
77
+ if (!info)
78
+ throw new Error(`"${String(module)}" is not one of the app's API modules.`);
79
+ if (this.proxies(module))
80
+ return joinUrl(this.proxyPath, info.key);
81
+ const root = this.browserRoot(module);
82
+ if (!root)
83
+ throw new Error(notConfiguredMessage(info.label, "browserRoot"));
84
+ return root;
85
+ }
86
+ /** A module's root for server calls, as set: `serverRoots`, else `serverRoot`. Throws when it is not set. May be relative. */
87
+ async serverUrl(module) {
88
+ const info = this.info(module);
89
+ if (!info)
90
+ throw new Error(`"${String(module)}" is not one of the app's API modules.`);
91
+ const root = this.options.serverRoots?.[module] ?? (await this.serverRoot(module));
92
+ if (!root)
93
+ throw new Error(notConfiguredMessage(info.label, info.env ?? "serverRoot"));
94
+ return root;
95
+ }
96
+ /** The URL the browser calls for an endpoint, for links, images, and downloads: `/api/app/reservation/Room/Detail/5`. */
97
+ browserApiUrl(endpoint, parts = {}) {
98
+ return appendQuery(joinUrl(this.browserUrl(endpoint.module), fillApiPath(endpoint.path, parts.params, parts.body)), endpoint.query, parts.query);
99
+ }
100
+ /** An environment variable, read at call time; for `serverRoot`. */
101
+ readEnv(name) {
102
+ return readEnvironment(name);
103
+ }
104
+ /** Endpoints typed to this app's modules: `MODULES.endpoint.get(HrModule.Staff, "Employee/Birthdays")`. */
105
+ endpoint = apiEndpoint;
106
+ /** A controller's standard endpoints, typed to this app's modules. See `apiController`. */
107
+ controller(module, controller, subject) {
108
+ return apiController(module, controller, subject);
109
+ }
110
+ }
111
+ /** The Nexus modules, their names in proxy URLs, and the environment variables with their roots (as in the previous project). */
112
+ export const NEXUS_MODULES = [
113
+ { module: NexusModule.Configuration, key: "configuration", label: "Configuration", env: "API_CONFIGURATION" },
114
+ { module: NexusModule.Identity, key: "identity", label: "Identity", env: "API_IDENTITY" },
115
+ { module: NexusModule.Enterprise, key: "enterprise", label: "Enterprise", env: "API_ENTERPRISE" },
116
+ { module: NexusModule.Drive, key: "drive", label: "Drive", env: "API_DRIVE" },
117
+ { module: NexusModule.Message, key: "message", label: "Message", env: "API_MESSAGE" },
118
+ { module: NexusModule.Chat, key: "chat", label: "Chat", env: "API_CHAT" },
119
+ { module: NexusModule.Accounting, key: "accounting", label: "Accounting", env: "API_ACCOUNTING" },
120
+ { module: NexusModule.Report, key: "report", label: "Report", env: "API_REPORT" },
121
+ { module: NexusModule.Requisition, key: "requisition", label: "Requisition", env: "API_REQUISITION" },
122
+ { module: NexusModule.Tenant, key: "tenant", label: "Tenant", env: "API_TENANT" },
123
+ { module: NexusModule.Subscription, key: "subscription", label: "Subscription", env: "API_SUBSCRIPTION" },
124
+ { module: NexusModule.Reservation, key: "reservation", label: "Reservation", env: "API_RESERVATION" },
125
+ { module: NexusModule.School, key: "school", label: "School", env: "API_SCHOOL" },
126
+ { module: NexusModule.Pharmacy, key: "pharmacy", label: "Pharmacy", env: "API_PHARMACY" },
127
+ { module: NexusModule.Staffing, key: "staffing", label: "Staffing", env: "API_STAFFING" },
128
+ { module: NexusModule.Portfolio, key: "portfolio", label: "Portfolio", env: "API_PORTFOLIO" },
129
+ // The app's own API routes (/api/...), never through the proxy.
130
+ { module: NexusModule.None, key: "app", label: "App" },
131
+ ];
132
+ /**
133
+ * The Nexus modules: each module's root from its environment variable on the server (`API_RESERVATION=…/App/Api`),
134
+ * browser calls through the proxy, and `NexusModule.None` for the app's own routes at `/api/…`. Extend it to change one
135
+ * thing, such as a module's root.
136
+ */
137
+ export class NexusApiModules extends ApiModules {
138
+ modules = NEXUS_MODULES;
139
+ serverRoot(module) {
140
+ if (module === NexusModule.None)
141
+ return "/api";
142
+ return this.readEnv(this.info(module)?.env);
143
+ }
144
+ browserRoot(module) {
145
+ return module === NexusModule.None ? "/api" : super.browserRoot(module);
146
+ }
147
+ proxies(module) {
148
+ return module !== NexusModule.None && super.proxies(module);
149
+ }
150
+ }
151
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
+ let registered;
153
+ /** Checks a modules class: every module once, keys that fit in a URL. Throws on the first problem. */
154
+ function checkModules(modules) {
155
+ const keys = new Set();
156
+ const values = new Set();
157
+ for (const info of modules.modules) {
158
+ if (!KEY_PATTERN.test(info.key))
159
+ throw new Error(`API module key "${info.key}" can hold letters, digits, dots, dashes, and underscores only.`);
160
+ const key = info.key.toLowerCase();
161
+ if (keys.has(key))
162
+ throw new Error(`Two API modules have the key "${info.key}".`);
163
+ if (values.has(info.module))
164
+ throw new Error(`Two API modules have the value ${String(info.module)}.`);
165
+ keys.add(key);
166
+ values.add(info.module);
167
+ }
168
+ }
169
+ /**
170
+ * Makes an app's modules the ones every call uses, and returns them. Define endpoints through what it returns, so any
171
+ * file that calls an endpoint has registered the modules first, on the server and in the browser alike.
172
+ *
173
+ * @example
174
+ * export const MODULES = defineApiModules(new NexusApiModules({ useProxy: process.env.NEXT_PUBLIC_API_PROXY !== "false" }));
175
+ * export const ROOMS = MODULES.controller(NexusModule.Reservation, "Room", "room");
176
+ */
177
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
178
+ export function defineApiModules(modules) {
179
+ checkModules(modules);
180
+ registered = modules;
181
+ return modules;
182
+ }
183
+ /** The app's modules: the ones given to `defineApiModules`, else `NexusApiModules`. */
184
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
185
+ export function getApiModules() {
186
+ return (registered ??= new NexusApiModules());
187
+ }
188
+ /** The URL the browser calls for an endpoint, through the app's modules. See `ApiModules.browserApiUrl`. */
189
+ export function browserApiUrl(endpoint, parts) {
190
+ return getApiModules().browserApiUrl(endpoint, parts);
191
+ }
192
+ /** Names a call for logs and messages: "reservation/Room/NormalListing". */
193
+ export function describeEndpoint(endpoint) {
194
+ return `${getApiModules().info(endpoint.module)?.key ?? String(endpoint.module)}/${endpoint.path}`;
195
+ }
@@ -0,0 +1,51 @@
1
+ import type { ApiErrorMessage, ApiResult } from "../Interfaces/ApiInterfaces.ts";
2
+ import type { MessageAction, MessageContext } from "../Interfaces/MessageInterfaces.ts";
3
+ /** Shown when nothing reached the server (the default wording; `messages.reason({ network: true })` follows `configureMessages`). */
4
+ export declare const NETWORK_ERROR_MESSAGE: string;
5
+ /** A message for a failed request that came without one of its own, by HTTP status and what the request did. Default action `save`. */
6
+ export declare function apiStatusMessage(status: number | undefined, action?: MessageAction): string;
7
+ /**
8
+ * Every problem a body lists, in its order, however it listed them: `errorMessages` as a list or as one message object,
9
+ * `errorMessage` either way, or an `errors` list (an `errors` map by field name is read by `readErrorMap` instead).
10
+ * Plain sentences count. Empty for a body that lists nothing; `fallback` is then read as the one problem, which is how
11
+ * the message and codes at the top level of a Nexus answer are read.
12
+ */
13
+ export declare function readErrorMessages(body: unknown, fallback?: unknown): ApiErrorMessage[];
14
+ /**
15
+ * Reads a server's answer as success or failure:
16
+ * - Nexus API responses, `{ isSuccess, code, errorMessages: [{ message, messageCode, errorCode, inputName }], result }`:
17
+ * every problem is kept, in the server's order, and `inputName` names the inputs a message is about (several with "|").
18
+ * `errorMessages` may be one message object instead of a list, and an older answer carries its one message and codes at
19
+ * the top level (`{ isSuccess, message, messageCode, errorCode, inputName }`); all of them are read the same way.
20
+ * - ASP.NET Core problem details and validation errors, `{ title, detail, errors: { Email: ["…"] } }`.
21
+ * - Anything else by its HTTP status; the body is the result.
22
+ *
23
+ * The server's own words are the message whenever it sent any. Only a failure that explains nothing gets a message by
24
+ * status, worded for what the call did (`context`, default: a save).
25
+ */
26
+ export declare function readApiResponse(body: unknown, status?: number, context?: MessageContext): ApiResult;
27
+ /** Reads a `fetch` response: its JSON (or text) through `read`. */
28
+ export declare function readFetchResponse(response: Response, read?: (body: unknown, status: number | undefined) => ApiResult): Promise<ApiResult>;
29
+ /** Whether an error is a request stopped on purpose (the form closed), which shows nothing. */
30
+ export declare function isAbortError(error: unknown): boolean;
31
+ /** A thrown error as a message: its own, or the network message for a failed `fetch`. */
32
+ export declare function errorMessage(error: unknown, fallback?: string): string;
33
+ /** Values as form fields: lists of plain values repeat their name, objects and lists of objects go as JSON text. */
34
+ export declare function toFormData(values: unknown): FormData;
35
+ /** Whether a value is already a read answer (`{ ok, … }`), not a server's body. */
36
+ export declare function isApiResult(value: unknown): value is ApiResult;
37
+ /** A failed answer as an error, for code that throws: a server component's error boundary, a form's `loadValue`. */
38
+ export declare class ApiError extends Error {
39
+ readonly response: ApiResult;
40
+ constructor(response: ApiResult);
41
+ get status(): number | undefined;
42
+ }
43
+ /**
44
+ * What the server sent back, or an `ApiError` thrown for a failure (an `AbortError` for an aborted call, which forms and
45
+ * loaders ignore).
46
+ *
47
+ * @example
48
+ * const rooms = unwrapApiResult(await serverApi.get<Room[]>(ROOMS.listing));
49
+ * <ApiForm loadValue={signal => api.get(ROOMS.details, { params: { id }, signal }).then(unwrapApiResult)} … />
50
+ */
51
+ export declare function unwrapApiResult<T>(response: ApiResult<T>): T;
@@ -0,0 +1,250 @@
1
+ import { buildMessage, describeResult } from "./MessageBuilder.js";
2
+ /*
3
+ * Reading a server's answer as success or failure, the same way for every call: Nexus API responses, ASP.NET Core problem
4
+ * details, or any JSON by its status. Pure, so it works in tests, in the browser, and on a server.
5
+ *
6
+ * What the server says always wins: a body that explains its refusal is shown in the server's own words, however it
7
+ * carried them (a list, one message object, or the message at the top level), and the messages built from the HTTP
8
+ * status are only the fallback for an answer that explains nothing.
9
+ */
10
+ /** Shown when nothing reached the server (the default wording; `messages.reason({ network: true })` follows `configureMessages`). */
11
+ export const NETWORK_ERROR_MESSAGE = buildMessage("reason", { network: true });
12
+ /** A message for a failed request that came without one of its own, by HTTP status and what the request did. Default action `save`. */
13
+ export function apiStatusMessage(status, action = "save") {
14
+ return buildMessage("reason", { action, status });
15
+ }
16
+ function isRecord(value) {
17
+ return typeof value === "object" && value !== null && !Array.isArray(value);
18
+ }
19
+ function text(value) {
20
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
21
+ }
22
+ function count(value) {
23
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
24
+ }
25
+ /** Messages by field from `{ errors: { Email: ["…"], "Addresses[0].City": "…" } }`. */
26
+ function readErrorMap(errors) {
27
+ if (!isRecord(errors))
28
+ return undefined;
29
+ const result = {};
30
+ for (const [key, value] of Object.entries(errors)) {
31
+ const message = Array.isArray(value) ? value.map(text).filter(Boolean).join(" ") : text(value);
32
+ if (message)
33
+ result[key] = message;
34
+ }
35
+ return Object.keys(result).length ? result : undefined;
36
+ }
37
+ /** The backend's defaults say nothing about what went wrong, so they are left out. */
38
+ const EMPTY_CODES = ["SUCCESS", "GENERIC_ERROR"];
39
+ /** A code worth keeping: not one of the backend's defaults. */
40
+ function codeOf(value) {
41
+ const code = text(value);
42
+ return code && !EMPTY_CODES.includes(code) ? code : undefined;
43
+ }
44
+ /**
45
+ * One problem from any shape a backend sends it in: `{ message, messageCode, errorCode, inputName }`, a plain sentence,
46
+ * or a wording of the same fields (`errorMessage`, `code`, `field`, `propertyName`). Nothing without words to show.
47
+ */
48
+ function readErrorMessage(value) {
49
+ if (typeof value === "string") {
50
+ const sentence = text(value);
51
+ return sentence ? { message: sentence } : undefined;
52
+ }
53
+ if (!isRecord(value))
54
+ return undefined;
55
+ const message = text(value.message) ?? text(value.errorMessage) ?? text(value.detail) ?? text(value.title);
56
+ if (!message)
57
+ return undefined;
58
+ const messageCode = codeOf(value.messageCode) ?? codeOf(value.code);
59
+ const errorCode = count(value.errorCode);
60
+ const inputName = text(value.inputName) ?? text(value.field) ?? text(value.propertyName);
61
+ return {
62
+ message,
63
+ ...(messageCode ? { messageCode } : {}),
64
+ ...(errorCode ? { errorCode } : {}),
65
+ ...(inputName ? { inputName } : {}),
66
+ };
67
+ }
68
+ /**
69
+ * Every problem a body lists, in its order, however it listed them: `errorMessages` as a list or as one message object,
70
+ * `errorMessage` either way, or an `errors` list (an `errors` map by field name is read by `readErrorMap` instead).
71
+ * Plain sentences count. Empty for a body that lists nothing; `fallback` is then read as the one problem, which is how
72
+ * the message and codes at the top level of a Nexus answer are read.
73
+ */
74
+ export function readErrorMessages(body, fallback) {
75
+ const list = (values) => values.map(readErrorMessage).filter((problem) => Boolean(problem));
76
+ if (Array.isArray(body))
77
+ return list(body);
78
+ if (isRecord(body)) {
79
+ for (const key of ["errorMessages", "errorMessage", "errors"]) {
80
+ const value = body[key];
81
+ if (value === undefined || value === null)
82
+ continue;
83
+ // `errors` is a list of problems only when it is a list; as an object it names fields.
84
+ if (key === "errors" && !Array.isArray(value))
85
+ continue;
86
+ const problems = list(Array.isArray(value) ? value : [value]);
87
+ if (problems.length)
88
+ return problems;
89
+ }
90
+ }
91
+ if (fallback === undefined)
92
+ return [];
93
+ const own = readErrorMessage(fallback);
94
+ return own ? [own] : [];
95
+ }
96
+ /** The problems that name an input, as one message per name: several on one name joined, `"floor|rate"` naming both. */
97
+ function fieldErrors(problems) {
98
+ const errors = {};
99
+ for (const { message, inputName } of problems) {
100
+ if (!inputName)
101
+ continue;
102
+ for (const part of inputName.split("|")) {
103
+ const name = part.trim();
104
+ if (!name)
105
+ continue;
106
+ errors[name] = errors[name] && errors[name] !== message ? `${errors[name]} ${message}` : message;
107
+ }
108
+ }
109
+ return Object.keys(errors).length ? errors : undefined;
110
+ }
111
+ /**
112
+ * Reads a server's answer as success or failure:
113
+ * - Nexus API responses, `{ isSuccess, code, errorMessages: [{ message, messageCode, errorCode, inputName }], result }`:
114
+ * every problem is kept, in the server's order, and `inputName` names the inputs a message is about (several with "|").
115
+ * `errorMessages` may be one message object instead of a list, and an older answer carries its one message and codes at
116
+ * the top level (`{ isSuccess, message, messageCode, errorCode, inputName }`); all of them are read the same way.
117
+ * - ASP.NET Core problem details and validation errors, `{ title, detail, errors: { Email: ["…"] } }`.
118
+ * - Anything else by its HTTP status; the body is the result.
119
+ *
120
+ * The server's own words are the message whenever it sent any. Only a failure that explains nothing gets a message by
121
+ * status, worded for what the call did (`context`, default: a save).
122
+ */
123
+ export function readApiResponse(body, status, context) {
124
+ const fallback = () => buildMessage("reason", { action: "save", ...context, status });
125
+ const statusOk = status === undefined || (status >= 200 && status < 300);
126
+ if (isRecord(body)) {
127
+ if (typeof body.isSuccess === "boolean") {
128
+ const ok = body.isSuccess && statusOk;
129
+ // A failure's problems: its list, else the message and codes at the top level as the one problem.
130
+ const problems = ok ? [] : readErrorMessages(body, body);
131
+ const said = problems.map(problem => problem.message).join(" ") || text(body.message);
132
+ const messageCode = problems[0]?.messageCode ?? codeOf(body.messageCode);
133
+ const errorCode = problems[0]?.errorCode ?? count(body.errorCode);
134
+ return {
135
+ ok,
136
+ // An answer read without its HTTP status (an app's own send function) still carries it as `code`.
137
+ status: status ?? count(body.code),
138
+ result: body.result,
139
+ message: said ?? (ok ? undefined : fallback()),
140
+ errors: fieldErrors(problems),
141
+ ...(problems.length ? { errorMessages: problems } : {}),
142
+ ...(messageCode ? { messageCode } : {}),
143
+ ...(errorCode ? { errorCode } : {}),
144
+ };
145
+ }
146
+ // Another backend's envelope: its problems if it lists any, ASP.NET Core's field map, or its status.
147
+ const problems = readErrorMessages(body);
148
+ const errors = readErrorMap(body.errors) ?? fieldErrors(problems);
149
+ if (problems.length || errors || !statusOk) {
150
+ const said = problems.map(problem => problem.message).join(" ") || (text(body.message) ?? (errors ? undefined : (text(body.detail) ?? text(body.title))));
151
+ const messageCode = problems[0]?.messageCode;
152
+ const errorCode = problems[0]?.errorCode;
153
+ return {
154
+ ok: false,
155
+ status,
156
+ result: body,
157
+ message: said ?? (errors ? undefined : fallback()),
158
+ errors,
159
+ ...(problems.length ? { errorMessages: problems } : {}),
160
+ ...(messageCode ? { messageCode } : {}),
161
+ ...(errorCode ? { errorCode } : {}),
162
+ };
163
+ }
164
+ return { ok: true, status, result: body, message: text(body.message) };
165
+ }
166
+ if (!statusOk) {
167
+ // A short text answer is the message; an HTML error page is not.
168
+ const plain = text(body);
169
+ const message = plain && plain.length <= 300 && !plain.startsWith("<") ? plain : fallback();
170
+ return { ok: false, status, message };
171
+ }
172
+ return { ok: true, status, result: body === "" ? undefined : body };
173
+ }
174
+ /** Reads a `fetch` response: its JSON (or text) through `read`. */
175
+ export async function readFetchResponse(response, read = readApiResponse) {
176
+ const type = response.headers.get("content-type") ?? "";
177
+ let body = null;
178
+ try {
179
+ body = type.includes("json") ? await response.json() : await response.text();
180
+ }
181
+ catch {
182
+ body = null;
183
+ }
184
+ return read(body, response.status);
185
+ }
186
+ /** Whether an error is a request stopped on purpose (the form closed), which shows nothing. */
187
+ export function isAbortError(error) {
188
+ return error?.name === "AbortError";
189
+ }
190
+ /** A thrown error as a message: its own, or the network message for a failed `fetch`. */
191
+ export function errorMessage(error, fallback = "Could not save the changes.") {
192
+ if (error instanceof TypeError)
193
+ return buildMessage("reason", { network: true });
194
+ return text(error?.message) ?? (typeof error === "string" && error ? error : fallback);
195
+ }
196
+ /** Values as form fields: lists of plain values repeat their name, objects and lists of objects go as JSON text. */
197
+ export function toFormData(values) {
198
+ if (typeof FormData !== "undefined" && values instanceof FormData)
199
+ return values;
200
+ const data = new FormData();
201
+ if (!isRecord(values))
202
+ return data;
203
+ for (const [key, value] of Object.entries(values)) {
204
+ if (value === undefined)
205
+ continue;
206
+ if (value instanceof Blob)
207
+ data.append(key, value);
208
+ else if (Array.isArray(value) && value.every(item => item === null || typeof item !== "object" || item instanceof Blob))
209
+ for (const item of value)
210
+ data.append(key, item instanceof Blob ? item : item === null ? "" : String(item));
211
+ else if (value !== null && typeof value === "object")
212
+ data.append(key, JSON.stringify(value));
213
+ else
214
+ data.append(key, value === null ? "" : String(value));
215
+ }
216
+ return data;
217
+ }
218
+ const RESULT_KEYS = ["result", "message", "errors", "errorMessages", "status", "network", "timeout", "aborted", "messageCode", "errorCode"];
219
+ /** Whether a value is already a read answer (`{ ok, … }`), not a server's body. */
220
+ export function isApiResult(value) {
221
+ if (!isRecord(value) || typeof value.ok !== "boolean" || "isSuccess" in value)
222
+ return false;
223
+ const keys = Object.keys(value);
224
+ return keys.length === 1 || keys.some(key => RESULT_KEYS.includes(key));
225
+ }
226
+ /** A failed answer as an error, for code that throws: a server component's error boundary, a form's `loadValue`. */
227
+ export class ApiError extends Error {
228
+ response;
229
+ constructor(response) {
230
+ super(describeResult(response)?.message ?? response.message ?? "The request failed.");
231
+ this.name = response.aborted ? "AbortError" : "ApiError";
232
+ this.response = response;
233
+ }
234
+ get status() {
235
+ return this.response.status;
236
+ }
237
+ }
238
+ /**
239
+ * What the server sent back, or an `ApiError` thrown for a failure (an `AbortError` for an aborted call, which forms and
240
+ * loaders ignore).
241
+ *
242
+ * @example
243
+ * const rooms = unwrapApiResult(await serverApi.get<Room[]>(ROOMS.listing));
244
+ * <ApiForm loadValue={signal => api.get(ROOMS.details, { params: { id }, signal }).then(unwrapApiResult)} … />
245
+ */
246
+ export function unwrapApiResult(response) {
247
+ if (!response.ok)
248
+ throw new ApiError(response);
249
+ return response.result;
250
+ }