@jmruthers/pace-core 0.5.135 → 0.5.137

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 (544) hide show
  1. package/dist/{DataTable-A36PJG6N.js → DataTable-6M4L6BI2.js} +26 -13
  2. package/dist/{DataTable-C7GaRZye.d.ts → DataTable-CWAZZcXC.d.ts} +1 -1
  3. package/dist/{PublicLoadingSpinner-CUAnTvcg.d.ts → EventLogo-rFL_kRjk.d.ts} +123 -135
  4. package/dist/{UnifiedAuthProvider-BVKmQd9u.d.ts → UnifiedAuthProvider-DJxGTftH.d.ts} +1 -1
  5. package/dist/{UnifiedAuthProvider-CQDZRJIS.js → UnifiedAuthProvider-XIQQ7LVU.js} +5 -5
  6. package/dist/{api-TNIBJWLM.js → api-45XYYO2A.js} +4 -3
  7. package/dist/{audit-T36HM7IM.js → audit-64X3VJXB.js} +3 -2
  8. package/dist/{chunk-F64FFPOZ.js → chunk-22WKWKRX.js} +26 -20
  9. package/dist/chunk-22WKWKRX.js.map +1 -0
  10. package/dist/{chunk-VZ5OR6HD.js → chunk-4C7EXCAR.js} +62 -150
  11. package/dist/chunk-4C7EXCAR.js.map +1 -0
  12. package/dist/{chunk-PYUXFQJ3.js → chunk-56XJ3TU6.js} +2 -2
  13. package/dist/chunk-56XJ3TU6.js.map +1 -0
  14. package/dist/{chunk-CTJRBUX2.js → chunk-6LAAY47Q.js} +2 -2
  15. package/dist/{chunk-UJI6WSMD.js → chunk-7QCC6MCP.js} +90 -3
  16. package/dist/chunk-7QCC6MCP.js.map +1 -0
  17. package/dist/{chunk-66C4BSAY.js → chunk-ANBQRTPX.js} +9 -2
  18. package/dist/chunk-ANBQRTPX.js.map +1 -0
  19. package/dist/{chunk-CQZU6TFE.js → chunk-BCIBECNB.js} +100 -62
  20. package/dist/chunk-BCIBECNB.js.map +1 -0
  21. package/dist/{chunk-GKHF54DI.js → chunk-BESYRHQM.js} +10 -4
  22. package/dist/chunk-BESYRHQM.js.map +1 -0
  23. package/dist/chunk-BJPBT3CU.js +21 -0
  24. package/dist/chunk-BJPBT3CU.js.map +1 -0
  25. package/dist/{chunk-BYXRHAIF.js → chunk-BLCXZEYF.js} +23 -14
  26. package/dist/chunk-BLCXZEYF.js.map +1 -0
  27. package/dist/{chunk-WP5I5GLN.js → chunk-BVYWGZVV.js} +112 -97
  28. package/dist/chunk-BVYWGZVV.js.map +1 -0
  29. package/dist/{chunk-GEVIB2UB.js → chunk-ERISIBYU.js} +14 -5
  30. package/dist/chunk-ERISIBYU.js.map +1 -0
  31. package/dist/{chunk-O3NWNXDY.js → chunk-FMUCXFII.js} +2 -2
  32. package/dist/chunk-FMUCXFII.js.map +1 -0
  33. package/dist/{chunk-GVDR7WNV.js → chunk-HAWZXGR2.js} +334 -614
  34. package/dist/chunk-HAWZXGR2.js.map +1 -0
  35. package/dist/{chunk-ZV77RZMU.js → chunk-INQLMHPF.js} +2 -2
  36. package/dist/chunk-JISYG63F.js +70 -0
  37. package/dist/chunk-JISYG63F.js.map +1 -0
  38. package/dist/{chunk-HMNOSGVA.js → chunk-KYRHUBIU.js} +576 -767
  39. package/dist/chunk-KYRHUBIU.js.map +1 -0
  40. package/dist/{chunk-M6DDYFUD.js → chunk-LS353YLY.js} +19 -16
  41. package/dist/chunk-LS353YLY.js.map +1 -0
  42. package/dist/{chunk-TGIY2AR2.js → chunk-MA6EPSGZ.js} +4 -3
  43. package/dist/{chunk-TGIY2AR2.js.map → chunk-MA6EPSGZ.js.map} +1 -1
  44. package/dist/chunk-OWAG3GSU.js +58 -0
  45. package/dist/chunk-OWAG3GSU.js.map +1 -0
  46. package/dist/{chunk-JCQZ6LA7.js → chunk-Q5QRDWKI.js} +9 -3
  47. package/dist/chunk-Q5QRDWKI.js.map +1 -0
  48. package/dist/chunk-S5OFRT4M.js +94 -0
  49. package/dist/chunk-S5OFRT4M.js.map +1 -0
  50. package/dist/{chunk-3DBFLLLU.js → chunk-SBVILCCA.js} +14 -9
  51. package/dist/chunk-SBVILCCA.js.map +1 -0
  52. package/dist/{chunk-ZYZCRSBD.js → chunk-T6JN6LH6.js} +16 -11
  53. package/dist/chunk-T6JN6LH6.js.map +1 -0
  54. package/dist/chunk-XDNLUEXI.js +138 -0
  55. package/dist/chunk-XDNLUEXI.js.map +1 -0
  56. package/dist/{chunk-3CG5L6RN.js → chunk-YCWDTTUK.js} +90 -75
  57. package/dist/chunk-YCWDTTUK.js.map +1 -0
  58. package/dist/{chunk-5F3NDPJV.js → chunk-ZZ2SS7NI.js} +10 -5
  59. package/dist/chunk-ZZ2SS7NI.js.map +1 -0
  60. package/dist/components.d.ts +7 -287
  61. package/dist/components.js +27 -157
  62. package/dist/components.js.map +1 -1
  63. package/dist/{file-reference-C9isKNPn.d.ts → file-reference-C6Gkn77H.d.ts} +1 -1
  64. package/dist/{formatting-DFcCxUEk.d.ts → formatting-CvUXy2mF.d.ts} +1 -1
  65. package/dist/hooks.d.ts +3 -3
  66. package/dist/hooks.js +21 -16
  67. package/dist/hooks.js.map +1 -1
  68. package/dist/index.d.ts +101 -9
  69. package/dist/index.js +44 -31
  70. package/dist/index.js.map +1 -1
  71. package/dist/providers.d.ts +1 -1
  72. package/dist/providers.js +4 -4
  73. package/dist/rbac/index.js +12 -12
  74. package/dist/schema-DTDZQe2u.d.ts +28 -0
  75. package/dist/styles/index.js +2 -1
  76. package/dist/theming/runtime.d.ts +2 -19
  77. package/dist/theming/runtime.js +2 -1
  78. package/dist/{types-D5rqZQXk.d.ts → types-Dfz9dmVH.d.ts} +12 -1
  79. package/dist/types.d.ts +153 -4
  80. package/dist/types.js +51 -16
  81. package/dist/types.js.map +1 -1
  82. package/dist/{useInactivityTracker-MRUU55XI.js → useInactivityTracker-TO6ZOF35.js} +3 -2
  83. package/dist/{usePublicRouteParams-Dyt1tzI9.d.ts → usePublicRouteParams-B7PabvuH.d.ts} +1 -1
  84. package/dist/utils.d.ts +221 -173
  85. package/dist/utils.js +185 -225
  86. package/dist/utils.js.map +1 -1
  87. package/dist/validation.d.ts +24 -115
  88. package/dist/validation.js +19 -474
  89. package/dist/validation.js.map +1 -1
  90. package/docs/api/classes/ColumnFactory.md +1 -1
  91. package/docs/api/classes/ErrorBoundary.md +6 -6
  92. package/docs/api/classes/InvalidScopeError.md +1 -1
  93. package/docs/api/classes/MissingUserContextError.md +1 -1
  94. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  95. package/docs/api/classes/PermissionDeniedError.md +1 -1
  96. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  97. package/docs/api/classes/RBACAuditManager.md +6 -6
  98. package/docs/api/classes/RBACCache.md +1 -1
  99. package/docs/api/classes/RBACEngine.md +7 -7
  100. package/docs/api/classes/RBACError.md +1 -1
  101. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  102. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  103. package/docs/api/classes/StorageUtils.md +1 -1
  104. package/docs/api/enums/FileCategory.md +1 -1
  105. package/docs/api/interfaces/AggregateConfig.md +4 -4
  106. package/docs/api/interfaces/BadgeProps.md +27 -0
  107. package/docs/api/interfaces/ButtonProps.md +1 -1
  108. package/docs/api/interfaces/CardProps.md +1 -1
  109. package/docs/api/interfaces/ColorPalette.md +1 -1
  110. package/docs/api/interfaces/ColorShade.md +29 -4
  111. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  112. package/docs/api/interfaces/DataRecord.md +1 -1
  113. package/docs/api/interfaces/DataTableAction.md +18 -18
  114. package/docs/api/interfaces/DataTableColumn.md +61 -1
  115. package/docs/api/interfaces/DataTableProps.md +1 -1
  116. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  117. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  118. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +14 -14
  119. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  120. package/docs/api/interfaces/EventLogoProps.md +152 -0
  121. package/docs/api/interfaces/ExportColumn.md +1 -1
  122. package/docs/api/interfaces/ExportOptions.md +8 -8
  123. package/docs/api/interfaces/FileDisplayProps.md +15 -15
  124. package/docs/api/interfaces/FileMetadata.md +1 -1
  125. package/docs/api/interfaces/FileReference.md +1 -1
  126. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  127. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  128. package/docs/api/interfaces/FileUploadProps.md +1 -1
  129. package/docs/api/interfaces/FooterProps.md +1 -1
  130. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  131. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  132. package/docs/api/interfaces/InputProps.md +1 -1
  133. package/docs/api/interfaces/LabelProps.md +1 -1
  134. package/docs/api/interfaces/LoginFormProps.md +1 -1
  135. package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
  136. package/docs/api/interfaces/NavigationContextType.md +9 -9
  137. package/docs/api/interfaces/NavigationGuardProps.md +10 -10
  138. package/docs/api/interfaces/NavigationItem.md +1 -1
  139. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  140. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  141. package/docs/api/interfaces/Organisation.md +1 -1
  142. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  143. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  144. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  145. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  146. package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
  147. package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
  148. package/docs/api/interfaces/PageAccessRecord.md +8 -8
  149. package/docs/api/interfaces/PagePermissionContextType.md +8 -8
  150. package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
  151. package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
  152. package/docs/api/interfaces/PaletteData.md +4 -4
  153. package/docs/api/interfaces/PermissionEnforcerProps.md +11 -11
  154. package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
  155. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  156. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  157. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  158. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  159. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  160. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  161. package/docs/api/interfaces/RBACConfig.md +1 -1
  162. package/docs/api/interfaces/RBACLogger.md +1 -1
  163. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  164. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  165. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  166. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  167. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  168. package/docs/api/interfaces/RouteConfig.md +10 -10
  169. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  170. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  171. package/docs/api/interfaces/SessionRestorationLoaderProps.md +21 -0
  172. package/docs/api/interfaces/StorageConfig.md +1 -1
  173. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  174. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  175. package/docs/api/interfaces/StorageListOptions.md +1 -1
  176. package/docs/api/interfaces/StorageListResult.md +1 -1
  177. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  178. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  179. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  180. package/docs/api/interfaces/StyleImport.md +1 -1
  181. package/docs/api/interfaces/SwitchProps.md +1 -1
  182. package/docs/api/interfaces/ToastActionElement.md +1 -1
  183. package/docs/api/interfaces/ToastProps.md +1 -1
  184. package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
  185. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  186. package/docs/api/interfaces/UseInactivityTrackerOptions.md +9 -9
  187. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  188. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  189. package/docs/api/interfaces/UsePublicEventReturn.md +5 -5
  190. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  191. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +9 -9
  192. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  193. package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
  194. package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
  195. package/docs/api/interfaces/UserEventAccess.md +11 -11
  196. package/docs/api/interfaces/UserMenuProps.md +1 -1
  197. package/docs/api/interfaces/UserProfile.md +1 -1
  198. package/docs/api/modules.md +591 -220
  199. package/docs/api-reference/components.md +106 -26
  200. package/docs/architecture/README.md +0 -3
  201. package/docs/implementation-guides/data-tables.md +277 -13
  202. package/docs/implementation-guides/forms.md +1 -16
  203. package/docs/implementation-guides/permission-enforcement.md +8 -2
  204. package/docs/styles/README.md +0 -2
  205. package/examples/README.md +30 -14
  206. package/examples/STRUCTURE.md +125 -0
  207. package/examples/components 2/DataTable/HierarchicalActionsExample.tsx +421 -0
  208. package/examples/components 2/DataTable/HierarchicalExample.tsx +475 -0
  209. package/examples/components 2/DataTable/InitialPageSizeExample.tsx +177 -0
  210. package/examples/components 2/DataTable/PerformanceExample.tsx +506 -0
  211. package/examples/components 2/DataTable/index.ts +13 -0
  212. package/examples/components 2/Dialog/BasicHtmlTest.tsx +55 -0
  213. package/examples/components 2/Dialog/DebugHtmlExample.tsx +68 -0
  214. package/examples/components 2/Dialog/HtmlDialogExample.tsx +202 -0
  215. package/examples/components 2/Dialog/ScrollableDialogExample.tsx +290 -0
  216. package/examples/components 2/Dialog/SimpleHtmlTest.tsx +61 -0
  217. package/examples/components 2/Dialog/SmartDialogExample.tsx +322 -0
  218. package/examples/components 2/Dialog/index.ts +15 -0
  219. package/examples/components 2/index.ts +11 -0
  220. package/examples/features/index.ts +12 -0
  221. package/{src/examples → examples/features/public-pages}/CorrectPublicPageImplementation.tsx +14 -17
  222. package/{src/examples → examples/features/public-pages}/PublicEventPage.tsx +14 -27
  223. package/{src/examples → examples/features/public-pages}/PublicPageApp.tsx +15 -28
  224. package/{src/examples → examples/features/public-pages}/PublicPageUsageExample.tsx +8 -10
  225. package/examples/features/public-pages/index.ts +14 -0
  226. package/examples/features/rbac/CompleteRBACExample.tsx +324 -0
  227. package/examples/features/rbac/EventBasedApp.tsx +239 -0
  228. package/examples/features/rbac/PermissionExample.tsx +151 -0
  229. package/examples/features/rbac/index.ts +13 -0
  230. package/examples/index.ts +11 -3
  231. package/package.json +30 -19
  232. package/src/__tests__/TEST_STANDARD.md +92 -0
  233. package/src/components/Alert/Alert.tsx +1 -1
  234. package/src/components/Avatar/Avatar.tsx +1 -1
  235. package/src/components/Badge/Badge.test.tsx +314 -0
  236. package/src/components/Badge/Badge.tsx +304 -0
  237. package/src/components/Badge/index.ts +3 -0
  238. package/src/components/Button/Button.tsx +1 -1
  239. package/src/components/Card/Card.tsx +1 -1
  240. package/src/components/Checkbox/Checkbox.tsx +1 -1
  241. package/src/components/DataTable/DataTable.test.tsx +1 -1
  242. package/src/components/DataTable/DataTable.tsx +1 -30
  243. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +562 -0
  244. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
  245. package/src/components/DataTable/__tests__/styles.test.ts +3 -3
  246. package/src/components/DataTable/components/ActionButtons.tsx +0 -15
  247. package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
  248. package/src/components/DataTable/components/DataTableBody.tsx +461 -0
  249. package/src/components/DataTable/components/DataTableCore.tsx +4 -185
  250. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +1 -1
  251. package/src/components/DataTable/components/DataTableModals.tsx +1 -27
  252. package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
  253. package/src/components/DataTable/components/EditableRow.tsx +1 -1
  254. package/src/components/DataTable/components/FilterRow.tsx +9 -3
  255. package/src/components/DataTable/components/ImportModal.tsx +2 -14
  256. package/src/components/DataTable/components/PaginationControls.tsx +2 -1
  257. package/src/components/DataTable/components/UnifiedTableBody.tsx +109 -82
  258. package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
  259. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
  260. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +1 -1
  261. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
  262. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +1 -1
  263. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +1 -1
  264. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
  265. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +1 -1
  266. package/src/components/DataTable/core/ActionManager.ts +235 -0
  267. package/src/components/DataTable/core/ColumnManager.ts +205 -0
  268. package/src/components/DataTable/core/DataManager.ts +188 -0
  269. package/src/components/DataTable/core/DataTableContext.tsx +181 -0
  270. package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
  271. package/src/components/DataTable/core/PluginRegistry.ts +229 -0
  272. package/src/components/DataTable/core/StateManager.ts +311 -0
  273. package/src/components/DataTable/core/interfaces.ts +338 -0
  274. package/src/components/DataTable/examples/GroupingAggregationExample.tsx +273 -0
  275. package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +1 -1
  276. package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +1 -1
  277. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +1 -1
  278. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +1 -1
  279. package/src/components/DataTable/hooks/useDataTablePermissions.ts +2 -23
  280. package/src/components/DataTable/index.ts +4 -0
  281. package/src/components/DataTable/styles.ts +28 -7
  282. package/src/components/DataTable/types.ts +13 -0
  283. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
  284. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +1 -1
  285. package/src/components/DataTable/utils/aggregationUtils.ts +161 -0
  286. package/src/components/DataTable/utils/columnUtils.ts +40 -0
  287. package/src/components/DataTable/utils/debugTools.ts +609 -0
  288. package/src/components/DataTable/utils/exportUtils.ts +1 -1
  289. package/src/components/DataTable/utils/flexibleImport.ts +1 -11
  290. package/src/components/DataTable/utils/index.ts +2 -0
  291. package/src/components/DataTable/utils/paginationUtils.ts +1 -1
  292. package/src/components/Dialog/Dialog.tsx +2 -2
  293. package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +8 -1
  294. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +35 -7
  295. package/src/components/ErrorBoundary/ErrorBoundary.tsx +5 -4
  296. package/src/components/EventSelector/EventSelector.tsx +3 -2
  297. package/src/components/FileDisplay/FileDisplay.tsx +2 -36
  298. package/src/components/FileUpload/FileUpload.test.tsx +2 -2
  299. package/src/components/FileUpload/FileUpload.tsx +2 -2
  300. package/src/components/Footer/Footer.test.tsx +1 -1
  301. package/src/components/Footer/Footer.tsx +1 -1
  302. package/src/components/Form/Form.test.tsx +5 -510
  303. package/src/components/Form/Form.tsx +1 -1
  304. package/src/components/Form/FormField.tsx +1 -1
  305. package/src/components/Form/index.ts +0 -12
  306. package/src/components/Header/Header.tsx +1 -1
  307. package/src/components/Input/Input.tsx +1 -1
  308. package/src/components/Label/Label.tsx +1 -1
  309. package/src/components/LoginForm/LoginForm.test.tsx +1 -1
  310. package/src/components/LoginForm/LoginForm.tsx +1 -1
  311. package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -3
  312. package/src/components/NavigationMenu/NavigationMenu.tsx +9 -8
  313. package/src/components/OrganisationSelector/OrganisationSelector.tsx +4 -3
  314. package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -12
  315. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +0 -16
  316. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
  317. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +0 -1
  318. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +0 -9
  319. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +35 -3
  320. package/src/components/PaceLoginPage/PaceLoginPage.tsx +14 -13
  321. package/src/components/PasswordReset/PasswordChangeForm.tsx +1 -1
  322. package/src/components/PasswordReset/index.ts +0 -2
  323. package/src/components/Progress/Progress.tsx +1 -1
  324. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +35 -8
  325. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -2
  326. package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
  327. package/src/components/PublicLayout/PublicLoadingSpinner.tsx +1 -1
  328. package/src/components/PublicLayout/PublicPageContextChecker.tsx +44 -43
  329. package/src/components/PublicLayout/PublicPageFooter.tsx +1 -1
  330. package/src/components/PublicLayout/PublicPageHeader.tsx +1 -15
  331. package/src/components/PublicLayout/PublicPageProvider.tsx +3 -2
  332. package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +2 -0
  333. package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
  334. package/src/components/PublicLayout/index.ts +4 -2
  335. package/src/components/Select/Select.test.tsx +1 -1
  336. package/src/components/Select/Select.tsx +21 -9
  337. package/src/components/{SessionRestorationLoader.tsx → SessionRestorationLoader/SessionRestorationLoader.tsx} +3 -2
  338. package/src/components/SessionRestorationLoader/index.ts +3 -0
  339. package/src/components/Switch/Switch.tsx +1 -1
  340. package/src/components/Table/Table.tsx +1 -1
  341. package/src/components/Table/__tests__/Table.test.tsx +1 -1
  342. package/src/components/Toast/Toast.tsx +1 -1
  343. package/src/components/Tooltip/Tooltip.tsx +1 -1
  344. package/src/components/index.ts +7 -10
  345. package/src/hooks/__tests__/hooks.integration.test.tsx +37 -22
  346. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +33 -17
  347. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +28 -3
  348. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +36 -9
  349. package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
  350. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +26 -2
  351. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +19 -6
  352. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +17 -4
  353. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +17 -4
  354. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +26 -6
  355. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +16 -6
  356. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +3 -3
  357. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +17 -3
  358. package/src/hooks/public/usePublicEvent.ts +7 -6
  359. package/src/hooks/public/usePublicEventLogo.ts +7 -4
  360. package/src/hooks/public/usePublicFileDisplay.ts +6 -150
  361. package/src/hooks/useComponentPerformance.ts +4 -1
  362. package/src/hooks/useDataTablePerformance.ts +4 -3
  363. package/src/hooks/useEventTheme.test.ts +18 -5
  364. package/src/hooks/useEventTheme.ts +4 -1
  365. package/src/hooks/useEvents.ts +2 -0
  366. package/src/hooks/useFileDisplay.ts +9 -8
  367. package/src/hooks/useFileReference.ts +4 -1
  368. package/src/hooks/useFileUrl.ts +4 -1
  369. package/src/hooks/useInactivityTracker.ts +5 -4
  370. package/src/hooks/useOrganisationSecurity.test.ts +33 -12
  371. package/src/hooks/useOrganisationSecurity.ts +8 -7
  372. package/src/hooks/usePerformanceMonitor.ts +6 -3
  373. package/src/hooks/usePermissionCache.ts +13 -6
  374. package/src/hooks/useSecureDataAccess.test.ts +2 -2
  375. package/src/hooks/useSecureDataAccess.ts +9 -8
  376. package/src/hooks/useSessionRestoration.ts +4 -1
  377. package/src/hooks/useStorage.ts +4 -1
  378. package/src/index.ts +20 -7
  379. package/src/providers/services/AuthServiceProvider.tsx +3 -2
  380. package/src/providers/services/EventServiceProvider.tsx +2 -1
  381. package/src/providers/services/InactivityServiceProvider.tsx +2 -1
  382. package/src/providers/services/OrganisationServiceProvider.tsx +2 -1
  383. package/src/providers/services/UnifiedAuthProvider.tsx +4 -3
  384. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +22 -2
  385. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +24 -2
  386. package/src/rbac/__tests__/cache-invalidation.test.ts +20 -6
  387. package/src/rbac/api.ts +5 -2
  388. package/src/rbac/audit-enhanced.ts +6 -6
  389. package/src/rbac/audit.test.ts +60 -38
  390. package/src/rbac/audit.ts +8 -8
  391. package/src/rbac/cache-invalidation.ts +7 -4
  392. package/src/rbac/components/EnhancedNavigationMenu.tsx +11 -5
  393. package/src/rbac/components/NavigationGuard.tsx +7 -3
  394. package/src/rbac/components/NavigationProvider.tsx +6 -3
  395. package/src/rbac/components/PagePermissionGuard.tsx +28 -16
  396. package/src/rbac/components/PagePermissionProvider.tsx +4 -1
  397. package/src/rbac/components/PermissionEnforcer.tsx +9 -3
  398. package/src/rbac/components/RoleBasedRouter.tsx +3 -1
  399. package/src/rbac/components/SecureDataProvider.tsx +7 -3
  400. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +87 -61
  401. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +83 -33
  402. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +36 -13
  403. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +2 -2
  404. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +22 -8
  405. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +19 -6
  406. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +43 -17
  407. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +42 -17
  408. package/src/rbac/engine.ts +15 -7
  409. package/src/rbac/hooks/usePermissions.ts +7 -3
  410. package/src/rbac/hooks/useResolvedScope.test.ts +2 -2
  411. package/src/rbac/hooks/useResolvedScope.ts +10 -7
  412. package/src/rbac/permissions.ts +5 -2
  413. package/src/rbac/security.test.ts +27 -16
  414. package/src/rbac/security.ts +5 -4
  415. package/src/services/AuthService.ts +22 -21
  416. package/src/services/EventService.ts +12 -12
  417. package/src/services/InactivityService.ts +5 -4
  418. package/src/services/OrganisationService.ts +26 -25
  419. package/src/services/__tests__/AuthService.test.ts +51 -19
  420. package/src/services/__tests__/EventService.test.ts +37 -5
  421. package/src/services/__tests__/InactivityService.test.ts +38 -4
  422. package/src/services/__tests__/OrganisationService.test.ts +3 -8
  423. package/src/services/base/BaseService.ts +3 -1
  424. package/src/styles/core.css +3 -0
  425. package/src/theming/__tests__/runtime.test.ts +21 -12
  426. package/src/theming/parseEventColours.ts +5 -19
  427. package/src/theming/runtime.ts +8 -4
  428. package/src/types/validation.ts +2 -29
  429. package/src/utils/__tests__/appConfig.unit.test.ts +1 -1
  430. package/src/utils/__tests__/audit.unit.test.ts +1 -1
  431. package/src/utils/__tests__/auth-utils.unit.test.ts +1 -1
  432. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +19 -19
  433. package/src/utils/__tests__/cn.unit.test.ts +1 -1
  434. package/src/utils/__tests__/debugLogger.test.ts +1 -1
  435. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +1 -1
  436. package/src/utils/__tests__/dynamicUtils.unit.test.ts +1 -1
  437. package/src/utils/__tests__/formatting.unit.test.ts +1 -1
  438. package/src/utils/__tests__/lazyLoad.unit.test.tsx +1 -1
  439. package/src/utils/__tests__/logger.unit.test.ts +1 -1
  440. package/src/utils/__tests__/organisationContext.unit.test.ts +1 -1
  441. package/src/utils/__tests__/performanceBenchmark.test.ts +1 -1
  442. package/src/utils/__tests__/performanceBudgets.unit.test.ts +1 -1
  443. package/src/utils/__tests__/permissionTypes.unit.test.ts +1 -1
  444. package/src/utils/__tests__/permissionUtils.unit.test.ts +1 -1
  445. package/src/utils/__tests__/sanitization.unit.test.ts +1 -1
  446. package/src/utils/__tests__/schemaUtils.unit.test.ts +1 -1
  447. package/src/utils/__tests__/secureDataAccess.unit.test.ts +1 -1
  448. package/src/utils/__tests__/secureErrors.unit.test.ts +33 -15
  449. package/src/utils/__tests__/secureStorage.unit.test.ts +1 -1
  450. package/src/utils/__tests__/security.unit.test.ts +40 -18
  451. package/src/utils/__tests__/securityMonitor.unit.test.ts +1 -1
  452. package/src/utils/__tests__/sessionTracking.unit.test.ts +40 -29
  453. package/src/utils/__tests__/validationUtils.unit.test.ts +19 -6
  454. package/src/utils/app/appConfig.ts +47 -0
  455. package/src/utils/app/appIdResolver.test.ts +497 -0
  456. package/src/utils/app/appIdResolver.ts +133 -0
  457. package/src/utils/app/appNameResolver.simple.test.ts +212 -0
  458. package/src/utils/app/appNameResolver.test.ts +121 -0
  459. package/src/utils/app/appNameResolver.ts +195 -0
  460. package/src/utils/audit/audit.ts +127 -0
  461. package/src/utils/context/organisationContext.test.ts +322 -0
  462. package/src/utils/context/organisationContext.ts +156 -0
  463. package/src/utils/context/sessionTracking.ts +125 -0
  464. package/src/utils/core/cn.ts +7 -0
  465. package/src/utils/core/debugLogger.ts +67 -0
  466. package/src/utils/core/logger.ts +181 -0
  467. package/src/utils/device/deviceFingerprint.ts +215 -0
  468. package/src/utils/dynamic/dynamicUtils.ts +105 -0
  469. package/src/utils/dynamic/lazyLoad.tsx +44 -0
  470. package/src/utils/file-reference/__tests__/file-reference.test.ts +788 -0
  471. package/src/utils/file-reference/index.ts +501 -0
  472. package/src/utils/formatting/formatDate.test.ts +237 -0
  473. package/src/utils/formatting/formatting.ts +133 -0
  474. package/src/utils/index.ts +39 -54
  475. package/src/utils/performance/bundleAnalysis.ts +129 -0
  476. package/src/utils/performance/performanceBenchmark.ts +64 -0
  477. package/src/utils/performance/performanceBudgets.ts +110 -0
  478. package/src/utils/permissions/permissionTypes.ts +37 -0
  479. package/src/utils/permissions/permissionUtils.test.ts +393 -0
  480. package/src/utils/permissions/permissionUtils.ts +34 -0
  481. package/src/utils/security/auth-utils.ts +96 -0
  482. package/src/utils/security/secureDataAccess.test.ts +711 -0
  483. package/src/utils/security/secureDataAccess.ts +377 -0
  484. package/src/utils/security/secureErrors.ts +82 -0
  485. package/src/utils/security/secureStorage.ts +244 -0
  486. package/src/utils/security/security.ts +159 -0
  487. package/src/utils/security/securityMonitor.ts +45 -0
  488. package/src/utils/storage/__tests__/helpers.unit.test.ts +1 -4
  489. package/src/utils/storage/helpers.ts +15 -8
  490. package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +598 -0
  491. package/src/{validation → utils/validation}/csrf.ts +1 -1
  492. package/src/utils/validation/htmlSanitization.ts +184 -0
  493. package/src/utils/validation/index.ts +79 -0
  494. package/src/utils/validation/sanitization.ts +333 -0
  495. package/src/{validation/schemaUtils.ts → utils/validation/schema.ts} +11 -6
  496. package/src/{validation → utils/validation}/sqlInjectionProtection.ts +2 -0
  497. package/src/utils/validation/validation.ts +111 -0
  498. package/src/utils/validation/validationUtils.ts +123 -0
  499. package/src/validation/index.ts +3 -34
  500. package/dist/chunk-24MKLB7U.js +0 -81
  501. package/dist/chunk-24MKLB7U.js.map +0 -1
  502. package/dist/chunk-3CG5L6RN.js.map +0 -1
  503. package/dist/chunk-3DBFLLLU.js.map +0 -1
  504. package/dist/chunk-5F3NDPJV.js.map +0 -1
  505. package/dist/chunk-66C4BSAY.js.map +0 -1
  506. package/dist/chunk-BDZUMRBD.js +0 -87
  507. package/dist/chunk-BDZUMRBD.js.map +0 -1
  508. package/dist/chunk-BYXRHAIF.js.map +0 -1
  509. package/dist/chunk-CDQ3PX7L.js +0 -18
  510. package/dist/chunk-CDQ3PX7L.js.map +0 -1
  511. package/dist/chunk-CQZU6TFE.js.map +0 -1
  512. package/dist/chunk-F64FFPOZ.js.map +0 -1
  513. package/dist/chunk-GEVIB2UB.js.map +0 -1
  514. package/dist/chunk-GKHF54DI.js.map +0 -1
  515. package/dist/chunk-GVDR7WNV.js.map +0 -1
  516. package/dist/chunk-HMNOSGVA.js.map +0 -1
  517. package/dist/chunk-JCQZ6LA7.js.map +0 -1
  518. package/dist/chunk-M6DDYFUD.js.map +0 -1
  519. package/dist/chunk-O3NWNXDY.js.map +0 -1
  520. package/dist/chunk-PYUXFQJ3.js.map +0 -1
  521. package/dist/chunk-UJI6WSMD.js.map +0 -1
  522. package/dist/chunk-VZ5OR6HD.js.map +0 -1
  523. package/dist/chunk-WP5I5GLN.js.map +0 -1
  524. package/dist/chunk-ZYZCRSBD.js.map +0 -1
  525. package/dist/validation-DnhrNMju.d.ts +0 -159
  526. package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +0 -185
  527. package/src/validation/__tests__/common.unit.test.ts +0 -101
  528. package/src/validation/__tests__/csrf.unit.test.ts +0 -365
  529. package/src/validation/__tests__/passwordSchema.unit.test.ts +0 -203
  530. package/src/validation/__tests__/sanitization.unit.test.ts +0 -250
  531. package/src/validation/__tests__/schemaUtils.unit.test.ts +0 -451
  532. package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +0 -462
  533. package/src/validation/__tests__/user.unit.test.ts +0 -440
  534. package/src/validation/sanitization.ts +0 -96
  535. /package/dist/{DataTable-A36PJG6N.js.map → DataTable-6M4L6BI2.js.map} +0 -0
  536. /package/dist/{UnifiedAuthProvider-CQDZRJIS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
  537. /package/dist/{api-TNIBJWLM.js.map → api-45XYYO2A.js.map} +0 -0
  538. /package/dist/{audit-T36HM7IM.js.map → audit-64X3VJXB.js.map} +0 -0
  539. /package/dist/{chunk-CTJRBUX2.js.map → chunk-6LAAY47Q.js.map} +0 -0
  540. /package/dist/{chunk-ZV77RZMU.js.map → chunk-INQLMHPF.js.map} +0 -0
  541. /package/dist/{useInactivityTracker-MRUU55XI.js.map → useInactivityTracker-TO6ZOF35.js.map} +0 -0
  542. /package/src/{validation → utils/validation}/common.ts +0 -0
  543. /package/src/{validation → utils/validation}/passwordSchema.ts +0 -0
  544. /package/src/{validation → utils/validation}/user.ts +0 -0
@@ -0,0 +1,184 @@
1
+ /**
2
+ * @file HTML Sanitization Utilities
3
+ * @package @jmruthers/pace-core
4
+ * @module Utils/Validation/HTMLSanitization
5
+ * @since 0.4.36
6
+ *
7
+ * Utilities for safely rendering HTML content.
8
+ * Provides sanitization and validation for basic HTML elements.
9
+ */
10
+
11
+ /**
12
+ * Allowed HTML tags for safe rendering
13
+ */
14
+ const ALLOWED_TAGS = [
15
+ 'p', 'div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
16
+ 'strong', 'em', 'b', 'i', 'u', 's', 'mark', 'small', 'sub', 'sup',
17
+ 'ul', 'ol', 'li', 'dl', 'dt', 'dd',
18
+ 'blockquote', 'pre', 'code', 'kbd', 'samp',
19
+ 'a', 'br', 'hr',
20
+ 'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td',
21
+ 'img', 'figure', 'figcaption',
22
+ 'section', 'article', 'aside', 'header', 'footer', 'main', 'nav',
23
+ 'details', 'summary'
24
+ ] as const;
25
+
26
+ /**
27
+ * Allowed HTML attributes for safe rendering
28
+ */
29
+ const ALLOWED_ATTRIBUTES = [
30
+ 'id', 'class', 'style', 'title', 'lang', 'dir',
31
+ 'href', 'target', 'rel', 'download',
32
+ 'src', 'alt', 'width', 'height', 'loading',
33
+ 'colspan', 'rowspan', 'scope', 'headers',
34
+ 'open', 'datetime', 'cite'
35
+ ] as const;
36
+
37
+ /**
38
+ * Basic HTML sanitization function using regex-based approach
39
+ * Removes potentially dangerous elements and attributes while preserving basic formatting
40
+ * This approach is more reliable in SSR environments and doesn't require DOM manipulation
41
+ *
42
+ * @param html - The HTML string to sanitize
43
+ * @returns Sanitized HTML string safe for rendering
44
+ *
45
+ * @example
46
+ * ```tsx
47
+ * const safeHtml = sanitizeHtml('<p>Hello <strong>world</strong>!</p>');
48
+ * // Returns: '<p>Hello <strong>world</strong>!</p>'
49
+ *
50
+ * const dangerousHtml = sanitizeHtml('<script>alert("xss")</script><p>Safe content</p>');
51
+ * // Returns: '<p>Safe content</p>'
52
+ * ```
53
+ */
54
+ export function sanitizeHtml(html: string): string {
55
+ if (!html || typeof html !== 'string') {
56
+ return '';
57
+ }
58
+
59
+ // Basic safety: just remove script tags and dangerous attributes
60
+ let sanitized = html
61
+ // Remove script tags (including self-closing and malformed)
62
+ .replace(/<script\b[^>]*>.*?<\/script>/gi, '')
63
+ .replace(/<script\b[^>]*\/>/gi, '')
64
+ // Remove iframe tags (including self-closing and malformed)
65
+ .replace(/<iframe\b[^>]*>.*?<\/iframe>/gi, '')
66
+ .replace(/<iframe\b[^>]*\/>/gi, '')
67
+ // Remove object tags (including self-closing and malformed)
68
+ .replace(/<object\b[^>]*>.*?<\/object>/gi, '')
69
+ .replace(/<object\b[^>]*\/>/gi, '')
70
+ // Remove embed tags (including self-closing)
71
+ .replace(/<embed\b[^>]*\/?>/gi, '')
72
+ // Remove form tags (including self-closing and malformed)
73
+ .replace(/<form\b[^>]*>.*?<\/form>/gi, '')
74
+ .replace(/<form\b[^>]*\/>/gi, '')
75
+ // Remove input tags (including self-closing)
76
+ .replace(/<input\b[^>]*\/?>/gi, '')
77
+ // Remove button tags (including self-closing and malformed)
78
+ .replace(/<button\b[^>]*>.*?<\/button>/gi, '')
79
+ .replace(/<button\b[^>]*\/>/gi, '')
80
+ // Remove event handlers from any remaining tags - correct pattern
81
+ .replace(/\s*on\w+\s*=\s*["'][^"']*["']/gi, '')
82
+ // Remove javascript: protocols - correct pattern
83
+ .replace(/javascript:[^"'\s>]*/gi, '')
84
+ // Remove data: protocols - correct pattern
85
+ .replace(/data:[^"'\s>]*/gi, '');
86
+
87
+ return sanitized;
88
+ }
89
+
90
+ /**
91
+ * Validates if HTML content is safe for rendering
92
+ *
93
+ * @param html - The HTML string to validate
94
+ * @returns Object with validation result and any warnings
95
+ *
96
+ * @example
97
+ * ```tsx
98
+ * const validation = validateHtml('<p>Safe content</p>');
99
+ * console.log(validation.isValid); // true
100
+ * console.log(validation.warnings); // []
101
+ * ```
102
+ */
103
+ export function validateHtml(html: string): { isValid: boolean; warnings: string[] } {
104
+ const warnings: string[] = [];
105
+
106
+ if (!html || typeof html !== 'string') {
107
+ return { isValid: false, warnings: ['HTML content must be a non-empty string'] };
108
+ }
109
+
110
+ // Check for potentially dangerous patterns
111
+ const dangerousPatterns = [
112
+ { pattern: /<script\b[^>]*>.*?<\/script>/gi, message: 'Script tags are not allowed' },
113
+ { pattern: /<script\b[^>]*\/>/gi, message: 'Script tags are not allowed' },
114
+ { pattern: /<iframe\b[^>]*>.*?<\/iframe>/gi, message: 'Iframe tags are not allowed' },
115
+ { pattern: /<iframe\b[^>]*\/>/gi, message: 'Iframe tags are not allowed' },
116
+ { pattern: /<object\b[^>]*>.*?<\/object>/gi, message: 'Object tags are not allowed' },
117
+ { pattern: /<object\b[^>]*\/>/gi, message: 'Object tags are not allowed' },
118
+ { pattern: /<embed\b[^>]*\/?>/gi, message: 'Embed tags are not allowed' },
119
+ { pattern: /<form\b[^>]*>.*?<\/form>/gi, message: 'Form tags are not allowed' },
120
+ { pattern: /<form\b[^>]*\/>/gi, message: 'Form tags are not allowed' },
121
+ { pattern: /<input\b[^>]*\/?>/gi, message: 'Input tags are not allowed' },
122
+ { pattern: /<button\b[^>]*>.*?<\/button>/gi, message: 'Button tags are not allowed' },
123
+ { pattern: /<button\b[^>]*\/>/gi, message: 'Button tags are not allowed' },
124
+ { pattern: /on\w+\s*=/gi, message: 'Event handlers are not allowed' },
125
+ { pattern: /javascript:/gi, message: 'JavaScript protocols are not allowed' },
126
+ { pattern: /data:/gi, message: 'Data protocols are not allowed' }
127
+ ];
128
+
129
+ dangerousPatterns.forEach(({ pattern, message }) => {
130
+ if (pattern.test(html)) {
131
+ warnings.push(message);
132
+ }
133
+ });
134
+
135
+ return {
136
+ isValid: warnings.length === 0,
137
+ warnings
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Safely renders HTML content with sanitization
143
+ *
144
+ * @param html - The HTML string to render
145
+ * @param options - Rendering options
146
+ * @returns Object with sanitized HTML and validation info
147
+ *
148
+ * @example
149
+ * ```tsx
150
+ * const result = renderSafeHtml('<p>Hello <strong>world</strong>!</p>');
151
+ * console.log(result.html); // Sanitized HTML
152
+ * console.log(result.isValid); // true
153
+ * ```
154
+ */
155
+ export function renderSafeHtml(
156
+ html: string,
157
+ options: {
158
+ strict?: boolean;
159
+ logWarnings?: boolean;
160
+ } = {}
161
+ ): {
162
+ html: string;
163
+ isValid: boolean;
164
+ warnings: string[]
165
+ } {
166
+ const { strict = true, logWarnings = false } = options;
167
+
168
+ const validation = validateHtml(html);
169
+ const sanitizedHtml = sanitizeHtml(html);
170
+
171
+ if (logWarnings && validation.warnings.length > 0) {
172
+ // Use logger if needed, but this is controlled by logWarnings option
173
+ // For now, keep console.warn as it's only called when explicitly requested
174
+ // via logWarnings option, which is typically for debugging
175
+ console.warn('HTML content warnings:', validation.warnings);
176
+ }
177
+
178
+ return {
179
+ html: sanitizedHtml,
180
+ isValid: validation.isValid,
181
+ warnings: validation.warnings
182
+ };
183
+ }
184
+
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @file Validation Module Exports
3
+ * @package @jmruthers/pace-core
4
+ * @module Utils/Validation
5
+ * @since 0.1.0
6
+ *
7
+ * Consolidated validation utilities and schemas for the PACE Core library.
8
+ */
9
+
10
+ // Schema utilities
11
+ export { pickSchema, combineSchemas } from './schema';
12
+
13
+ // Common validation functions
14
+ export {
15
+ isValidEmail,
16
+ isEmpty,
17
+ isStrongPassword,
18
+ isValidUrl,
19
+ isValidDate,
20
+ isWithinRange,
21
+ matchesPattern,
22
+ deepMerge,
23
+ isObject
24
+ } from './validation';
25
+
26
+ // Enhanced validation with sanitization
27
+ export {
28
+ validateUserInput,
29
+ sanitizeUserInput_deprecated,
30
+ emailSchema as enhancedEmailSchema,
31
+ passwordSchema as enhancedPasswordSchema,
32
+ usernameSchema,
33
+ nameSchema as enhancedNameSchema,
34
+ phoneSchema as enhancedPhoneSchema,
35
+ urlSchema as enhancedUrlSchema
36
+ } from './validationUtils';
37
+
38
+ // Sanitization utilities
39
+ export {
40
+ sanitizeUserInput,
41
+ sanitizeFormData,
42
+ sanitizeHtml,
43
+ type SanitizationOptions
44
+ } from './sanitization';
45
+
46
+ // HTML sanitization utilities (allows safe HTML tags)
47
+ export {
48
+ sanitizeHtml as sanitizeHtmlAdvanced,
49
+ validateHtml,
50
+ renderSafeHtml
51
+ } from './htmlSanitization';
52
+
53
+ // Common validation schemas
54
+ export {
55
+ emailSchema,
56
+ nameSchema,
57
+ phoneSchema,
58
+ urlSchema,
59
+ dateSchema
60
+ } from './common';
61
+
62
+ // Security validation
63
+ export * from './csrf';
64
+ export * from './sqlInjectionProtection';
65
+ export * from './passwordSchema';
66
+ export * from './user';
67
+
68
+ // Re-export schemas from types for convenience (these are the canonical schemas)
69
+ export {
70
+ loginSchema,
71
+ registrationSchema,
72
+ secureLoginSchema,
73
+ passwordResetSchema,
74
+ changePasswordSchema,
75
+ userProfileSchema,
76
+ contactFormSchema,
77
+ securePasswordSchema
78
+ } from '../../types/validation';
79
+
@@ -0,0 +1,333 @@
1
+ /**
2
+ * @file Input Sanitization Layer
3
+ * @package @jmruthers/pace-core
4
+ * @module Utils/Validation/Sanitization
5
+ * @since 0.1.0
6
+ *
7
+ * Comprehensive input sanitization utilities to prevent XSS, injection attacks,
8
+ * and other security vulnerabilities.
9
+ */
10
+
11
+ import { z } from 'zod';
12
+
13
+ /**
14
+ * Sanitization options for different contexts
15
+ */
16
+ export interface SanitizationOptions {
17
+ allowHtml?: boolean;
18
+ allowedTags?: string[];
19
+ maxLength?: number;
20
+ trim?: boolean;
21
+ removeScripts?: boolean;
22
+ removeEvents?: boolean;
23
+ }
24
+
25
+ /**
26
+ * Default sanitization options
27
+ */
28
+ const DEFAULT_OPTIONS: SanitizationOptions = {
29
+ allowHtml: false,
30
+ allowedTags: [],
31
+ maxLength: 1000,
32
+ trim: true,
33
+ removeScripts: true,
34
+ removeEvents: true
35
+ };
36
+
37
+ /**
38
+ * Sanitizes user input by removing potentially dangerous characters and patterns
39
+ */
40
+ export function sanitizeUserInput(input: string, options: SanitizationOptions = {}): string {
41
+ if (typeof input !== 'string') {
42
+ return '';
43
+ }
44
+
45
+ const opts = { ...DEFAULT_OPTIONS, ...options };
46
+ let sanitized = input;
47
+
48
+ // Trim whitespace if requested
49
+ if (opts.trim) {
50
+ sanitized = sanitized.trim();
51
+ }
52
+
53
+ // Enforce maximum length
54
+ if (opts.maxLength && sanitized.length > opts.maxLength) {
55
+ sanitized = sanitized.substring(0, opts.maxLength);
56
+ }
57
+
58
+ // Remove or escape HTML if not allowed
59
+ if (!opts.allowHtml) {
60
+ sanitized = sanitized
61
+ .replace(/</g, '&lt;')
62
+ .replace(/>/g, '&gt;')
63
+ .replace(/"/g, '&quot;')
64
+ .replace(/'/g, '&#x27;')
65
+ .replace(/\//g, '&#x2F;');
66
+ } else if (opts.allowedTags && opts.allowedTags.length > 0) {
67
+ // If HTML is allowed, only permit specific tags
68
+ const allowedTagsRegex = new RegExp(`<(?!\/?(?:${opts.allowedTags.join('|')})\s*\/?>)[^>]+>`, 'gi');
69
+ sanitized = sanitized.replace(allowedTagsRegex, '');
70
+ }
71
+
72
+ // Remove script tags and javascript: protocols
73
+ if (opts.removeScripts) {
74
+ sanitized = sanitized
75
+ .replace(/<script[^>]*>.*?<\/script>/gi, '')
76
+ .replace(/javascript:/gi, '')
77
+ .replace(/vbscript:/gi, '')
78
+ .replace(/data:/gi, '');
79
+ }
80
+
81
+ // Remove event handlers
82
+ if (opts.removeEvents) {
83
+ sanitized = sanitized.replace(/on\w+\s*=/gi, '');
84
+ }
85
+
86
+ return sanitized;
87
+ }
88
+
89
+ /**
90
+ * Sanitizes email addresses
91
+ */
92
+ export function sanitizeEmail(email: string): string {
93
+ if (typeof email !== 'string') {
94
+ return '';
95
+ }
96
+
97
+ return email
98
+ .trim()
99
+ .toLowerCase()
100
+ .replace(/[^\w@.-]/g, ''); // Only allow word characters, @, ., and -
101
+ }
102
+
103
+ /**
104
+ * Sanitizes phone numbers
105
+ */
106
+ export function sanitizePhoneNumber(phone: string): string {
107
+ if (typeof phone !== 'string') {
108
+ return '';
109
+ }
110
+
111
+ return phone.replace(/[^\d+\-\s()]/g, '').trim();
112
+ }
113
+
114
+ /**
115
+ * Sanitizes URLs
116
+ */
117
+ export function sanitizeUrl(url: string): string {
118
+ if (typeof url !== 'string') {
119
+ return '';
120
+ }
121
+
122
+ const sanitized = url.trim();
123
+
124
+ // Only allow http(s) and ftp protocols
125
+ if (!/^https?:\/\/|^ftp:\/\//i.test(sanitized)) {
126
+ return '';
127
+ }
128
+
129
+ // Remove javascript: and other dangerous protocols
130
+ if (/javascript:|data:|vbscript:/i.test(sanitized)) {
131
+ return '';
132
+ }
133
+
134
+ return sanitized;
135
+ }
136
+
137
+ /**
138
+ * Sanitizes file names
139
+ */
140
+ export function sanitizeFileName(fileName: string): string {
141
+ if (typeof fileName !== 'string') {
142
+ return '';
143
+ }
144
+
145
+ return fileName
146
+ .trim()
147
+ .replace(/[<>:"/\\|?*]/g, '') // Remove invalid file name characters
148
+ .replace(/\.\./g, '') // Remove directory traversal attempts
149
+ .substring(0, 255); // Limit length
150
+ }
151
+
152
+ /**
153
+ * Sanitizes SQL input to prevent injection
154
+ */
155
+ export function sanitizeSqlInput(input: string): string {
156
+ if (typeof input !== 'string') {
157
+ return '';
158
+ }
159
+
160
+ return input
161
+ .replace(/['";\\]/g, '') // Remove SQL special characters
162
+ .replace(/--.*$/gm, '') // Remove SQL comments
163
+ .replace(/\/\*.*?\*\//g, '') // Remove SQL block comments
164
+ .trim();
165
+ }
166
+
167
+ /**
168
+ * Validates and sanitizes form data using Zod schemas
169
+ */
170
+ export function sanitizeFormData<T>(
171
+ data: unknown,
172
+ schema: z.ZodSchema<T>,
173
+ sanitizationRules?: Record<string, SanitizationOptions>
174
+ ): { success: boolean; data?: T; error?: string } {
175
+ try {
176
+ // First, sanitize string fields if rules are provided
177
+ if (sanitizationRules && typeof data === 'object' && data !== null) {
178
+ const sanitizedData = { ...data } as Record<string, unknown>;
179
+
180
+ Object.entries(sanitizationRules).forEach(([field, options]) => {
181
+ if (typeof sanitizedData[field] === 'string') {
182
+ sanitizedData[field] = sanitizeUserInput(sanitizedData[field] as string, options);
183
+ }
184
+ });
185
+
186
+ data = sanitizedData;
187
+ }
188
+
189
+ // Then validate with Zod schema
190
+ const result = schema.parse(data);
191
+ return { success: true, data: result };
192
+ } catch (error) {
193
+ if (error instanceof z.ZodError) {
194
+ return {
195
+ success: false,
196
+ error: error.errors.map(e => e.message).join(', ')
197
+ };
198
+ }
199
+ return {
200
+ success: false,
201
+ error: 'Validation failed'
202
+ };
203
+ }
204
+ }
205
+
206
+ // Re-export HTML sanitization from the consolidated module
207
+ // The new implementation allows safe HTML tags while removing dangerous ones
208
+ export { sanitizeHtml } from './htmlSanitization';
209
+
210
+ /**
211
+ * Content Security Policy (CSP) utilities
212
+ */
213
+ export const CSP_DIRECTIVES = {
214
+ default: "default-src 'self'",
215
+ script: "script-src 'self' 'unsafe-inline'",
216
+ style: "style-src 'self' 'unsafe-inline'",
217
+ img: "img-src 'self' data: https:",
218
+ font: "font-src 'self'",
219
+ connect: "connect-src 'self'",
220
+ frame: "frame-src 'none'"
221
+ };
222
+
223
+ export function generateCSPHeader(customDirectives?: Partial<typeof CSP_DIRECTIVES>): string {
224
+ const directives = { ...CSP_DIRECTIVES, ...customDirectives };
225
+ return Object.values(directives).join('; ');
226
+ }
227
+
228
+ /**
229
+ * Rate limiting utilities
230
+ */
231
+ export class RateLimiter {
232
+ private attempts: Map<string, { count: number; resetTime: number }> = new Map();
233
+
234
+ constructor(
235
+ private maxAttempts: number = 5,
236
+ private windowMs: number = 15 * 60 * 1000 // 15 minutes
237
+ ) {}
238
+
239
+ isAllowed(identifier: string): boolean {
240
+ const now = Date.now();
241
+ const record = this.attempts.get(identifier);
242
+
243
+ if (!record || now > record.resetTime) {
244
+ this.attempts.set(identifier, { count: 1, resetTime: now + this.windowMs });
245
+ return true;
246
+ }
247
+
248
+ if (record.count >= this.maxAttempts) {
249
+ return false;
250
+ }
251
+
252
+ record.count++;
253
+ return true;
254
+ }
255
+
256
+ getRemainingAttempts(identifier: string): number {
257
+ const record = this.attempts.get(identifier);
258
+ if (!record || Date.now() > record.resetTime) {
259
+ return this.maxAttempts;
260
+ }
261
+ return Math.max(0, this.maxAttempts - record.count);
262
+ }
263
+
264
+ reset(identifier: string): void {
265
+ this.attempts.delete(identifier);
266
+ }
267
+ }
268
+
269
+ // Validation schemas (kept from previous version)
270
+ /**
271
+ * Enhanced email schema with security checks
272
+ */
273
+ export const secureEmailSchema = z
274
+ .string()
275
+ .min(1, 'Email is required')
276
+ .email('Invalid email format')
277
+ .max(254, 'Email too long')
278
+ .refine(
279
+ (email) => {
280
+ if (!email || typeof email !== 'string') return false;
281
+ // Basic domain validation
282
+ const domain = email.split('@')[1];
283
+ return domain && domain.includes('.') && domain.length > 3;
284
+ },
285
+ 'Invalid email domain'
286
+ )
287
+ .transform((email) => sanitizeEmail(email));
288
+
289
+ /**
290
+ * Basic email schema for common use
291
+ */
292
+ export const emailSchema = z
293
+ .string()
294
+ .min(1, 'Email is required')
295
+ .email('Invalid email format');
296
+
297
+ /**
298
+ * Name validation schema
299
+ */
300
+ export const nameSchema = z
301
+ .string()
302
+ .min(1, 'Name is required')
303
+ .max(100, 'Name too long')
304
+ .regex(/^[a-zA-Z\s'-]+$/, 'Name contains invalid characters');
305
+
306
+ /**
307
+ * Phone validation schema
308
+ */
309
+ export const phoneSchema = z
310
+ .string()
311
+ .regex(/^[\+]?[1-9][\d]{0,15}$/, 'Invalid phone number format');
312
+
313
+ /**
314
+ * URL validation schema
315
+ */
316
+ export const urlSchema = z
317
+ .string()
318
+ .url('Invalid URL format');
319
+
320
+ /**
321
+ * Date validation schema
322
+ */
323
+ export const dateSchema = z
324
+ .string()
325
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'Invalid date format (YYYY-MM-DD)');
326
+
327
+ /**
328
+ * Secure login schema
329
+ */
330
+ export const secureLoginSchema = z.object({
331
+ email: secureEmailSchema,
332
+ password: z.string().min(1, 'Password is required'),
333
+ });
@@ -1,6 +1,10 @@
1
-
2
1
  /**
3
2
  * @file Schema utility functions
3
+ * @package @jmruthers/pace-core
4
+ * @module Utils/Validation/Schema
5
+ * @since 0.1.0
6
+ *
7
+ * Utility functions for working with Zod schemas.
4
8
  */
5
9
 
6
10
  import { z } from 'zod';
@@ -15,15 +19,15 @@ import { z } from 'zod';
15
19
  export function pickSchema<T extends z.ZodObject<any, any, any>, K extends keyof z.infer<T>>(
16
20
  schema: T,
17
21
  keys: K[]
18
- ): z.ZodObject<any> {
22
+ ): z.ZodObject<Pick<z.infer<T>, K>> {
19
23
  const shape = Object.entries(schema.shape)
20
24
  .filter(([key]) => keys.includes(key as K))
21
25
  .reduce((acc, [key, value]) => {
22
- acc[key] = value as z.ZodTypeAny;
26
+ (acc as Record<string, unknown>)[key] = value as unknown;
23
27
  return acc;
24
- }, {} as Record<string, z.ZodTypeAny>);
28
+ }, {} as Record<string, unknown>);
25
29
 
26
- return z.object(shape);
30
+ return z.object(shape as Record<string, z.ZodTypeAny>) as z.ZodObject<Pick<z.infer<T>, K>>;
27
31
  }
28
32
 
29
33
  /**
@@ -34,9 +38,10 @@ export function pickSchema<T extends z.ZodObject<any, any, any>, K extends keyof
34
38
  */
35
39
  export function combineSchemas<T extends z.ZodObject<any, any, any>[]>(
36
40
  schemas: T
37
- ): z.ZodObject<any> {
41
+ ): z.ZodObject<any, any, any> {
38
42
  return schemas.reduce(
39
43
  (merged, schema) => merged.merge(schema),
40
44
  z.object({})
41
45
  );
42
46
  }
47
+
@@ -110,6 +110,8 @@ export function sanitizeFilters(filters: Record<string, unknown>): Record<string
110
110
  const keyValidation = sqlIdentifierSchema.safeParse(key);
111
111
  if (!keyValidation.success) {
112
112
  // Log warning for invalid filter keys
113
+ // Note: Using console.warn here is intentional for security events
114
+ // This should eventually use the security audit system
113
115
  console.warn(`[SECURITY] Invalid filter key detected and removed: ${key}`);
114
116
  continue;
115
117
  }